Ribbon使用心得
简单使用
先整理思路,写一个生产端和一个消费端,启动两个生产端,然后消费端通过Ribbon的负载均衡功能访问生产端。
生产端
- 引入依赖
- 写配置文件
- 启动类
- 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
消费端
- 引入依赖
- 写配置文件
- 启动类
- 配置类
- 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还支持自定义配置负载均衡策略,假设有订单中心要采用随机算法调用,库存中心要采用轮询算法调用。
要建立三个服务,第一个是订单中心、第二个是库存中心,第三个是调用方。
订单中心
- 引入依赖
- 写配置文件
- 启动类
- 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;
}
}
库存中心
- 引入依赖
- 写配置文件
- 启动类
- 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;
}
}
客户端
- 引入依赖
- 写配置文件(包含负载均衡的配置策略)
- 启动类
- 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-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支持权重配置,这是个比较实用的功能,例如:
- 把性能差的机器权重设低,性能好的机器权重设高,让请求优先打到性能高的机器上去
- 某个实例出现异常时,把权重设低,排查问题,问题排查完再把权重恢复
- 想要下线某个实例时,可先将该实例的权重设为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
网友评论