使用 Spring Cloud Gateway + Ribbon 負載均衡實戰

前言

通常我們如果有一個服務,會部署到多臺服務器上,這些微服務如果都暴露給客戶,是非常難以管理的,我們系統需要有一個唯一的出口,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 負載均衡項目實戰結束,全文完。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章