定制化开发使用 Spring Cloud Gateway + Ribbon 负载均衡实战

前言

定制化开发通常我们如果有一个服务,定制化开发会部署到多台服务器上,定制化开发这些微服务如果都暴露给客户,定制化开发是非常难以管理的,定制化开发我们系统需要有一个唯一的出口,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 负载均衡项目实战结束,全文完。

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发