【Spring Cloud 微服务教程】 Spring Cloud Ribbon 负载均衡

2020/05/17

Spring Cloud Ribbon

概述

目前主流的负载方案分为两种:一种是集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的(比如F5),也有软件的(比如Nginx)。另一种则是客户端自己做负载均衡,根据自己的请求情况做负载,Ribbon就属于客户端自己做负载。如果用一句话介绍,那就是:Ribbon是Netflix开源的一款用于客户端负载均衡的工具软件。GitHub地址:https://github.com/Netflix/ribbon。《Spring Cloud微服务:入门、实战与进阶 》

服务端

创建应用

创建一个命名为: spring-cloud-ribbon-server-example 的 Spring cloud 应用。

   <properties>
        <java.version>1.8</java.version>
        <spring.cloud.alibaba.version>2.1.2.RELEASE</spring.cloud.alibaba.version>
        <spring.cloud.version>Greenwich.RELEASE</spring.cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </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>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring.cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

        </dependencies>
    </dependencyManagement>

配置

application.yml中配置

server:
  port: 8080
spring:
  application:
    name: ribbon-server
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

控制类实现

@RestController
@RequestMapping("/user")
public class UserController {

    private static final Log log = LogFactory.getLog(UserController.class);

    @GetMapping("/sayHello")
    public String sayHello(){
        log.info("say hello");
        return "sayHello";
    }

}

启动两个服务端 ,端口为80808081

客户端

创建应用

创建一个命名为: spring-cloud-ribbon-resttemplate-example 的 Spring cloud 应用。

   <properties>
        <java.version>1.8</java.version>
        <spring.cloud.alibaba.version>2.1.2.RELEASE</spring.cloud.alibaba.version>
        <spring.cloud.version>Greenwich.RELEASE</spring.cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </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>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring.cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

        </dependencies>
    </dependencyManagement>

配置

application.yml中配置

server:
  port: 8083
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  application:
    name: ribbon-rest


RestTemplate 配置

@Configuration
public class RestTemplateConfig {

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

控制类

@RestController
@RequestMapping("/test")
public class TestController {
    private static final Log log = LogFactory.getLog(TestController.class);

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/instances")
    public  List<ServiceInstance> instances(){
        List<ServiceInstance> instances = discoveryClient.getInstances("ribbon-server");
        return instances;
    }

    @GetMapping("/randomServerUrl")
    public String randomServerUrl(){
        List<ServiceInstance> instances = discoveryClient.getInstances("ribbon-server");
        List<String> urls = instances.stream().map(s->s.getUri().toString()).collect(Collectors.toList());
        // 随机获取
        int i = ThreadLocalRandom.current().nextInt(urls.size());
        String requestUrl = urls.get(i).concat("/user/sayHello");
        log.info("request url = "+requestUrl);
        String result = restTemplate.getForObject(requestUrl, String.class);
        return result;
    }



}

  • 接口instances:查询 ribbon-server 基本信息
  • 接口randomServerUrl:随机访问服务端

启动程序

访问接口 http://localhost:8083/test/instances ,显示服务端如下信息:

[
    {
        serviceId: "ribbon-server",
        host: "192.168.2.105",
        port: 8081,
        secure: false,
        metadata: {
            nacos.instanceId: "192.168.2.105#8081#DEFAULT#DEFAULT_GROUP@@ribbon-server",
            nacos.weight: "1.0",
            nacos.cluster: "DEFAULT",
            nacos.healthy: "true",
            preserved.register.source: "SPRING_CLOUD"
        },
        uri: "http://192.168.2.105:8081",
        scheme: null,
        instanceId: null
    },
    {
        serviceId: "ribbon-server",
        host: "192.168.2.105",
        port: 8080,
        secure: false,
        metadata: {
            nacos.instanceId: "192.168.2.105#8080#DEFAULT#DEFAULT_GROUP@@ribbon-server",
            nacos.weight: "1.0",
            nacos.cluster: "DEFAULT",
            nacos.healthy: "true",
            preserved.register.source: "SPRING_CLOUD"
        },
        uri: "http://192.168.2.105:8080",
        scheme: null,
        instanceId: null
    }
]

通过应用名称获取实例的基本信息。

浏览器多次访问接口 http://localhost:8083/test/randomServerUrl ,进入控制台查看,

从输出的结果中可以看到,负载起作用了。

@RestTemplate 整合 Ribbon

加入依赖

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

使用spring-cloud-starter-alibaba-nacos-discovery 在当前版本不需要额外整合 ribbon

修改

@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
  • ` @LoadBalanced`:开启客户端负载均衡功能

控制类

  @GetMapping("/serviceId")
    public String serviceId(){
        String result = restTemplate.getForObject("http://ribbon-server/user/sayHello", String.class);
        return result;
    }

也能达到上面相同的效果。

Ribbon 相关类和概念说明

Ribbon 负载均衡策略

Java代码配置

通过代码实现 随机访问服务端

@Configuration
public class RibbonConfig {

    @Bean
    public IRule iRule(){
        //NacosRule() :nacos 集群同地区优先获取
        return new RandomRule();
    }
}

此配置类需要和配置类隔离,不能被 @ComponentScan 扫描到,spring 和 ribbon 上下文重叠,不然会被 ribbon 客户端共享,官方提示如下:

The CustomConfiguration class must be a @Configuration class, but take care that it is not in a @ComponentScan for the main application context. Otherwise, it is shared by all the @RibbonClients. If you use @ComponentScan (or @SpringBootApplication), you need to take steps to avoid it being included (for instance, you can put it in a separate, non-overlapping package or specify the packages to scan explicitly in the @ComponentScan).

@SpringBootApplication
@RibbonClient(name = "ribbon-server",configuration = RibbonConfig.class)
public class SpringCloudRibbonResttemplateExampleApplication {

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

}
  • @RibbonClient(name = "ribbon-server",configuration = RibbonConfig.class): 请求ribbon-server服务端,采用的RibbonConfig随机算法。

全局配置

@RibbonClients(defaultConfiguration = RibbonConfig.class ) 

请求所有微服务都采用自定义配置。

启动测试

启动 ribbon-server 8081和 8080 端口,启动客户端 8083 ,访问 http://localhost:8083/test/serviceId ,通过服务端控制端日志查看。

配置项配置

配置属性方式:

application.yml 配置如下

# 针对 ribbon-server ribbon配置
ribbon-server:
  ribbon:
    # 加载规则
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

同上面的 java 代码配置一样,不需要考虑上下文扫描问题,也更容易理解

首次加载过慢问题

在客户端请求 http://localhost:8083/test/serviceId

    @GetMapping("/serviceId")
    public String serviceId(){
        String result = restTemplate.getForObject("http://ribbon-server/user/sayHello", String.class);
        return result;
    }

在地址中通过 serviceId 请求首次加载相对比较慢,可以通过在application.yml配置如下解决:

# 饥饿加载
ribbon:
  eager-load:
    enabled: true
    # 加载列表 多个 使用逗号隔开
    clients: ribbon-server

文章参考

  • https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html

代码示例

本文示例代码访问下面查看仓库:

其中,本文示例代码名称:

  • spring-cloud-ribbon-server-example: Spring Cloud Ribbon 服务端
  • spring-cloud-ribbon-resttemplate-example: Spring Cloud Ribbon RestTemplate


微信扫描二维码,关注一个有故事的程序员

(转载本站文章请注明作者和出处 山间木匠-mtcarpenter

Post Directory

扫码关注公众号:山间木匠
发送 290992
即可立即永久解锁本站全部文章