阅读 254

Spring Cloud Alibaba 使用之Ribbon

Ribbon使用心得

简单使用

先整理思路,写一个生产端和一个消费端,启动两个生产端,然后消费端通过Ribbon的负载均衡功能访问生产端。

生产端

  1. 引入依赖
  2. 写配置文件
  3. 启动类
  4. controller类

项目结构

整体项目结构如下图

image

引入依赖

在父项目中引入依赖,参考前文Nacos使用心得,子项目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>
        <artifactId>Learn</artifactId>
        <groupId>zyz.springCloudAlibaba</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ribbon-produce</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-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</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
复制代码

写配置文件

编写配置文件application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  application:
    name: ribbon-provider
server:
  port: 8081
复制代码

新建启动类

新建启动类RibbonProvider

package com.zyz;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class RibbonProvider {

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

}
复制代码

新建Controller类

package com.zyz.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class ProviderController {

    @GetMapping(value = "/ribbon/provider/{string}")
    public String echo(@PathVariable String string){
        log.info("Receive this request");
        return "Hello Ribbon provider Discovery " + string;
    }

}
复制代码

引入日志注解@Slf4j

参考

消费端

  1. 引入依赖
  2. 写配置文件
  3. 启动类
  4. 配置类
  5. controller类

整体项目结构

整体项目结构如下图

image

引入依赖

<?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>
        <artifactId>Learn</artifactId>
        <groupId>zyz.springCloudAlibaba</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ribbon-consumer</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-discovery</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-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

</project>
复制代码

写配置文件

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  application:
    name: ribbon-consumer
server:
  port: 8083
复制代码

启动类

package com.zyz;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class RibbonConsumer {

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

}
复制代码

配置类

package com.zyz.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class WebConfig {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
复制代码

controller类

package com.zyz.contorller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping(value = "/ribbon/consumer/{str}")
    public String rest(@PathVariable String str){
        return restTemplate.getForObject("http://ribbon-provider/ribbon/provider/" + str, String.class);
    }

}
复制代码

启动项目

启动两个生产端,用消费端进行访问,验证结果

启动技巧

生产端的端口是8081,我们用8081启动一个服务后,然后修改端口为8082,然后修改下图,那么就可以同一个工程启动两个实例

Mac端:选中Allow parallel run image

验证成功

Ribbon的内置的负载均衡算法

RandomRule

随机选择一个Server

RetryRule

对选定的负载均衡策略加上重试机制,在一个配置时间段内当选择Server不成功, 则一直尝试使用subRule的方式选择一个可用的server

RoundRobinRule

轮询选择, 轮询index,选择index对应位置的Server

AvailabilityFilteringRule

过滤掉一直连接失败的被标记为circuit tripped的后端Server,并过滤掉那些高并发的后端 Server或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查 status里记录的各个Server的运行状态

BestAvailableRule

选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过。

WeightedResponseTimeRule

根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低

ZoneAvoidanceRule(默认是这个)

复合判断Server所在Zone的性能和Server的可用性选择Server,在没有Zone的情况下类是轮询。

Ribbon的负载均衡策略自定义配置

Ribbon还支持自定义配置负载均衡策略,假设有订单中心要采用随机算法调用,库存中心要采用轮询算法调用。

要建立三个服务,第一个是订单中心、第二个是库存中心,第三个是调用方。

订单中心

  1. 引入依赖
  2. 写配置文件
  3. 启动类
  4. controller类

整体结构

引入依赖

<?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>
        <artifactId>Learn</artifactId>
        <groupId>zyz.springCloudAlibaba</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ribbon-product</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-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</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
复制代码

写配置文件

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  application:
    name: ribbon-product
server:
  port: 8081
复制代码

启动类

package com.zyz;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class RibbonProduct {

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

}
复制代码

controlelr类

package com.zyz.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class ProductController {

    @GetMapping(value = "/ribbon/product/{string}")
    public String echo(@PathVariable String string){
        log.info("Receive this request");
        return "Hello Ribbon product Discovery " + string;
    }

}
复制代码

库存中心

  1. 引入依赖
  2. 写配置文件
  3. 启动类
  4. controller类

引入依赖

<dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-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</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
复制代码

写配置文件

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  application:
    name: ribbon-order
server:
  port: 8083
复制代码

启动类

package com.zyz;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class RibbonOrder {
    public static void main(String[] args) {
        SpringApplication.run(RibbonOrder.class, args);
    }
}
复制代码

controller类

package com.zyz.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class OrderController {
    @GetMapping(value = "/ribbon/order/{str}")
    public String order(@PathVariable String string){
        log.info("Receive this request");
        return "Hello Ribbon order Discovery " + string;
    }
}
复制代码

客户端

  1. 引入依赖
  2. 写配置文件(包含负载均衡的配置策略)
  3. 启动类
  4. controller类
  5. 配置类

整体结构

image

引入依赖

<?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>
        <artifactId>Learn</artifactId>
        <groupId>zyz.springCloudAlibaba</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ribbon-app</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-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</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
复制代码

写配置文件

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  application:
    name: ribbon-app
server:
  port: 8088

ribbon-order:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

ribbon-product:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule

#开启ribbon饥饿加载,解决微服务调用第一次很慢的情况下
ribbon:
  eager-load:
    enabled: true
    #可以指定多个微服务用逗号分隔
    clients: ribbon-order,ribbon-product
复制代码

启动类

package com.zyz;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class RibbonApp {

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

}
复制代码

controller类

package com.zyz.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@Slf4j
@RestController
public class RibbonController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping(value = "/ribbon/app/product/{str}")
    public String product(@PathVariable String str){
        return restTemplate.getForObject("http://ribbon-product/ribbon/product/" + str, String.class);
    }

    @GetMapping(value = "/ribbon/app/order/{str}")
    public String order(@PathVariable String str){
        return restTemplate.getForObject("http://ribbon-order/ribbon/order/" + str, String.class);
    }

}
复制代码

配置类

package com.zyz.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class WebConfig {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
复制代码

Ribbon常用参数详解

常用参数

Ribbon自定义负载均衡策略

Nacos支持权重配置,这是个比较实用的功能,例如:

  1. 把性能差的机器权重设低,性能好的机器权重设高,让请求优先打到性能高的机器上去
  2. 某个实例出现异常时,把权重设低,排查问题,问题排查完再把权重恢复
  3. 想要下线某个实例时,可先将该实例的权重设为0,这样流量就不会打到该实例上了——此时再去关停该实例,这样就能实现优雅下线

下面就对Ribbon进行扩展,让其支持Nacos的权重配置

自定义权重规则类

package com.zyz.rule;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

@Slf4j
public class WeightRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        //读取配置文件并且初始化,ribbon内部的 几乎用不上
    }

    @Override
    public Server choose(Object key) {
        try {
            log.info("key:{}",key);
            BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
            log.info("baseLoadBalancer--->:{}",baseLoadBalancer);

            //获取微服务的名称
            String serviceName = baseLoadBalancer.getName();

            //获取Nacos服务发现的相关组件API
            NamingService namingService = discoveryProperties.namingServiceInstance();

            //获取一个基于nacos client实现权重的负载均衡算法
            Instance instance = namingService.selectOneHealthyInstance(serviceName);

            //返回一个server
            return new NacosServer(instance);
        }catch (NacosException e){
            log.error("权重算法错误");
        }
        return null;
    }
}
复制代码

添加配置

ribbon-product:
  ribbon:
    NFLoadBalancerRuleClassName: com.zyz.rule.WeightRule
复制代码
文章分类
后端
文章标签