项目复盘思考
對自己所參與過的事物做個復盤是個好習慣,能讓自己對事物有個更深的了解。比如說,讀了一本書,那么這個時候你需要回去思考:這本書講了些啥,對你有何影響;又或者,你參與了一個活動,你完了后,你也可以對其進行復盤,思考下這個活動的意義,這個活動是讓你增加見識了還是讓你放松心情了等;再或者,你參與了一個項目,這個時候還是可以復盤,考慮項目的進展中的難點等。其實復盤,就是對自己經歷過的事物進行思考總結。是一個很好的自我矯正升華的方式。
下面我以一個程序員參與的項目的復盤來簡要說明下,復盤過程中可能思考的問題和方向。
拋出問題
問題解答
這里以之前我做過的一個出單項目為例子簡單說一下可能的問題思考總結。
為什么要做這個項目
面對日益增加的互聯網合作伙伴對接平安的出單系統,原有的系統已經無法完全滿足與日俱增出單壓力。對此,針對保險單較為單一的投保,分離業務,做成一個互聯網出單系統。快速響應合作伙伴的出單需求.
項目是怎么選型的
SpringCloud: 成熟的為服務體系,JAVA生態,成員已有springboot基礎. 公司內部已有基于springcloud的成功案例.
MongoDB: 簡單保單類型適合嵌套文檔形式存儲. 分布式(支持分片,可擴展), 高可用(可配置副本)
RockMq: 在保證了可靠性的前提下,隊列特性足夠豐富,可運維程度比較高。其快速橫向擴展的能力,也能保證未來幾年我們對其性能的要求。RocketMQ基于Java語言開發
RabbitMQ由于采用同步刷盤,其可靠性非常強(采用ACK機制),但是性能卻是這三款開源產品中最差的,在實際的測試中也能很輕易的得到驗證。所以RabbitMQ更適合使用在對消息可靠性要求很高,但對消息吞吐量不太敏感的場景中。這里額外提一下一個個人經驗,我們在對開源產品選型時,建議大家需要了解該產品是基于什么協議/規范實現的,這樣大致就清楚了這款開源產品可以解決什么問題,適用于哪些場景。例如消息中間件領域的AMQP、JMS、STOMP協議,工作流領域的bpmn2.0標準。
Kafka是由LinkedIn開發的一個分布式的消息系統,使用Scala編寫,它以可水平擴展和高吞吐率而被廣泛使用。目前越來越多的開源分布式處理系統如Cloudera、Apache Storm、Spark都支持與Kafka集成。Kafka的高吞吐是其最重要的一個特點,這也帶來了一個潛在隱患在異常情況下,其消息丟失率較高。所以它比較適用于將高吞吐作為首要考慮,能容忍少量消息丟失的場景。例如作為大數據平臺數據傳輸的流式管道,其在日志采集場景中也被廣泛應用。
RocketMQ的核心架構設計參考了Kafka,有網友甚至提出它是Kafka Java版本的實現。不可否認的是RocketMQ的設計實現的確參考了很多Kafka的設計思想,比如說均采用了基于文件的存儲方案、Topic的并行化操作、底層網絡協議均基于TCP (RocketMQ使用netty成熟的解決方案,Kafka使用一套自行設計的基于TCP層的協議),但兩者之間相對特點還是比較明顯,RocketMQ在犧牲了部分性能的前提下,提供了更多的機制來保證消息的可靠性,同時也提供了更為豐富和實用的消息隊列特性。比如說消息的查詢、消息的回溯。這些接口的提供,極大的便利了消息中間件的運維,提高了在一些異常場景中的故障恢復速度。
最終選擇了RocketMQ,理由是RocketMQ在保證了可靠性的前提下,隊列特性足夠豐富,可運維程度比較高。其快速橫向擴展的能力,也能保證未來幾年我們對其性能的要求。另外RocketMQ基于Java語言開發,也降低了我們后續對其進行擴展和二次開發的難度。
項目的規模(數據量、并發量、增量等)
1wQPS, 每日單量60w、目前已有6000w單量.
還有比如說項目部署的規模等情況(eg. 服務器多少臺,緩存服務,消息中間件等是如何部署的)
項目中遇到了什么難點?是如何解決這些問題的
壓測監控查看各方面的系統資源并未達到瓶頸,但是并發量達不到預期
問題解決和排查思路: 老一套方案,首先定位性能瓶頸: CPU/內存/IO等
ps aux | grep appname 查看進程ID
top -H pid查看cpu和內存的使用情況(未發現應用服務器的cpu和內存有較高的使用率)
cpu繁忙一般可能的情況如下:
- 線程中的不合理循環
- 發生了頻繁的full gc
- 多線程的上下文頻繁切換
通過jmap/jstack命令查看內存/線程信息(可能是線程出現等待導致,所以也需要查看線程信息)
通過Arthas工具查看接口執行過程中每個方法的耗時情況
$trace xxx #cost
方法內部調用路徑,并輸出方法路徑上的每個節點上耗時, trace 命令能主動搜索 class-pattern/method-pattern 對應的方法調用路徑,渲染和統計整個調用鏈路上的所有性能開銷和追蹤調用鏈路。trace 能方便的幫助你定位和發現因 RT 高而導致的性能問題缺陷,但其每次只能跟蹤一級方法的調用鏈路
結果如下(包名經過處理):
+---[0.001162ms] com.xxx.service.dto.ApplyResultDTO:setApplyPolicyNo()+---[0.511972ms] com.xxx.service.apply.save.ApplySaveService:fillApplyPolicyNo()+---[19.709352ms] com.xxx.service.apply.save.ApplySaveService:saveContractDTOInfo()+---[1.313593ms] com.xxx.service.apply.save.ApplySaveService:saveApplyParameterInfo()+---[min=4.35E-4ms,max=9.48E-4ms,total=0.002326ms,count=3] java.lang.StringBuilder:<init>()+---[min=5.1E-4ms,max=0.00146ms,total=0.004705ms,count=6] java.lang.StringBuilder:append()+---[0.001361ms] com.xxx.base.dto.BaseInfoDTO:getTransactionNo()+---[min=4.86E-4ms,max=6.61E-4ms,total=0.001777ms,count=3] java.lang.StringBuilder:toString()+---[523.544075ms] com.xxx.common.service.redis.RedisService:stringSet()+---[9.91E-4ms] com.xxx.common.util.StringUtils:isNotBlank()+---[min=6.07E-4ms,max=7.75E-4ms,total=0.001382ms,count=2] java.lang.String:equals()+---[min=5.03E-4ms,max=6.95E-4ms,total=0.001198ms,count=2] java.lang.Integer:intValue()+---[min=592.763104ms,max=868.853641ms,total=1461.616745ms,count=2] com.xxx.common.service.redis.RedisService:ObjectSet()通過trace 定位到是redis耗時后,定位redis問題
解決方案
項目還有什么不足?有什么解決方案?
spring-cloud-zuul-ratelimit、 針對不同合作伙伴分別限流
調整jvm參數,如增加老年代比例。減少gc耗時停頓時間
目前使用緩存的時候,策略是先刪除緩存后更新數據庫的,在并發情況下,可能會導致緩存和數據庫的數據不一致,該問題需要解決。目前考慮的解決方案有如下幾種:
- 在寫數據庫操作的時候。利用Redis加鎖,使得數據更新完成之后才能讀緩存
- 寫數據庫后,啟動一個延遲定時任務,定時任務負責讀取數據庫的最新數據更新Redis中的緩存
- Redis讀寫請求入隊列,根據請求類型決定是否需要阻塞等待,以保證數據的一致性
總結
上面只是給了一個例子,還有更多的思考點等著自己去發掘。再者每個人的經歷也不一樣,思考方向也許也會有更多的不同,但是最重要的是一個保持思考總結的習慣,該習慣并不是一定要等事情完結后才去考慮,在過程中我們也需要不斷的思考總結。
人會在思考總結中進步,如果只是走馬觀花一般,那就真的像網上傳的一樣(十年工作經驗,其實就是一年的工作經驗重復可十年)。希望大家不是活成了段子,而是在不斷的進步,看到越來越好的自己。
總結
- 上一篇: linux pam模块 cron,Lin
- 下一篇: 字符串大小写字母转换c 语言,towlo