客户端负载均衡Ribbon实例

打印 上一主题 下一主题

主题 1795|帖子 1795|积分 5385

一,概述

一样平常来说,提到负载均衡,大家一样平常很轻易想到欣赏器 -> NGINX -> 反向代理多个Tomcat这样的架构图——业界管这种负载均衡模式叫“服务器端负载均衡”,因为此种模式下,负载均衡算法是NGINX提供的,而NGINX部署在服务器端。
本文所讲的Ribbon则是一个客户端侧负载均衡组件——通俗地说,就是集成在客户端(服务消耗者一侧),并提供负载均衡算法的一个组件。Ribbon是Netflix发布的负载均衡器,它可以帮我们控制HTTP和TCP客户端的行为。只需为Ribbon配置服务提供者地址列表,Ribbon就可基于负载均衡算法计算出要请求的目的服务地址。Ribbon默认为我们提供了很多的负载均衡算法,例如轮询、随机、响应时间加权等。
二,实现过程

一样平常环境下,负载均衡组件Ribbon和微服务注册中心Eureka是配合使用的。
为了在非springCloud微服务项目中,使用Ribbon的客户端负载均衡能力,我们可以按如下步骤实现:

  • 定义服务的被调用方Client,编写webApi接口
  • 定义服务的调用方Cousumer,调用webApi接口
  • 定义网关模块Gateway,通过定义路由的方式重新定义webApi接口路径,并引入Ribbon客户端负载均衡
  • 将步骤2中的WebApi地址切换为网关模块路由接口地址,从而使原来的webApi地址具有了客户端负载均衡功能
  • 使用docker部署Client(多实例)、Gateway,并在编排文件中使用服务名代替客户端列表地址,解耦Cousumer与Client之间的代码接口。
三,项目源码


1. 源码放送:

https://gitee.com/00fly/microservice-all-in-one/tree/master/ribbon-demo
或者使用下面的备份文件恢复成原始的项目代码
怎样恢复,请移步查阅:神奇代码恢复工具
  1. //goto docker-auto-ip\docker-compose.yml
  2. version: '3.8'
  3. services:
  4.   #gateway
  5.   ribbon-gateway:
  6.     image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-gateway:0.0.1
  7.     container_name: ribbon-gateway
  8.     deploy:
  9.       resources:
  10.         limits:
  11.           cpus: '1'
  12.           memory: 300M
  13.         reservations:
  14.           memory: 200M
  15.     ports:
  16.     - 8085:8080
  17.     environment:
  18.       USER_SERVERS: ribbon-user-0:8081,ribbon-user-1:8081
  19.       MOVIE_SERVERS: ribbon-movie:8082
  20.     restart: on-failure
  21.     logging:
  22.       driver: json-file
  23.       options:
  24.         max-size: 5m
  25.         max-file: '1'
  26.         
  27.   #client01
  28.   ribbon-user-0:
  29.     image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-user:0.0.1
  30.     container_name: ribbon-user-0
  31.     deploy:
  32.       resources:
  33.         limits:
  34.           cpus: '1'
  35.           memory: 300M
  36.         reservations:
  37.           memory: 200M
  38.     restart: on-failure
  39.     logging:
  40.       driver: json-file
  41.       options:
  42.         max-size: 5m
  43.         max-file: '1'
  44.   #client02
  45.   ribbon-user-1:
  46.     image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-user:0.0.1
  47.     container_name: ribbon-user-1
  48.     deploy:
  49.       resources:
  50.         limits:
  51.           cpus: '1'
  52.           memory: 300M
  53.         reservations:
  54.           memory: 200M
  55.     restart: on-failure
  56.     logging:
  57.       driver: json-file
  58.       options:
  59.         max-size: 5m
  60.         max-file: '1'
  61.   ribbon-movie:
  62.     image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-movie:0.0.1
  63.     container_name: ribbon-movie
  64.     deploy:
  65.       resources:
  66.         limits:
  67.           cpus: '1'
  68.           memory: 300M
  69.         reservations:
  70.           memory: 200M
  71.     environment:
  72.       USER_API_URL: ribbon-gateway:8080/user/
  73.     restart: on-failure
  74.     logging:
  75.       driver: json-file
  76.       options:
  77.         max-size: 5m
  78.         max-file: '1'
  79. //goto docker-auto-ip\restart.sh
  80. #!/bin/bash
  81. docker-compose down && \
  82. docker-compose --compatibility up -d && \
  83. docker stats
  84. //goto docker-auto-ip\stop.sh
  85. #!/bin/bash
  86. docker-compose down
  87. //goto docker-fix-ip\docker-compose.yml
  88. version: '3.8'
  89. networks:
  90.   default:
  91.     name: devops
  92.     driver: bridge
  93.     ipam:
  94.       config:
  95.       - subnet: 172.88.88.0/24
  96. services:
  97.   #gateway
  98.   ribbon-gateway:
  99.     image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-gateway:0.0.1
  100.     container_name: ribbon-gateway
  101.     deploy:
  102.       resources:
  103.         limits:
  104.           cpus: '1'
  105.           memory: 300M
  106.         reservations:
  107.           memory: 200M
  108.     ports:
  109.     - 8085:8080
  110.     environment:
  111.       MOVIE_SERVERS: 172.88.88.101:8082
  112.       USER_SERVERS: 172.88.88.200:8081,172.88.88.201:8081
  113.     restart: on-failure
  114.     logging:
  115.       driver: json-file
  116.       options:
  117.         max-size: 5m
  118.         max-file: '1'
  119.     networks:
  120.       default:
  121.         ipv4_address: 172.88.88.100
  122.         
  123.   #client01
  124.   ribbon-user-0:
  125.     image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-user:0.0.1
  126.     container_name: ribbon-user-0
  127.     deploy:
  128.       resources:
  129.         limits:
  130.           cpus: '1'
  131.           memory: 300M
  132.         reservations:
  133.           memory: 200M
  134.     restart: on-failure
  135.     logging:
  136.       driver: json-file
  137.       options:
  138.         max-size: 5m
  139.         max-file: '1'
  140.     networks:
  141.       default:
  142.         ipv4_address: 172.88.88.200
  143.   #client02
  144.   ribbon-user-1:
  145.     image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-user:0.0.1
  146.     container_name: ribbon-user-1
  147.     deploy:
  148.       resources:
  149.         limits:
  150.           cpus: '1'
  151.           memory: 300M
  152.         reservations:
  153.           memory: 200M
  154.     restart: on-failure
  155.     logging:
  156.       driver: json-file
  157.       options:
  158.         max-size: 5m
  159.         max-file: '1'
  160.     networks:
  161.       default:
  162.         ipv4_address: 172.88.88.201
  163.   ribbon-movie:
  164.     image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-movie:0.0.1
  165.     container_name: ribbon-movie
  166.     deploy:
  167.       resources:
  168.         limits:
  169.           cpus: '1'
  170.           memory: 300M
  171.         reservations:
  172.           memory: 200M
  173.     environment:
  174.       USER_API_URL: http://172.88.88.100:8080/user/
  175.     restart: on-failure
  176.     logging:
  177.       driver: json-file
  178.       options:
  179.         max-size: 5m
  180.         max-file: '1'
  181.     networks:
  182.       default:
  183.         ipv4_address: 172.88.88.101
  184. //goto docker-fix-ip\restart.sh
  185. #!/bin/bash
  186. docker-compose down && \
  187. docker-compose --compatibility up -d && \
  188. docker stats
  189. //goto docker-fix-ip\stop.sh
  190. #!/bin/bash
  191. docker-compose down
  192. //goto docker-scale\docker-compose.yml
  193. version: '3.8'
  194. services:
  195.   #gateway
  196.   ribbon-gateway:
  197.     image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-gateway:0.0.1
  198.     container_name: ribbon-gateway
  199.     deploy:
  200.       resources:
  201.         limits:
  202.           cpus: '1'
  203.           memory: 300M
  204.         reservations:
  205.           memory: 200M
  206.     ports:
  207.     - 8085:8080
  208.     environment:
  209.       USER_SERVERS: ribbon-user:8081
  210.       MOVIE_SERVERS: ribbon-movie:8082
  211.     restart: on-failure
  212.     logging:
  213.       driver: json-file
  214.       options:
  215.         max-size: 5m
  216.         max-file: '1'
  217.         
  218.   #client
  219.   ribbon-user:
  220.     image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-user:0.0.1
  221.     deploy:
  222.       resources:
  223.         limits:
  224.           cpus: '1'
  225.           memory: 300M
  226.         reservations:
  227.           memory: 200M
  228.     restart: on-failure
  229.     logging:
  230.       driver: json-file
  231.       options:
  232.         max-size: 5m
  233.         max-file: '1'
  234.   ribbon-movie:
  235.     image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-movie:0.0.1
  236.     deploy:
  237.       resources:
  238.         limits:
  239.           cpus: '1'
  240.           memory: 300M
  241.         reservations:
  242.           memory: 200M
  243.     environment:
  244.       USER_API_URL: ribbon-gateway:8080/user/
  245.     restart: on-failure
  246.     logging:
  247.       driver: json-file
  248.       options:
  249.         max-size: 5m
  250.         max-file: '1'
  251. //goto docker-scale\restart.sh
  252. #!/bin/bash
  253. docker-compose down && \
  254. docker-compose --compatibility up -d --scale ribbon-user=2 --scale ribbon-movie=2 && \
  255. docker stats
  256. //goto docker-scale\stop.sh
  257. #!/bin/bash
  258. docker-compose down
  259. //goto pom.xml
  260. <?xml version="1.0" encoding="UTF-8"?>
  261. <project xmlns="http://maven.apache.org/POM/4.0.0"
  262.         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  263.         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  264.         <modelVersion>4.0.0</modelVersion>
  265.         <groupId>com.itmuch.cloud</groupId>
  266.         <artifactId>ribbon-all-in-one</artifactId>
  267.         <version>0.0.1</version>
  268.         <packaging>pom</packaging>
  269.         <!-- 引入spring boot的依赖 -->
  270.         <parent>
  271.                 <groupId>org.springframework.boot</groupId>
  272.                 <artifactId>spring-boot-starter-parent</artifactId>
  273.                 <version>2.2.6.RELEASE</version>
  274.                 <relativePath />
  275.         </parent>
  276.         <modules>
  277.                 <module>ribbon-gateway</module>
  278.                 <module>ribbon-movie</module>
  279.                 <module>ribbon-user</module>
  280.         </modules>
  281.         <properties>
  282.                 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  283.                 <maven.build.timestamp.format>yyyyMMdd-HH</maven.build.timestamp.format>
  284.                 <docker.hub>registry.cn-shanghai.aliyuncs.com</docker.hub>
  285.                 <java.version>1.8</java.version>
  286.                 <docker.plugin.version>1.2.2</docker.plugin.version>
  287.                 <docker.image.prefix>00fly</docker.image.prefix>
  288.                 <spring.cloud.version>Hoxton.SR6</spring.cloud.version>
  289.                 <knife4j.version>2.0.8</knife4j.version>
  290.                 <skipTests>true</skipTests>
  291.         </properties>
  292.         <dependencyManagement>
  293.                 <dependencies>
  294.                         <!-- 引入spring cloud的依赖 -->
  295.                         <dependency>
  296.                                 <groupId>org.springframework.cloud</groupId>
  297.                                 <artifactId>spring-cloud-dependencies</artifactId>
  298.                                 <version>${spring.cloud.version}</version>
  299.                                 <type>pom</type>
  300.                                 <scope>import</scope>
  301.                         </dependency>
  302.                         <dependency>
  303.                                 <groupId>com.github.xiaoymin</groupId>
  304.                                 <artifactId>knife4j-spring-boot-starter</artifactId>
  305.                                 <version>${knife4j.version}</version>
  306.                         </dependency>
  307.                         <dependency>
  308.                                 <groupId>com.github.xiaoymin</groupId>
  309.                                 <artifactId>knife4j-micro-spring-boot-starter</artifactId>
  310.                                 <version>${knife4j.version}</version>
  311.                         </dependency>
  312.                 </dependencies>
  313.         </dependencyManagement>
  314.         <build>
  315.                 <pluginManagement>
  316.                         <plugins>
  317.                                 <plugin>
  318.                                         <groupId>org.springframework.boot</groupId>
  319.                                         <artifactId>spring-boot-maven-plugin</artifactId>
  320.                                         <executions>
  321.                                                 <execution>
  322.                                                         <goals>
  323.                                                                 <goal>repackage</goal>
  324.                                                         </goals>
  325.                                                 </execution>
  326.                                         </executions>
  327.                                 </plugin>
  328.                                 <!-- 添加docker-maven插件 -->
  329.                                 <plugin>
  330.                                         <groupId>io.fabric8</groupId>
  331.                                         <artifactId>docker-maven-plugin</artifactId>
  332.                                         <version>0.40.3</version>
  333.                                         <executions>
  334.                                                 <execution>
  335.                                                         <phase>package</phase>
  336.                                                         <goals>
  337.                                                                 <goal>build</goal>
  338.                                                                 <!--<goal>push</goal>-->
  339.                                                                 <!--<goal>remove</goal>-->
  340.                                                         </goals>
  341.                                                 </execution>
  342.                                         </executions>
  343.                                         <configuration>
  344.                                                 <!-- 连接到带docker环境的linux服务器编译image -->
  345.                                                 <!--<dockerHost>http://192.168.182.10:2375</dockerHost>-->
  346.                                                 <!-- Docker 推送镜像仓库地址 -->
  347.                                                 <pushRegistry>${docker.hub}</pushRegistry>
  348.                                                 <images>
  349.                                                         <image>
  350.                                                                 <!--推送到私有镜像仓库,镜像名需要添加仓库地址 -->
  351.                                                                 <name>${docker.hub}/00fly/${project.artifactId}:${project.version}</name>
  352.                                                                 <!--定义镜像构建行为 -->
  353.                                                                 <build>
  354.                                                                         <dockerFileDir>${project.basedir}</dockerFileDir>
  355.                                                                 </build>
  356.                                                         </image>
  357.                                                 </images>
  358.                                                 <authConfig>
  359.                                                         <!--认证配置,用于私有镜像仓库registry认证 -->
  360.                                                         <username>${docker.username}</username>
  361.                                                         <password>${docker.password}</password>
  362.                                                 </authConfig>
  363.                                         </configuration>
  364.                                 </plugin>
  365.                         </plugins>
  366.                 </pluginManagement>
  367.         </build>
  368. </project>
  369. //goto ribbon-gateway\Dockerfile
  370. #基础镜像
  371. FROM adoptopenjdk/openjdk8-openj9:alpine-slim
  372. COPY wait-for.sh /
  373. RUN chmod +x /wait-for.sh && \
  374.     ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
  375. #引入运行包
  376. COPY target/*.jar /app.jar
  377. #指定交互端口
  378. EXPOSE 8080
  379. CMD ["--server.port=8080"]
  380. #项目的启动方式
  381. ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Xshareclasses", "-Xquickstart", "-jar", "/app.jar"]
  382. //goto ribbon-gateway\pom.xml
  383. <?xml version="1.0" encoding="UTF-8"?>
  384. <project xmlns="http://maven.apache.org/POM/4.0.0"
  385.         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  386.         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  387.         <parent>
  388.                 <groupId>com.itmuch.cloud</groupId>
  389.                 <artifactId>ribbon-all-in-one</artifactId>
  390.                 <version>0.0.1</version>
  391.         </parent>
  392.         <modelVersion>4.0.0</modelVersion>
  393.         <artifactId>ribbon-gateway</artifactId>
  394.         <packaging>jar</packaging>
  395.         <dependencies>
  396.                 <dependency>
  397.                         <groupId>org.springframework.cloud</groupId>
  398.                         <artifactId>spring-cloud-starter-gateway</artifactId>
  399.                         <exclusions>
  400.                                 <exclusion>
  401.                                         <groupId>org.springframework.boot</groupId>
  402.                                         <artifactId>spring-boot-starter-logging</artifactId>
  403.                                 </exclusion>
  404.                         </exclusions>
  405.                 </dependency>
  406.                 <dependency>
  407.                         <groupId>org.springframework.boot</groupId>
  408.                         <artifactId>spring-boot-starter-log4j2</artifactId>
  409.                 </dependency>
  410.                 <dependency>
  411.                         <groupId>org.springframework.cloud</groupId>
  412.                         <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
  413.                 </dependency>
  414.                 <!-- 集成 knife4j -->
  415.                 <dependency>
  416.                         <groupId>com.github.xiaoymin</groupId>
  417.                         <artifactId>knife4j-spring-boot-starter</artifactId>
  418.                 </dependency>
  419.                 <dependency>
  420.                         <groupId>org.projectlombok</groupId>
  421.                         <artifactId>lombok</artifactId>
  422.                 </dependency>
  423.         </dependencies>
  424.         <build>
  425.                 <finalName>${project.artifactId}</finalName>
  426.                 <plugins>
  427.                         <plugin>
  428.                                 <groupId>org.springframework.boot</groupId>
  429.                                 <artifactId>spring-boot-maven-plugin</artifactId>
  430.                         </plugin>
  431.                         <plugin>
  432.                                 <groupId>io.fabric8</groupId>
  433.                                 <artifactId>docker-maven-plugin</artifactId>
  434.                         </plugin>
  435.                 </plugins>
  436.         </build>
  437. </project>
  438. //goto ribbon-gateway\src\main\java\com\fly\gateway\config\SwaggerHeaderFilter.java
  439. package com.fly.gateway.config;
  440. import org.springframework.cloud.gateway.filter.GatewayFilter;
  441. import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
  442. import org.springframework.http.server.reactive.ServerHttpRequest;
  443. import org.springframework.stereotype.Component;
  444. import org.springframework.util.StringUtils;
  445. import org.springframework.web.server.ServerWebExchange;
  446. /**
  447. * @author fsl
  448. * @description: SwaggerHeaderFilter
  449. * @date 2019-06-0310:47
  450. */
  451. @Component
  452. public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory<Object>
  453. {
  454.     private static final String HEADER_NAME = "X-Forwarded-Prefix";
  455.    
  456.     private static final String URI = "/v2/api-docs";
  457.    
  458.     /**
  459.      * 网关过滤器
  460.      */
  461.     @Override
  462.     public GatewayFilter apply(Object config)
  463.     {
  464.         return (exchange, chain) -> {
  465.             ServerHttpRequest request = exchange.getRequest();
  466.             String path = request.getURI().getPath();
  467.             if (!StringUtils.endsWithIgnoreCase(path, URI))
  468.             {
  469.                 return chain.filter(exchange);
  470.             }
  471.             String basePath = path.substring(0, path.lastIndexOf(URI));
  472.             ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
  473.             ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
  474.             return chain.filter(newExchange);
  475.         };
  476.     }
  477. }
  478. //goto ribbon-gateway\src\main\java\com\fly\gateway\config\SwaggerResourceConfig.java
  479. package com.fly.gateway.config;
  480. import java.util.ArrayList;
  481. import java.util.List;
  482. import org.springframework.cloud.gateway.config.GatewayProperties;
  483. import org.springframework.cloud.gateway.route.RouteLocator;
  484. import org.springframework.cloud.gateway.support.NameUtils;
  485. import org.springframework.context.annotation.Primary;
  486. import org.springframework.stereotype.Component;
  487. import lombok.AllArgsConstructor;
  488. import lombok.extern.slf4j.Slf4j;
  489. import springfox.documentation.swagger.web.SwaggerResource;
  490. import springfox.documentation.swagger.web.SwaggerResourcesProvider;
  491. /***
  492. * 聚合各个服务的swagger接口
  493. */
  494. @Slf4j
  495. @Component
  496. @Primary
  497. @AllArgsConstructor
  498. public class SwaggerResourceConfig implements SwaggerResourcesProvider
  499. {
  500.     /**
  501.      * 网关路由
  502.      */
  503.     private final RouteLocator routeLocator;
  504.    
  505.     private final GatewayProperties gatewayProperties;
  506.    
  507.     @Override
  508.     public List<SwaggerResource> get()
  509.     {
  510.         List<SwaggerResource> resources = new ArrayList<>();
  511.         List<String> routes = new ArrayList<>();
  512.         routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
  513.         gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {
  514.             route.getPredicates()
  515.                 .stream()
  516.                 .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
  517.                 .forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(), predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("**", "v2/api-docs"))));
  518.         });
  519.         return resources;
  520.     }
  521.    
  522.     private SwaggerResource swaggerResource(String name, String location)
  523.     {
  524.         log.info("name:{},location:{}", name, location);
  525.         SwaggerResource swaggerResource = new SwaggerResource();
  526.         swaggerResource.setName(name);
  527.         swaggerResource.setLocation(location);
  528.         swaggerResource.setSwaggerVersion("2.0");
  529.         return swaggerResource;
  530.     }
  531. }
  532. //goto ribbon-gateway\src\main\java\com\fly\gateway\GateWayApplication.java
  533. package com.fly.gateway;
  534. import org.springframework.boot.SpringApplication;
  535. import org.springframework.boot.autoconfigure.SpringBootApplication;
  536. @SpringBootApplication
  537. public class GateWayApplication
  538. {
  539.     public static void main(String[] args)
  540.     {
  541.         SpringApplication.run(GateWayApplication.class, args);
  542.     }
  543. }
  544. //goto ribbon-gateway\src\main\java\com\fly\gateway\handler\SwaggerHandler.java
  545. package com.fly.gateway.handler;
  546. import java.util.Optional;
  547. import org.springframework.beans.factory.annotation.Autowired;
  548. import org.springframework.http.HttpStatus;
  549. import org.springframework.http.ResponseEntity;
  550. import org.springframework.web.bind.annotation.GetMapping;
  551. import org.springframework.web.bind.annotation.RestController;
  552. import reactor.core.publisher.Mono;
  553. import springfox.documentation.swagger.web.SecurityConfiguration;
  554. import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
  555. import springfox.documentation.swagger.web.SwaggerResourcesProvider;
  556. import springfox.documentation.swagger.web.UiConfiguration;
  557. import springfox.documentation.swagger.web.UiConfigurationBuilder;
  558. /**
  559. * swagger聚合接口
  560. *
  561. */
  562. @RestController
  563. public class SwaggerHandler
  564. {
  565.     @Autowired(required = false)
  566.     private SecurityConfiguration securityConfiguration;
  567.    
  568.     @Autowired(required = false)
  569.     private UiConfiguration uiConfiguration;
  570.    
  571.     private final SwaggerResourcesProvider swaggerResources;
  572.    
  573.     public SwaggerHandler(SwaggerResourcesProvider swaggerResources)
  574.     {
  575.         this.swaggerResources = swaggerResources;
  576.     }
  577.    
  578.     @GetMapping("/swagger-resources/configuration/security")
  579.     public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration()
  580.     {
  581.         return Mono.just(new ResponseEntity<>(Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
  582.     }
  583.    
  584.     @GetMapping("/swagger-resources/configuration/ui")
  585.     public Mono<ResponseEntity<UiConfiguration>> uiConfiguration()
  586.     {
  587.         return Mono.just(new ResponseEntity<>(Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
  588.     }
  589.    
  590.     @GetMapping("/swagger-resources")
  591.     public Mono<ResponseEntity<?>> swaggerResources()
  592.     {
  593.         return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
  594.     }
  595. }
  596. //goto ribbon-gateway\src\main\resources\application.yml
  597. server:
  598.   port: 8080
  599. spring:
  600.   application:
  601.     name: ribbon-gateway
  602.   cloud:
  603.     gateway:
  604.       discovery:
  605.         locator:
  606.           lowerCaseServiceId: true
  607.       routes:
  608.         - id: ribbon-user
  609.           uri: lb://ribbon-user
  610.           predicates:
  611.             - Path=/user/**
  612.           filters:
  613.             - StripPrefix=1
  614.         - id:  ribbon-movie
  615.           uri: lb://ribbon-movie
  616.           predicates:
  617.             - Path=/movie/**
  618.           filters:
  619.             - StripPrefix=1
  620. ribbon-movie:
  621.   ribbon:
  622.     listOfServers: ${MOVIE_SERVERS:127.0.0.1:8082}
  623.   
  624. ribbon-user:
  625.   ribbon:
  626.     listOfServers: ${USER_SERVERS:127.0.0.1:8081}
  627. //goto ribbon-gateway\wait-for.sh
  628. #!/bin/sh
  629. TIMEOUT=15
  630. QUIET=0
  631. echoerr() {
  632.   if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
  633. }
  634. usage() {
  635.   exitcode="$1"
  636.   cat << USAGE >&2
  637. Usage:
  638.   $cmdname host:port [-t timeout] [-- command args]
  639.   -q | --quiet                        Do not output any status messages
  640.   -t TIMEOUT | --timeout=timeout      Timeout in seconds, zero for no timeout
  641.   -- COMMAND ARGS                     Execute command with args after the test finishes
  642. USAGE
  643.   exit "$exitcode"
  644. }
  645. wait_for() {
  646.   for i in `seq $TIMEOUT` ; do
  647.     nc -z "$HOST" "$PORT" > /dev/null 2>&1
  648.     result=$?
  649.     if [ $result -eq 0 ] ; then
  650.       if [ $# -gt 0 ] ; then
  651.         exec "$@"
  652.       fi
  653.       exit 0
  654.     fi
  655.     sleep 1
  656.   done
  657.   echo "Operation timed out" >&2
  658.   exit 1
  659. }
  660. while [ $# -gt 0 ]
  661. do
  662.   case "$1" in
  663.     *:* )
  664.     HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
  665.     PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
  666.     shift 1
  667.     ;;
  668.     -q | --quiet)
  669.     QUIET=1
  670.     shift 1
  671.     ;;
  672.     -t)
  673.     TIMEOUT="$2"
  674.     if [ "$TIMEOUT" = "" ]; then break; fi
  675.     shift 2
  676.     ;;
  677.     --timeout=*)
  678.     TIMEOUT="${1#*=}"
  679.     shift 1
  680.     ;;
  681.     --)
  682.     shift
  683.     break
  684.     ;;
  685.     --help)
  686.     usage 0
  687.     ;;
  688.     *)
  689.     echoerr "Unknown argument: $1"
  690.     usage 1
  691.     ;;
  692.   esac
  693. done
  694. if [ "$HOST" = "" -o "$PORT" = "" ]; then
  695.   echoerr "Error: you need to provide a host and port to test."
  696.   usage 2
  697. fi
  698. wait_for "$@"
  699. //goto ribbon-movie\Dockerfile
  700. #基础镜像
  701. FROM adoptopenjdk/openjdk8-openj9:alpine-slim
  702. COPY wait-for.sh /
  703. RUN chmod +x /wait-for.sh && \
  704.     ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
  705. #引入运行包
  706. COPY target/*.jar /app.jar
  707. #指定交互端口
  708. EXPOSE 8082
  709. CMD ["--server.port=8082"]
  710. #项目的启动方式
  711. ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Xshareclasses", "-Xquickstart", "-jar", "/app.jar"]
  712. //goto ribbon-movie\pom.xml
  713. <?xml version="1.0" encoding="UTF-8"?>
  714. <project xmlns="http://maven.apache.org/POM/4.0.0"
  715.         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  716.         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  717.         <parent>
  718.                 <groupId>com.itmuch.cloud</groupId>
  719.                 <artifactId>ribbon-all-in-one</artifactId>
  720.                 <version>0.0.1</version>
  721.         </parent>
  722.         <modelVersion>4.0.0</modelVersion>
  723.         <artifactId>ribbon-movie</artifactId>
  724.         <packaging>jar</packaging>
  725.         <dependencies>
  726.                 <dependency>
  727.                         <groupId>org.springframework.boot</groupId>
  728.                         <artifactId>spring-boot-starter-web</artifactId>
  729.                         <exclusions>
  730.                                 <exclusion>
  731.                                         <groupId>org.springframework.boot</groupId>
  732.                                         <artifactId>spring-boot-starter-logging</artifactId>
  733.                                 </exclusion>
  734.                         </exclusions>
  735.                 </dependency>
  736.                 <dependency>
  737.                         <groupId>org.springframework.boot</groupId>
  738.                         <artifactId>spring-boot-starter-log4j2</artifactId>
  739.                 </dependency>
  740.                 <dependency>
  741.                         <groupId>org.springframework.boot</groupId>
  742.                         <artifactId>spring-boot-starter-webflux</artifactId>
  743.                 </dependency>
  744.                 <dependency>
  745.                         <groupId>org.springframework.cloud</groupId>
  746.                         <artifactId>spring-cloud-starter-openfeign</artifactId>
  747.                 </dependency>
  748.                 <dependency>
  749.                         <groupId>org.springframework.cloud</groupId>
  750.                         <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  751.                 </dependency>
  752.                 <dependency>
  753.                         <groupId>org.apache.commons</groupId>
  754.                         <artifactId>commons-lang3</artifactId>
  755.                 </dependency>
  756.                 <!-- 使用Apache HttpClient替换Feign原生httpclient -->
  757.                 <dependency>
  758.                         <groupId>io.github.openfeign</groupId>
  759.                         <artifactId>feign-okhttp</artifactId>
  760.                 </dependency>
  761.                 <dependency>
  762.                         <groupId>org.projectlombok</groupId>
  763.                         <artifactId>lombok</artifactId>
  764.                         <scope>provided</scope>
  765.                 </dependency>
  766.                 <!-- 集成 knife4j -->
  767.                 <dependency>
  768.                         <groupId>com.github.xiaoymin</groupId>
  769.                         <artifactId>knife4j-spring-boot-starter</artifactId>
  770.                 </dependency>
  771.         </dependencies>
  772.         <build>
  773.                 <finalName>${project.artifactId}</finalName>
  774.                 <plugins>
  775.                         <plugin>
  776.                                 <groupId>org.springframework.boot</groupId>
  777.                                 <artifactId>spring-boot-maven-plugin</artifactId>
  778.                         </plugin>
  779.                         <plugin>
  780.                                 <groupId>io.fabric8</groupId>
  781.                                 <artifactId>docker-maven-plugin</artifactId>
  782.                         </plugin>
  783.                 </plugins>
  784.         </build>
  785. </project>
  786. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\ConsumerMovieApplication.java
  787. package com.itmuch.cloud.study;
  788. import org.springframework.boot.SpringApplication;
  789. import org.springframework.boot.autoconfigure.SpringBootApplication;
  790. import org.springframework.cache.annotation.EnableCaching;
  791. import org.springframework.cloud.openfeign.EnableFeignClients;
  792. @EnableCaching
  793. @EnableFeignClients
  794. @SpringBootApplication
  795. public class ConsumerMovieApplication
  796. {
  797.     public static void main(String[] args)
  798.     {
  799.         SpringApplication.run(ConsumerMovieApplication.class, args);
  800.     }
  801. }
  802. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\core\config\Knife4jConfig.java
  803. package com.itmuch.cloud.study.core.config;
  804. import org.springframework.beans.factory.annotation.Value;
  805. import org.springframework.context.annotation.Bean;
  806. import org.springframework.context.annotation.Configuration;
  807. import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
  808. import io.swagger.annotations.ApiOperation;
  809. import springfox.documentation.builders.ApiInfoBuilder;
  810. import springfox.documentation.builders.PathSelectors;
  811. import springfox.documentation.builders.RequestHandlerSelectors;
  812. import springfox.documentation.service.ApiInfo;
  813. import springfox.documentation.spi.DocumentationType;
  814. import springfox.documentation.spring.web.plugins.Docket;
  815. import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
  816. /**
  817. * Knife4j配置
  818. *
  819. */
  820. @Configuration
  821. @EnableKnife4j
  822. @EnableSwagger2WebMvc
  823. public class Knife4jConfig
  824. {
  825.     @Value("${knife4j.enable:true}")
  826.     private boolean enable;
  827.    
  828.     /**
  829.      * 开发、测试环境接口文档打开
  830.      *
  831.      * @return
  832.      * @see [类、类#方法、类#成员]
  833.      */
  834.     @Bean
  835.     Docket createRestApi()
  836.     {
  837.         return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
  838.             .enable(enable)
  839.             .select()
  840.             .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
  841.             .paths(PathSelectors.any()) // 包下的类,生成接口文档
  842.             .build();
  843.     }
  844.    
  845.     private ApiInfo apiInfo()
  846.     {
  847.         return new ApiInfoBuilder().title("movie模块API").description("接口文档").termsOfServiceUrl("http://00fly.online/").version("1.0.0").build();
  848.     }
  849. }
  850. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\core\config\RibbonConfiguration.java
  851. //package com.itmuch.cloud.study.core.config;
  852. //
  853. //import org.springframework.context.annotation.Bean;
  854. //import org.springframework.context.annotation.Configuration;
  855. //
  856. //import com.netflix.loadbalancer.IRule;
  857. //import com.netflix.loadbalancer.RandomRule;
  858. //
  859. ///**
  860. // * 该类为Ribbon的配置类 注意:该类不应该在主应用程序上下文的@ComponentScan 中。
  861. // *
  862. // */
  863. //@Configuration
  864. //public class RibbonConfiguration
  865. //{
  866. //    @Bean
  867. //    IRule ribbonRule()
  868. //    {
  869. //        // 负载均衡规则,改为随机
  870. //        return new RandomRule();
  871. //    }
  872. //}
  873. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\core\config\TestConfiguration.java
  874. //package com.itmuch.cloud.study.core.config;
  875. //
  876. //import org.springframework.cloud.netflix.ribbon.RibbonClient;
  877. //import org.springframework.context.annotation.Configuration;
  878. //
  879. ///**
  880. // * 使用RibbonClient,为特定name的Ribbon Client自定义配置. 使用@RibbonClient的configuration属性,指定Ribbon的配置类. <br>
  881. // * 可参考的示例: http://spring.io/guides/gs/client-side-load-balancing/
  882. // *
  883. // */
  884. //@Configuration
  885. //@RibbonClient(name = "microservice-ribbon-user", configuration = RibbonConfiguration.class)
  886. //public class TestConfiguration
  887. //{
  888. //}
  889. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\core\config\WebMvcConfig.java
  890. package com.itmuch.cloud.study.core.config;
  891. import org.springframework.context.annotation.Configuration;
  892. import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
  893. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  894. /**
  895. *
  896. * mvc配置
  897. *
  898. * @author 00fly
  899. * @version [版本号, 2021年4月23日]
  900. * @see [相关类/方法]
  901. * @since [产品/模块版本]
  902. */
  903. @Configuration
  904. public class WebMvcConfig implements WebMvcConfigurer
  905. {
  906.     /**
  907.      * @param registry
  908.      */
  909.     @Override
  910.     public void addViewControllers(final ViewControllerRegistry registry)
  911.     {
  912.         registry.addViewController("/").setViewName("doc.html");
  913.     }
  914. }
  915. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\core\utils\JsonBeanUtils.java
  916. package com.itmuch.cloud.study.core.utils;
  917. import java.io.IOException;
  918. import com.fasterxml.jackson.core.type.TypeReference;
  919. import com.fasterxml.jackson.databind.DeserializationFeature;
  920. import com.fasterxml.jackson.databind.JavaType;
  921. import com.fasterxml.jackson.databind.ObjectMapper;
  922. /**
  923. * JsonBean转换工具
  924. *
  925. * @author 00fly
  926. *
  927. */
  928. public class JsonBeanUtils
  929. {
  930.     private static ObjectMapper objectMapper = new ObjectMapper();
  931.    
  932.     /**
  933.      * bean转json字符串
  934.      *
  935.      * @param bean
  936.      * @return
  937.      * @throws IOException
  938.      */
  939.     public static String beanToJson(Object bean)
  940.         throws IOException
  941.     {
  942.         String jsonText = objectMapper.writeValueAsString(bean);
  943.         return objectMapper.readTree(jsonText).toPrettyString();
  944.     }
  945.    
  946.     /**
  947.      * bean转json字符串
  948.      *
  949.      * @param bean
  950.      * @param pretty 是否格式美化
  951.      * @return
  952.      * @throws IOException
  953.      */
  954.     public static String beanToJson(Object bean, boolean pretty)
  955.         throws IOException
  956.     {
  957.         if (pretty)
  958.         {
  959.             return beanToJson(bean);
  960.         }
  961.         String jsonText = objectMapper.writeValueAsString(bean);
  962.         return objectMapper.readTree(jsonText).toString();
  963.     }
  964.    
  965.     /**
  966.      * json字符串转bean
  967.      *
  968.      * @param jsonText
  969.      * @return
  970.      * @throws IOException
  971.      */
  972.     public static <T> T jsonToBean(String jsonText, Class<T> clazz)
  973.         throws IOException
  974.     {
  975.         return objectMapper.readValue(jsonText, clazz);
  976.     }
  977.    
  978.     /**
  979.      * json字符串转bean
  980.      *
  981.      * @param jsonText
  982.      * @return
  983.      * @throws IOException
  984.      */
  985.     public static <T> T jsonToBean(String jsonText, JavaType javaType)
  986.         throws IOException
  987.     {
  988.         return objectMapper.readValue(jsonText, javaType);
  989.     }
  990.    
  991.     /**
  992.      * json字符串转bean
  993.      *
  994.      * @param jsonText
  995.      * @param clazz
  996.      * @param ingoreError 是否忽略无法识别字段
  997.      * @return
  998.      * @throws IOException
  999.      */
  1000.     public static <T> T jsonToBean(String jsonText, Class<T> clazz, boolean ingoreError)
  1001.         throws IOException
  1002.     {
  1003.         objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, !ingoreError);
  1004.         return objectMapper.readValue(jsonText, clazz);
  1005.     }
  1006.    
  1007.     /**
  1008.      * json字符串转bean
  1009.      *
  1010.      * @param jsonText
  1011.      * @return
  1012.      * @throws IOException
  1013.      */
  1014.     public static <T> T jsonToBean(String jsonText, TypeReference<T> typeRef)
  1015.         throws IOException
  1016.     {
  1017.         return objectMapper.readValue(jsonText, typeRef);
  1018.     }
  1019. }
  1020. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\user\controller\DataPushController.java
  1021. package com.itmuch.cloud.study.user.controller;
  1022. import java.io.IOException;
  1023. import java.util.List;
  1024. import java.util.concurrent.ScheduledThreadPoolExecutor;
  1025. import java.util.concurrent.TimeUnit;
  1026. import javax.annotation.PostConstruct;
  1027. import org.apache.commons.lang3.RandomUtils;
  1028. import org.springframework.beans.factory.annotation.Autowired;
  1029. import org.springframework.web.bind.annotation.CrossOrigin;
  1030. import org.springframework.web.bind.annotation.GetMapping;
  1031. import org.springframework.web.bind.annotation.PathVariable;
  1032. import org.springframework.web.bind.annotation.RestController;
  1033. import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
  1034. import com.itmuch.cloud.study.core.utils.JsonBeanUtils;
  1035. import com.itmuch.cloud.study.user.entity.Article;
  1036. import com.itmuch.cloud.study.user.service.DataService;
  1037. import com.itmuch.cloud.study.user.service.SSEServer;
  1038. import io.swagger.annotations.Api;
  1039. import lombok.extern.slf4j.Slf4j;
  1040. @Slf4j
  1041. @Api(tags = "DataPush模块")
  1042. @RestController
  1043. public class DataPushController
  1044. {
  1045.     long init = 0L;
  1046.    
  1047.     @Autowired
  1048.     DataService dataService;
  1049.    
  1050.     @PostConstruct
  1051.     private void init()
  1052.     {
  1053.         log.info("Server-Sent Events start");
  1054.         new ScheduledThreadPoolExecutor(2).scheduleAtFixedRate(() -> {
  1055.             long now = (init + RandomUtils.nextInt(5, 10)) % 101;
  1056.             SSEServer.batchSendMessage(String.valueOf(init));
  1057.             if (now < init)
  1058.             {
  1059.                 try
  1060.                 {
  1061.                     // 随机选择2个,返回访问量小的
  1062.                     List<Article> articles = dataService.getArticles();
  1063.                     int length = articles.size();
  1064.                     Article article001 = articles.get(RandomUtils.nextInt(0, length));
  1065.                     Article article002 = articles.get(RandomUtils.nextInt(0, length));
  1066.                     SSEServer.batchSendMessage("json", JsonBeanUtils.beanToJson(article001.getViewCount() > article002.getViewCount() ? article002 : article001, false));
  1067.                 }
  1068.                 catch (IOException e)
  1069.                 {
  1070.                 }
  1071.             }
  1072.             init = now;
  1073.         }, 2000, 1000, TimeUnit.MILLISECONDS);
  1074.     }
  1075.    
  1076.     @CrossOrigin
  1077.     @GetMapping("/sse/connect/{userId}")
  1078.     public SseEmitter connect(@PathVariable String userId)
  1079.     {
  1080.         return SSEServer.connect();
  1081.     }
  1082. }
  1083. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\user\controller\MovieController.java
  1084. package com.itmuch.cloud.study.user.controller;
  1085. import java.net.InetAddress;
  1086. import java.net.UnknownHostException;
  1087. import javax.annotation.PostConstruct;
  1088. import org.springframework.beans.factory.annotation.Autowired;
  1089. import org.springframework.web.bind.annotation.GetMapping;
  1090. import org.springframework.web.bind.annotation.PathVariable;
  1091. import org.springframework.web.bind.annotation.RestController;
  1092. import com.itmuch.cloud.study.user.entity.User;
  1093. import com.itmuch.cloud.study.user.feign.UserFeignClient;
  1094. import io.swagger.annotations.Api;
  1095. import io.swagger.annotations.ApiOperation;
  1096. @Api(tags = "movie模块")
  1097. @RestController
  1098. public class MovieController
  1099. {
  1100.     String serverIp;
  1101.    
  1102.     @Autowired
  1103.     private UserFeignClient userFeignClient;
  1104.    
  1105.     @PostConstruct
  1106.     private void init()
  1107.     {
  1108.         try
  1109.         {
  1110.             serverIp = InetAddress.getLocalHost().getHostAddress();
  1111.         }
  1112.         catch (UnknownHostException e)
  1113.         {
  1114.         }
  1115.     }
  1116.    
  1117.     @ApiOperation("查询用户")
  1118.     @GetMapping("/user/{id}")
  1119.     public User findById(@PathVariable Long id)
  1120.     {
  1121.         // 带出serverIp方便判断数据来源容器
  1122.         User user = this.userFeignClient.findById(id);
  1123.         if (user.getId() > 0)
  1124.         {
  1125.             user.setName(user.getName() + " === in server:" + serverIp);
  1126.         }
  1127.         return user;
  1128.     }
  1129. }
  1130. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\user\entity\Article.java
  1131. package com.itmuch.cloud.study.user.entity;
  1132. import lombok.Data;
  1133. @Data
  1134. public class Article
  1135. {
  1136.     String title;
  1137.    
  1138.     String url;
  1139.    
  1140.     Long viewCount;
  1141. }
  1142. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\user\entity\BlogData.java
  1143. package com.itmuch.cloud.study.user.entity;
  1144. import lombok.Data;
  1145. @Data
  1146. public class BlogData
  1147. {
  1148.     private Record data;
  1149. }
  1150. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\user\entity\Record.java
  1151. package com.itmuch.cloud.study.user.entity;
  1152. import java.util.List;
  1153. import lombok.Data;
  1154. @Data
  1155. public class Record
  1156. {
  1157.     private List<Article> list;
  1158. }
  1159. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\user\entity\User.java
  1160. package com.itmuch.cloud.study.user.entity;
  1161. import java.math.BigDecimal;
  1162. import lombok.Data;
  1163. @Data
  1164. public class User
  1165. {
  1166.     private Long id;
  1167.    
  1168.     private String username;
  1169.    
  1170.     private String name;
  1171.    
  1172.     private Integer age;
  1173.    
  1174.     private BigDecimal balance;
  1175. }
  1176. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\user\feign\UserFeignClient.java
  1177. package com.itmuch.cloud.study.user.feign;
  1178. import org.springframework.cloud.openfeign.FeignClient;
  1179. import org.springframework.stereotype.Component;
  1180. import org.springframework.web.bind.annotation.GetMapping;
  1181. import org.springframework.web.bind.annotation.PathVariable;
  1182. import com.itmuch.cloud.study.user.entity.User;
  1183. @FeignClient(name = "microservice-ribbon-user", url = "${user.api.url:127.0.0.1:8081}", fallback = FeignClientFallback.class)
  1184. public interface UserFeignClient
  1185. {
  1186.     @GetMapping("/{id}")
  1187.     public User findById(@PathVariable("id") Long id);
  1188. }
  1189. /**
  1190. * 回退类FeignClientFallback需实现Feign Client接口,FeignClientFallback也可以是public class,没有区别
  1191. *
  1192. */
  1193. @Component
  1194. class FeignClientFallback implements UserFeignClient
  1195. {
  1196.     @Override
  1197.     public User findById(Long id)
  1198.     {
  1199.         User user = new User();
  1200.         user.setId(-1L);
  1201.         user.setUsername("默认用户");
  1202.         return user;
  1203.     }
  1204. }
  1205. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\user\service\DataService.java
  1206. package com.itmuch.cloud.study.user.service;
  1207. import java.io.IOException;
  1208. import java.nio.charset.StandardCharsets;
  1209. import java.util.List;
  1210. import org.springframework.cache.annotation.Cacheable;
  1211. import org.springframework.http.MediaType;
  1212. import org.springframework.stereotype.Service;
  1213. import org.springframework.web.reactive.function.client.WebClient;
  1214. import com.itmuch.cloud.study.core.utils.JsonBeanUtils;
  1215. import com.itmuch.cloud.study.user.entity.Article;
  1216. import com.itmuch.cloud.study.user.entity.BlogData;
  1217. import lombok.extern.slf4j.Slf4j;
  1218. /**
  1219. * DataService
  1220. */
  1221. @Slf4j
  1222. @Service
  1223. public class DataService
  1224. {
  1225.     WebClient webClient = WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build();
  1226.    
  1227.     /**
  1228.      * 获取Article数据列表
  1229.      *
  1230.      * @return
  1231.      * @throws IOException
  1232.      */
  1233.     @Cacheable(cacheNames = "data", key = "'articles'", sync = true)
  1234.     public List<Article> getArticles()
  1235.         throws IOException
  1236.     {
  1237.         log.info("★★★★★★★★ getData from webApi ★★★★★★★★");
  1238.         String resp = webClient.get().uri("https://00fly.online/upload/data.json").acceptCharset(StandardCharsets.UTF_8).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(String.class).block();
  1239.         return JsonBeanUtils.jsonToBean(resp, BlogData.class, true).getData().getList();
  1240.     }
  1241. }
  1242. //goto ribbon-movie\src\main\java\com\itmuch\cloud\study\user\service\SSEServer.java
  1243. package com.itmuch.cloud.study.user.service;
  1244. import java.io.IOException;
  1245. import java.util.List;
  1246. import java.util.concurrent.CopyOnWriteArrayList;
  1247. import java.util.function.Consumer;
  1248. import org.springframework.http.MediaType;
  1249. import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
  1250. import lombok.extern.slf4j.Slf4j;
  1251. /**
  1252. * Server-Sent Events <BR>
  1253. * https://blog.csdn.net/hhl18730252820/article/details/126244274
  1254. */
  1255. @Slf4j
  1256. public class SSEServer
  1257. {
  1258.     private static List<SseEmitter> sseEmitters = new CopyOnWriteArrayList<>();
  1259.    
  1260.     public static SseEmitter connect()
  1261.     {
  1262.         SseEmitter sseEmitter = new SseEmitter(0L); // 设置超时时间,0表示不过期,默认是30秒,超过时间未完成会抛出异常
  1263.         
  1264.         // 注册回调
  1265.         sseEmitter.onCompletion(completionCallBack(sseEmitter));
  1266.         sseEmitter.onError(errorCallBack(sseEmitter));
  1267.         sseEmitter.onTimeout(timeOutCallBack(sseEmitter));
  1268.         sseEmitters.add(sseEmitter);
  1269.         log.info("###### create new sse connect, count: {}", sseEmitters.size());
  1270.         return sseEmitter;
  1271.     }
  1272.    
  1273.     public static void batchSendMessage(String message)
  1274.     {
  1275.         sseEmitters.forEach(it -> {
  1276.             try
  1277.             {
  1278.                 it.send(message, MediaType.APPLICATION_JSON);
  1279.             }
  1280.             catch (IOException e)
  1281.             {
  1282.                 log.error("send message error: {}", e.getMessage());
  1283.                 remove(it);
  1284.             }
  1285.         });
  1286.     }
  1287.    
  1288.     /**
  1289.      * 指定name,发送message
  1290.      *
  1291.      * @param name
  1292.      * @param message 普通字符串或json数据
  1293.      */
  1294.     public static void batchSendMessage(String name, String message)
  1295.     {
  1296.         sseEmitters.forEach(it -> {
  1297.             try
  1298.             {
  1299.                 it.send(SseEmitter.event().name(name).data(message));
  1300.             }
  1301.             catch (IOException e)
  1302.             {
  1303.                 log.error("send message error: {}", e.getMessage());
  1304.                 remove(it);
  1305.             }
  1306.         });
  1307.     }
  1308.    
  1309.     public static void remove(SseEmitter s)
  1310.     {
  1311.         if (sseEmitters.contains(s))
  1312.         {
  1313.             sseEmitters.remove(s);
  1314.             log.info("###### remove SseEmitter, count: {}", sseEmitters.size());
  1315.         }
  1316.     }
  1317.    
  1318.     private static Runnable completionCallBack(SseEmitter s)
  1319.     {
  1320.         return () -> {
  1321.             log.info("结束连接");
  1322.             remove(s);
  1323.         };
  1324.     }
  1325.    
  1326.     private static Runnable timeOutCallBack(SseEmitter s)
  1327.     {
  1328.         return () -> {
  1329.             log.info("连接超时");
  1330.             remove(s);
  1331.         };
  1332.     }
  1333.    
  1334.     private static Consumer<Throwable> errorCallBack(SseEmitter s)
  1335.     {
  1336.         return throwable -> {
  1337.             log.error("连接异常");
  1338.             remove(s);
  1339.         };
  1340.     }
  1341. }
  1342. //goto ribbon-movie\src\main\resources\application-ribbon.yml
  1343. server:
  1344.   port: 8082
  1345. spring:
  1346.   application:
  1347.     name: ribbon-movie
  1348.   cache:
  1349.     type: simple
  1350.    
  1351. #设置负载均衡参数
  1352. microservice-ribbon-user:
  1353.   ribbon:
  1354.     #配置规则
  1355.     NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  1356.    
  1357.     #配置地址:宿主机ip+映射端口或docker自定义网络指定地址
  1358.     #listOfServers: 172.22.208.1:8081,172.22.208.1:8091
  1359.     listOfServers: 172.88.88.200:8081,172.88.88.201:8081
  1360. feign:
  1361.   httpclient:
  1362.     enabled: true
  1363. ribbon:
  1364.   ReadTimeout: 30000
  1365.   ConnectTimeout: 30000
  1366. logging:
  1367.   level:
  1368.     root: INFO
  1369. //goto ribbon-movie\src\main\resources\application.yml
  1370. server:
  1371.   port: 8082
  1372. spring:
  1373.   application:
  1374.     name: ribbon-movie
  1375.   cache:
  1376.     type: simple
  1377.   
  1378. #feign.okhttp.enabled默认不开启
  1379. #从Spring Cloud Dalston开始,Feign默认是不开启Hystrix的。
  1380. feign:
  1381.   okhttp:
  1382.     enabled: true
  1383.   hystrix:
  1384.     enabled: true
  1385. logging:
  1386.   level:
  1387.     root: INFO
  1388. //goto ribbon-movie\wait-for.sh
  1389. #!/bin/sh
  1390. TIMEOUT=15
  1391. QUIET=0
  1392. echoerr() {
  1393.   if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
  1394. }
  1395. usage() {
  1396.   exitcode="$1"
  1397.   cat << USAGE >&2
  1398. Usage:
  1399.   $cmdname host:port [-t timeout] [-- command args]
  1400.   -q | --quiet                        Do not output any status messages
  1401.   -t TIMEOUT | --timeout=timeout      Timeout in seconds, zero for no timeout
  1402.   -- COMMAND ARGS                     Execute command with args after the test finishes
  1403. USAGE
  1404.   exit "$exitcode"
  1405. }
  1406. wait_for() {
  1407.   for i in `seq $TIMEOUT` ; do
  1408.     nc -z "$HOST" "$PORT" > /dev/null 2>&1
  1409.     result=$?
  1410.     if [ $result -eq 0 ] ; then
  1411.       if [ $# -gt 0 ] ; then
  1412.         exec "$@"
  1413.       fi
  1414.       exit 0
  1415.     fi
  1416.     sleep 1
  1417.   done
  1418.   echo "Operation timed out" >&2
  1419.   exit 1
  1420. }
  1421. while [ $# -gt 0 ]
  1422. do
  1423.   case "$1" in
  1424.     *:* )
  1425.     HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
  1426.     PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
  1427.     shift 1
  1428.     ;;
  1429.     -q | --quiet)
  1430.     QUIET=1
  1431.     shift 1
  1432.     ;;
  1433.     -t)
  1434.     TIMEOUT="$2"
  1435.     if [ "$TIMEOUT" = "" ]; then break; fi
  1436.     shift 2
  1437.     ;;
  1438.     --timeout=*)
  1439.     TIMEOUT="${1#*=}"
  1440.     shift 1
  1441.     ;;
  1442.     --)
  1443.     shift
  1444.     break
  1445.     ;;
  1446.     --help)
  1447.     usage 0
  1448.     ;;
  1449.     *)
  1450.     echoerr "Unknown argument: $1"
  1451.     usage 1
  1452.     ;;
  1453.   esac
  1454. done
  1455. if [ "$HOST" = "" -o "$PORT" = "" ]; then
  1456.   echoerr "Error: you need to provide a host and port to test."
  1457.   usage 2
  1458. fi
  1459. wait_for "$@"
  1460. //goto ribbon-user\Dockerfile
  1461. #基础镜像
  1462. FROM adoptopenjdk/openjdk8-openj9:alpine-slim
  1463. COPY wait-for.sh /
  1464. RUN chmod +x /wait-for.sh && \
  1465.     ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
  1466. #引入运行包
  1467. COPY target/*.jar /app.jar
  1468. #指定交互端口
  1469. EXPOSE 8081
  1470. CMD ["--server.port=8081"]
  1471. #项目的启动方式
  1472. ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Xshareclasses", "-Xquickstart", "-jar", "/app.jar"]
  1473. //goto ribbon-user\pom.xml
  1474. <?xml version="1.0" encoding="UTF-8"?>
  1475. <project xmlns="http://maven.apache.org/POM/4.0.0"
  1476.         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  1477.         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  1478.         <parent>
  1479.                 <groupId>com.itmuch.cloud</groupId>
  1480.                 <artifactId>ribbon-all-in-one</artifactId>
  1481.                 <version>0.0.1</version>
  1482.         </parent>
  1483.         <modelVersion>4.0.0</modelVersion>
  1484.         <artifactId>ribbon-user</artifactId>
  1485.         <packaging>jar</packaging>
  1486.         <dependencies>
  1487.                 <dependency>
  1488.                         <groupId>org.springframework.boot</groupId>
  1489.                         <artifactId>spring-boot-starter-web</artifactId>
  1490.                         <exclusions>
  1491.                                 <exclusion>
  1492.                                         <groupId>org.springframework.boot</groupId>
  1493.                                         <artifactId>spring-boot-starter-logging</artifactId>
  1494.                                 </exclusion>
  1495.                         </exclusions>
  1496.                 </dependency>
  1497.                 <dependency>
  1498.                         <groupId>org.springframework.boot</groupId>
  1499.                         <artifactId>spring-boot-starter-log4j2</artifactId>
  1500.                 </dependency>
  1501.                 <dependency>
  1502.                         <groupId>org.springframework.boot</groupId>
  1503.                         <artifactId>spring-boot-starter-data-jpa</artifactId>
  1504.                 </dependency>
  1505.                 <dependency>
  1506.                         <groupId>com.h2database</groupId>
  1507.                         <artifactId>h2</artifactId>
  1508.                 </dependency>
  1509.                 <!-- 集成 knife4j -->
  1510.                 <dependency>
  1511.                         <groupId>com.github.xiaoymin</groupId>
  1512.                         <artifactId>knife4j-spring-boot-starter</artifactId>
  1513.                 </dependency>
  1514.         </dependencies>
  1515.         <build>
  1516.                 <finalName>${project.artifactId}</finalName>
  1517.                 <plugins>
  1518.                         <plugin>
  1519.                                 <groupId>org.springframework.boot</groupId>
  1520.                                 <artifactId>spring-boot-maven-plugin</artifactId>
  1521.                         </plugin>
  1522.                         <plugin>
  1523.                                 <groupId>io.fabric8</groupId>
  1524.                                 <artifactId>docker-maven-plugin</artifactId>
  1525.                         </plugin>
  1526.                 </plugins>
  1527.         </build>
  1528. </project>
  1529. //goto ribbon-user\src\main\java\com\itmuch\cloud\study\controller\UserController.java
  1530. package com.itmuch.cloud.study.controller;
  1531. import java.util.List;
  1532. import java.util.Optional;
  1533. import org.springframework.beans.factory.annotation.Autowired;
  1534. import org.springframework.web.bind.annotation.GetMapping;
  1535. import org.springframework.web.bind.annotation.PathVariable;
  1536. import org.springframework.web.bind.annotation.RestController;
  1537. import com.itmuch.cloud.study.entity.User;
  1538. import com.itmuch.cloud.study.repository.UserRepository;
  1539. import io.swagger.annotations.Api;
  1540. import io.swagger.annotations.ApiOperation;
  1541. @Api(tags = "user模块")
  1542. @RestController
  1543. public class UserController
  1544. {
  1545.     @Autowired
  1546.     private UserRepository userRepository;
  1547.    
  1548.     @ApiOperation("查询用户")
  1549.     @GetMapping("/{id:\\d+}")
  1550.     public Optional<User> findById(@PathVariable Long id)
  1551.     {
  1552.         return this.userRepository.findById(id);
  1553.     }
  1554.    
  1555.     @ApiOperation("查询全部用户")
  1556.     @GetMapping("getAll")
  1557.     public List<User> getAll()
  1558.     {
  1559.         return this.userRepository.findAll();
  1560.     }
  1561. }
  1562. //goto ribbon-user\src\main\java\com\itmuch\cloud\study\core\config\Knife4jConfig.java
  1563. package com.itmuch.cloud.study.core.config;
  1564. import org.springframework.beans.factory.annotation.Value;
  1565. import org.springframework.context.annotation.Bean;
  1566. import org.springframework.context.annotation.Configuration;
  1567. import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
  1568. import io.swagger.annotations.ApiOperation;
  1569. import springfox.documentation.builders.ApiInfoBuilder;
  1570. import springfox.documentation.builders.PathSelectors;
  1571. import springfox.documentation.builders.RequestHandlerSelectors;
  1572. import springfox.documentation.service.ApiInfo;
  1573. import springfox.documentation.spi.DocumentationType;
  1574. import springfox.documentation.spring.web.plugins.Docket;
  1575. import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
  1576. /**
  1577. * Knife4j配置
  1578. *
  1579. */
  1580. @Configuration
  1581. @EnableKnife4j
  1582. @EnableSwagger2WebMvc
  1583. public class Knife4jConfig
  1584. {
  1585.     @Value("${knife4j.enable:true}")
  1586.     private boolean enable;
  1587.    
  1588.     /**
  1589.      * 开发、测试环境接口文档打开
  1590.      *
  1591.      * @return
  1592.      * @see [类、类#方法、类#成员]
  1593.      */
  1594.     @Bean
  1595.     Docket createRestApi()
  1596.     {
  1597.         return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
  1598.             .enable(enable)
  1599.             .select()
  1600.             .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
  1601.             .paths(PathSelectors.any()) // 包下的类,生成接口文档
  1602.             .build();
  1603.     }
  1604.    
  1605.     private ApiInfo apiInfo()
  1606.     {
  1607.         return new ApiInfoBuilder().title("user模块API").description("接口文档").termsOfServiceUrl("http://00fly.online/").version("1.0.0").build();
  1608.     }
  1609. }
  1610. //goto ribbon-user\src\main\java\com\itmuch\cloud\study\core\config\WebMvcConfig.java
  1611. package com.itmuch.cloud.study.core.config;
  1612. import org.springframework.context.annotation.Configuration;
  1613. import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
  1614. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  1615. /**
  1616. *
  1617. * mvc配置
  1618. *
  1619. * @author 00fly
  1620. * @version [版本号, 2021年4月23日]
  1621. * @see [相关类/方法]
  1622. * @since [产品/模块版本]
  1623. */
  1624. @Configuration
  1625. public class WebMvcConfig implements WebMvcConfigurer
  1626. {
  1627.     /**
  1628.      * @param registry
  1629.      */
  1630.     @Override
  1631.     public void addViewControllers(final ViewControllerRegistry registry)
  1632.     {
  1633.         registry.addViewController("/").setViewName("doc.html");
  1634.     }
  1635. }
  1636. //goto ribbon-user\src\main\java\com\itmuch\cloud\study\entity\User.java
  1637. package com.itmuch.cloud.study.entity;
  1638. import java.math.BigDecimal;
  1639. import javax.persistence.Column;
  1640. import javax.persistence.Entity;
  1641. import javax.persistence.GeneratedValue;
  1642. import javax.persistence.GenerationType;
  1643. import javax.persistence.Id;
  1644. @Entity
  1645. public class User
  1646. {
  1647.     public User()
  1648.     {
  1649.     }
  1650.     public User(Long id, String username, String name, Integer age, BigDecimal balance)
  1651.     {
  1652.         this.id = id;
  1653.         this.username = username;
  1654.         this.name = name;
  1655.         this.age = age;
  1656.         this.balance = balance;
  1657.     }
  1658.     @Id
  1659.     @GeneratedValue(strategy = GenerationType.AUTO)
  1660.     private Long id;
  1661.     @Column
  1662.     private String username;
  1663.     @Column
  1664.     private String name;
  1665.     @Column
  1666.     private Integer age;
  1667.     @Column
  1668.     private BigDecimal balance;
  1669.     public Long getId()
  1670.     {
  1671.         return this.id;
  1672.     }
  1673.     public void setId(Long id)
  1674.     {
  1675.         this.id = id;
  1676.     }
  1677.     public String getUsername()
  1678.     {
  1679.         return this.username;
  1680.     }
  1681.     public void setUsername(String username)
  1682.     {
  1683.         this.username = username;
  1684.     }
  1685.     public String getName()
  1686.     {
  1687.         return this.name;
  1688.     }
  1689.     public void setName(String name)
  1690.     {
  1691.         this.name = name;
  1692.     }
  1693.     public Integer getAge()
  1694.     {
  1695.         return this.age;
  1696.     }
  1697.     public void setAge(Integer age)
  1698.     {
  1699.         this.age = age;
  1700.     }
  1701.     public BigDecimal getBalance()
  1702.     {
  1703.         return this.balance;
  1704.     }
  1705.     public void setBalance(BigDecimal balance)
  1706.     {
  1707.         this.balance = balance;
  1708.     }
  1709. }
  1710. //goto ribbon-user\src\main\java\com\itmuch\cloud\study\ProviderUserApplication.java
  1711. package com.itmuch.cloud.study;
  1712. import java.math.BigDecimal;
  1713. import java.net.InetAddress;
  1714. import java.util.stream.Stream;
  1715. import org.springframework.boot.ApplicationRunner;
  1716. import org.springframework.boot.SpringApplication;
  1717. import org.springframework.boot.autoconfigure.SpringBootApplication;
  1718. import org.springframework.context.annotation.Bean;
  1719. import com.itmuch.cloud.study.entity.User;
  1720. import com.itmuch.cloud.study.repository.UserRepository;
  1721. @SpringBootApplication
  1722. public class ProviderUserApplication
  1723. {
  1724.     public static void main(String[] args)
  1725.     {
  1726.         SpringApplication.run(ProviderUserApplication.class, args);
  1727.     }
  1728.    
  1729.     /**
  1730.      * 初始化用户信息 注:Spring Boot2不能像1.x一样,用spring.datasource.schema/data指定初始化SQL脚本,否则与actuator不能共存<br>
  1731.      * 原因:https://github.com/spring-projects/spring-boot/issues/13042<br>
  1732.      * https://github.com/spring-projects/spring-boot/issues/13539
  1733.      *
  1734.      * @param repository repo
  1735.      * @return runner
  1736.      */
  1737.     @Bean
  1738.     ApplicationRunner init(UserRepository repository)
  1739.     {
  1740.         return args -> {
  1741.             String ip = InetAddress.getLocalHost().getHostAddress();
  1742.             int init = (int)(System.currentTimeMillis() % 10);
  1743.             User user1 = new User(1L, "account1", "张三 from " + ip, init + 20, new BigDecimal(100.00));
  1744.             User user2 = new User(2L, "account2", "李四 from " + ip, init + 30, new BigDecimal(180.00));
  1745.             User user3 = new User(3L, "account3", "王五 from " + ip, init + 40, new BigDecimal(280.00));
  1746.             Stream.of(user1, user2, user3).forEach(repository::save);
  1747.         };
  1748.     }
  1749. }
  1750. //goto ribbon-user\src\main\java\com\itmuch\cloud\study\repository\UserRepository.java
  1751. package com.itmuch.cloud.study.repository;
  1752. import org.springframework.data.jpa.repository.JpaRepository;
  1753. import org.springframework.stereotype.Repository;
  1754. import com.itmuch.cloud.study.entity.User;
  1755. @Repository
  1756. public interface UserRepository extends JpaRepository<User, Long>
  1757. {
  1758. }
  1759. //goto ribbon-user\src\main\resources\application.yml
  1760. server:
  1761.   port: 8081
  1762. spring:
  1763.   application:
  1764.     name: ribbon-user
  1765.   jpa:
  1766.     generate-ddl: false
  1767.     show-sql: true
  1768.     hibernate:
  1769.       ddl-auto: create-drop
  1770. logging:
  1771.   level:
  1772.     root: INFO
  1773. //goto ribbon-user\wait-for.sh
  1774. #!/bin/sh
  1775. TIMEOUT=15
  1776. QUIET=0
  1777. echoerr() {
  1778.   if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
  1779. }
  1780. usage() {
  1781.   exitcode="$1"
  1782.   cat << USAGE >&2
  1783. Usage:
  1784.   $cmdname host:port [-t timeout] [-- command args]
  1785.   -q | --quiet                        Do not output any status messages
  1786.   -t TIMEOUT | --timeout=timeout      Timeout in seconds, zero for no timeout
  1787.   -- COMMAND ARGS                     Execute command with args after the test finishes
  1788. USAGE
  1789.   exit "$exitcode"
  1790. }
  1791. wait_for() {
  1792.   for i in `seq $TIMEOUT` ; do
  1793.     nc -z "$HOST" "$PORT" > /dev/null 2>&1
  1794.     result=$?
  1795.     if [ $result -eq 0 ] ; then
  1796.       if [ $# -gt 0 ] ; then
  1797.         exec "$@"
  1798.       fi
  1799.       exit 0
  1800.     fi
  1801.     sleep 1
  1802.   done
  1803.   echo "Operation timed out" >&2
  1804.   exit 1
  1805. }
  1806. while [ $# -gt 0 ]
  1807. do
  1808.   case "$1" in
  1809.     *:* )
  1810.     HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
  1811.     PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
  1812.     shift 1
  1813.     ;;
  1814.     -q | --quiet)
  1815.     QUIET=1
  1816.     shift 1
  1817.     ;;
  1818.     -t)
  1819.     TIMEOUT="$2"
  1820.     if [ "$TIMEOUT" = "" ]; then break; fi
  1821.     shift 2
  1822.     ;;
  1823.     --timeout=*)
  1824.     TIMEOUT="${1#*=}"
  1825.     shift 1
  1826.     ;;
  1827.     --)
  1828.     shift
  1829.     break
  1830.     ;;
  1831.     --help)
  1832.     usage 0
  1833.     ;;
  1834.     *)
  1835.     echoerr "Unknown argument: $1"
  1836.     usage 1
  1837.     ;;
  1838.   esac
  1839. done
  1840. if [ "$HOST" = "" -o "$PORT" = "" ]; then
  1841.   echoerr "Error: you need to provide a host and port to test."
  1842.   usage 2
  1843. fi
  1844. wait_for "$@"
复制代码
2. 部署方式

这边提供了3种docker部署方式


  • 自动ip(推荐)
  • 固定ip
  • docker scale 水平扩展
分别对应上图的docker-auto-ip、docker-fix-ip、docker-scale 目录,有兴趣的同砚,可以研究研究!
四,功能演示

http://124.71.129.204:8085/doc.html



五,其他

此实例整合了gateway、ribbon、feign、hystrix、swagger,
大家会发现hystrix熔断器起作用时并不从负载均衡中移除故障节点,大家可以思考比较下hystrix和ribbon 异同!

有任何问题和建议,都可以向我提问讨论,大家一起进步,谢谢!
-over-

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

曹旭辉

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表