自定义loadbalance实现feignclient的自定义路由

自定义loadbalance实现feignclient的自定义路由

项目背景

服务A有多个同事同时开发,每个同事都在dev或者test环境发布自己的代码,注册到注册中心有好几个(本文nacos为例),这时候调用feign可能会导致请求到不同分支的服务上面,会出现一些问题,本文重点在于解决该问题

实操

解决方案

/**
 * @author authorZhao
 * @since 2023-08-03
 */
@EnableConfigurationProperties(LoadBalancerProperties.class)
public class LoadBalanceConfig{


    @Bean
    @ConditionalOnMissingBean
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new MyRoundRobinLoadBalancer(
                loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}
//@Configuration(proxyBeanMethods = false)
//@LoadBalancerClients(defaultConfiguration = LoadBalanceConfig.class) 全局配置
//@LoadBalancerClient(value = "user-center-service",configuration = LoadBalanceConfig.class) //单个配置
public class LoadBalance {

}
/**
 * A Round-Robin-based implementation of {@link ReactorServiceInstanceLoadBalancer}.
 *
 * @author Spencer Gibb
 * @author Olga Maciaszek-Sharma
 * @author Zhuozhi JI
 */
public class MyRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {

	private static final Log log = LogFactory.getLog(CubeRoundRobinLoadBalancer.class);

	final AtomicInteger position;

	final String serviceId;

	ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

	/**
	 * @param serviceInstanceListSupplierProvider a provider of
	 * {@link ServiceInstanceListSupplier} that will be used to get available instances
	 * @param serviceId id of the service for which to choose an instance
	 */
	public CubeRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
			String serviceId) {
		this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));
	}

	/**
	 * @param serviceInstanceListSupplierProvider a provider of
	 * {@link ServiceInstanceListSupplier} that will be used to get available instances
	 * @param serviceId id of the service for which to choose an instance
	 * @param seedPosition Round Robin element position marker
	 */
	public CubeRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
			String serviceId, int seedPosition) {
		this.serviceId = serviceId;
		this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
		this.position = new AtomicInteger(seedPosition);
	}

	@SuppressWarnings("rawtypes")
	@Override
	// see original
	// https://github.com/Netflix/ocelli/blob/master/ocelli-core/
	// src/main/java/netflix/ocelli/loadbalancer/RoundRobinLoadBalancer.java
	public Mono<Response<ServiceInstance>> choose(Request request) {
		ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
				.getIfAvailable(NoopServiceInstanceListSupplier::new);
		return supplier.get(request).next()
				.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
	}

	private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
			List<ServiceInstance> serviceInstances) {
		Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);
		if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
			((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
		}
		return serviceInstanceResponse;
	}

	private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
		for (ServiceInstance instance : instances) {
			if(instance instanceof NacosServiceInstance nacosServiceInstance){
                //项目注册的时候在配置一个元数据,后面可以根据当前上下文拿到当前tag,进行匹配,默认使用base分支
				String s = instance.getMetadata().get("feature-tag");
				if("base".equals(s)){
					return new DefaultResponse(nacosServiceInstance);
				}
			}
		}

		if (instances.isEmpty()) {
			if (log.isWarnEnabled()) {
				log.warn("No servers available for service: " + serviceId);
			}
			return new EmptyResponse();
		}



		// Ignore the sign bit, this allows pos to loop sequentially from 0 to
		// Integer.MAX_VALUE
		int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;

		ServiceInstance instance = instances.get(pos % instances.size());

		return new DefaultResponse(instance);
	}

}

loadbalance执行过程

调用流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lf3NGlrZ-1692865252643)(img/image-20230824160138272.png)]

feign.SynchronousMethodHandler#executeAndDecode
->feign.Client#execute
->FeignBlockingLoadBalancerClient#execute


public Response execute(Request request, Request.Options options) throws IOException {
		final URI originalUri = URI.create(request.url());
		String serviceId = originalUri.getHost();
		Assert.state(serviceId != null, "Request URI does not contain a valid hostname: " + originalUri);
		String hint = getHint(serviceId);
		DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(
				new RequestDataContext(buildRequestData(request), hint));
		Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
				.getSupportedLifecycleProcessors(
            //loadBalancerClientFactory是NamedContextFactory的子类 容器map,刷新user-center-service子容器
						loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
						RequestDataContext.class, ResponseData.class, ServiceInstance.class);
		supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
    //根据serviceId拿到ReactiveLoadBalancer,执行choose方法,然后到了我们的方法
		ServiceInstance instance = loadBalancerClient.choose(serviceId, lbRequest);
		org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
				instance);
		if (instance == null) {
			String message = "Load balancer does not contain an instance for the service " + serviceId;
			if (LOG.isWarnEnabled()) {
				LOG.warn(message);
			}
			supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
					.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
							CompletionContext.Status.DISCARD, lbRequest, lbResponse)));
			return Response.builder().request(request).status(HttpStatus.SERVICE_UNAVAILABLE.value())
					.body(message, StandardCharsets.UTF_8).build();
		}
		String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString();
		Request newRequest = buildRequest(request, reconstructedUrl);
		LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId);
		return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbRequest, lbResponse,
				supportedLifecycleProcessors, loadBalancerProperties.isUseRawStatusCodeInResponseData());
	}

在这里插入图片描述

后面MyRoundRobinLoadBalancer其实是抄袭RoundRobinLoadBalancer的实现,默认就是轮训

本文为原创,转载请申明

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/91374.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《华为认证》6to4自动隧道

实验需求&#xff1a; 在NE1和NE3之间使用tunnel 口创建6to4自动隧道&#xff0c;实现PC1和PC2互访。 步骤1:配置ipv4地址&#xff0c;如图所示&#xff1a; 步骤2&#xff1a;配置NE1和NE3的ipv4路由&#xff0c;是两端的ipv4网络能够互访 R1: ip route-static 0.0.0.0 0…

大数据(二)大数据行业相关统计数据

大数据&#xff08;二&#xff09;大数据行业相关统计数据 目录 一、大数据相关的各种资讯 二、转载自网络的大数据统计数据 2.1、国家大数据政策 2.2、产业结构分析 2.3、应用结构分析 2.4、数据中心 2.5、云计算 一、大数据相关的各种资讯 1. 据IDC预测&#xff0…

C语言练习题Day1

从今天开始分享C语言的练习题&#xff0c;每天都分享&#xff0c;差不多持续16天&#xff0c;看完对C语言的理解可能更进一步&#xff0c;让我们开始今天的分享吧&#xff01; 题目一 执行下面的代码&#xff0c;输出结果是&#xff08;&#xff09; int x5,y7; void swap()…

理解图傅里叶变换和图卷积

图神经网络&#xff08;GNN&#xff09;代表了一类强大的深度神经网络架构。在一个日益互联的世界里&#xff0c;因为信息的联通性&#xff0c;大部分的信息可以被建模为图。例如&#xff0c;化合物中的原子是节点&#xff0c;它们之间的键是边。 图神经网络的美妙之处在于它们…

大数据(三)大数据相关的职位

大数据&#xff08;三&#xff09;大数据相关的职位 本文目录&#xff1a; 一、写在前面的题外话 二、2022年就业状况 2.1、不同企业性质高校毕业生 CIER 指数 2.2、不同企业规模高校毕业生 CIER 指数 2.3、高校毕业生供求 TOP15 城市 2.4、一季度景气指数较高和较低的行…

AIGC ChatGPT 制作地图可视化分析

地图可视化分析是一种将数据通过地图的形式进行展示的方法&#xff0c;可以让人们更加直观、快速、准确的理解和分析数据。以下是地图可视化分析的一些主要好处&#xff1a; 加强数据理解&#xff1a;地图可视化可以将抽象的数字转化为直观的图形&#xff0c;帮助我们更好地理解…

UML建模以及几种类图的理解

文章目录 前言1.用例与用例图1.1 参与者1.2 用例之间的关系1.3 用例图1.4 用例的描述 2.交互图2.1 顺序图2.2 协作图 3.类图和对象图3.1 关联关系3.2 聚合和组合3.3 泛化关系3.4 依赖关系 4.状态图与活动图4.1 状态图4.2 活动图 5.构件图 前言 UML通过图形化的表示机制从多个侧…

软件工程(十四) 设计模式之结构型模式(二)

1、组合模式 简要说明 将对象组合成树形结构以表示“整体-部分”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。 速记关键字 树形目录结构 类图如下 由类图其实可以看出,组合模式就是将具有父子关系的结构,组装形成一棵树,并且根据规范,树干节点和叶子节…

【Linux操作系统】Linux系统编程中的互斥锁

文章目录 1. 互斥锁的原理2. 互斥锁的相关函数3. 互斥锁的例子总结 1. 互斥锁的原理 在Linux系统编程中&#xff0c;互斥锁&#xff08;Mutex&#xff09;是一种用于保护共享资源的同步机制。它可以确保在任意时刻只有一个线程可以访问被保护的资源&#xff0c;从而避免了多个…

记录一次“top负1”比赛经历

获奖啦&#xff01; 比赛题目&#xff1a;中文语义病句识别与纠正挑战赛 比赛链接&#xff1a;https://challenge.xfyun.cn/topic/info?typeidentification-and-correction&optionphb“请介绍你们团队” “各位评委老师&#xff0c;我是来自WOT团队的选手AMBT&#xff0…

知识储备--基础算法篇-动态规划

1.前言 第一次接触动态规划&#xff0c;不知道具体什么意思&#xff0c;做了题才发现动态规划就是把大问题变成小问题&#xff0c;并解决了小问题重复计算的方法称为动态规划。比如上楼梯&#xff0c;一次上一阶或二阶&#xff0c;求有多少种算法&#xff0c;就可以拆成最后一…

【Flutter】Flutter 使用 infinite_scroll_pagination 实现无限滚动分页

【Flutter】Flutter 使用 infinite_scroll_pagination 实现无限滚动分页 文章目录 一、前言二、安装和基本使用1. 添加依赖2. 基础配置和初始化 三、实际业务中的用法1. 与 API 集成2. 错误处理 四、完整示例1. 创建一个无限滚动列表2. 使用在你的应用中3. 完整代码示例 五、总…

SFM structure from motion

struction就是空间三维点的位置 motion 就是相机每帧的位移 https://www.youtube.com/watch?vUhkb8Zq-dnM&listPL2zRqk16wsdoYzrWStffqBAoUY8XdvatV&index9

VBA Excel自定义函数的使用 简单的语法

一个简单的教程&#xff0c;实现VBA自定义函数。 新建模块 复制后面的代码放进来 函数的入口参数不定义&#xff0c;则认为是一块区域&#xff1b; 反之&#xff0c;如FindChar1 As String&#xff0c;则认为是输入的单值。 循环和分支如下例子&#xff0c;VB比较接近自然语…

Ubuntu22.04安装中文输入法►由踩坑到上岸版◄

Ubuntu22.04安装中文输入法►由踩坑到上岸版◄ 了解入坑上岸 更新一发&#xff1a;Gedit中文乱码问题的解决 为了方便回忆和记录甚至后面继续重装系统&#xff0c;我还是写一下以便将来用到或参考&#xff5e; 了解 安装Ubuntu22.04&#xff08;截至2023年08月26日11&#xff…

Docker架构及原理

一、Docker的架构图 二、底层原理 Docker是怎么工作的&#xff1f; Docker是一个Client-Server结构的系统&#xff0c;Docker守护进程运行在主机上&#xff0c; 然后通过Socket连接从客户端访问&#xff0c;守护进程从客户端接受命令并管理运行在主机上的容器。 容器&#xf…

wireshark流量分析

一、题目一(1.pcap) 题目要求&#xff1a; 1.黑客攻击的第一个受害主机的网卡IP地址 2.黑客对URL的哪一个参数实施了SQL注入 3.第一个受害主机网站数据库的表前缀&#xff08;加上下划线例如abc&#xff09; 4.第一个受害主机网站数据库的名字 看到题目SQL注入&#xff0…

微服务(多级缓存)

目录 多级缓存 1.什么是多级缓存 2.JVM进程缓存 2.2.初识Caffeine 2.3.实现JVM进程缓存 2.3.1.需求 2.3.2.实现 3.Lua语法入门 3.1.初识Lua 3.1.HelloWorld 3.2.变量和循环 3.2.1.Lua的数据类型 3.2.2.声明变量 3.2.3.循环 3.3.条件控制、函数 3.3.1.函数 3.3.…

机器学习简介[01/2]:简单线性回归

Python 中的机器学习简介&#xff1a;简单线性回归 一、说明 简单线性回归为机器学习提供了优雅的介绍。它可用于标识自变量和因变量之间的关系。使用梯度下降&#xff0c;可以训练基本模型以拟合一组点以供未来预测。 二、技术背景 这是涵盖回归、梯度下降、分类和机器学习的其…

下载的文件被Windows 11 安全中心自动删除

今天从CSDN上下载了自己曾经上传的文件&#xff0c;但是浏览器下载完之后文件被Windows安全中心自动删除&#xff0c;说是带病毒。实际是没有病毒的&#xff0c;再说了即便有病毒也不应该直接删除啊&#xff0c;至少给用户一个保留或删除的选项。 研究了一番&#xff0c;可以暂…