說起來,也是一段比較有挑戰有壓力的經歷。做完之后,有一種雲淡風輕的感覺,故記之。
緣起###
周二下午,忽報:QA 環境下單之后,訂單搜索不出來了。
略排查,發現訂單記錄並未同步到 ES 索引里。進一步發現,訂單同步工程 S 雖然進程還在,但已經不再處理消息了。昨天因為一個項目的需求才測試過 QA 環境訂單同步無問題,上午也沒動靜,怎么下午就突然報問題了呢?
很快聯想到,前兩日,框架層發了通告:不再為使用了 3.2.x 以下 dubbo 版本的應用提供自動注冊 dubbo 服務的能力。很可能是 S dubbo 版本過低,無法注冊和訪問 dubbo 服務,無法進行訂單同步,進而影響訂單搜索和詳情,嚴重阻塞了項目的測試。訂單是核心嘛~~
於是我將 pom 里的 dubbo 版本改成了最新版本。可還是不行。要找框架同學一起排查下了。
舊世界###
ClassVisitor####
框架同學東順說,早上貌似改過 zan 版本,也許是這個導致的。於是,我請求降回到原來的版本,先解決問題再說。sadly,即使降回到原來的版本進行部署, S 的服務依然起不來。退路已斷。
框架同學子傑的第一個想法,是在本地啟動調試。因為這樣方便且高效。不過 S 應用已經很久沒有在本地啟動。而且 S 依賴比較多,在本地恐怕很難啟動。
報 ClassVisitor should be a interface , not a class 。此類錯誤通常是 jar 沖突導致。因此,我在工程里搜索了下 ClassVisitor ,果然發現有兩個,一個是接口,一個是類。 看來要排掉那個有類 ClassVisitor 的 jar 包。
<dependency>
<groupId>com.youzan.platform</groupId>
<artifactId>bootstrap</artifactId>
<version>3.2.7.3-RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</exclusion>
</exclusions>
</dependency>
排掉之后,就不再報這個錯了。 然而, S 依然不處理消息。
一邊是測試同學在催促,一邊是毫無頭緒。心里有些急,可一時也想不到如何解決。子傑期望本地調試,可本地啟動總是報奇怪的 groovy 不兼容錯誤。調了一晚上,終於把這個問題解決了。 但 S 依然不處理消息。
補丁####
周三早上,繼續戰斗。子傑發了一段配置,讓放在 S 工程的 XML 配置里。說這段配置是用來注冊 dubbo 服務的。 加入之后,報錯: cause: Could not initialize class com.coreos.jetcd.api.KVGrpc, dubbo version: 1.0.1-SNAPSHOT, current host: x.x.x.x
。
子傑看了,開心地說,這個錯誤之前見過,可能 protobuf 版本沖突了。 離成功不遠了。
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.2.6</version>
<exclusions>
<exclusion>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</exclusion>
</exclusions>
</dependency>
排除 protobuf-java 包之后,終於可以起來了。 驗證了下,訂單同步 OK 了。 松了一口氣。
間歇性跪###
才沒輕松多久, 又跪了。又跪了。又跪了。又跪了。 測試同學又來催了。
溝通####
在這種情形下,發生一點小“糾紛”總是可能的。有一次,在吃飯的時候,S 跪了。 測試同學群里喊:進度如何了,找到根本原因了么? 心里有點惱火:作為開發,我肯定要盡職盡責盡早排查和解決問題;可是你也不能只是催,在這樣尷尬的時刻,可以支援一下嘛。 不過,大家都很通情達理,我提供了“臨時啟動服務”的方法,測試同學就幫忙先啟動服務了。
反思一下,測試同學的做法也是合情合理的,因為他們要負責保障更大域范圍的環境穩定,需要知道進度和原因。解決棘手問題時,同步進度和原因也是很重要的,心里應該裝下更大的世界。此外,若我能及早同步“臨時解決方案”,讓測試同學知道怎么解決,也不會有這樣的“糾紛”。有話好好說,總能找到更柔和的方式來解決。
於是我在群里回復:他們的擔心和考慮是合理的,且已經快接近成功了。稍安。其實我也不知道距離成功還有多遠。
蛛絲馬跡####
排查問題,只能從錯誤日志里尋找一切蛛絲馬跡。子傑找了一段 JVMExitHook 的代碼,加在啟動的時候,這樣方便在進程退出的時候,打印一些線索。 不過,這招沒起效果,打印出的信息似乎沒有派上用場。
同時,我也在仔細觀察日志里出現的各種細微錯誤。不能放過一個嫌疑份子。確認了有四種可能的影響因素:1. 應用 M 跪; 2. Hbase 讀寫失敗; 3. 臟數據; 4. 底層 jar 不兼容,導致某種隱藏的問題。
M跪
又跪了。發現 S 有個任務訪問 M 服務報錯,登錄機器,發現 M 服務跪了。重啟 M 。 過了一段時間,S 又跪了,M 服務也跪了。會不會受了這個服務的影響呢 ? 將 M 服務所涉及的任務暫時禁用了。將 M 禁用后,S 似乎就沒有跪過了。
M 報錯的原因,是因為零售同學將一個接口遷移到了新的工程,也就是說,原來的接口下線了。 S 去注冊這個接口的服務的時候找不到。
HBase
發現有一些 HBase 寫入失敗。咨詢 HBase 同學,是因為 QA 集群運行了一些離線任務,有時資源比較緊張。 雖然有疑點,但不太可能導致 S 跪。
臟數據
在排查過程中,也發現有些臟數據,導致 NSQ 消息處理失敗。臟數據可能把一個任務搞跪,不過 S 里有很多任務,要把 S 跪掉,還需要一些“道行” 吧。
隱藏的坑
只是猜測,但確實是比較大的嫌疑。 因為前三者都是局部的影響,不太可能將 S 整個搞跪掉,但這個可能是全局的影響。比如說,某個 jar 版本的組件,與另一個組件交互,可能產生 bug , 吃內存,導致內存 OOM 。只是猜測。
暫時維穩####
子傑有了新的發現。從 dmsg 日志里看到,S 獨占內存過多,Linux 內核將 S 的 java 進程 kill 掉了。調整了堆內存從 6.5G 到 4G,然后運行了一整天都沒有跪。
java invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
java cpuset=/ mems_allowed=0
Pid: 4288, comm: java Not tainted 2.6.32-754.9.1.el6.x86_64 #1
[ 4286] 602 4286 3321904 1898888 2 0 0 java
Out of memory: Kill process 4286 (java) score 929 or sacrifice child
Killed process 4286, UID 602, (java) total-vm:13287616kB, anon-rss:7595528kB, file-rss:24kB
新工程###
QA 環境終於可以暫時安靜一會了。 可是,生產環境如何解決呢?
S 承載着公司億級訂單的同步,顯然不能容忍用打補丁的方式來解決(何況這個補丁很惡心,會埋很大的坑),而且也無法接入 aladdin ,自動升級 jar 版本。 因此, QA 的做法,只能暫時維穩,不能用作生產環境的解決方案。
咨詢子傑 ,是否有辦法可以以最小成本來改造 S 應用 ? 回答:使用 youzan-boot 標准化工程。可是要改造成 youzan-boot 工程,勢必改動很大,整個工程結構都變了,部署方式也變了,上線風險是很大的。對於 S 這樣的應用,稍有不慎,就是 P1 或 P0 故障。
進退兩難。
因為自己不太熟悉應用打包和部署的方式,有點畏難。但想了想,本質上就是把應用里的任務想辦法啟動起來。絕大部分代碼都是可以復用的,只需要在新的工程結構中,將原來的啟動代碼嵌入進去。想到這里,有了一些信心,決定采用新工程的方式來解決這個問題。
new出了null####
根據子傑提供的文檔及界面,很快建立了新的標准化腳手架工程。接着,將原工程的代碼拷貝到新的工程里,並在啟動的入口,將原來啟動任務的類的方法添加進去。
我是一個不解決主要問題寢食難安的人。深夜,繼續。部署后,初始化任務出錯。只好加日志,看看哪里拋異常了。不加不要緊,一加he一跳。
````Task task = new Task(); logger.info("task:{}", task) ,``` 竟然打出了個 null !
new 出了個 null ? 百思不得其解。想着,恐怕又沒進展了, 打算睡覺了。正准備睡覺的時候,突然靈光一閃,去查看了下 toString 方法,是打印 task 實例中的 taskConfig 的,而此時 taskConfig 作為入參還沒有設置到 task 中,因此打印為 null 。真是腦經急轉彎啊。 遇到的每個問題,都是一道面試題。):
看來,在程序的世界里,一切奇怪的事情,總有一個合理的緣由。 聯想到前不久的 “奇怪之事總有緣由:訂單狀態對比不一致問題排查” ,有所領會。
環境變量引用改造####
繼續排查為什么 task 沒有正確初始化。對比原工程,發現 task 是一個聲明為 prototype 的類,所引用的組件,也是 prototype 的。新老工程沒有差異。
從日志里可以看到,環境變量讀取到應用里,依然是占位符,並沒有正確地被替換。 原工程為了靈活,使用了 yaml 文件來配置環境變量,並在代碼里做了遍歷,這樣,如果需要增加新的環境變量,只需要在 yaml 增加一項即可。
<bean id="mysqlDataSource" class="com.youzan.trade.sync.v2.EnvConfigLoader" init-method="init">
<constructor-arg value="config/config.yaml"/>
</bean>
@Slf4j
public class EnvConfigLoader {
private final String dir;
public EnvConfigLoader(String dir) {
this.dir = dir;
}
public void init() {
try {
String content =
StreamUtils.copyToString((InputStream) this.getClass().getClassLoader().getResource(dir).getContent(), Charset.forName("utf-8"));
YamlReader reader = new YamlReader(content);
Object object = reader.read();
JSONObject config = JSONObject.parseObject(JSONObject.toJSONString(object));
for (String key : config.keySet()) {
Constants.urlMap.put(key, config.getString(key));
}
} catch (Exception e) {
throw new RuntimeException("load config failed:" +dir, e);
}
log.info("load config success ,config is :{}", JsonUtils.toJson(Constants.urlMap));
}
}
config/config.yaml 文件里的配置, 比如:
zk: ${hbase.zookeeper.address}
改造成新工程之后,由於 yaml 文件里的引用變量比如 ${hbase.zookeeper.address} 已經無法讀取,因此,需要改造成新的方式讀取環境變量。
考慮到 S 的任務所使用到的環境依賴基本已經齊全,很小概率會再添加一項。因此,打算改造成不那么靈活但足夠清晰的方式。直接將 zk 作為 EnvConfigLoader 的實例變量注入,省去了讀 yaml 文件的步驟。
@Slf4j
public class EnvConfigLoader {
@Setter
private String zk;
public void init() {
Constants.urlMap.put("zk", zk);
log.info("load config success , config is :{}", JsonUtils.toJson(Constants.urlMap));
}
}
折騰到凌晨一點,終於能夠看到,任務在正確消費消息了。階段性的重大進展! 奇怪的是,消息消費的任務日志沒有打出來 。先睡覺吧。
日志####
第二天一大早,就去找東順排查日志為什么沒找到。東順在一個 home_IS_UNDEFINED 的目錄下找到了消息消費的日志文件。 日志路徑的配置有問題。 按照他的指點,導入了標准的 XML 日志配置,在期望的目錄打印了任務處理日志。略開懷。
可錯誤日志沒打出來。這也是要命的事情。在發布線上的過程中,若沒有錯誤日志的提醒,那是冒着槍林彈雨的沖鋒,是容易中彈身亡的。
想起東順之前提到:可以把原來的日志配置文件直接拷貝到新工程的配置文件里。錯誤日志終於打出來了! 開心 !~~ 又前進了一步,more closed to success。
打印日志到本地磁盤之后,還要上報到公司內部的日志平台上,方便查看多台服務器的日志。照葫蘆畫瓢,折騰了下,搞定了。喜。
簡化####
至此,新工程的整體和主要部分沒問題了。還需要做一些簡化,比如去掉一些無用的模塊和文件,優化 pom 里的 jar 引用等,保持工程的簡潔與干凈,沒有贅余。
需求支持####
在新工程發布之前,有兩個項目已經快接近尾聲,需要在 trade-sync 做一個改動支持。一個改動很小,一個改動略大。如果等 trade-sync 改造上線之后再上線,恐怕這兩個項目都要被延誤。怎么辦呢 ?
決定: 先在老的系統上改,隨老的系統先發布; 然后再遷移到新的工程里,這些改動所涉及的任務重新再回歸一遍。有個擔心,如果把這些改動復制到新的工程里,恐怕容易有遺漏。所幸,使用 git merge 命令,可以很方便地將改動從老的上面挪到新的工程上。
發布准備###
接下來就要考慮發布了。
CR####
工程改動太多了,有點失控的感覺。 在發布之前,找幾位有經驗的同學幫忙一起 check 下是明智的選擇,也是必經的流程。一位是一直幫我排查 QA 同步問題的子傑同學, 一位是見識比較廣能力不錯的王立同學,因為他作為后端同學解決了react 容器部署的問題,讓 S 的新界面又重新問世啦!靠譜,厲害!還有一位是我的有伴水王,也是很踏實的。
業務回歸####
CR 的同時,也要驗證這個工程是否能保證原有的任務都能正常進行。
前面談到, 訂單同步使得搜索和詳情的任務都是 OK 的,也就是最重要的任務沒問題;
有些任務是與第三方交互的,在 QA 是無法驗證的。 好在:這個工程里絕大部分都是訪問 DB, ES, HBase 的組件,絕大多數的業務邏輯都在配置的任務里。可以說是“將基礎技術設施與業務邏輯相分離而實現配置化“的典范做法。因此,從基本層面看,只要驗證部分任務, 覆蓋到所有的組件類別, 就可以看作是驗證了所有任務。
既可以把這種做法看成一種巧妙的方式,也可以看作是某種取巧和偷懶。這種方式還是會有漏網之魚。
虛驚一場####
使用新工程部署之后,一直在觀察中,沒有跪過。 有點安心了。 在發布前晚,突然發現: 跪掉了。 天 ! 難道還有什么細坑 ?又得繼續觀察和排查細坑了 ! 恐怕要延期發布了。
幸好,當天早上, 有伴告訴我,他重啟過一次,運維同學也重啟過一次,但是沒啟動成功。 我立即想到,可能他們是用老的 zan 版本啟動的, 因為不兼容導致失敗。我用 zan 的新版本啟動是 OK 的。
很快確認了這一點, 虛驚一場。
廟算幾何####
孫子曰:“夫未戰而廟算勝者,得算多也,未戰而廟算不勝者,得算少也;多算勝,少算不勝,而況於無算呼?”
距離計划發布時間只有幾個小時了。 這個工程的發布勝算在 92% 以上, 有 8% 的不確定性。勝算如下:
- 最主要的訂單同步任務是沒有問題的;
- 十幾個的任務及子路徑,我驗證了大部分是可以正確輸出的;
- 組件代碼無改動,只是工程結構變了;
- 在 QA 和 預發環境運行穩定。
不確定性如下:
- 有一個任務是外部交互的,無法驗證; 四個任務是邊緣業務,不太好構造測試用例; 但這些任務所使用的組件,都在其他被驗證過的任務中使用過了,因此,理論上也應該是間接被驗證了;其實是有點懶,結果是受了懲罰。
- 引入了 youzan-boot 的包,還有以前工程里的 jar 包,可能存在一些細微的不兼容的坑,未被發現。
不管怎樣, 勝算還是很大的。在發布的時候,再細心一些, 應該不會出什么問題。
決戰時刻###
預發的啟示####
在預發部署的時候,發現新的進程並沒有正常起來,必須先手動 kill 掉原來的進程,再部署新的工程,才能讓新進程正常啟動和運行服務。 這一點尤為重要, 否則很容易出問題。
如果能對預發出現的問題敏銳地感知,有時預發的問題真的是神明的指示。 前不久做過的一個項目中,需要做一些交易配置的變更。在預發驗證拼團訂單的時候,訂單狀態沒有正常流轉,經排查日志發現:預發的請求竟然發到線上去了! 聯系營銷同學得知,訂單的后續通知,取決於接收最后一個參團訂單的請求的機器所處的環境。比如說吧,拼團發起者的下單請求在 T1 機器, 拼團參團者的下單請求在 T2 機器上。 機器 T1 切換到了新的配置, T2 還沒有。 此時, T2 將向營銷發送消息,營銷處理消息完成后,向交易機器 T3 發送回送消息。如果 T3 還沒有切新配置,就可能導致走不下去。 發現這個問題后, 仔細思考了下,發現在 下午正常發布窗口發布是有風險的。因為如果正好在那個時間段有很多拼團活動,勢必會導致很多拼團訂單的狀態流轉有問題,不僅會對商家的交易有影響,還要應對后續復雜麻煩的數據修復。 想想,還是辛苦一點,凌晨發布好了, 此時拼團活動極少, 且有更多時間來驗證項目改動和回歸業務。
經過溝通,決定在凌晨發布。安全上線。
發布文檔####
由於要趕在新進程起來之前手動 kill 老進程,這個時機得把握好, 為了避免誤操作,臨陣手忙腳亂,因此,必要寫個發布文檔, 寫清楚每一個步驟要執行的命令, 在真正發布的時候,只要簡單的復制粘貼就可以了。
本來想把這些命令寫成一個腳本,可是 kill 老進程的執行要切換用戶,一時間沒有太多時間調試這個,且要執行的批量命令也不多, 因此,還是直接復制粘貼了。好在發布的時候,機器的編號是逐漸遞增的,可以預測每批次發哪幾台機器, 這就容易多了:只要在每批次發布之前,先 kill 掉將要發布的機器的老進程即可。
會影響線上服務么? 由於還有其他服務器在處理消息,且凌晨的消息量極少,因此 kill 幾台機器的進程不會對線上服務有影響。
單測####
寫好發布文檔之后,已經 十一點半了。 該提發布申請了。
在提發布申請之前,得先運行並單測全部通過。到單測平台上運行了下,發現報錯: groovy 與 spock-core 的版本不兼容。
郁悶了。在本地運行是 OK 的,為啥在這個節骨眼報錯了。 看了下工程的配置,還是比較老的版本。想想可以升級到較新的版本,對任務運行應該沒有影響。於是從核心工程拷貝了更新的版本號,並修復了一個單測文件。
為了簡單,根據報錯只把 groovy 的版本升到了 2.0.1 , —— 給自己買了個坑。在程序的世界里,因果命中注定。
小插曲####
突然想到, 除了 S ,還有一個附屬應用 s, 代碼與 S 相同 ,但只運行對比任務。嗯, 這個應用也要發布, 也在預發驗證下。
我在 QA 也部署了應用 s ,可啟動日志顯示:訪問交易配置表報錯了:用戶名被拒絕。這是怎么回事呢? 在發布之前,有一些配置變更我提交了。我想到應該是運維同學更新過,不會有問題。 QA 沒法驗證, 只好上預發驗證了。
於是,我在預發也部署了 s 應用。啟動日志沒報錯,可也沒消費日志。想了想,也許之前就沒有吧。 應該是 OK 的,就做發布前的准備去了。
突然被拉到一個群里,是預發交易配置庫訪問出錯排查。由於我集中精力在准備發布,因此對這個並未投以太多關注。但群里好像說是比較嚴重的事情,堅持要查明白,似乎說我故意配錯了用戶名和密碼,以致於訪問到錯誤的地方,險些造成安全事故,還連累了很多同學耗費時間來查問題。可我只是提交了被別人改動的配置(並且我相信是運維同學改動且應該是正確的),並沒做出格的事情。難道,我一不留神就犯下了錯誤 ?
事情原委是:DBA 同學楊大俠監控到交易配置庫有大量訪問錯誤,而且還來自於不同的機房,認為這是很大的不合理。初步排查到,訪問出錯來自於預發的 s 應用。這有什么關聯呢? 我一時也有點懵逼,仿佛處於雲山霧繞。
&####
我提交了發布申請,開始發布 s 應用。未料,線上也報錯了。這可奇怪了,我對比了下 S 與 s 的 交易配置表的用戶名和密碼,完全一樣的啊 ! 在 跳板機上用 mysql 連接了下,也是通的; S 在預發部署也沒報錯,這可奇怪了。
楊大俠提醒: DB 連接的 jdbcURL中有 &
這樣的字符。 我突然想到了, 在 QA 遇到過這個問題,因為高版本的 mysql 不支持這個老的語法,需要將這個變成 & 本身。
哪里還藏有 &
這個字符呢 ? 重新再check 了下 s 應用的配置。 發現,交易配置表的配置有兩處,其中一處是完全一樣的,但還有一處。 重復出現的 DB 配置,真坑啊!
修改之后,就沒有報錯了。DBA 監控也沒有報錯了。 排查這個錯誤耗費了將近三個小時。發布完 s 應用后,已經三點了。
Bean沒有找到####
S 應用的發布似乎順利一些。發了幾台,沒有報 DB 訪問錯誤,或者其他奇怪的錯誤。
四點半了, 剛發了幾台機器,發現有錯誤: RefundBizNsqInput 的 bean 找不到,任務啟動報錯。這個任務是消費退款消息,寫入退款數據,供訂單導出來計算退款狀態及金額的。如果這個數據有誤,可能會影響訂單狀態的展示,誤導商家發貨。
內心有點崩潰。 這可怎么辦呢? 繼續發布完成 ? 這樣會導致這個任務大量報錯, 退款消息無法消費, 引起上述問題, 造成故障; 如果暫停發布,等待下午發布,一則現在新老混布有較大風險,二則下午發布風險更大; 重新發布,已經快到 5 點的發布截止窗口了,稍有延遲,就要走緊急發布了。
思前想后,還是趁這會清凈,趕緊發完吧。於是,我緊急看了下代碼,發現 RefundBizNsqInput 不在組件自動 scan 的包路徑下。 於是將這個任務涉及的組件類都移到了可以被自動掃描組件的路徑下,修復了下單測。此刻,如果要再驗證 OK 再發布的話,恐怕會耗時太長。 於是,我做了個大膽的決定,不驗證就直接發布。因為只是通過 IDE 的重構功能挪了下包路徑,理論上是不會有問題的。 其實心里還是有點虛的。**有時,直覺很重要,雖然它來自於大量經驗的積累;當機立斷也很重要。 **
想定,取消了之前的發布單,聯系運維同學,幫忙重新開了個緊急發布的綠燈, 重新開始發布。幸運的是,這次沒有再報錯誤日志。於是,我按照寫好的發布文檔,一個人在這靜靜的天亮時分,按部就班地開始發布, 一直發了兩個小時,到七點半多才發完,一如 08 年踏雪歸來的感覺。
反思: 如果在 QA 有驗證那個退款消息的任務, 就不會出現這個尷尬的局面了。因為這個任務的消息處理,需要加載一個自定義的 消息接收器,而這個沒有被覆蓋到。 這是一條漏網之魚。
尾聲###
電子發票同步####
正要鳴金收兵,忽現新軍情:電子發票索引同步的一隊小騎兵,趁我困倦之時,偷襲過來,想來個以逸待勞。任務里的一段 groovy 腳本無法執行。有幾條消息就出錯幾條。得趕緊解決,不然線上又要報問題了。
排查這個錯誤,有點尷尬。 錯誤日志只打印了: failed to execute 。。。 啥信息也沒有。 我總不可能添加一行日志, 再發布一次吧。近乎崩潰。怎么辦怎么辦 ? 掃了一眼腳本,有個奇怪的格式化日期的變量似乎沒定義。想用另一種先替換看看。沒效果。試來試去,沒效果。
此刻,意識有點模糊,有點扛不住了。可是又不能就此睡去。 怎么辦 ? 思來想去,只好打擾一下團隊同學了。 叫醒了三位懂些 groovy 的同學,幫忙一起排查下。
孔鵬同學提醒說,在本地運行下這個腳本。 我馬上想到了,可以把消息和腳本放到本地工程里運行下,看看哪里報錯。那個被懷疑的變量也找到了定義的地方。在本地運行是通過的。 這可讓人有點摸不着頭腦了。 天已經亮了,八點多了。
有同學建議說,回滾吧。可是,回滾的代價太大了。 花了一整晚,終於將老的工程遷移到新的工程, 難道要因為一個小的問題,再滾回到原來的 ? 且新的工程回滾到老的,也是有比較大的風險的,發布也要經過一系列比較繁瑣的操作,容易出錯。 我寧可背一個 P4 故障,也要把這個問題解決掉。已經沒有退路了。
突然靈光一閃,可以在預發起相同的任務,然后加調試日志,在預發看看是什么緣故 。 燃起了新的希望。
用最后的精力折騰了一下, 發現報 unable to solve groovy.json.JsonSlurper 。 我以為是沒有 import ,因此加了一行 import 。可是還是報錯。 孔鵬同學忽然在群里說, JsonSlurper 這個類沒有找到啊,且最好把 groovy-all 也引用進去吧。關鍵的一擊啊 ! 我馬上去工程里看了下,groovy 2.0.8 才支持 JsonSlurper ,而我之前為了單測通過,只用了 groovy 2.0.1 。 細心的讀者應該注意到,前面跑單測的時候,我把 groovy 的版本改成了 2.0.1 。 因果的命中注定 ,是說,**在你寫下這行代碼或配置的時候,命運已經注定了,只待前來認領。要慎重啊! **
立即修改,部署到預發, 解決了。 耶 !!
線上消費出錯怎么辦呢 ? 先用預發的任務消費線上的消息吧,確保后續的數據都寫入 OK , 待明天做個優化后,再切回到線上的任務消費。原來預發可以作為線上的 “備胎” 啊!
反思: 不用說,這個任務也沒回歸到,因為很邊緣。 然而,這個任務引用了一個類,依賴更高版本的 groovy ,是個局部細節問題,沒有覆蓋到。 看來, 凡有疏忽,必受懲罰。 老天爺的懲罰還是比較輕的: 照這樣算下來,勝算大抵只有 80% 左右。
終於可以碎覺了。 emm.... 別打擾我。
監控統計####
下午來到公司, 王爺 跟我說, S 的監控統計不對勁, 曲線值很低,跟業務量完全不匹配。我想,可能是日志平台又出問題了 ? 之前間歇性也會這樣。但王爺堅持要查一下。於是,我開完一個會之后,就找日志平台的同學蘇蘇一起去查了。
蘇蘇顯然更相信自己抓包看到的數據,說,這個業務量可能就是這樣。但我深知,業務量肯定不是這樣,更可能是改造工程的過程中,某個配置有問題,但暫時不知道是哪里有問題。
不過蘇蘇查底層問題還是很有章法的,脾氣也不錯,說起批評人的話來也是很溫和。在經過我認可后,在機器上安裝工具,抓包,看監控圖表,發現 在 S 發布點之后,寫監控統計就一直報錯了。查來查去,發現 S 引用了一個自研輕量級 R 的包,來寫入監控數據, R 則會讀取 S 的一個配置文件,而這個文件的 NSQ 配置是錯誤的。至此,真相大白。
想到之前還沒仔細看過監控統計的代碼,領悟: 每一個不清楚的想要暫時跳過的地方, 老天爺都會恩賜一個問題, 逼着你最終弄清楚。
小結###
像是走過了一段較長的路。 面對大流量下的大改動的發布,曾經感覺到比較大的壓力。發布完成之后,感覺似乎沒那么可怕了。 一切都過去了。
流量似山海,心內亦平川。 平常心就好。