前言
通常我们如果有一个服务,会部署到多台服务器上,这些微服务如果都暴露给客户,是非常难以管理的,我们系统需要有一个唯一的出口,Spring Cloud Gateway 网关是一个服务,是系统的唯一出口。Spring Cloud Gateway 网关封装了系统内部的微服务,为客户端提供一个定制的API。客户端只需要调用网关接口,就可以调用到实际的微服务,实际的服务对客户不可见,并且容易扩展服务。
Spring Cloud Gateway 网关可以结合Ribbon完成负载均衡的功能,可以自动检查微服务的状况,及时剔除或者加入某个微服务到可用服务列表。此外Spring Cloud Gateway 网关可以完成权限检查、限流、统计等功能。下面我们将一一完成上面的功能。注意微服务只是提供rest的接口,不会有额外的组件依赖,不需要eureka等。只需要两个工程,一个是微服务,我们可以部署到多台服务器,那么只是访问的ip不同,在演示的时候,我们在本机演示,修改端口,达到启动多个微服务的目的,另一个就是网关,主要是 Spring Cloud Gateway 和 Ribbon两大组件来实现网关和负载均衡等功能。
1、构建两个工程
这里不再介绍如何构建两个微服务步骤,请自行构建,我建立微服务于情况如下:
1.1、搭建两个微服务,端口为8072、8073
1.2、在controller控制层编写测试接口
1.2、访问地址
http://localhost:8072
http://localhost:8073
2、构建网关微服务
请自行构建网关微服务,我构建的网关微服务名字:esb-gateway,可以使用:https://start.spring.io/
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>
2.2、修改application.yml文件
server:
port: 8070
spring:
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=1
load-balanced-service:
ribbon:
# 负载地址
listOfServers: http://localhost:8072, http://localhost:8073
# 负载轮询策略
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
# 健康检查
NFLoadBalancerPingClassName: com.modules.scistor.config.HealthExamination
注意:
listOfServers:配置的微服务的服务器ip端口,多个用,隔开配置。
NFLoadBalancerRuleClassName:使用的负载均衡策略。
负载均衡策略介绍
RoundRobinRule:简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。
AvailabilityFilteringRule:对以下两种服务器进行忽略:
(1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。
注意:可以通过修改配置loadbalancer.<clientName>.connectionFailureCountThreshold来修改连接失败多少次之后被设置为短路状态。默认是3次。
(2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上线,可以由客户端的<clientName>.<clientConfigNameSpace>.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)
{
}
}
(2)修改配置文件
2.3、测试
访问 http://localhost:8070/crs/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: 健康检查配置类
*/
@Configuration
public class HealthConfig {
@Bean
public RestTemplate restTemplate()
{
return new RestTemplate();
}
}
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;
}
}
}
上面代码继承IPing接口,判断服务是否可用。我们在微服务中增加heath接口,在gateway中调用该接口,如果返回正常则认为微服务可用。
3.3、配置文件修改
3.4、项目结构
至此使用 Spring Cloud Gateway + Ribbon 负载均衡项目实战结束,全文完。