ribbon
是一个客户端负载均衡器
spring-cloud是微服务的解决方案它会把服务注册到 注册中心 比如nacos 这时我们就可以通过调用nacos的接口让它返回我们想要的模块信息然后把信息拼装成请求地址 再使用调用接口的类比如
1)Httpclient
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 Http 协议的客户端编程工具包,并且它支持 HTTP 协议最新版本和建议。HttpClient 相比传统 JDK 自带的 URLConnection,提升了易用性和灵活性,使客户端发送 HTTP 请求变得容易,提高了开发的效率。
2)Okhttp
一个处理网络请求的开源项目,是安卓端最火的轻量级框架,由 Square 公司贡献,用于替代 HttpUrlConnection 和 Apache HttpClient。OkHttp 拥有简洁的 API、高效的性能,并支持多种协议(HTTP/2 和 SPDY)。
3)HttpURLConnection
HttpURLConnection 是 Java 的标准类,它继承自 URLConnection,可用于向指定网站发送 GET 请求、POST 请求。HttpURLConnection 使用比较复杂,不像 HttpClient 那样容易使用。
4)RestTemplate WebClient
RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 HTTP 服务的方法,能够大大提高客户端的编写效率。
就可以请求到模块对应的方法上 而ribbon跟loadBalanced给我实现了去注册中心查找模块的信息 然后拼装成请求地址的功能 比如使用RestTemplate只需要给RestTemplate添加一个ribbon的拦截器 我们就不要管 去注册中心查找服务的信息拼装请求地址的业务了 只需要关注我们的业务就可以了
@Autowired
LoadBalancerClient loadBalancer;
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
// return new RestTemplate();
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(new LoadBalancerInterceptor(loadBalancer)));
return restTemplate;
}
@Autowired
LoadBalancerClient loadBalancer; 这句代码 如果导入了ribbon依赖会创建RibbonLoadBalancerClient这样的一个实例 然后通过restTemplate.setInterceptors(Collections.singletonList(new LoadBalancerInterceptor(loadBalancer)));这句代码给RestTemplate设置拦截器 设置了拦截器 当我们使用RestTemplate调用接口时会进入ribbon的拦截器 给我解析请求 比如 aa是服务名 请求地址是http://aa/a 就是去访问aa服务中的a接口 这是ribbon会给我把aa当作服务名去注册中心查找这个服务的真实地址 比如127.0.0.1:9090 就会把地址拼接成http://127.0.0.1:9090/a 这样 然后根据这个真实地址去调用服务
Spring-Cloud 整合Ribbon
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
添加@loadBalanced注解
@Configuration
public class RestConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
添加了@LoadBalanced这个注解 就是会给RestTemplate添加一个ribbon实现了拦截器 这个拦截器就是去注册中心根据服务名去找地址 然后调用
controller层
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/findOrderByUserId/{id}")
public R findOrderByUserId(@PathVariable("id") Integer id) {
// RestTemplate调用
//String url = "http://localhost:8020/order/findOrderByUserId/"+id;
//模拟ribbon实现 mall-order是服务名
//String url = getUri("mall-order")+"/order/findOrderByUserId/"+id;
// 添加@LoadBalanced mall-order是服务名
String url = "http://mall-order/order/findOrderByUserId/"+id;
R result = restTemplate.getForObject(url,R.class);
return result;
}
模拟ribbon实现
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/findOrderByUserId/{id}")
public R findOrderByUserId(@PathVariable("id") Integer id) {
// RestTemplate调用
//String url = "http://localhost:8020/order/findOrderByUserId/"+id;
//模拟ribbon实现
String url = getUri("mall-order")+"/order/findOrderByUserId/"+id;
// 添加@LoadBalanced
//String url = "http://mall-order/order/findOrderByUserId/"+id;
R result = restTemplate.getForObject(url,R.class);
return result;
}
@Autowired
private DiscoveryClient discoveryClient;
public String getUri(String serviceName) {
List serviceInstances = discoveryClient.getInstances(serviceName);
if (serviceInstances == null || serviceInstances.isEmpty()) {
return null;
}
int serviceSize = serviceInstances.size();
//轮询
int indexServer = incrementAndGetModulo(serviceSize);
return serviceInstances.get(indexServer).getUri().toString();
}
private AtomicInteger nextIndex = new AtomicInteger(0);
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextIndex.get();
int next = (current + 1) % modulo;
if (nextIndex.compareAndSet(current, next) && current < modulo){
return current;
}
}
}
Ribbon扩展点
参考: org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration
IClientConfig:Ribbon的客户端配置,默认采用DefaultClientConfigImpl实现。
IRule:Ribbon的负载均衡策略,默认采用ZoneAvoidanceRule实现,该策略能够在多区域环境下选出最佳区域的实例进行访问。
IPing:Ribbon的实例检查策略,默认采用DummyPing实现,该检查策略是一个特殊的实现,实际上它并不会检查实例是否可用,而是始终返回true,默认认为所有服务实例都是可用的。
ServerList:服务实例清单的维护机制,默认采用ConfigurationBasedServerList实现。
ServerListFilter:服务实例清单过滤机制,默认采ZonePreferenceServerListFilter,该策略能够优先过滤出与请求方处于同区域的服务实例。
ILoadBalancer:负载均衡器,默认采用ZoneAwareLoadBalancer实现,它具备了区域感知的能力。
Spring Cloud LoadBalancer
LoadBalancer是spring cloud官方用来替代ribbon的
提供了两个客户端
RestTemplate
RestTemplate是spring提供的用来访问http服务的工具类 可以提高编程效率
WebClient
非阻塞的基于响应式编程的进行Http请求的客户端工具。
RestTemplate整合LoadBalancer
org.springframework.cloud
spring-cloud-starter-loadbalancer
org.springframework.boot
spring-boot-starter-web
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
在yml文件中设置不适用ribbon即可
spring:
application:
name: mall-user-loadbalancer-demo
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 不使用ribbon
loadbalancer:
ribbon:
enabled: false
使用@LoadBalanced配置RestTemplate
@Configuration
public class RestConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
使用
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/findOrderByUserId/{id}")
public R findOrderByUserId(@PathVariable("id") Integer id) {
String url = "http://mall-order/order/findOrderByUserId/"+id;
R result = restTemplate.getForObject(url,R.class);
return result;
}
}
WebClient整合LoadBalancer
引入依赖
org.springframework.cloud
spring-cloud-starter-loadbalancer
org.springframework.boot
spring-boot-starter-webflux
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
配置WebClient作为负载均衡器的client
@Configuration
public class WebClientConfig {
@LoadBalanced
@Bean
WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
@Bean
WebClient webClient() {
return webClientBuilder().build();
}
}
使用
@Autowired
private WebClient webClient;
@RequestMapping(value = "/findOrderByUserId2/{id}")
public Mono findOrderByUserIdWithWebClient(@PathVariable("id") Integer id) {
String url = "http://mall-order/order/findOrderByUserId/"+id;
//基于WebClient
Mono result = webClient.get().uri(url)
.retrieve().bodyToMono(R.class);
return result;
}
使用webFlux
@Autowired
private ReactorLoadBalancerExchangeFilterFunction lbFunction;
@RequestMapping(value = "/findOrderByUserId3/{id}")
public Mono findOrderByUserIdWithWebFlux(@PathVariable("id") Integer id) {
String url = "http://mall-order/order/findOrderByUserId/"+id;
//基于WebClient+webFlux
Mono result = WebClient.builder()
.filter(lbFunction)
.build()
.get()
.uri(url)
.retrieve()
.bodyToMono(R.class);
return result;
}