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

这篇具有很好参考价值的文章主要介绍了自定义loadbalance实现feignclient的自定义路由。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

项目背景

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

实操

解决方案

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;

/**
 * @author authorZhao
 * @since 2023-08-03
 */
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);
    }
}

import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
@LoadBalancerClient(value = "user-service",configuration = LoadBalanceConfig.class) //单个配置
public class LoadBalance {

}

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

import com.alibaba.cloud.nacos.NacosServiceInstance;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import reactor.core.publisher.Mono;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;

/**
 * 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(MyRoundRobinLoadBalancer.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 MyRoundRobinLoadBalancer(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 MyRoundRobinLoadBalancer(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());
	}

自定义loadbalance实现feignclient的自定义路由,后端框架,feign,loadbalance

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

本文为原创,转载请申明文章来源地址https://www.toymoban.com/news/detail-670929.html

到了这里,关于自定义loadbalance实现feignclient的自定义路由的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • (二) 盘古UI,全网独创,较为全面的自定义Android UI框架,绝对帮助你快速开发!(盘古导航栏-PanguNavBar)

    (二) 盘古UI,较为全面的自定义UI框架,帮助你绝对的快速开发!(长期维护中) demo地址,点击查看github 1, 样例展示图 2, 介绍 个性化导航栏,标题栏,可以灵活设置和配置各种属性和事件! 下面直接上属性列表: attr 属性 对应的方法 method 介绍 introduction pangu_title_mid setMidTitle(String title)

    2024年04月14日
    浏览(38)
  • 使用ETLCloud强大的自定义规则实现自定义数据处理算法

    实时数据处理规则有什么作用 ? 在大数据中的实时数据采集、ETL批量数据传输过程中很多数据处理过程以及数据质量都希望实时进行处理和检测并把不符合要求的脏数据过滤掉或者进行实时的数据质量告警等。 在数据仓库建设过程中,每家企业的数据处理过程中肯定会有一

    2024年02月08日
    浏览(35)
  • react实现模拟弹框遮罩的自定义hook

    点击按钮用于检测鼠标是否命中按钮 React好玩的自定义hook-useClickOutSide_哔哩哔哩_bilibili

    2024年02月12日
    浏览(28)
  • android-使用PopupWindow实现随机排列的自定义密码键盘

    break; } else { b2 = true; } } if (b2) { data[i] = x; b = false; break; } } } return data; } keyboard_bg_big.xml ?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"? item android:state_pressed=“true” android:drawable=“@drawable/key11”/ item android:state_focused=“true” android:drawable=“@drawable/key12”/ item android:state_focused=“false” android:state_p

    2024年04月27日
    浏览(30)
  • 基于ETLCloud的自定义规则调用第三方jar包实现繁体中文转为简体中文

    前面曾体验过通过零代码、可视化、拖拉拽的方式快速完成了从 MySQL 到 ClickHouse 的数据迁移,但是在实际生产环境,我们在迁移到目标库之前还需要做一些过滤和转换工作;比如,在诗词数据迁移后,发现原来 MySQL 中的诗词数据都是繁体字,这就导致在直接迁移到 ClickHous

    2024年02月11日
    浏览(31)
  • VBA中类的解读及应用第八讲:实现定时器功能的自定义类事件

    《VBA中类的解读及应用》教程【10165646】是我推出的第五套教程,目前已经是第一版修订了。这套教程定位于最高级,是学完初级,中级后的教程。 类,是非常抽象的,更具研究的价值。随着我们学习、应用VBA的深入,有必要理解这些抽象的理论知识。对象,类,过程,方法

    2024年02月02日
    浏览(28)
  • 【Spring Cloud】基于 Feign 实现远程调用,深入探索 Feign 的自定义配置、性能优化以及最佳实践方案

    在微服务架构中,服务之间的通信是至关重要的,而远程调用则成为实现这种通信的一种常见方式。在 Java 中,使用 RestTemplate 是一种传统的远程调用方式,但它存在一些问题,如代码可读性差、编程体验不一致以及参数复杂URL难以维护等。 在本文中,我们将探讨如何通过使

    2024年02月04日
    浏览(37)
  • 如何使用torch.nn.utils.prune稀疏神经网络,以及如何扩展它以实现自己的自定义剪裁技术

    最新的深度学习技术依赖于难以部署的过度参数化模型。 相反,已知生物神经网络使用有效的稀疏连通性。 为了减少内存,电池和硬件消耗,同时又不牺牲精度,在设备上部署轻量级模型并通过私有设备上计算来确保私密性,确定通过减少模型中的参数数量来压缩模型的最

    2024年02月12日
    浏览(35)
  • 前端实现动态路由(前端控制全部路由,后端返回用户角色)

    优点: 不用后端帮助,路由表维护在前端 逻辑相对比较简单,比较容易上手 权限少的系统用前端鉴权更加方便 缺点: 线上版本每次修改权限页面,都需要重新打包项目 大型项目不适用 如果需要在页面中增加角色并且控制可以访问的页面,则不能用前端鉴权 1、前端定义静态

    2024年02月10日
    浏览(30)
  • Vue从后端取数据,实现动态路由

            将获取菜单的方法放在全局中,以便每次刷新页面时,能够加载出。 this.$store.state.userInfo 是登入后存放在Vuex的用户信息  按照上面要求即可实现

    2024年01月20日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包