负载均衡分类
- 服务端负载均衡 ,比如我们常见的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
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