4. Netflix.Ribbon
4.1 简介
(1) 概念
- Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡工具。
(2) 负载均衡(LB:LoadBalance)和集群架构
- 应用集群:将同一应用部署到多台机器上,组成处理集群,接收负载均衡设备分发的请求,进行处理,并返回相应数据。
- 负载均衡设备:将用户访问的请求,根据负载均衡算法,分发到集群中的一台处理服务器。(一种把网络请求分散到一个服务器集群中的可用服务器上去的设备)。
- 负载均衡的作用(解决的问题):
- 解决并发压力,提高应用处理性能(增加吞吐量,加强网络处理能力);
- 提供故障转移,实现高可用;
- 通过添加或减少服务器数量,提供网站伸缩性(扩展性);
- 安全防护;(负载均衡设备上做一些过滤,黑白名单等处理)
(3) 负载均衡分类
- 集中式 LB
- 在服务的消费方和提供方之间使用独立的 LB 设施,如 Nginx(反向代理服务器),由该设施把访问请求通过某种策略转发至服务的提供方。
- 进程式 LB
- 将 LB 逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后从这些地址中选用一个服务器。
- Ribbon 属于进程式 LB,它是一个类库,集成于消费方进程,消费方通过它来获取提供方的地址。
4.2 集成 Ribbon
调整 springcloud-consumer-dept-80 服务
● 添加依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-ribbon</artifactId><version>1.4.6.RELEASE</version>
</dependency><!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka</artifactId><version>1.4.6.RELEASE</version>
</dependency>
● 为 RestTemplate 配置负载均衡
@Configuration // spring applicationContext.xml
public class ConfigBean {// 注册 bean <bean></bean>@Bean// 配置负载均衡实现 RestTemplate@LoadBalancedpublic RestTemplate getRestTemplate() {return new RestTemplate();}
}
● 修改获取远程服务的地址
// DeptConsumerController
// Ribbon 实现负载均衡,此处的地址应是一个变量——服务名
// private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
● 添加 Eureka 配置
# eureka
eureka:client:# 不向 eureka 注册自己register-with-eureka: false# 列出可使用的服务地址供消费者服务选择service-url:defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
● 开启 Eureka 客户端服务
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {public static void main(String[] args) {SpringApplication.run(DeptConsumer_80.class, args);}
}
4.3 模拟多服务访问
● 创建数据库
cloud01.dept.db_source = cloud01
cloud02.dept.db_source = cloud02
cloud03.dept.db_source = cloud03
● 创建两个 maven 模块作为服务提供者
springcloud-provider-dept-8001
springcloud-provider-dept-8002
springcloud-provider-dept-8003
-
修改配置文件
springcloud-provider-dept-8001 => cloud01 springcloud-provider-dept-8002 => cloud02 springcloud-provider-dept-8003 => cloud03
● 访问效果
4.4 自定义 Ribbon 规则
(1) 创建 Ribbon 配置类
-
自定义配置类的组件会覆盖 RibbonClientConfiguration 中的组件来完成配置。
-
自定义配置类的注解必须是 @Configuration,其不在主应用程序下的 @ComponentScan 中,否则将由所有 @RibbonClients 共享,若使用 @ComponentScan / @SpingBootApplication,则需要采取避免措施,,如将其放在一个单独的,不重叠的包中,或指定在 @ComponentScan。
-
自定义配置类放在启动类所在的目录外
/*** 每个服务访问 5 次,选择下一个服务* @author why* @since 2021/9/7 21:02*/ public class RoundFiveRule extends AbstractLoadBalancerRule {// 被调用次数private int toltal = 0;// 当前提供服务的索引private int currentIndex = 0;// @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")public Server choose(ILoadBalancer lb, Object key) {if (lb == null) {return null;}Server server = null;while (server == null) {if (Thread.interrupted()) {return null;}// 获取可用服务List<Server> upList = lb.getReachableServers();// 获取所有服务List<Server> allList = lb.getAllServers();int serverCount = allList.size();if (serverCount == 0) {return null;}// // 生成区间随机数// int index = chooseRandomInt(serverCount);// // 随机获取可用服务// server = upList.get(index);//===============================================================================if (toltal < 5) {server = upList.get(currentIndex);toltal++;} else {toltal = 1;currentIndex++;if (currentIndex >= upList.size()) {currentIndex = 0;}server = upList.get(currentIndex);}System.out.print(" " + currentIndex + " ");//================================================================================if (server == null) {Thread.yield();continue;}if (server.isAlive()) {return (server);}server = null;Thread.yield();}return server;}protected int chooseRandomInt(int serverCount) {return ThreadLocalRandom.current().nextInt(serverCount);}@Overridepublic Server choose(Object key) {return choose(getLoadBalancer(), key);}@Overridepublic void initWithNiwsConfig(IClientConfig clientConfig) {// TODO Auto-generated method stub}}
(2) 将自定义配置类注册到 Spring 中
@Configuration
public class MyRule {// 将自定义配置类注册到 Spring 中@Beanpublic IRule myRule() {return new RoundFiveRule();}
}
(3) 配置启动类自动加载自定义配置类
@SpringBootApplication
@EnableEurekaClient
/** 微服务启动时,加载 Ribbon 配置类* @param name 服务名* @param configuration 自定义配置类的反射*/
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT", configuration = RoundFiveRule.class)
public class DeptConsumer_80 {public static void main(String[] args) {SpringApplication.run(DeptConsumer_80.class, args);}
}
虽然 index 为轮询方式,但由于 index 并未与服务进行一对一绑定,所以服务的访问准确来说并不是每 5 次就更换下一个服务。
5. Netflix.Feign
5.1 简介
- Feign 是声明式的 web service 客户端,它简化了微服务之间的调用。
- Ribbon 使用微服务名字调用微服务,Feign 使用接口和注解调用微服务。
- 创建一个接口,使用 Feign 注解进行配置,即可完成对服务提供方的接口绑定。
- Feign 集成了 Ribbon,通过添加一层的方式增加了代码的可读性,但是性能有所降低
5.2 Feign 实现
调整 springcloud-consumer-dept-80 微服务
● 创建 maven moudle
● 添加依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-feign -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-feign</artifactId><version>1.4.6.RELEASE</version>
</dependency>
● 删除 Ribbon 配置
● 编写 Feign 服务层
在公共服务 springcloud-api 中添加 Feign 服务层
/*** Feign 实现服务的调用** @author why* @since 2021/9/9 9:24*/
// 将接口注册到 Spring,否则会报红,但是却不影响调用,原因不明
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
public interface DeptClientService {@GetMapping("/dept/getDept/{id}")public Dept queryById(@PathVariable("id") Long id);@GetMapping("/dept/list")public List<Dept> queryAll();@PostMapping("/dept/add")public boolean addDept(Dept dept);
}
● 启动类开启 Feign 扫描
@SpringBootApplication
@EnableEurekaClient
/** @EnableFeignClients(basePackages = {"com.why.springcloud"}) 扫描 springcloud-api * 务 com.why.springcloud 目录及以内目录下的 @FeignClient*/
@EnableFeignClients(basePackages = {"com.why.springcloud"})
public class FeignDeptConsumer_80 {public static void main(String[] args) {SpringApplication.run(FeignDeptConsumer_80.class, args);}
}
● 远程调用
Feign 服务层也可以放在当前服务服务中,此时无需对注解 @EnableFeignClients 的属性赋值
@SpringBootApplication
@EnableEurekaClient
/** @EnableFeignClients 扫描当前服务主类同级及以内目录下的 @FeignClient*/
@EnableFeignClients
public class FeignDeptConsumer_80 {public static void main(String[] args) {SpringApplication.run(FeignDeptConsumer_80.class, args);}
}