前言
定制化开发通常我们如果有一个服务,定制化开发会部署到多台服务器上,定制化开发这些微服务如果都暴露给客户,定制化开发是非常难以管理的,定制化开发我们系统需要有一个唯一的出口,Spring Cloud 定制化开发是一个服务,定制化开发是系统的唯一出口。Spring Cloud Gateway定制化开发网关封装了系统内部的微服务,定制化开发为客户端提供一个定制的API。定制化开发客户端只需要调用网关接口,定制化开发就可以调用到实际的微服务,实际的服务对客户不可见,并且容易扩展服务。
Spring Cloud Gateway网关可以结合完成负载均衡的功能,可以自动检查微服务的状况,及时剔除或者加入某个微服务到可用服务列表。此外Spring Cloud Gateway网关可以完成权限检查、限流、统计等功能。下面我们将一一完成上面的功能。注意微服务只是提供rest的接口,不会有额外的组件依赖,不需要eureka等。只需要两个工程,一个是微服务,我们可以部署到多台服务器,那么只是访问的ip不同,在演示的时候,我们在本机演示,修改端口,达到启动多个微服务的目的,另一个就是网关,主要是 Spring Cloud Gateway 和 Ribbon两大组件来实现网关和负载均衡等功能。
1、构建两个工程
这里不再介绍如何构建两个微服务步骤,请自行构建,我建立微服务于情况如下:
1.1、搭建两个微服务,端口为8072、8073
1.2、在controller控制层编写测试接口
1.2、访问地址
3
2、构建网关微服务
请自行构建网关微服务,我构建的网关微服务名字:esb-gateway,可以使用:
2.1、修改pom.xml文件
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.modules.scistor</groupId> <artifactId>esb-gateway</artifactId> <version>0.0.1-SNAPSHOT</version> <name>esb-gateway</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR3</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!-- 网关gateway --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!-- 负载ribbon --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> </dependencies> <!-- 依赖版本管理 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!--添加配置跳过测试--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.2</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> </plugins> </build></project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
2.2、修改application.yml文件
server: port: 8070spring: cloud: gateway: # 跨域配置 globalcors: cors-configurations: '[/**]': allowedOrigins: "*" allowedMethods: - GET - POST - PUT - DELETE - OPTIONS # 路由负载配置 default-filters: routes: - id: my_route uri: lb://load-balanced-service predicates: - Path=/crs/** filters: - StripPrefix=1load-balanced-service: ribbon: # 负载地址 listOfServers: http://localhost:8072, http://localhost:8073 # 负载轮询策略 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule # 健康检查 NFLoadBalancerPingClassName: com.modules.scistor.config.HealthExamination
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
注意:
listOfServers:配置的微服务的服务器ip端口,多个用,隔开配置。
NFLoadBalancerRuleClassName:使用的负载均衡策略。
负载均衡策略介绍
**RoundRobinRule:**简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。
**AvailabilityFilteringRule:**对以下两种服务器进行忽略:
(1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。
注意:可以通过修改配置loadbalancer..connectionFailureCountThreshold来修改连接失败多少次之后被设置为短路状态。默认是3次。
(2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上线,可以由客户端的..ActiveConnectionsLimit属性进行配置
**WeightedResponseTimeRule:**为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。
**ZoneAvoidanceRule:**以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。
**BestAvailableRule:**忽略哪些短路的服务器,并选择并发数较低的服务器。
**RandomRule:**随机选择一个可用的服务器。
**Retry:**重试机制的选择逻辑
自定义负载均衡策略
可以自定义负载均衡算法。需求是:每个服务器访问三次再跳转到下一个服务器。
(1)负载均衡算法(参考RandomRule)
package com.modules.scistor.config;import com.netflix.client.config.IClientConfig;import com.netflix.loadbalancer.AbstractLoadBalancerRule;import com.netflix.loadbalancer.ILoadBalancer;import com.netflix.loadbalancer.Server;import java.util.ArrayList;import java.util.List;public class MyRule extends AbstractLoadBalancerRule{ private volatile int total; private volatile int index; List<Server> upList = new ArrayList<>(); public MyRule() { } public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } else { Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } List<Server> allList = lb.getAllServers(); int serverCount = allList.size(); if (serverCount == 0) { return null; } if (total == 0) { upList = lb.getReachableServers(); } if (total < 3) { if (upList.size() != lb.getReachableServers().size()) { index = 0; } server = lb.getReachableServers().get(index); total++; } else { total = 0; index++; if (index >= lb.getReachableServers().size()) { index = 0; } } if (server == null) { Thread.yield(); } else { if (server.isAlive()) { return server; } server = null; Thread.yield(); } } return server; } } public Server choose(Object key) { return this.choose(this.getLoadBalancer(), key); } public void initWithNiwsConfig(IClientConfig clientConfig) { }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
(2)修改配置文件
2.3、测试
访问health
3、健康监测
3.1、新建一个Config类
package com.modules.scistor.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.client.RestTemplate;/** * @Auther: lc * @Date: 2020/7/1 11:37 * @Description: 健康检查配置类 */@Configurationpublic class HealthConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
3.2、新建一个健康检查的类,调用heath接口
package com.modules.scistor.config;import com.netflix.loadbalancer.IPing;import com.netflix.loadbalancer.Server;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.client.RestTemplate;/** * @Auther: lc * @Date: 2020/7/1 11:39 * @Description: 健康检查实现 */public class HealthExamination implements IPing { @Autowired private RestTemplate restTemplate; @Override public boolean isAlive(Server server) { String url = "http://" + server.getId() + "/health"; try { ResponseEntity<String> heath = restTemplate.getForEntity(url, String.class); if (heath.getStatusCode() == HttpStatus.OK) { System.out.println("ping " + url + " success and response is " + heath.getBody()); return true; } System.out.println("ping " + url + " error and response is " + heath.getBody()); return false; } catch (Exception e) { System.out.println("ping " + url + " failed"); return false; } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
上面代码继承IPing接口,判断服务是否可用。我们在微服务中增加heath接口,在gateway中调用该接口,如果返回正常则认为微服务可用。
3.3、配置文件修改
3.4、项目结构
至此使用 Spring Cloud Gateway + Ribbon 负载均衡项目实战结束,全文完。