当前位置:网站首页>微服务实战|手把手教你开发负载均衡组件
微服务实战|手把手教你开发负载均衡组件
2022-07-02 06:33:00 【_时光煮雨】
前言
上一篇文章中我们通过原生态的方式实现了服务的发现和调用,但是在集群环境中,并没有达到负载均衡的目的,这一篇文章,我们将编写自己的负载均衡器,以实现服务调用的负载均衡功能。
自定义负载均衡
回顾
首先,我们再先来回顾一下上一篇文章中的代码:
@GetMapping("/hello")
public String hello(String name) {
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance instance = list.get(0);
String host = instance.getHost();
int port = instance.getPort();
String returnInfo = restTemplate.getForObject("http://" + host + ":" + port + "/hello?name={1}", String.class, name);
return returnInfo;
}
我们先通过discoveryClient获取了注册中心的其中一个服务的ip和端口,然后使用restTemplate调用其getForObjec()方法进行接口的调用。
通过跟踪该方法的源码,我们发现最终都会调用其doExecute()方法,源码如下:
@Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "URI is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpResponse response = null;
Object var14;
try {
ClientHttpRequest request = this.createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
this.handleResponse(url, method, response);
var14 = responseExtractor != null ? responseExtractor.extractData(response) : null;
} catch (IOException var12) {
String resource = url.toString();
String query = url.getRawQuery();
resource = query != null ? resource.substring(0, resource.indexOf(63)) : resource;
throw new ResourceAccessException("I/O error on " + method.name() + " request for \"" + resource + "\": " + var12.getMessage(), var12);
} finally {
if (response != null) {
response.close();
}
}
return var14;
}
那我们能不能继承RestTemplate类,并重写doExecute方法,在doExecute方法中调用createRequest之前对其做一下改变呢?
实现负载均衡
首先创建我们自己的RestTemplate,命名为MyRestTemplate.java,示例如下:
/** * 自定义负载均衡器 * @Author:公众号:程序员965 * @create 2022-06-06 **/
public class MyRestTemplate extends RestTemplate {
private DiscoveryClient discoveryClient;
public MyRestTemplate (DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
/** * 接口调用 * @Author:公众号:程序员965 * @create 2022-06-06 **/
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "URI is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpResponse response = null;
try {
System.out.println("替换前url :"+url.toString());
url = replaceUrl(url);
System.out.println("替换后url :"+url.toString());
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0,resource.indexOf('?')) : resource);
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
} finally {
if (response != null) {
response.close();
}
}
}
/** * 替换url * @Author:公众号:程序员965 * @create 2022-06-06 **/
private URI replaceUrl(URI url){
String sourceUrl = url.toString();
String [] httpUrl = sourceUrl.split("//");
int index = httpUrl[1].replaceFirst("/","@").indexOf("@");
String serviceName = httpUrl[1].substring(0,index);
List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(serviceName);
//采取随机算法,获取其中一个服务信息
Random random = new Random();
Integer randomIndex = random.nextInt(serviceInstanceList.size());
String serviceIp = serviceInstanceList.get(randomIndex).getUri().toString();
String targetSource = httpUrl[1].replace(serviceName,serviceIp);
try {
return new URI(targetSource);
} catch (URISyntaxException e) {
e.printStackTrace();
}
return url;
}
}
在doExecute方法中,调用createRequest方法之前,我们增加了一行replaceUrl()方法调用,在该方法中,我们通过解析Url,获得服务名称,然后依然根据服务名称获取服务信息列表,并通过随机算法,返回服务信息列表中其中一个服务的uri并返回,实现了随机算法的负载均衡。
接口调用
回过头来,我们修改一下接口的调用点,首先restTemplate改为创建我们自定义的MyRestTemplate:
@Bean
RestTemplate restTemplate(DiscoveryClient discoveryClient) {
return new MyRestTemplate(discoveryClient);
}
然后修改接口,将接口地址直接改为用服务名称进行调用:
@GetMapping("/hello2")
public String hello2(String name) {
String returnInfo = restTemplate.getForObject( "http://provider/hello?name={1}", String.class, name);
return returnInfo;
}
启动调试

运行成功,正确返回了我们的结果!
这时候,可能有人会有疑问:直接在接口调用处随机获取一个ip和端口进行调用不就行了?为什么费劲扒源码然后继承RestTemplate类并改写其doExecute方法呢?不是多此一举吗?
小伙伴们?想想看,为什么要这么做呢?这个问题留到我们使用负载均衡组件的时候再讲吧。
总结
本文中,我们使用随机算法实现了负载均衡的功能,当然你也可以根据需要实现比如轮询,权重等各种各样的负载算法。你学会了吗?
边栏推荐
- [go practical basis] how to bind and use URL parameters in gin
- Qt——如何在QWidget中设置阴影效果
- QT qtimer class
- Cloudreve自建云盘实践,我说了没人能限制得了我的容量和速度
- 小米电视不能访问电脑共享文件的解决方案
- Minecraft plug-in service opening
- 随笔:RGB图像颜色分离(附代码)
- 1、 QT's core class QObject
- win10使用docker拉取redis镜像报错read-only file system: unknown
- Analysis and solution of a classical Joseph problem
猜你喜欢

Function ‘ngram‘ is not defined

Data type case of machine learning -- using data to distinguish men and women based on Naive Bayesian method

Avoid breaking changes caused by modifying constructor input parameters

kubernetes部署loki日志系统
![[go practical basis] how to set the route in gin](/img/23/f38d68c4fd238d453b9a7670483002.png)
[go practical basis] how to set the route in gin

Solution of Xiaomi TV's inability to access computer shared files

Qt——如何在QWidget中设置阴影效果

Complete solution of servlet: inheritance relationship, life cycle, container, request forwarding and redirection, etc

Hengyuan cloud_ Can aiphacode replace programmers?

Linux安装Oracle Database 19c RAC
随机推荐
Leetcode sword finger offer brush questions - day 23
统计字符串中各类字符的个数
Npoi export word font size correspondence
C # save web pages as pictures (using WebBrowser)
Aneng logistics' share price hit a new low: the market value evaporated by nearly 10 billion yuan, and it's useless for chairman Wang Yongjun to increase his holdings
QT -- how to set shadow effect in QWidget
Solution of Xiaomi TV's inability to access computer shared files
Qunhui NAS configuring iSCSI storage
【Go实战基础】gin 如何绑定与使用 url 参数
Kubernetes deploys Loki logging system
Installing Oracle database 19C for Linux
Loadbalancer dynamically refreshes Nacos server
「Redis源码系列」关于源码阅读的学习与思考
What is the future value of fluorite mine of karaqin Xinbao Mining Co., Ltd. under zhongang mining?
Redis安装部署(Windows/Linux)
聊聊消息队列高性能的秘密——零拷贝技术
Cloudreve自建云盘实践,我说了没人能限制得了我的容量和速度
How to realize asynchronous programming in a synchronous way?
commands out of sync. did you run multiple statements at once
队列管理器running状态下无法查看通道