后端架构.md 12 KB

后端架构

服务器架构

  • 测试服务器(172.16.0.223)
    • 内网地址:172.16.0.223
    • 公网地址:139.159.229.250
    • ssh端口:22
    • 安装的服务:
    • 测试应用
    • 测试数据库
    • redis
    • rocketmq
  • 生产服务器
    • 生产应用服务器(172.16.0.246)
    • 内网地址:172.16.0.246
    • 公网地址:139.159.210.228
    • ssh端口:7711
    • 安装的服务
      • 生产应用
      • redis
      • rocketmq
      • nginx(为前端应用提供web服务)
    • 生产数据库服务器(172.16.0.159)
    • 内网地址:172.16.0.159
    • 公网地址:无
    • 安装的服务:
      • 生产数据库
  • 共通服务器
    • 堡垒机(以后只有堡垒机才有公网访问权限,提供跳板机服务已经nginx转发服务)
    • 内网地址:172.16.0.99
    • 公网地址:139.9.56.49
    • ssh端口:22
    • 安装的服务:
      • Jumpserver(提供跳板机服务)
      • nginx(域名转发)
    • DevOps服务器
    • 内网地址:172.16.0.61
    • 公网地址:139.159.196.162
    • ssh端口:22
    • 安装的服务
      • 在线文档服务
      • gogs(提供git服务)
      • YApi(提供接口文档服务)
      • Jenkins(提供发版服务)
      • Nexus(提供 maven 私服服务)
      • 代码生成器

网络架构

应用架构

  • 公共包
    • wisdom-common(核心包+各个微服务rpc共用的pojo)
    • wisdom-ds(动态数据源)
    • wisdom-generator(代码生成器)
    • wisdom-mq(mq接入封装)
    • wisdom-parent(maven parent 统一管理第三方包版本)
  • 监控

    • wisdom-monitor(微服务健康状况监控)
    • wisdom-track(微服务请求链追踪)
  • 网关

    • wisdom-gateway
  • 业务

    • swagger-ui(springfox-swagger2)
    • 动态数据源(dynamic-datasource-spring-boot-starter)
    • MyBatis 增强 (tk.mybatis、pagehelper)
    • Lombok
    • Feign
    • OkHttp
    • Druid

WEB端访问要完成的事情

  server {
      listen 80;
      server_name wy.huiguanjia.cn;
      return 301 https://wy.huiguanjia.cn$request_uri;
  }
  server {
      listen 443;
      server_name wy.huiguanjia.cn;
   
      root   html;
      index  index.html index.htm index.php;
  
      ssl on;
      ssl_certificate cert/wy.huiguanjia.cn.pem;
      ssl_certificate_key cert/wy.huiguanjia.cn.key;
      ssl_session_timeout 5m;
      ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
      ssl_prefer_server_ciphers on;

      location / {
          proxy_pass  http://172.16.0.246:3000/;
   
          #Proxy Settings
          proxy_redirect     off;
          proxy_set_header   Host             $host;
          proxy_set_header   X-Real-IP        $remote_addr;
          proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
          proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
          proxy_max_temp_file_size 0;
          proxy_connect_timeout      300;
          proxy_send_timeout         300;
          proxy_read_timeout         300;
          proxy_buffer_size          4k;
          proxy_buffers              4 32k;
          proxy_busy_buffers_size    64k;
          proxy_temp_file_write_size 64k;
     }
  
  }
  • 转发请求到应用服务器的前端服务
  server {
      listen 3000;
      server_name 127.0.0.1;
   
      root   html;
      index  index.html index.htm index.php;

      location / {
          root /data/bin/hgj2/web_publish/admin/public/;
  	index index.html;
  	try_files $uri $uri/ /index.html;
  
          #Proxy Settings
          proxy_redirect     off;
          proxy_set_header   Host             $host;
          proxy_set_header   X-Real-IP        $remote_addr;
          proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
          proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
          proxy_max_temp_file_size 0;
          proxy_connect_timeout      300;
          proxy_send_timeout         300;
          proxy_read_timeout         300;
          proxy_buffer_size          4k;
          proxy_buffers              4 32k;
          proxy_busy_buffers_size    64k;
          proxy_temp_file_write_size 64k;
     }
     
     location /9ad134a361f8d778/ {
  	proxy_pass http://127.0.0.1:8161/;
  	proxy_read_timeout 600;
     }
  
     location /ureport/res/ {
          proxy_pass http://127.0.0.1:8161/ureport/res/;
     }
  
     location /ureport/preview/ {
          proxy_pass http://127.0.0.1:8161/ureport/preview/;
     }
  }
  • 跨域处理,是接口请求同源
  location /9ad134a361f8d778/ {
  	proxy_pass http://127.0.0.1:8161/;
  	proxy_read_timeout 600;
  }
  • 后端gateway网关拦截器拦截所有请求
  // wisdom-gateway
  // CustomGatewayFilter.java
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){
  	....
  }
  • gateway网关从注册中心获取所有服务的访问地址
  # eureka
  eureka.instance.status-page-url-path=/actuator/info
  eureka.instance.health-check-url-path=/actuator/health
  eureka.instance.home-page-url-path=/
  eureka.client.service-url.defaultZone=http://${EUREKA_HOST:localhost}:${EUREKA_PORT:8162}/eureka/
  ## 表示eureka client间隔多久去拉取服务注册信息,默认为30秒,对于api-gateway,如果要迅速获取服务注册状态,可以缩小该值
  eureka.client.registry-fetch-interval-seconds=5
  ## 表示eureka client发送心跳给server端的频率。如果在leaseExpirationDurationInSeconds后,server端没有收到client的心跳,则将摘除该instance
  eureka.instance.lease-renewal-interval-in-seconds=30
  • gateway网关根据接口正则前缀将请求路由到各个服务
  ## platform
  spring.cloud.gateway.routes[0].id=wisdom-platform
  spring.cloud.gateway.routes[0].uri=lb://wisdom-platform
  spring.cloud.gateway.routes[0].predicates[0]=Path=/platform/**
  spring.cloud.gateway.routes[0].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[0].filters[1]=StripPrefix=1
  ## asset
  spring.cloud.gateway.routes[1].id=wisdom-asset
  spring.cloud.gateway.routes[1].uri=lb://wisdom-asset
  spring.cloud.gateway.routes[1].predicates[0]=Path=/asset/**
  spring.cloud.gateway.routes[1].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[1].filters[1]=StripPrefix=1
  ## auth
  spring.cloud.gateway.routes[2].id=wisdom-auth
  spring.cloud.gateway.routes[2].uri=lb://wisdom-auth
  spring.cloud.gateway.routes[2].predicates[0]=Path=/auth/**
  spring.cloud.gateway.routes[2].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[2].filters[1]=StripPrefix=1
  ## payment
  spring.cloud.gateway.routes[3].id=wisdom-payment
  spring.cloud.gateway.routes[3].uri=lb://wisdom-payment
  spring.cloud.gateway.routes[3].predicates[0]=Path=/payment/**
  spring.cloud.gateway.routes[3].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[3].filters[1]=StripPrefix=1
  ## serve
  spring.cloud.gateway.routes[4].id=wisdom-serve
  spring.cloud.gateway.routes[4].uri=lb://wisdom-serve
  spring.cloud.gateway.routes[4].predicates[0]=Path=/serve/**
  spring.cloud.gateway.routes[4].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[4].filters[1]=StripPrefix=1
  ## file
  spring.cloud.gateway.routes[5].id=wisdom-file
  spring.cloud.gateway.routes[5].uri=lb://wisdom-file
  spring.cloud.gateway.routes[5].predicates[0]=Path=/file/**
  spring.cloud.gateway.routes[5].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[5].filters[1]=StripPrefix=1
  ## bill
  spring.cloud.gateway.routes[6].id=wisdom-bill
  spring.cloud.gateway.routes[6].uri=lb://wisdom-bill
  spring.cloud.gateway.routes[6].predicates[0]=Path=/bill/**
  spring.cloud.gateway.routes[6].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[6].filters[1]=StripPrefix=1
  ## report
  spring.cloud.gateway.routes[7].id=wisdom-report
  spring.cloud.gateway.routes[7].uri=lb://wisdom-report
  spring.cloud.gateway.routes[7].predicates[0]=Path=/report/**
  spring.cloud.gateway.routes[7].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[7].filters[1]=StripPrefix=1
  ## ureport
  spring.cloud.gateway.routes[8].id=wisdom-report
  spring.cloud.gateway.routes[8].uri=lb://wisdom-report
  spring.cloud.gateway.routes[8].predicates[0]=Path=/ureport/**
  spring.cloud.gateway.routes[8].filters[0]=GatewaySwaggerHeaderFilter
  ## log
  spring.cloud.gateway.routes[9].id=wisdom-log
  spring.cloud.gateway.routes[9].uri=lb://wisdom-log
  spring.cloud.gateway.routes[9].predicates[0]=Path=/log/**
  spring.cloud.gateway.routes[9].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[9].filters[1]=StripPrefix=1
  ## task
  spring.cloud.gateway.routes[10].id=wisdom-task
  spring.cloud.gateway.routes[10].uri=lb://wisdom-task
  spring.cloud.gateway.routes[10].predicates[0]=Path=/task/**
  spring.cloud.gateway.routes[10].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[10].filters[1]=StripPrefix=1
  ## hardware
  spring.cloud.gateway.routes[11].id=wisdom-hardware
  spring.cloud.gateway.routes[11].uri=lb://wisdom-hardware
  spring.cloud.gateway.routes[11].predicates[0]=Path=/hardware/**
  spring.cloud.gateway.routes[11].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[11].filters[1]=StripPrefix=1
  ## hris
  spring.cloud.gateway.routes[12].id=wisdom-hris
  spring.cloud.gateway.routes[12].uri=lb://wisdom-hris
  spring.cloud.gateway.routes[12].predicates[0]=Path=/hris/**
  spring.cloud.gateway.routes[12].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[12].filters[1]=StripPrefix=1
  ## app
  spring.cloud.gateway.routes[13].id=wisdom-app
  spring.cloud.gateway.routes[13].uri=lb://wisdom-app
  spring.cloud.gateway.routes[13].predicates[0]=Path=/app/**
  spring.cloud.gateway.routes[13].filters[0]=GatewaySwaggerHeaderFilter
  spring.cloud.gateway.routes[13].filters[1]=StripPrefix=1
  • 服务接收到请求之后,先判断header是否指定数据源,否,则接着判断是否有会话,从会话获取当前请求需要访问的数据源名称
  // wisdom-ds
  // DsCustomerProcessor.java
  // 从header获取数据源
  ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("entCode");
  // 从登陆会话获取数据源
  UserInfoUtil.getUserDs();
  • 接口日志拦截
  // wisdom-common
  // ControllerLoggerAspect.java
  @Pointcut("@annotation(io.swagger.annotations.ApiOperation)")
  public void controllerPointCut() {
  }
  
  @Around("controllerPointCut()")
  public Object handlerControllerMethod(ProceedingJoinPoint pjp){
      ...
  }
  • 数据权限拦截
  // wisdom-common
  // PermissionInterceptor.java
  实现小区、数据权限sql拦截
  • 统一异常拦截
  // wisdom-common
  // PromiseExceptionHandler.java
  @ExceptionHandler(PromiseException.class)
  public ResponseEntity handleBizException(PromiseException ex) {
      ...
  }
  @ExceptionHandler(MethodArgumentNotValidException.class)
  public ResponseEntity argumentNotValidException(MethodArgumentNotValidException ex) {
      ...
  }
  @ExceptionHandler(FeignException.class)
  public ResponseEntity handleException(FeignException ex){
      ...
  }
  @ExceptionHandler(BindException.class)
  public ResponseEntity handleException(BindException ex){
      ...
  }
  @ExceptionHandler(Exception.class)
  public ResponseEntity handleException(Exception ex) {
      ...
  }

需要解决的问题

  • quartz定时任务多数据源问题
  • 分布式事务
  • Redis集群
  • MQ集群
  • 单元测试
  • 自动化测试
  • 多数据源运维