HZERO微服務平台10: 代碼分析之admin服務刷新路由、權限、swagger的過程 .md


admin服務接收到業務服務注冊信息, 負責完成路由刷新、權限刷新、swagger信息刷新的過程;

整體流程

業務服務調用admin的注冊接口:

/service-init-registry/register
ServiceInitRegistryEndpoint#register
RestServiceInitRegistry#register
assertHealth(service); //只判斷服務的狀態, 如果up就把服務添加到啟動成功的列表里
serviceInitRegistryRepository.add(service);

其中的ServiceInitRegistryRepository接口:
維護需要初始化(未刷新路由、權限、swagger)的服務信息; 具體實現:RedisServiceInitRegistryRepository, 存儲在redis db1 hadm.services-to-init;

其中的RestServiceInitRegistry類:

public class RestServiceInitRegistry implements ServiceInitRegistry, SmartLifecycle { ...
RestServiceInitRegistry#start  //SmartLifecycle.start, 應用啟動成功后自動執行; 
RestServiceInitRegistry#doStart 
this.initExecutor = new ThreadPoolExecutor( ... //服務刷新執行器, 執行刷新任務; 
this.checkExecutor = new ThreadPoolExecutor( ...  //檢查執行器, while循環檢查, 負責清理已下線的過期服務、如果檢查到未刷新服務喚醒服務刷新線程(initExecutor)

initExecutor的流程;

RestServiceInitRegistry#doStart
this.initExecutor.execute(() -> {
init(getUnInitializedServices())  //刷新未初始化的服務
ServiceInitRegistry#init
RestServiceInitRegistry#doInit
RestServiceInitRegistry#executeInit //初始化鏈, 調用刷新鏈
buildInitChain().doChain(context);
//依次執行InitFilter的實例
RouteInitFilter
PermissionInitFilter
SwaggerInitFilter

任務最終執行是責任鏈/過濾鏈模式, 相關代碼: ...admin.infra.chain包、InitFilter、InitChain、DefaultInitChain、InitChainFactoryBean

我的理解:

  • InitFilter是實際干活的
  • InitChain是個容器, 用來裝InitFilter, 並且依次執行所有filter;
  • InitChainFactoryBean是個工廠, 用來創造InitChain的bean

權限刷新

自動刷新權限

服務注冊后自動刷新權限流程:

admin服務:

PermissionInitFilter#doFilter
PermissionRefreshService#innerRefresh
v1/tool/permission/inner/fresh
ACTUATOR_PERMISSION("/v2/actuator/permission", HttpMethod.GET, false, "獲取服務權限信息"); //注意端口不是管理端口, 是服務端口; 

iam服務:

ToolPermissionController#innerRefresh
IDocumentServiceImpl#refreshPermissionAsync
IDocumentServiceImpl#refreshPermission
ParseServicePermissionImpl#parser
List<Permission> permissions = this.permissionHandler.handle(serviceName, instance);
AbstractPermissionHandler#handle
ActuatorPermissionHandler#doHandle
fetchPermissionDataByIp

手動刷新權限

開發管理 - 系統工具 - 刷新權限: /iam/v1/tool/permission/fresh

iam服務:

ToolPermissionController#refresh
IDocumentServiceImpl#refreshPermission
ParseServicePermissionImpl#parser
AbstractPermissionHandler#handle
ParseServicePermissionImpl#processPermissions

ParseService#parser的javadoc:

解析權限步驟:

  • 判斷是否要跳過解析服務權限,默認跳過 register, gateway, oauth
  • 調用服務接口 /v2/choerodon/api-docs 獲取服務 swagger json 文檔
  • 從 json 中解析權限
  • 如果權限編碼重復,加上 HttpMethod 后綴
  • 保存權限,編碼存在則更新,不存在則新增
  • 如果要清除過期權限,則清除過期權限
  • 緩存權限到Redis,默認存儲到 db4>gateway:permissions

實測: 手動刷新權限不會更新HADM_SWAGGER表里的數據; 程序每次啟動的時候會更新HADM_SWAGGER並且刷新權限;

權限刷新報錯: parse_permission_data.failure

症狀: 【系統工具】-【刷新權限】報錯:

hiam.error.parse_permission_data.failure

原因: iam刷新權限時獲取的結果是亂碼, 導致解析失敗;

亂碼原因: jhipster的prod模式開啟了http響應壓縮, 返回的數據被壓縮, response header里包含: content-encoding: gzip;
restTemplate沒有兼容壓縮的情況;
關閉壓縮: server.compression.enabled: false
Using JHipster in production

路由刷新

路由配置

ChoerodonRouteData choerodonRouteData = new ChoerodonRouteData();
choerodonRouteData.setName(environment.getProperty("hzero.service.current.name", "demo"));
choerodonRouteData.setPath(environment.getProperty("hzero.service.current.path", "/demo/**"));
choerodonRouteData.setServiceId(environment.getProperty("hzero.service.current.service-name", "demo"));
  • choerodonRouteData.name: 路由名稱, gatewayRoute的標識,對應gatewayRoute的id字段
  • choerodonRouteData.path: 路由的路徑;
  • choerodonRouteData.serviceId: 通過實測、查看代碼, 業務服務的這個配置無效; 雖然無效, 但是也不能不設置, 否則程序啟動報錯, bean實例化失敗;
admin刷新路由的時候, 會把路由配置里的serviceId設置為serviceName: 
ParseRouteServiceImpl#executeRefreshRoute
data.setServiceId(serviceName);

如果開啟了context-path, 路由配置需注意

//如果開啟了context-path, 這里的路徑必須和context-path保持一致
choerodonRouteData.setPath(environment.getProperty("hzero.service.current.path", "/demo-qxx/**"));
//這里設置為false
choerodonRouteData.setStripPrefix(false);

業務服務路由的name或path重復

新服務注冊的路由如果和已有服務重復, 新服務會注冊不上路由, 業務服務的日志里沒有任何提示; admin服務里會info:

ParseRouteServiceImpl#executeRefreshRoute
LOGGER.info("route conflict, try to modify it on the interface, cause: {}", cause.toString());

路由被占用, 服務路由注冊失敗, swagger里會看不到服務, 普通開發者很難排查問題;
最終采用的解決辦法: 服務啟動時路由注冊成功才能啟動(hzero原版檢查健康狀態為up就能啟動)

手動刷新路由報錯: http transport failed, ExceptionResponse: For input string: "80,80"

dev環境正常, prod環境訪問服務時報錯error.permission.routeNotFound, 路由管理能看到路由, 手動刷新路由報錯:

refresh service route error, serviceName=hw-platform, ex=fetch failed, instance: hw-platform, ex=http transport failed, ExceptionResponse: For input string: "80,80"

admin報錯代碼位置:

StringHttpTransporter#transport
ExceptionResponse response = objectMapper.readValue(body, ExceptionResponse.class);
if (response != null && response.getFailed()) {
    throw new RestClientException("http transport failed, ExceptionResponse: " + response.getMessage());
}

服務報錯:

java.lang.NumberFormatException: For input string: "80,80"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[na:1.8.0_272]
    at java.lang.Integer.parseInt(Integer.java:580) ~[na:1.8.0_272]
    at java.lang.Integer.parseInt(Integer.java:615) ~[na:1.8.0_272]
    at ....swagger.controller.CustomController.componentsFrom(CustomController.java:145) ~[starter-core-2.0.0.RELEASE.jar!/:2.0.0.RELEASE]

代碼位置:

...swagger.controller.CustomController#componentsFrom
String port = request.getHeader("X-Forwarded-Port");

if (hasText(port)) {
    builder.port(Integer.parseInt(port));
}

報錯原因: X-Forwarded-Port=80,80;
修復方法: 加try..catch, 重置為-1;


免責聲明!

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



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