博客 / 詳情

返回

【架構師角度】詳細解析 OpenStack4j 中 PropagateOnStatus 的異常處理機制

異常處理機制架構分析

Openstack4j 提供了一套可插拔的 HttpExecutor 框架,支持 Apache HttpClient、Resteasy等,根據分析默認使用 Apache Httpclient。

目前尚不清楚為什麼要提供這個HTTP框架統一層,看起來有些多餘。

 

但是在吸收這個庫的時候發現,它有着目前為止我個人見過的最優秀的HTTP狀態碼錯誤處理機制。

這個機制可以精確到每個API的級別,在默認的異常狀態碼處理機制之上,可以自由指定是否向上傳播,正如 Java 的 Exception 機制一樣。

相比 OpenFeign,一般處理 Http 錯誤碼時,有一個默認的 feign ErrorDecoder,本身非常方便,但是當需要精確控制異常的時候,還需要自定義 ErrorDecoder。

以下是對這個異常處理機制的梳理,如果用到了就會十分強大。

 

是怎麼發現的?

在某些API的請求處理過程中,思考過一個問題:有時候API應該向上層拋出,有些API 應該忽略錯誤。

比如説DELETE的時候報錯 404,顯然這個異常可以忽略。

在魔改的過程中,發現了這一優秀設計,並發現部分API使用了這個異常傳播機制。

  1. 核心組件關係

  Invocation.execute(ExecutionOptions<R>)
      ↓
  HttpResponse.getEntity(returnType, options)
      ↓
  ExecutionOptions.propagate(HttpResponse response)
      ↓
  PropagateOnStatus.propagate(HttpResponse response)
      ↓
  ResponseException.mapException(response, ar.getFault())
      ↓
  拋出具體異常類型

  2. BaseOpenStackService.Invocation.execute() 流程

  在 BaseOpenStackService.java:232-241 中:

  public R execute(ExecutionOptions<R> options) {
      header(HEADER_USER_AGENT, USER_AGENT);
      HttpRequest<R> request = req.build();
      HttpResponse res = HttpExecutor.create().execute(request);
      reqIdContainer.set(getRequestId(res));
      requestThreadLocal.set(request);
      return res.getEntity(request.getReturnType(), options);  // 關鍵:傳入options
  }

  3. PropagateOnStatus 的工作機制

  設計原理

  PropagateOnStatus 實現了 狀態碼驅動的異常傳播 機制:

  - 作用:當 HTTP 響應狀態碼匹配指定值時,自動拋出異常
  - 優勢:避免在每個調用處手動檢查狀態碼和處理異常

  核心實現

  public class PropagateOnStatus implements PropagateResponse {
      private final int statusCode;

      public static PropagateOnStatus on(int statusCode) {
          return new PropagateOnStatus(statusCode);
      }

      @Override
      public void propagate(HttpResponse response) {
          if (response.getStatus() == statusCode) {  // 狀態碼匹配
              ActionResponse ar = ResponseToActionResponse.INSTANCE.apply(response);
              throw ResponseException.mapException(response, ar.getFault());  // 拋出異常
          }
      }
  }

  4. 使用模式和場景

  常見狀態碼使用

  - 404 (Not Found): 資源不存在時拋出異常
  - 500 (Internal Server Error): 服務器內部錯誤時拋出異常

  實際應用示例

  // 示例1: FloatingIP 創建 - 網絡404時拋出異常
  return post(NeutronFloatingIP.class, uri("/floatingips")).entity(floatingIp)
      .execute(ExecutionOptions.create(PropagateOnStatus.on(404)));

  // 示例2: Sahara Image 操作 - 鏡像不存在時拋出異常  
  return post(SaharaImage.class, uri("/images/%s", imageId))
      .execute(ExecutionOptions.<SaharaImage>create(PropagateOnStatus.on(404)));

  // 示例3: Tacker VNF 創建 - 服務器500錯誤時拋出異常
  return post(TackerVnf.class, uri("/vnfs")).entity(vnf)
      .execute(ExecutionOptions.<TackerVnf>create(PropagateOnStatus.on(500)));

  5. 異常映射機制

  ResponseException.mapException() 根據狀態碼自動映射為具體異常類型:

  public static ResponseException mapException(String message, int status, Throwable cause) {
      if (status == 401)
          return new AuthenticationException(message, status, cause);  // 認證異常
      if (status >= 400 && status < 499)
          return new ClientResponseException(message, status, cause);   // 客户端異常
      if (status >= 500 && status < 600)
          return new ServerResponseException(message, status, cause);   // 服務端異常
      return new ResponseException(message, status, cause);             // 通用異常
  }

  6. 日誌追蹤和調試支持

  異常對象會自動包含:
  - X-Openstack-Request-Id: OpenStack 請求追蹤ID
  - 請求信息: HTTP 方法和URL
  - 狀態碼: HTTP 響應狀態碼

  public static ResponseException mapException(HttpResponse response, String message) {
      ResponseException re = mapException(message, response.getStatus(), null);
      re.setRequestInfo(BaseOpenStackService.getRequest());    // 設置請求信息
      re.setRequestId(response.header("X-Openstack-Request-Id"));  // 設置追蹤ID
      return re;
  }

  7. 優勢和最佳實踐

  優勢

  1. 代碼簡潔: 避免重複的 if-else 狀態碼檢查
  2. 統一異常處理: 集中的異常映射和創建邏輯
  3. 調試友好: 自動包含請求追蹤信息
  4. 類型安全: 泛型支持不同返回類型

  使用建議

  1. 明確業務含義: 為需要特殊處理的錯誤碼使用 PropagateOnStatus
  2. 合理異常處理: 在調用處捕獲預期的異常類型
  3. 日誌記錄: 利用內置的追蹤ID進行問題排查

  這種設計模式實現了聲明式異常處理,讓開發者能夠專注於業務邏輯,而將錯誤處理邏輯抽象為配置化的傳播策略。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.