[Java復習]架構部署 超時重試 冪等防重


畫一下你們系統的整體架構圖,說說各個服務在生產環境怎么部署的?

核心:服務框架、注冊中心、網關

即使你沒有用很多微服務架構里的東西,只要有上述三個東西,配合上寫一些文檔,接口文檔,分布式系統架構,其實就出來了,很多中小型公司,一個小型技術團隊,后端開發工程師總共就10多個人。

常見生產實踐問題:

關注分布式系統一些生產實踐的問題,應對你們公司的流量,各個服務、網關、注冊中心,都是如何部署的?

部署幾台機器,每台機器的配置是什么,每天整體的流量有多少,高峰期的請求量有多少,你的整體系統是否抗住了?

各個服務之間的超時、重試、冪等性?

 

中小型系統,拆分10-20個微服務。大型互聯網公司,一般幾百個,幾千個微服務。

中小型,一般2-3台機器足夠,把服務上線,服務發現優化到極致。

服務上線:注冊表多級緩存同步至1秒,拉取頻率降低至1秒。

服務心跳:1秒報1次。

故障發現:1秒檢查一次,2,3秒沒有認為沒有故障等。

服務注冊中心沒有壓力,服務注冊中心部署2台機器,每台4C8G,高可用,每秒輕松幾百請求,甚至上千。前提是數據庫SQL別寫的太爛。

網關機器配置稍微高一些,4C8G,一台扛每秒幾百個請求,部署3~4台,保證每台網關機器壓力較小,進一步保證可靠性。

 

 

為什么網關Zuul之前還要配置Nginx

Nginx反向代理,軟負載,把請求均勻負載到多台Zuul的機器上。LVS和Nginx處於不同的負載維度,主要是運維工程師負責管理。

 數據庫,MYSQL,16C32G,物理機最佳,平時扛每秒幾百請求,高峰期最多每秒扛三四千請求。

扛幾千請求時機器會負載很高,CPU,IO,網絡負載很高。DBA在優化一下。

 

你們系統每天有多大訪問量?每個服務高峰QPS多少?壓測過服務最大QPS嗎?

每天服務多少請求量,高峰每秒qps,在代碼里稍微加一些metrics代碼,對自己運行過程中各種請求量,每秒請求量,成功次數,失敗次數,在內存里直接做一些計數。

在負責的核心服務里,核心接口,開發一個簡單的metric統計機制,AtomicLong,保證原則性,並發數據統計准確。

每個接口被調用時,可以對每個接口每分鍾做一個metric統計,每個接口每天統計計數。

再通過Log4j, logback等日志組件,把次數直接打印到日志文件,統計出高峰期每秒系統被訪問的次數,每條每個接口訪問量。

響應延時

計算一下每個接口從請求到執行完畢,需要耗費多長時間,算一下每個接口平均的請求延時,

TP99,TP95,TP90,TP50,TP99,99%的請求耗費的時間在100ms以內,但是1%的請求可能耗費的時間在100ms以上

TP99 = 100ms TP95 = 50ms,95%的請求耗費的時間多在50ms以內,但是5%的請求耗費的時間在50ms以上

 

壓測工具,java壓測工具,開源的可以用的,模擬出來同時有多少用戶發起多少請求,每秒發起1000請求能抗住嗎?每秒鍾發起2000請求能抗住嗎?

假設你的系統每秒鍾最多抗800請求,如果你的壓測工具每秒發起了1000個請求,此時會發現最多只有800個請求同時可以被處理,

剩余200個請求需要進行排隊被阻塞住了,表示你這個系統每秒鍾最多抗800個請求。

 

如果系統訪問量比現在增加10倍,你們考慮過系統的擴容方案嗎?

網關直接多部署10倍的機器即可,前面的Nginx做會負載均衡,把流量均勻分發給各個網關機器。

服務擴容,都很簡單的,多加機器,部署啟動,自動注冊到注冊中心去,此時其他服務會自動感知到你的服務多加了一些機器。

服務實例變多了10倍,此時幾十個服務實例,幾百個服務實例,對eureka機器會造成每秒幾百請求,沒問題,eureka機器,8核16G的配置,單機抗上千請求,很輕松。

數據庫本來是每秒幾百請求,10倍,每秒高峰期是三四千請求,橫向擴容很麻煩,

此時可以考慮給單個數據庫部署的機器提高配置,32核128G高配物理機,每秒鍾抗幾千請求問題不大。

總結: 最基本的操作就是擴容

網關:橫向加機器

注冊中心:縱向升配置

數據庫:縱向升配置

 當然還有很多其他專門針對分布式,高並發的優化和操作,不過加機器都是最簡單直接的。

 

你們生產環境的服務是怎么配置超時和重試參數的?為什么要這樣配置?

背景:Spring Cloud生產優化,系統第一次啟動的時候,調用請求經常出現timeout。

原因:每個服務第一次被請求的時候,他會去初始化一個Ribbon的組件,初始化這些組件需要耗費一定的時間,所以很容易會導致超時。

解決方案:讓每個服務啟動的時候就直接初始化Ribbon相關的組件,避免第一次請求的時候初始化。

ribbon:
  eager-load:
    enabled: true

zuul:
  ribbon:
    eager-load:
      enabled: true

feign:
  hystrix:
     enabled: false

線上的服務,每個服務部署上線的時候,一般來說都需要配置相關的超時時間還有重試次數

訂單服務 -> 積分服務、庫存服務、倉促服務

訂單服務對於其他服務的調用,一般來說限制在多長時間就必須認為是超時了,如果超時之后如何進行重試

積分服務部署了兩台機器,機器1和機器2

訂單服務在一次請求積分服務的機器1的時候,超過1秒鍾,超時了;此時需要進行重試,對積分服務當前的這台機器1重試幾次?如果說機器1不行,是否可以重試一下積分服務的機器2?

ribbon:
  ConnectTimeout: 3000
  ReadTimeout: 3000
  OkToRetryOnAllOperations: true
  MaxAutoRetries: 1
  MaxAutoRetriesNextServer: 1

中小型的系統,沒必要直接開啟hystrix,資源隔離、熔斷、降級,如果你沒有設計好一整套系統高可用的方案。

增加重試機制依賴:

<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>

 

 

如果出現服務請求重試,會不會出現類似重復下單的問題?

可能會。

訂單服務 -> 創建訂單 -> 庫存服務 -> 扣減庫存 -> wms服務 -> 通知發貨 -> 積分服務 -> 增加積分

 場景:

訂單服務調用庫存服務的時候,因為網絡抖動,請求超時了,超過了秒鍾,此時訂單服務會重試,再次調用一下庫存服務,發送一模一樣的請求過去。

比如說,訂單服務第一次請求庫存服務,庫存服務其實是把扣減庫存的業務邏輯執行成功了,

只不過網絡問題,導致響應遲遲沒有返回給訂單服務,可能在1.2s之后返回了響應給訂單服務。

訂單服務就認為請求超時了,他就再次發送了一個一模一樣的請求給庫存服務,庫存服務可能會再次對庫存進行扣減。

 

對於核心接口的防重冪等性,你們是怎么設計的?怎么防止重復下單問題?

常見方案:

1. 數據庫唯一索引

2. 基於Redis實現冪等性防重

 

核心接口,冪等性都是自己保證,對應Create操作,通過DB唯一索引來保證;對於Update操作,建議在核心接口基於業務邏輯,配合Redis,來保證冪等性。

比如庫存,定制化的針對接口開發冪等性的機制,比如說一旦庫存扣減成功之后,就立馬要寫一條數據到redis里去,order_id_11356_stock_deduct,寫入redis中,如果寫入成功,就說明之前這個訂單的庫存扣減,沒人執行過。

但是如果此時有一些重試的請求過來了,調用了你的庫存扣減接口,他同時也進行了庫存的扣減,但是他用同樣的一個key,order_id_11356_stock_deduct,寫入redis中,此時會發現已經有人寫過key,key已經存在了。

此時你就應該直接對剛才的庫存扣減邏輯做一個反向的回滾邏輯,

update product_stock set stock = stock - 100,update product_stock set stock = stock + 100,反向邏輯,回滾自己,避免重復扣減庫存。

 

參考資料:

21天互聯網Java進階面試訓練營(分布式篇)-- 中華石杉


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM