• 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;

  • 相关阅读:
    INV*更新物料信息
    WPF设置样式的几种方式
    使用InternetGetConnectedState判断本地网络状态(C#举例)
    WinInet API详解
    WPF导航总结
    WPF中的命令与命令绑定导航
    WPF依赖属性相关博客导航
    关于WPF自定义控件(导航)
    WPF送走控件的focus方法
    MvvmLight学习篇—— Mvvm Light Toolkit for wpf/silverlight系列(导航)
  • 原文地址:https://www.cnblogs.com/QIAOXINGXING001/p/15631302.html
Copyright © 2020-2023  润新知