前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Cloud Alibaba - 06 RestTemplate 实现自定义负载均衡算法

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

作者头像
小小工匠
发布2022-02-03 16:00:52
5390
发布2022-02-03 16:00:52
举报
文章被收录于专栏:小工匠聊架构小工匠聊架构

文章目录

在这里插入图片描述
在这里插入图片描述

负载均衡分类

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

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

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

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

分析

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

在这里插入图片描述
在这里插入图片描述

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

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

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

工程

artisan-cloud-custom-lb-order

artisan-cloud-custom-lb-product

代码语言:javascript
复制
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;
    }
}

调用

【配置方式一】

在这里插入图片描述
在这里插入图片描述

【调用】

在这里插入图片描述
在这里插入图片描述

测试

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

在这里插入图片描述
在这里插入图片描述

源码

https://github.com/yangshangwei/SpringCloudAlibabMaster

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-02-02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 负载均衡分类
  • 分析
  • 工程
  • 调用
  • 测试
  • 源码
相关产品与服务
负载均衡
负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com