Spring Cloud Alibaba - 06 RestTemplate 实现自定义负载均衡算法

本文涉及的产品
网络型负载均衡 NLB,每月750个小时 15LCU
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
简介: Spring Cloud Alibaba - 06 RestTemplate 实现自定义负载均衡算法

6735aa4777de402592fbe82e8b40ee3d.png

负载均衡分类


  • 服务端负载均衡 ,比如我们常见的ng
  • 客户端负载均衡 ,比如微服务体系中的ribbon


spring cloud ribbon是 基于NetFilix ribbon 实现的一套客户端的负载均衡工具,Ribbon客户端组件提供一系列的完善的配置,如超时,重试等。


通过Load Balancer(LB)获取到服务提供的所有机器实例,Ribbon会自动基于某种规则(轮询,随机)去调用这些服务。


Ribbon也支持自定义负载均衡算法


分析


我们前面的工程都是通过DiscoveryClient组件来去Nacos服务端拉取指定名称的微服务列表,然后通过RestTemplate执行远程调用


5fa46e4f17f948cb836b4b799365f82a.png


如果服务存在多个的话,加上我们使用的地址都是使用注册中心的地址 http://artisan-product-center/selectProductInfoById/ , RestTemplate 就处理不了这种问题了。所以我们才有第二步。


那如何让RestTemplate 自身也具备这种功能呢?


思路: 分析RestTemplate的源码,不管是post,get请求最终是会调用doExecute()方法,所以写一个CustomRestTemplate类继承RestTemplate,重写doExucute()方法即可


工程

artisan-cloud-custom-lb-order

artisan-cloud-custom-lb-product

package com.artisan.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.client.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Random;
/**
 * @author 小工匠
 * @version 1.0
 * @description: 根据RestTemplate特性自己改造
 * @date 2022/2/2 13:32
 * @mark: show me the code , change the world
 */
@Slf4j
public class CustomRestTemplate extends RestTemplate {
    @Autowired
    private DiscoveryClient discoveryClient;
    public CustomRestTemplate(DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }
    @Override
    protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor) throws RestClientException {
        Assert.notNull(url, "URI is required");
        Assert.notNull(method, "HttpMethod is required");
        ClientHttpResponse response = null;
        try {
            /**
             * 在这里拦截一下子 偷梁换柱
             */
            //把服务名 替换成我们的IP
            url = replaceUrl(url);
            log.info("替换后的请求路径:{}", url);
            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();
            }
        }
    }
    /**
     * 方法实现说明:把微服务名称  去注册中心拉取对应IP进行调用
     * http://artisan-product-center/selectProductInfoById/1
     *
     * @param url:请求的url
     * @return:
     * @exception:
     */
    private URI replaceUrl(URI url) {
        log.info("原始请求路径为:{}", url);
        //1:从URI中解析调用的调用的serviceName=artisan-product-center
        String serviceName = url.getHost();
        log.info("调用微服务的名称:{}", serviceName);
        //2:解析我们的请求路径 reqPath= /selectProductInfoById/1
        String reqPath = url.getPath();
        log.info("请求path:{}", reqPath);
        //通过微服务的名称去nacos服务端获取 对应的实例列表
        List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(serviceName);
        if (serviceInstanceList.isEmpty()) {
            throw new RuntimeException("没有可用的微服务实例列表:" + serviceName);
        }
        String serviceIp = chooseTargetIp(serviceInstanceList);
        String source = serviceIp + reqPath;
        try {
            return new URI(source);
        } catch (URISyntaxException e) {
            log.error("根据source:{}构建URI异常", source);
        }
        return url;
    }
    /**
     * 方法实现说明:从服务列表中 随机选举一个ip
     *
     * @param serviceInstanceList 服务列表
     * @return: 调用的ip
     * @exception:
     */
    private String chooseTargetIp(List<ServiceInstance> serviceInstanceList) {
        //采取随机的获取一个
        Random random = new Random();
        Integer randomIndex = random.nextInt(serviceInstanceList.size());
        String serviceIp = serviceInstanceList.get(randomIndex).getUri().toString();
        log.info("随机选举的服务IP:{}", serviceIp);
        return serviceIp;
    }
}


调用

【配置方式一】


70033160bb7f4900b9e085ccb87c9795.png


【调用】


a020df490de64c3293fe25c8081ed35c.png


测试

访问 : http://localhost:8080/v2/selectOrderInfoById/1


a1ad99d2fac74d5a94744b4b3cf194ec.png


源码

https://github.com/yangshangwei/SpringCloudAlibabMaster


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
相关文章
|
存储 负载均衡 算法
负载均衡算法
负载均衡算法
155 1
|
7月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
负载均衡 算法 搜索推荐
Nginx 常用的负载均衡算法
【10月更文挑战第17天】在实际应用中,我们需要根据具体的情况来选择合适的负载均衡算法。同时,还可以结合其他的优化措施,如服务器健康检查、动态调整权重等,来进一步提高负载均衡的效果和系统的稳定性。
367 59
|
缓存 负载均衡 算法
slb支持多种负载均衡算法
slb支持多种负载均衡算法
400 6
|
8月前
|
Java 测试技术 API
在 Spring 中 Mock RestTemplate
本文介绍了两种在单元测试中 mock RestTemplate 调用的方法,避免真实 HTTP API 调用以提高测试可控性。一是使用 Mockito 模拟库,通过 @Mock 和 when/then 方法定义模拟行为;二是借助 Spring Test 提供的 MockRestServiceServer,创建模拟服务器定义请求响应交互。文中结合具体代码示例展示了两种方法的实现细节,并强调了 RestTemplate 实例的一致性配置。适用于需要模拟外部 HTTP 调用的集成测试场景。
190 1
|
11月前
|
负载均衡 算法
架构学习:7种负载均衡算法策略
四层负载均衡包括数据链路层、网络层和应用层负载均衡。数据链路层通过修改MAC地址转发帧;网络层通过改变IP地址实现数据包转发;应用层有多种策略,如轮循、权重轮循、随机、权重随机、一致性哈希、响应速度和最少连接数均衡,确保请求合理分配到服务器,提升性能与稳定性。
2296 11
架构学习:7种负载均衡算法策略
|
负载均衡 算法 应用服务中间件
5大负载均衡算法及原理,图解易懂!
本文详细介绍负载均衡的5大核心算法:轮询、加权轮询、随机、最少连接和源地址散列,帮助你深入理解分布式架构中的关键技术。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
5大负载均衡算法及原理,图解易懂!
|
负载均衡 算法
SLB-Backend的负载均衡算法
【10月更文挑战第19天】
217 5
|
负载均衡 算法 应用服务中间件
Nginx 常用的负载均衡算法
【10月更文挑战第22天】不同的负载均衡算法各有特点和适用场景。在实际应用中,需要根据具体的业务需求、服务器性能和网络环境等因素来选择合适的算法。
374 3
|
缓存 负载均衡 算法
nginx学习:配置文件详解,负载均衡三种算法学习,上接nginx实操篇
Nginx 是一款高性能的 HTTP 和反向代理服务器,也是一个通用的 TCP/UDP 代理服务器,以及一个邮件代理服务器和通用的 HTTP 缓存服务器。
618 0
nginx学习:配置文件详解,负载均衡三种算法学习,上接nginx实操篇

热门文章

最新文章

相关实验场景

更多