本文已收录于专栏

《中间件合集》

目录

概念说明什么是RibbonRibbon和Nginx负载均衡的区别

工作流程代码实现RibbonSDK发送请求端引入RibbonSDK和Nacos的依赖配置文件中填写负载均衡策略调用代码

接收请求端执行效果发送请求端接收请求端

总结提升

概念说明

什么是Ribbon

  Ribbon 是一个客户端负载均衡器,它是Spring Cloud Netflix开源的一个组件,用于在分布式系统中实现对服务实例的负载均衡。它可以作为一个独立的组件使用,也可以与 Spring Cloud 等微服务框架集成使用。   Ribbon 的主要功能是根据一定的负载均衡策略,将客户端请求分配到可用的服务实例上,以提高系统的可用性和性能。它通过周期性地从服务注册中心(如 Eureka)获取可用的服务实例列表,并根据配置的负载均衡策略选择合适的实例来处理请求。Ribbon 支持多种负载均衡策略,如轮询、随机、加权随机、加权轮询等。

Ribbon和Nginx负载均衡的区别

工作流程

客户端发起请求到 Ribbon。Ribbon 从服务注册中心获取可用的服务实例列表。根据配置的负载均衡策略,选择一个合适的服务实例。将请求转发给选中的服务实例进行处理。如果请求失败或超时,Ribbon 会尝试选择其他的服务实例进行重试。

代码实现

RibbonSDK

sdk是每个使用ribbon的服务中需要引入的jar包,需要借助jar包中的功能来完成ribbon的使用。

package com.example.ribbonsdk.config.test;

import com.example.client.Controller.SDKController;

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import org.springframework.context.annotation.Bean;

import org.springframework.core.env.Environment;

import org.springframework.http.*;

import org.springframework.http.client.ClientHttpRequestExecution;

import org.springframework.http.client.ClientHttpRequestInterceptor;

import org.springframework.http.client.ClientHttpResponse;

import org.springframework.stereotype.Component;

import org.springframework.web.client.RestTemplate;

import org.springframework.web.util.UriComponentsBuilder;

import java.io.ByteArrayInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.net.URI;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.Random;

/**

* @BelongsProject: ribbonDemo

* @BelongsPackage: com.example.ribbonsdk.config

* @Author: Wuzilong

* @Description: RibbonSDK

* @CreateTime: 2023-07-31 22:47

* @Version: 1.0

*/

@Component

public class RequestInterceptor implements ClientHttpRequestInterceptor, ApplicationContextAware {

public static ApplicationContext applicationContext;

int index = 0;

// 目前是写死的,应该放到注册中心中去,动态的添加注册服务和权重

public Map serverList = new HashMap<>(){{

put("localhost:9002",7); // 权重值为7

put("localhost:9005",3); // 权重值为3

}};

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

if (this.applicationContext == null) {

this.applicationContext = applicationContext;

}

}

/**

* @Author:Wuzilong

* @Description: 手动注入AnnotationConfigApplicationContext用于判断

* @CreateTime: 2023/6/19 17:36

* @param:

* @return:

**/

@Bean

public AnnotationConfigApplicationContext annotationConfigApplicationContext() {

return new AnnotationConfigApplicationContext();

}

@Override

public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {

System.out.println("拦截器拦截进来了,拦截的地址是:"+request.getURI());

RestTemplate restTemplate = new RestTemplate();

//获取服务名

String serveName = request.getURI().getAuthority();

String newAuthority = null;

Environment environment = applicationContext.getBean(Environment.class);

String loadBalanceName = environment.getProperty("ribbon.loadBalanceName");

if (loadBalanceName.equals("polling")){

newAuthority = this.polling(serveName);

System.out.println("采用的是负载均衡策略————轮询");

}else if (loadBalanceName.equals("weight")){

newAuthority = this.weight();

System.out.println("采用的是负载均衡策略————权重");

}

String newHost= newAuthority.split(":")[0];

String newPort= newAuthority.split(":")[1];

URI newUri = UriComponentsBuilder.fromUri(request.getURI())

.host(newHost)

.port(newPort)

.build()

.toUri();

RequestEntity tRequestEntity = new RequestEntity(HttpMethod.GET, newUri);

ResponseEntity exchange = restTemplate.exchange(tRequestEntity, String.class);

System.out.println("请求的服务是"+exchange.getBody());

// 创建一个ClientHttpResponse对象,并将实际的响应内容传递给它

ClientHttpResponse response = new ClientHttpResponse() {

@Override

public HttpStatus getStatusCode() {

return exchange.getStatusCode();

}

@Override

public int getRawStatusCode() {

return exchange.getStatusCodeValue();

}

@Override

public String getStatusText() {

return exchange.getBody();

}

@Override

public void close() {

}

@Override

public InputStream getBody() {

return new ByteArrayInputStream(exchange.getBody().getBytes());

}

@Override

public HttpHeaders getHeaders() {

return exchange.getHeaders();

}

};

return response;

}

//轮询获取服务的IP地址

public String polling(String serverName){

List pollingList = applicationContext.getBean(SDKController.class).getList(serverName);

String ipContext = pollingList.get(index);

index=(index+1)%pollingList.size();

return ipContext;

}

//权重获取服务的IP地址

public String weight() {

int totalWeight = serverList.values().stream().mapToInt(Integer::intValue).sum();

int randomWeight = new Random().nextInt(totalWeight); // 生成一个随机权重值

int cumulativeWeight = 0; // 累计权重值

for (Map.Entry server : serverList.entrySet()) {

cumulativeWeight += server.getValue();

if (randomWeight < cumulativeWeight) {

return server.getKey();

}

}

return null; // 没有找到合适的服务器

}

}

  RequestInterceptor 类实现了两个接口,一个是ClientHttpRequestInterceptor用来重写intercept方法,也就是说重写了拦截器中的业务逻辑,我们可以把拦截到的请求进行处理,处理的过程可以写到intercept方法中,另一个是ApplicationContextAware这个接口是用来获取bean容器中对象的。

发送请求端

引入RibbonSDK和Nacos的依赖

com.example

RibbonSDK

1.0-SNAPSHOT

com.example

Client

2.5-20230615.123611-1

Nacos的其他配置可参考:手写Naocs注册中心基本原理  手写Nacos配置中心基本原理

配置文件中填写负载均衡策略

ribbon:

loadBalanceName: polling

调用代码

import com.example.ribbonsdk.config.test.RequestInterceptor;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.web.client.RestTemplateBuilder;

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.stereotype.Service;

import org.springframework.web.client.RestTemplate;

/**

* @BelongsProject: ribbonDemo

* @BelongsPackage: com.example.ribbonsdk.service

* @Author: Wuzilong

* @Description: 请求端

* @CreateTime: 2023-08-28 08:20

* @Version: 1.0

*/

@Service

public class ServiceA {

@Autowired

private RequestInterceptor requestInterceptor;

public void getServiceInfo(){

String url = "http://"+"localhost"+"/B/receiveMessage/";

RestTemplate restTemplate=new RestTemplateBuilder().build();

restTemplate.getInterceptors().add(requestInterceptor);

ResponseEntity forEntity = restTemplate.getForEntity(url, String.class);

if (forEntity.getStatusCode() == HttpStatus.OK) {

System.out.println("调用B服务成功!");

}

}

}

import com.example.ribbonsdk.service.ServiceA;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RestController;

/**

* @BelongsProject: ribbonDemo

* @BelongsPackage: com.example.ribbonsdk.Controller

* @Author: Wuzilong

* @Description: 描述什么人干什么事儿

* @CreateTime: 2023-07-31 22:54

* @Version: 1.0

*/

@RestController

@RequestMapping("/ribbonsdk")

public class ServiceAController {

@Autowired

private ServiceA serviceA;

@RequestMapping(value="getInfo",method= RequestMethod.GET)

public void getInfo(){

serviceA.getServiceInfo();

}

}

接收请求端

import org.springframework.beans.factory.annotation.Value;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import java.net.InetAddress;

import java.net.UnknownHostException;

/**

* @BelongsProject: ServiceB

* @BelongsPackage: com.example.serviceb.Controller

* @Author: Wuzilong

* @Description: B服务

* @CreateTime: 2023-06-07 19:08

* @Version: 1.0

*/

@RestController

@RequestMapping("/B")

public class ServiceBController {

@Value("${server.port}")

private String serverPort;

@GetMapping("/receiveMessage")

public String receiveMessage() throws UnknownHostException {

System.out.println("B:我被调用了");

//返回的内容是ip地址和端口号

return InetAddress.getLocalHost().getHostAddress()+":"+serverPort;

}

}

执行效果

发送请求端

接收请求端

总结提升

  Ribbon 是一个强大的客户端负载均衡器,可以帮助构建可靠和高性能的分布式系统。它通过负载均衡策略将请求分发到多个服务实例上,提供了灵活的配置选项和额外的功能。

此文章对你有用的话记得留言+点赞+收藏哦

好文推荐

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。