Spring Cloud 快速入门系列之Ribbon–客户端负载均衡

Spring Cloud Ribbon 是一套应用于微服务的负载均衡,它和我们熟知的服务端负载均衡的不同之处在于,它是客户端负载均衡。

服务端负载均衡是服务端收到客户端请求后,根据相应机制,将请求分发到对应后台服务来处理请求;客户端负载均衡也就是调用方来控制调用哪个服务,在Spring Cloud中Ribbon的负载均衡机制默认采用轮询机制,也就是轮流访问,这种机制有一个前提就是,客户端手里持有一个服务列表,这样才能做到轮询服务。

我们接下来就实现一个负载均衡的例子。

实操O(∩_∩)O~开始

我们将于前面的Service-a服务,复制出来,新建两个服务Service-c、Service-d。

Service-c负责提供服务,因为要实现负载均衡(实现轮询调用效果),所有服务c至少要启动两个服务。

Service-d负责发起调用,也就是在d里实现负载均衡调用功能。

1、Service-c代码,因为和Service-a相同,我们这里只贴出提供服务调用的Controller代码(可以看一下之前的文章,获取Service-a项目代码)

package com.itzhimei.service.c.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ServiceCController {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCController.class);

    @GetMapping(value = "/getRibbonInfo")
    public String getInfo(){
        LOGGER.info("当前请求服务为Service-C, /getRibbonInfo");
        return "当前请求服务为Service-C";
    }
}

2、然后启动两个服务C,命令如下:

java -jar d:/service-c-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev --server.port=1130

java -jar d:/service-c-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev --server.port=1131

两个服务分别启动在1130和1131端口。

此时在注册中心,你也能看到有两个服务C注册了。

接下来就是构建发起请求方的代码,也就是Service-d

3、Service-d的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itzhimei</groupId>
    <artifactId>service-d</artifactId>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR5</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</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>
    </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>
        </plugins>
    </build>

</project>

这里和之前的服务项目的区别在于多了一个依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

4、应用启动类

package com.itzhimei.d;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@SpringBootApplication
public class ServiceDApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

	public static void main(String[] args) {
		SpringApplication.run(ServiceDApplication.class, args);
	}

}

这里重点在这里:

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

RestTemplate:是一个REST请求的模板,封装了不同Rest请求类型,支持get、、post、put等请求方式,这里同时使用了@LoadBalanced注解,相当于开启了负载均衡。

5、业务调用类,在这里就是真正发生请求的代码,ribbon的效果也在这里应用了。

package com.itzhimei.d.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class ServiceDController {

    @Autowired
    RestTemplate restTemplate;

    @RequestMapping(value = "/getRibbonInfo", method = RequestMethod.GET)
    public String getRibbonInfo() {
        String url = "http://SERVICE-C/getRibbonInfo";
        String body = restTemplate.getForEntity(url, String.class).getBody();
        return body;
    }
}

我通过ServiceId,找到服务C,就是通过这里:String url = “http://SERVICE-C/getRibbonInfo”。

然后用restTemplate发起调用,并返回结果。

6、验证结果

服务D向服务C发起请求,

http://localhost:1140/getRibbonInfo

页面输出:当前请求服务为Service-C

我一共发起了6次请求,两个服务C的控制台都输出了三次日志,如下图: