微服务之Ribbon原理解析

客户端负载均衡 Ribbon RibbonLoadBalanceClient是的实现类,它用来执行最终的负载均衡请求。其中,的一个choose()方法用于选择具体的服务实例,其内部是通过方法交给完成的。是一个接口,该接口定义了一系列实现负载均衡的方法。接口的实现类结果如下图所示。 阅读详情

Ribbon原理解析

1.引言

什么是Ribbon

  • Ribbon是Netflix公司的一个负载均衡项目,可以在进行服务调用时做负载均衡;

2.Ribbon基本使用

Ribbon结合Http

  • Ribbon通常和Http请求结合,对Http请求进行负载均衡;最简单的使用如下,通过注入RestTemplate,并且打上@LoadBlanced注解,即可得到一个带有负载均衡效果的RestTemplate
@Configuration
public class HttpConfiguration {
    @Bean
    @LoadBalanced
    public RestTempttpTlate restTemplate() {
        return new RestTemplate();
    }
}
  • 使用时,只需要把RestTemplate发送请求时的URL中的主机名host替换为服务提供方的名字;那么在调用时,就会自动进行负载均衡
@RestController
public class TestController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/test")
    public String test() {
        //HelloServiceProvider为服务提供方名称
        return restTemplate.getForEntity("http://HelloServiceProvider/hello?info=okkk", String.class).getBody();
    }
}
  • 当然,这里还有配置好注册中心,将服务提供方和消费方都注册到注册中心上

3.Ribbon和RestTemplate整合原理

原理概述

  • RestTemplate相信大家都有用过,相当于一个发送Http请求的工具;但是为何引入@LoadBlanced就可以实现负载均衡了呢?
  • 简单来说:
    • RestTemplate在发送请求过程中,会构造一条具有多个拦截器的执行链
    • Ribbon可以借助拦截器,在RestTemplate中加入一个LoadBalancerInterceptor拦截器;
    • 这样在发送请求时,请求经过拦截器,Ribbon就可以根据请求的URL中的主机名(即服务名, 下面的HelloServiceProvider),去注册中心拿到提供该服务的所有主机
    • 并且根据负载均衡策略,选择其中一个,然后把服务名替换为真正的IP,接着继续执行下一个拦截器,最终发送请求
//restTempalte负载均衡发送请求示例,其中HelloServiceProvider为服务名,需要被替换为真实IP地址
restTemplate.getForEntity("http://HelloServiceProvider/hello?info=okkk", String.class).getBody();

源码解析

  • Spring的IOC容器在刷新过程中,会执行到如下过程;该过程是bean容器刷新比较后面的一步,这是大部分单例bean都已经实例化完成;会遍历所有实现了SmartInitializingSingleton接口的bean,并调用它的afterSingletonsInstantiated方法
//DefaultListableBeanFactory#preInstantiateSingletons部分代码

for (String beanName : beanNames) {
    Object singletonInstance = getSingleton(beanName);
    if (singletonInstance instanceof SmartInitializingSingleton) {
        final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                smartSingleton.afterSingletonsInstantiated();
                return null;
            }, getAccessControlContext());
        }
        else {
            //调用afterSingletonsInstantiated方法
            smartSingleton.afterSingletonsInstantiated();
        }
    }
}
  • LoadBalancerAutoConfiguration类:SpringCloud中的一个自动配置类,负责负载均衡方面的配置,会向容器中注入如下几个Bean对象(截取部分源码); 其中第一个是SmartInitializingSingleton类型的对象,也就是上面所说的接口;所以它的方法会在上面的过程中被调用;具体方法内容看下面注释;(下面这两个Bean对象都是lambda表达式的形式,注意别看混了)
//LoadBalancerAutoConfiguration

public class LoadBalancerAutoConfiguration {
    
    @Bean //参数传入的是restTemplateCustomizers,这个应该是有ApplicationContext进行注入所有的customizer
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
        final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
        return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            //遍历所有RestTemplate对象
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) {
                    //调用customizer#customize方法,对restTemplate做一些操作,具体看下面的Bean
                    customizer.customize(restTemplate);
                }
            }
        });
    }

    //注入customizer对象
    @Bean
    @ConditionalOnMissingBean
    public RestTemplateCustomizer restTemplateCustomizer(
        final LoadBalancerInterceptor loadBalancerInterceptor) {
        return restTemplate -> {
            //这就是上面的Bean执行customize方法,拿到该restTemplate所有拦截器
            List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                restTemplate.getInterceptors());
            //加入负载均衡拦截器
            list.add(loadBalancerInterceptor);
            //重新设置拦截器给restTemplate
            restTemplate.setInterceptors(list);
        };
    }
}
  • 通过上面的步骤,restTemplate中就会被注入了LoadBalancerInterceptor拦截器;发送http请求时会经过拦截器,就可以将URL的服务名替换为负载均衡过后的真正IP地址了

4.负载均衡流程

核心类

  • LoadBalanceClient:上面说到Http请求发送时,会经过Ribbon的LoadBalancerInterceptor拦截器进行负载均衡,该拦截器会把请求交给LoadBalancerClient进行负载均衡;该类是负载均衡客户端,也可以看成负载均衡的入口;采用的实现类是RibbonLoadBalancerClient,看一下继承图

在这里插入图片描述

  • ILoadBalancer:RibbonLoadBalancerClient内部的execute方法会以Http请求的服务名为key,找到ILoadBalancer对象,这个ILoadBalancer就是专门负责这个服务的负载均衡;继承图如下
    • DynamicServerListLoadBalance: 具有ServerList功能的ILoadBalance,ServerList后面会讲,它能够维护和更新本服务对应的所有主机信息
    • ZoneAwareLoadBalance: 该类是DynamicServerListLoadBalancer的子类,具有区域感知(ZoneAware)功能,它可以把所有主机根据请求状况进行区域划分,当主机请求超时时,它会把相关主机从活跃区域踢出,避免影响服务质量;详情可看类上的注释
/**
 * Load balancer that can avoid a zone as a whole when choosing server. 
 *<p>
 * The key metric used to measure the zone condition is Average Active Requests,
which is aggregated per rest client per zone. It is the
total outstanding requests in a zone divided by number of available targeted instances (excluding circuit breaker tripped instances).
This metric is very effective when timeout occurs slowly on a bad zone.
<p>
The  LoadBalancer will calculate and examine zone stats of all available zones. If the Average Active Requests for any zone has reached a configured threshold, this zone will be dropped from the active server list. In case more than one zone has reached the threshold, the zone with the most active requests per server will be dropped.
Once the the worst zone is dropped, a zone will be chosen among the rest with the probability proportional to its number of instances.
A server will be returned from the chosen zone with a given Rule (A Rule is a load balancing strategy, for example {@link AvailabilityFilteringRule})
For each request, the steps above will be repeated. That is to say, each zone related load balancing decisions are made at real time with the up-to-date statistics aiding the choice.
 */

在这里插入图片描述

  • IRule:代表负载均衡策略,ILoadBalancer的负载均衡由它进行处理,Ribbon内置了多种负载均衡策略,都实现了该接口

在这里插入图片描述

源码流程

  • 前面说了负载均衡入口是RestTemplate拦截器,所以看一下LoadBalancerInterceptor的intercept方法
//LoadBalancerInterceptor#intercept
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
                                    final ClientHttpRequestExecution execution) throws IOException {
    final URI originalUri = request.getURI();
    String serviceName = originalUri.getHost();
    Assert.state(serviceName != null,
                 "Request URI does not contain a valid hostname: " + originalUri);
    //拿到loadBalancer,它是一个LoadBalancerClient类型对象,并调用execute方法
    return this.loadBalancer.execute(serviceName,
                                     this.requestFactory.createRequest(request, body, execution));
}
  • 上述LoadBalancerClient实际用的实现类是RibbonLoadBalancerClient,先看一下其父接口LoadBalancerClient的方法
public interface LoadBalancerClient extends ServiceInstanceChooser {
    //用于执行http负载均衡请求
	<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

	<T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException;
    //重构URI,替换服务名
	URI reconstructURI(ServiceInstance instance, URI original);

}
  • RibbonLoadBalancerClient的execute方法如下
//RibbonLoadBalancerClient#execute
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
    throws IOException {
    //根据serviceId拿到ILoadBalancer,交由它进行负载均衡
    ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
    //根据loadBalancer拿到真正的服务提供者
    Server server = getServer(loadBalancer, hint);
    if (server == null) {
        throw new IllegalStateException("No instances available for " + serviceId);
    }
    //包装Server
    RibbonServer ribbonServer = new RibbonServer(serviceId, server,
                                                 isSecure(server, serviceId),
                                                 serverIntrospector(serviceId).getMetadata(server));

    //执行请求
    return execute(serviceId, ribbonServer, request);
}

//RibbonLoadBalancerClient#getLoadBalancer
protected ILoadBalancer getLoadBalancer(String serviceId) {
	//从clientFactory中获得该服务对应的ILoadBalancer	
    return this.clientFactory.getLoadBalancer(serviceId);
}
  • 这里ILoadBalancer loadBalancer = getLoadBalancer(serviceId)这句代码是重点,它会根据当前URL中的serviceId,先从工厂中拿到对应的ApplicationContext(首次取没有则新建容器并refresh),接着再从IOC容器中拿到ILoadBalancer类型的bean;由于调用栈比较复杂,下面只摘取部分重点
protected ILoadBalancer getLoadBalancer(String serviceId) {
    //从SpringClientFactory工厂中获取该serviceId对应的ILoadBalancer
    return this.clientFactory.getLoadBalancer(serviceId);
}

public ILoadBalancer getLoadBalancer(String name) {
    return getInstance(name, ILoadBalancer.class);
}

public <T> T getInstance(String name, Class<T> type) {
    //获取服务名对应的ApplicationContext
    AnnotationConfigApplicationContext context = getContext(name);
    if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
                                                            type).length > 0) {
        //从ApplicationContext中后去ILoadBalancer类型的bean
        return context.getBean(type);
    }
    return null;
}

protected AnnotationConfigApplicationContext getContext(String name) {
    //大致逻辑就是直接容map中拿,没有则创建一个并缓存
    if (!this.contexts.containsKey(name)) {
        synchronized (this.contexts) {
            if (!this.contexts.containsKey(name)) {
                this.contexts.put(name, createContext(name));
            }
        }
    }
    return this.contexts.get(name);
}
  • 因此可以看出,Ribbon中,每个服务会对应一个ApplicationContext,内部维护了各种bean信息,包括负责负载均衡的ILoadBalancer;每个ApplicationContext内部都有自己的ILoadBalancer
  • getServer()方法最终会调用ILoadBalancer的chooseServer方法,代码如下
//BaseLoadBalancer#chooseServer
public Server chooseServer(Object key) {
    if (counter == null) {
        counter = createCounter();
    }
    counter.increment();
    //如果没有配置rule则返回
    if (rule == null) {
        return null;
    } else {
        try {
            //由rule执行真正的负载均衡
            return rule.choose(key);
        } catch (Exception e) {
            logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
            return null;
        }
    }
}
  • 下面以RandomRule为例看一下choose方法,篇幅原因只保留部分代码,详情可自己debug
//RandomRule#choose
public Server choose(ILoadBalancer lb, Object key) {
    if (lb == null) {
        return null;
    }
    Server server = null;

    while (server == null) {
        //... 从ILoadBalancer中拿到该服务对应的所有Server
        List<Server> upList = lb.getReachableServers();
        List<Server> allList = lb.getAllServers();
		//... 产生随机数,并拿到对应Server
        int index = chooseRandomInt(serverCount);
        server = upList.get(index);
		//...

        if (server.isAlive()) {
            return (server);
        }
		//...
    }
    return server;
}

小结

  • 这样就完成了负载均衡过程,梳理一下大致流程
    • Http请求经过LoadBalancerInterceptor拦截器,它将调用LoadBalancerClient进行处理
    • LoadBalancerClient(实现类Ribbon``LoadBalancerClient)根据服务名拿到对应的ApplicationContext,并从容器中拿到ILoadBalancer(实际类ZoneAwareLoadBalancer)
    • 从ILoadBalancer中获取服务提供者Server,具体是调用chooseServer方法,内部会使用IRule进行负载均衡,并返回合适的Server
    • IRule才是真正的负载均衡实现接口,Ribbon内置多种默认负载均衡策略
  • 以上流程讲述了从RestTemplate与Ribbon整合,到一次请求发起实现负载均衡的大致流程;但是还不涉及Ribbon如何维护注册中心中的服务及对应的主机列表操作;即Ribbon需要定时获取注册中心的服务信息,供负载均衡使用

5.服务列表拉取和刷新

核心类

  • ServerList:该接口用于表示向注册中心拉取服务信息
public interface ServerList<T extends Server> {
    public List<T> getInitialListOfServers();
    
    public List<T> getUpdatedListOfServers();   
}

源码流程

  • 前面说到过,每个服务都有自己的ILoadBalancer(默认是ZoneAwareLoadBalancer),其父类为DynamicServerListLoadBalancer,它会负责服务对应的主机列表信息;
  • DynamicServerListLoadBalancer在创建过程中,会调用如下方法进行服务列表初始化
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
                                     ServerList<T> serverList, ServerListFilter<T> filter,
                                     ServerListUpdater serverListUpdater) {
    super(clientConfig, rule, ping);
    this.serverListImpl = serverList;
    this.filter = filter;
    this.serverListUpdater = serverListUpdater;
    if (filter instanceof AbstractServerListFilter) {
        ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
    }
    //调用该方法进行主机列表初始化
    restOfInit(clientConfig);
}

void restOfInit(IClientConfig clientConfig) {
    //...
    updateListOfServers();
    //...
}

//这是另一个构造方法,可能是使用自定义配置时
public DynamicServerListLoadBalancer(IClientConfig clientConfig) {
    initWithNiwsConfig(clientConfig);
}
    
//DynamicServerListLoadBalancer#initWithNiwsConfig
public void initWithNiwsConfig(IClientConfig clientConfig) {
    try {
        super.initWithNiwsConfig(clientConfig);
        //从配置文件中拿到NIWSServerListClassName对应的值,应该是拿到配置的ServerList实现类,负责维护服务信息
        //当注册中心不同时,应该可以动态替换
        String niwsServerListClassName = clientConfig.getPropertyAsString(
            CommonClientConfigKey.NIWSServerListClassName,
            DefaultClientConfigImpl.DEFAULT_SEVER_LIST_CLASS);
		//实例化ServerList
        ServerList<T> niwsServerListImpl = (ServerList<T>) ClientFactory
            .instantiateInstanceWithClientConfig(niwsServerListClassName, clientConfig);
        //保存
        this.serverListImpl = niwsServerListImpl;
		//...
		//拿到ServerListUpdaterClassName配置的类
        String serverListUpdaterClassName = clientConfig.getPropertyAsString(
            CommonClientConfigKey.ServerListUpdaterClassName,
            DefaultClientConfigImpl.DEFAULT_SERVER_LIST_UPDATER_CLASS
        );
		//实例化
        this.serverListUpdater = (ServerListUpdater) ClientFactory
            .instantiateInstanceWithClientConfig(serverListUpdaterClassName, clientConfig);
		//在该函数中,会进行服务初始化
        restOfInit(clientConfig);
    } catch (Exception e) {
        throw new RuntimeException(
            "Exception while initializing NIWSDiscoveryLoadBalancer:"
            + clientConfig.getClientName()
            + ", niwsClientConfig:" + clientConfig, e);
    }
}
  • 接下来调用DynamicServerListLoadBalancer#restOfInit方法,最终会调用updateListOfServers方法进行列表更新
@VisibleForTesting
public void updateListOfServers() {
    List<T> servers = new ArrayList<T>();
    //判断serverListImpl是否有配置
    if (serverListImpl != null) {
        //拉取所有的Servers
        servers = serverListImpl.getUpdatedListOfServers();
        LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
                     getIdentifier(), servers);
		//如果由过滤规则,对Server进行过滤
        if (filter != null) {
            servers = filter.getFilteredListOfServers(servers);
            LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
                         getIdentifier(), servers);
        }
    }
    //更新本地的ServerList
    updateAllServerList(servers);
}

@Override
public List<DiscoveryEnabledServer> getUpdatedListOfServers() {
    List<DiscoveryEnabledServer> servers = setZones(
        this.list.getUpdatedListOfServers());	//这里才是真正获取主机的方法, list是一个ServerList类型的变量,根据注册中心不同,具体实现不同
    return servers;
}
  • 如果注册中心使用Eureka,那么ServerList使用的是DiscoveryEnabledNIWSServerList,如果是其他注册中心,可以替换,下面看一下它的getUpdatedListOfServers方法,层层调用后进入如下方法,篇幅原因省略部分代码
//DiscoveryEnabledNIWSServerList#obtainServersViaDiscovery
private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
    List<DiscoveryEnabledServer> serverList = new ArrayList<DiscoveryEnabledServer>();
	//...
	//拿到Eureka客户端,它可以进行服务拉取,注册等
    EurekaClient eurekaClient = eurekaClientProvider.get();
    if (vipAddresses!=null){
        for (String vipAddress : vipAddresses.split(",")) {
            // if targetRegion is null, it will be interpreted as the same region of client
            List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion);
            for (InstanceInfo ii : listOfInstanceInfo) {
                if (ii.getStatus().equals(InstanceStatus.UP)) {
					//... 封装服务并保存
                    DiscoveryEnabledServer des = createServer(ii, isSecure, shouldUseIpAddr);
                    serverList.add(des);
                }
            }
            if (serverList.size()>0 && prioritizeVipAddressBasedServers){
                break; // if the current vipAddress has servers, we dont use subsequent vipAddress based servers
            }
        }
    }
    return serverList;
}

6.服务列表更新

  • Ribbon服务列表更新是通过定时任务来完成的;相关类是PollingServerListUpdater;可以看一下它的start方法
public synchronized void start(final UpdateAction updateAction) {
    if (isActive.compareAndSet(false, true)) {
        //创建定时任务
        final Runnable wrapperRunnable = new Runnable() {
            @Override
            public void run() {
                //...
                try {
                    //更新主机信息
                    updateAction.doUpdate();
                    lastUpdated = System.currentTimeMillis();
                } catch (Exception e) {
                    logger.warn("Failed one update cycle", e);
                }
            }
        };
		//设置定时任务
        scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
            wrapperRunnable,
            initialDelayMs,
            refreshIntervalMs,
            TimeUnit.MILLISECONDS
        );
    }
}
  • 另外,Ribbon还会定期去Ping每一个主机,保证主机存活

7.服务注册中心

  • Ribbon默认的服务注册中心是eureka,如果想用nacos做服务注册中心,可以直接添加nacos依赖即可;
  • nacos中包含了Ribbon,并且提供了NacosServerList来与注册中心进行信息拉取
  • 而Eureka中使用的是DiscoveryEnabledNIWSServerList
  • 以上纯属猜测,具体需要自己实践一下;但Ribbon的注册中心替换原理应该类似,通过提供ServerList来实现
2021升级版微服务教程6—Ribbon使用+原理+整合Nacos权重+实战优化 一篇搞定 2021升级版SpringCloud教程从入门到实战精通「H版&alibaba&链路追踪&日志&事务&锁」 教程全目录「含视频」:https://gitee.com/bingqilinpeishenme/Java-Wiki Ribbon使用+原理+整合Nacos权重+实战优化 一篇搞定 Ribbon基本使用 简介 Ribbon是一个客户端负载均衡工具,封装Netflix Ribbon组件,能够提供客户端负载均衡能力。 理解Ribbon最重要的就是理解客户端这个概念 阅读详情

相关推荐

Ribbon架构和原理

文章重点介绍了Ribbon一个久经考验的功能:区域感知负载均衡器(Zone Aware Load Balancer)。 区域感知负载均衡器内置电路跳闸逻辑,可被配置基于区域同源关系(Zone Affinity,也就是更倾向于选择发出调用的服务所在的托管区域内,这样可用降低延迟,节省成本)选择目标服务实例。它监控每个区域中运行的实例的运 维行为,而且能够实时快速丢弃一整个区域。在面对整个区域的故障时,这帮我们提升了弹性。

Ribbon实战与原理剖析

通过实现IRule接口可以自定义负载策略,主要的选择服务逻辑在 choose 方法中。}

qq_38639813的博客 2846

SAP-ABAP:SAP中如何查看系统表之间的关联关系?

在业务逻辑开发或者ALV开发过程中,我们通常要从系统中找出几个标准表之间的关联关系,有的是head与item,通过外键进行关联的关系。那么怎么去找到呢?以上就是具体如何产看系统表之间的关联关系的步骤,对于不同代码的业务顾问,也是必须要掌握的。

LinkStar的博客 2430

Ribbon原理解析

一. 核心接口 ILoadBalancer Ribbon通过ILoadBalancer接口对外提供统一的选择服务器(Server)的功能,此接口会根据不同的负载均衡策略(IRule)选择合适的Server返回给使用者。其核心方法如下: public interface ILoadBalancer { public void addServers(List<Server> newServers); public Server chooseServer(Object key);

奋斗吧_-小青年 2664

Weaxor勒索病毒实战分析:从加密算法到防御策略(附检测工具清单)

本文对Weaxor勒索病毒进行了深度实战剖析,详细拆解了其采用ChaCha20与AES-CTR的混合加密算法、横向移动手法及数据回传行为。文章重点分析了其密钥生成机制的“漏洞修复”,并提供了从主机加固、网络监控到基于IOC威胁狩猎的立体化企业级防御策略与检测工具清单。

weixin_29233333的博客 262

IDEA 2023.2最新破解安装教程(含详细配置步骤及常见问题解决)

本文详细介绍了IDEA 2023.2的安装流程及高效开发环境搭建指南,涵盖基础配置、项目结构优化、核心功能使用、插件推荐、性能优化等全方位内容,帮助开发者快速掌握这一强大的Java开发工具。

x2y3z4a5b的博客 1752

Ribbon框架原理及解析

Ribbon框架原理及解析

多看多听多总结 1103

AC7811无感FOC实战:从开环到闭环的电机控制调试全记录

本文详细记录了基于国产AC7811芯片实现无感FOC电机控制的全过程。从硬件软件环境搭建、开环I/F启动调试,到滑模观测器参数整定,再到平滑切入闭环及双环PID调节,提供了完整的实战步骤与参数调试心法。文章重点分享了无感控制从开环到闭环切换的关键技巧与常见问题排查清单,为工程师提供了一份避坑指南。

kotlin6android的博客 713

SpringCloud——Ribbon 微服务负载均衡原理解析

首先,order-service发送请求,但请求会被Ribbon给拦截,它会识别要发送请求的对象是哪个模块的,然后向我们的eureka-server去要,eureka-server有就会返回给它,然后Ribbon将HTTP请求进行加工处理,通过自身的负载均衡算法,去挑选其中一个user-service发送请求,再得到返回的结果,然后将结果返回给order-service。不难猜想,中间一定有一个角色,帮我们实现了这个功能,它就是我们上面所提到的Ribbon,我们来看下面这幅图,就是Ribbon的工作原理。

m0_70325779的博客 624

图像标注神器:完全开源免费的多功能标注工具Labelme详解

Labelme是MIT开发的免费开源图像标注工具,支持多边形、矩形等多种标注类型,适用于目标检测、分割等计算机视觉任务。提供Windows可执行文件和Python安装两种方式,可输出JSON格式并转换为COCO等标准数据集。具有跨平台、功能全面、社区活跃等优势,能显著提升标注效率,是AI视觉项目的重要数据准备工具。

数字人生 528

【CAD二次开发】炸开所有的嵌套块

代码】【CAD二次开发】炸开所有的嵌套块。

1513

SpringCloud(7)Ribbon 与负载均衡

Ribbon SpringCloud Ribbon 是基于NetFlix Ribbon实现的一套客户端负载均衡的工具 简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer (简称LB) 后面所有的机器,Ribbon 会自动帮助你基于某种规则(如 简单轮询,随机连接等 )去连接这些机器。我们容易使用Ribbon 实现自定义负载均衡的算法

OVO_LQ_Start的博客 711

【第33话:车辆控制】自动驾驶车辆运动学模型推导及详解

自动驾驶车辆的运动学模型以自行车模型为核心,通过状态方程。

一碗白开水一 831

微服务精通之Ribbon原理解析

文章目录前言一、Ribbon是什么?二、负载均衡是什么?三、Ribbon原理解析1.Ribbon工作流程2.Eureka高可用架构3.Eureka服务端缓存架构4.Eureka健康检查5.Eureka分区四、Eureka实战1.创建Eureka服务端(1)创建名为eureka-server的maven工程(2)引入Eureka-server依赖(3)创建启动类(4)创建application.yml配置文件2.创建Eureka客户端(1)创建名为service-hi的maven工程(2)引入Eureka-c

欢迎关注同名公众号,一起探索Java技术的奥秘,共同成长! 1441

前沿探索:基于强化学习的目标检测与跟踪技术及论文解析

前沿探索:基于强化学习的目标检测与跟踪技术及论文解析 ,人工智能,计算机视觉,大模型,AI,本文聚焦基于强化学习的目标检测与跟踪技术,先介绍相关技术基础,包括目标检测、跟踪及强化学习的算法等。接着阐述强化学习在目标检测与跟踪中的应用思路、框架及典型算法,解析三篇相关论文的背景、方法、实验结果。还探讨了该技术面临的样本效率、奖励函数设计等挑战,以及结合迁移学习等的未来发展方向,为该领域研究提供参考。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 11万+

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Spring Cloud Ribbon客户端负载均衡深度解析:从原理到实战,打造高效微服务调用

本文深入剖析Spring Cloud Ribbon客户端负载均衡的核心机制与实战应用,结合Eureka服务注册中心,详解其高可用架构设计及内置算法实现(如轮询、随机、响应时间加权等)。通过源码逐层解读RoundRobinRule轮询策略,手把手指导如何通过@RibbonClient注解实现服务级负载策略定制,并规避@ComponentScan作用域陷阱。 更提供独创性实战案例:基于自旋锁实现原子计数器,手写仿Ribbon轮询算法,助你掌握微服务调用的底层逻辑。适合微服务开发者、架构师及对分布式系统设计感兴趣

Zyw907155124的博客 1157

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

【深入理解SpringCloud微服务】深入理解Ribbon原理并手写一个微服务负载均衡器

创建LoadBalancer前首先通过Java的SPI机制加载所有的负载均衡策略类LoadBalanceRule,再通过@Rule注解与配置文件的配置进行匹配,匹配出一个LoadBalanceRule。即使只有一个服务提供者,它的ip地址和端口好也是有可能会变的。由于有了负载均衡器,服务消费者请求服务提供者不再需要通过ip地址加端口号的方式,而是可以以服务名作为域名,负载均衡器会通过一定的负载均衡策略,选择服务名对应的微服务集群中的其中一个服务提供者节点,将请求地址中的服务名替换为该节点的ip地址端口号。

weixin_43889578的博客 1376

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

【SpringCloud微服务系列】SpringCloud组件原理:Eureka,Feign,Ribbon,Hystrix,Zuul

目录 1.前序 1.1 分布式的优缺点 1.1.1 分布式优点 1.1.2 分布式缺点 1.2 分布式服务框架 2.SpringCloud常用组件 2.1 Eureka 2.1.1 同为注册中心,Eureka和Zookeeper的区别: 2.2 Feign 2.3 Ribbon 2.4 Hystrix ...

Yangy的博客 1万+

spring cloud Ribbon的使用和实现原理

转载链接:https://blog.youkuaiyun.com/qq_20597727/article/details/82860521 简介 这篇文章主要介绍一下ribbon在程序中的基本使用,在这里是单独拿出来写用例测试的,实际生产一般是配置feign一起使用,更加方便开发。同时这里也通过源码来简单分析一下ribbon的基本实现原理。 基本使用 这里使用基于zookeeper注册中心+...

qq_29860591的博客 267

【一起学源码-微服务】Ribbon源码五:Ribbon源码解读汇总篇~

前言 想说的话 【一起学源码-微服务-Ribbon】专栏到这里就已经全部结束了,共更新四篇文章。 Ribbon比较小巧,这里是直接 读的spring cloud 内嵌封装的版本,里面的各种configuration确实有点绕,不过看看第三讲Ribbon初始化的过程总结图就会清晰很多。 紧接着会继续整理学习Feign源码相关的,敬请期待。 说明 原创不易,如若转载 请标明来源! 总结...

weixin_44626952的博客 286

Ribbon详解

简介 Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在...

清纯玉面可爱小郎君 662

微服务流程介绍

原地址:https://blog.youkuaiyun.com/qq_42046105 目录 一、业务场景介绍 二、Spring Cloud核心组件:Eureka 三、Spring Cloud核心组件:Feign 四、Spring Cloud核心组件:Ribbon 五、Spring Cloud核心组件:Hystrix 六、Spring Cloud核心组件:Zuul 七、总结 ...

天才战士的博客 1万+

【详解】Ribbon 负载均衡服务调用原理及默认轮询负载均衡算法源码解析、手写

Ribbon 负载均衡服务调用 一、什么是 Ribbon Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端 负载均衡的工具。 简单的说,Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端负载均衡算法和服务调用。Ribbon 客户组件提供一系列完善的配置如连接超时时,重试等。简单的说,就是在配置文件中列出 Load Balancer(简称 L B)后面的所有机器,Ribbon 会自动的帮助我们基于某种规则(如简单的轮询,随机连接等)去连接这些机

Welcome,Everyone. 2601

SpringCloud-Ribbon负载均衡

本文描述了Ribbon的负载均衡策略并对其中的轮询策略进行了详细分析

weixin_46120888的博客 4222

SpringCloud Ribbon 设计原理

Ribbon 是netflix 公司开源的基于客户端的负载均衡组件,是Spring Cloud大家庭中非常重要的一个模块; Ribbon应该也是整个大家庭中相对而言比较复杂的模块,直接影响到服务调度的质量和性能。 全面掌握Ribbon可以帮助我们了解在分布式微服务集群工作模式下,服务调度应该考虑到的每个环节。 本文将详细地剖析Ribbon的设计原理,帮助大家对Spring Cloud 有一个更好...

jackcheng1117的博客 1475
上一篇: Java线程池源码详解(一)
下一篇: 微服务之Hystrix原理解析
___mystack
博客等级 码龄9年 3粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值