当前位置:网站首页>微服务实战|手把手教你开发负载均衡组件
微服务实战|手把手教你开发负载均衡组件
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方法呢?不是多此一举吗?
小伙伴们?想想看,为什么要这么做呢?这个问题留到我们使用负载均衡组件的时候再讲吧。
总结
本文中,我们使用随机算法实现了负载均衡的功能,当然你也可以根据需要实现比如轮询,权重等各种各样的负载算法。你学会了吗?
边栏推荐
- 使用IBM MQ远程连接时报错AMQ 4043解决思路
- Web技术发展史
- kubernetes部署loki日志系统
- 队列的基本概念介绍以及典型应用示例
- [staff] time sign and note duration (full note | half note | quarter note | eighth note | sixteenth note | thirty second note)
- 一、Qt的核心类QObject
- C nail development: obtain all employee address books and send work notices
- Kubernetes deploys Loki logging system
- [go practical basis] how can gin get the request parameters of get and post
- Mirror protocol of synthetic asset track
猜你喜欢

Dix ans d'expérience dans le développement de programmeurs vous disent quelles compétences de base vous manquez encore?

机器学习之数据类型案例——基于朴素贝叶斯法,用数据辩男女

Linux二进制安装Oracle Database 19c

【Go实战基础】gin 如何绑定与使用 url 参数

【Go实战基础】gin 如何获取 GET 和 POST 的请求参数

commands out of sync. did you run multiple statements at once

Cloudreve自建云盘实践,我说了没人能限制得了我的容量和速度
![[staff] time sign and note duration (full note | half note | quarter note | eighth note | sixteenth note | thirty second note)](/img/bf/2b0b9c640bdad2c55293f905a22055.jpg)
[staff] time sign and note duration (full note | half note | quarter note | eighth note | sixteenth note | thirty second note)

【Go实战基础】gin 如何设置路由

远程连接IBM MQ报错AMQ4036解决方法
随机推荐
Gocv boundary fill
Function ‘ngram‘ is not defined
Cartoon rendering - average normal stroke
Jingdong senior engineer has developed for ten years and compiled "core technology of 100 million traffic website architecture"
Openshift container platform community okd 4.10.0 deployment
Qt的拖动事件
Matplotlib swordsman Tour - an artist tutorial to accommodate all rivers
Cloudreve自建云盘实践,我说了没人能限制得了我的容量和速度
队列的基本概念介绍以及典型应用示例
Matplotlib剑客行——容纳百川的艺术家教程
将一串数字顺序后移
Pyspark de duplication dropduplicates, distinct; withColumn、lit、col; unionByName、groupBy
C Gaode map obtains the address according to longitude and latitude
Introduction to the basic concept of queue and typical application examples
Kubernetes deploys Loki logging system
gocv边界填充
Cloudrev self built cloud disk practice, I said that no one can limit my capacity and speed
一、Qt的核心类QObject
libusb的使用
分布式服务架构精讲pdf文档:原理+设计+实战,(收藏再看)