当前位置: 首页 > news >正文

Ribbon源码

学了feign源码之后感觉,这部分还是按运行流程分块学合适。核心组件什么的,当专业术语学妥了。

序章:认识真正のRibbon

但只用认识一点点

之前我们学习Ribbon的简单使用时,都是集成了Eureka-client或者Feign等组件,甚至在使用时感受不到ribbon的存在,顶多提一嘴他的负载均衡接口。而本文要专注于讲解微服务体系下,Ribbon部分的基本原理。

ribbon项目目前已经进入On Maintenance维护状态,大家可以学的轻松些。

摘自Github:

Ribbon is a client side IPC library that is battle-tested in cloud. It provides the following features

  • Load balancing 负载均衡
  • Fault tolerance 容错
  • Multiple protocol (HTTP, TCP, UDP) support in an asynchronous and reactive model
    • 多协议、异步、reactive模型
  • Caching and batching 缓存和批处理

Ribbon是一个久经沙场的IPC库。IPC(Inter-Process Communication)进程间通信,是指两个进程的数据之间产生交互。

Ribbon的核心类

ribbon-loadbalancer模块

Server

对服务实例的封装,里面封装了服务实例的ip、端口、是否可用等信息。

ServerList

ServerList是个接口,提供了两个方法,都是获取Server实例列表的。

eureka、nacos等注册中心都实现了这个接口,将注册中心的服务实例数据提供给Ribbon,供Ribbon来进行负载均衡。

public interface ServerList<T extends Server> {public List<T> getInitialListOfServers();        /*** Return updated list of servers. This is called say every 30 secs * (configurable) by the Loadbalancer's Ping cycle     **/    public List<T> getUpdatedListOfServers();   
}
  • 是Eureka-client里实现了吗?那单独的ribbon有没有实现?能不能单独用Ribbon?
  • 这个get是说从注册中心拿,还是本地调用的时候从这里内存拿?
  • nacos怎么实现的?怎么和Feign结合的?

3、ServerListUpdater

用来更新服务注册列表的数据,他有唯一的实现类PollingServerListUpdater,该实现类有一个核心的方法start()。

start()方法首先通过CAS原子操作:isActive.compareAndSet(false, true)来获取锁(并发编程操作),然后将传入的参数updateAction的doUpdate()方法封装进了一个Runnable,然后包装好的Runnable扔到了定时调度的线程池,经过initialDelayMs(默认1s)时间后,会调用第一次,之后每隔refreshIntervalMs(默认30s)调用一次。

  • 参数updateAction哪里来,干什么?
@Override    
public synchronized void start(final UpdateAction updateAction) {   if (isActive.compareAndSet(false, true)) {           final Runnable wrapperRunnable = new Runnable() {     @Override            public void run() {    if (!isActive.get()) {      if (scheduledFuture != null) {    scheduledFuture.cancel(true);      }return;}try {updateAction.doUpdate();   lastUpdated = System.currentTimeMillis();   } catch (Exception e) {       logger.warn("Failed one update cycle", e);     }}};scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(wrapperRunnable, initialDelayMs,refreshIntervalMs, TimeUnit.MILLISECONDS);  } else { logger.info("Already active, no-op"); }
}

4、IRule

public interface IRule{/*  * choose one alive server from lb.allServers or   * lb.upServers according to key     ** @return choosen Server object. NULL is returned if none    *  server is available */public Server choose(Object key);        public void setLoadBalancer(ILoadBalancer lb);        public ILoadBalancer getLoadBalancer();    }

IRule是负载均衡的算法的统一接口,其实现类是各种负载均衡算法的具体实现。

比如说RandomRule类,随机轮询。

5、IClientConfig

配置接口,有个默认的实现DefaultClientConfigImpl,通过这个可以获取到一些配置Ribbon的一些配置。

6、ILoadBalancer

public interface ILoadBalancer {public void addServers(List<Server> newServers);   public Server chooseServer(Object key);   public void markServerDown(Server server);  @Deprecated public List<Server> getServerList(boolean availableOnly);public List<Server> getReachableServers();public List<Server> getAllServers();
}

ILoadBalancer主要提供了获取服务实例列表和选择服务实例的功能。

虽然对外主要提供获取服务的功能,但是在实现的时候,主要是用来协调上面提到的各个核心组件的,使得他们能够协调工作

这个接口的实现有好几个实现类,但是我讲两个比较重要的。

BaseLoadBalancer

public class BaseLoadBalancer extends AbstractLoadBalancer implementsPrimeConnections.PrimeConnectionListener, IClientConfigAware {private final static IRule DEFAULT_RULE = new RoundRobinRule();    protected IRule rule = DEFAULT_RULE;private IClientConfig config; protected volatile List<Server> allServerList = Collections.synchronizedList(new ArrayList<Server>());protected volatile List<Server> upServerList = Collections.synchronizedList(new ArrayList<Server>());public BaseLoadBalancer(String name, IRule rule, LoadBalancerStats stats,IPing ping, IPingStrategy pingStrategy) {logger.debug("LoadBalancer [{}]:  initialized", name);this.name = name;this.ping = ping;this.pingStrategy = pingStrategy;setRule(rule);setupPingTask();lbStats = stats;init();}public BaseLoadBalancer(IClientConfig config) {initWithNiwsConfig(config);}public BaseLoadBalancer(IClientConfig config, IRule rule, IPing ping) {initWithConfig(config, rule, ping, createLoadBalancerStatsFromConfig(config));}void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping, LoadBalancerStats stats) {this.config = clientConfig;String clientName = clientConfig.getClientName();this.name = clientName;int pingIntervalTime = Integer.parseInt(""+ clientConfig.getProperty(CommonClientConfigKey.NFLoadBalancerPingInterval,Integer.parseInt("30")));int maxTotalPingTime = Integer.parseInt(""+ clientConfig.getProperty(CommonClientConfigKey.NFLoadBalancerMaxTotalPingTime,Integer.parseInt("2")));setPingInterval(pingIntervalTime);setMaxTotalPingTime(maxTotalPingTime);// cross associate with each other// i.e. Rule,Ping meet your container LB// LB, these are your Ping and Rule guys ...setRule(rule);setPing(ping);setLoadBalancerStats(stats);rule.setLoadBalancer(this);if (ping instanceof AbstractLoadBalancerPing) {((AbstractLoadBalancerPing) ping).setLoadBalancer(this);}logger.info("Client: {} instantiated a LoadBalancer: {}", name, this);boolean enablePrimeConnections = clientConfig.get(CommonClientConfigKey.EnablePrimeConnections, DefaultClientConfigImpl.DEFAULT_ENABLE_PRIME_CONNECTIONS);if (enablePrimeConnections) {this.setEnablePrimingConnections(true);PrimeConnections primeConnections = new PrimeConnections(this.getName(), clientConfig);this.setPrimeConnections(primeConnections);}init();}public void setRule(IRule rule) {if (rule != null) {this.rule = rule;} else {/* default rule */this.rule = new RoundRobinRule();}if (this.rule.getLoadBalancer() != this) {this.rule.setLoadBalancer(this);}}public Server chooseServer(Object key) {if (counter == null) {counter = createCounter();}counter.increment();if (rule == null) {return null;} else {try {return rule.choose(key);} catch (Exception e) {logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);return null;}}}}

核心属性

allServerList:缓存了所有的服务实例数据

upServerList:缓存了能够使用的服务实例数据。

rule:负载均衡算法组件,默认是RoundRobinRule

核心方法

setRule:这个方法是设置负载均衡算法的,并将当前这个ILoadBalancer对象设置给IRule,从这可以得出一个结论,IRule进行负载均衡的服务实例列表是通过ILoadBalancer获取的,也就是 IRule 和 ILoadBalancer相互引用。setRule(rule)一般是在构造对象的时候会调用。

chooseServer:就是选择一个服务实例,是委派给IRule的choose方法来实现服务实例的选择。

DynamicServerListLoadBalancer

public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicServerListLoadBalancer.class);volatile ServerList<T> serverListImpl;    volatile ServerListFilter<T> filter;    protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {        @Override        public void doUpdate() {            updateListOfServers();        }    };    protected volatile ServerListUpdater serverListUpdater;        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);    }            @Override    public void setServersList(List lsrv) {        super.setServersList(lsrv);        List<T> serverList = (List<T>) lsrv;        Map<String, List<Server>> serversInZones = new HashMap<String, List<Server>>();        for (Server server : serverList) {            // make sure ServerStats is created to avoid creating them on hot            // path            getLoadBalancerStats().getSingleServerStat(server);            String zone = server.getZone();            if (zone != null) {                zone = zone.toLowerCase();                List<Server> servers = serversInZones.get(zone);                if (servers == null) {                    servers = new ArrayList<Server>();                    serversInZones.put(zone, servers);                }                servers.add(server);            }        }        setServerListForZones(serversInZones);    }protected void setServerListForZones(            Map<String, List<Server>> zoneServersMap) {        LOGGER.debug("Setting server list for zones: {}", zoneServersMap);        getLoadBalancerStats().updateZoneServerMapping(zoneServersMap);    }@VisibleForTesting    public void updateListOfServers() {        List<T> servers = new ArrayList<T>();        if (serverListImpl != null) {            servers = serverListImpl.getUpdatedListOfServers();            LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",                    getIdentifier(), servers);if (filter != null) {                servers = filter.getFilteredListOfServers(servers);                LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",                        getIdentifier(), servers);            }        }        updateAllServerList(servers);    }/**     * Update the AllServer list in the LoadBalancer if necessary and enabled     *      * @param ls     */    protected void updateAllServerList(List<T> ls) {        // other threads might be doing this - in which case, we pass        if (serverListUpdateInProgress.compareAndSet(false, true)) {            try {                for (T s : ls) {                    s.setAlive(true); // set so that clients can start using these                                      // servers right away instead                                      // of having to wait out the ping cycle.                }                setServersList(ls);                super.forceQuickPing();            } finally {                serverListUpdateInProgress.set(false);            }        }    }}

DynamicServerListLoadBalancer继承自BaseLoadBalancer, 进行了一些扩展。

成员变量

serverListImpl:上面说过,通过这个接口获取服务列表

filter:起到过滤的作用

updateAction:是个匿名内部类,实现了doUpdate方法,会调用updateListOfServers方法

serverListUpdater:上面说到过,默认就是唯一的实现类PollingServerListUpdater,也就是每个30s就会调用传入的updateAction的doUpdate方法。

这不是巧了么,serverListUpdater的start方法需要一个updateAction,刚刚好成员变量有个updateAction的匿名内部类的实现,所以serverListUpdater的start方法传入的updateAction的实现其实就是这个匿名内部类。
那么哪里调用了serverListUpdater的start方法传入了updateAction呢?是在构造的时候调用的,具体的调用链路是调用 restOfInit -> enableAndInitLearnNewServersFeature(),这里就不贴源码了

所以,其实DynamicServerListLoadBalancer在构造完成之后,默认每隔30s中,就会调用updateAction的匿名内部类的doUpdate方法,从而会调用updateListOfServers。所以我们来看一看 updateListOfServers 方法干了什么。

public void updateListOfServers() {List<T> servers = new ArrayList<T>();if (serverListImpl != null) {servers = serverListImpl.getUpdatedListOfServers();LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",getIdentifier(), servers);if (filter != null) {servers = filter.getFilteredListOfServers(servers);LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",getIdentifier(), servers);}}updateAllServerList(servers);}

这个方法实现很简单,就是通过调用 ServerList 的getUpdatedListOfServers获取到一批服务实例数据,然后过滤一下,最后调用updateAllServerList方法。

查看updateAllServerList()源码:

  protected void updateAllServerList(List<T> ls) {// other threads might be doing this - in which case, we passif (serverListUpdateInProgress.compareAndSet(false, true)) {try {for (T s : ls) {s.setAlive(true); // set so that clients can start using these// servers right away instead// of having to wait out the ping cycle.}setServersList(ls);super.forceQuickPing();} finally {serverListUpdateInProgress.set(false);}}}

该方法调用每个服务实例的setAlive()方法,将isAliveFlag设置成true,然后调用setServersList()将服务实例更新到内部的缓存中,也就是上面提到的allServerList和upServerList中,这里就不贴源码了。

其实分析完updateListOfServers方法之后,再结合上面源码的分析,我们可以清楚的得出一个结论,那就是默认每隔30s都会重新通过ServerList组件获取到服务实例数据,然后更新到BaseLoadBalancer缓存中,IRule的负载均衡所需的服务实例数据,就是这个内部缓存。

从DynamicServerListLoadBalancer的命名也可以看出,他相对于父类BaseLoadBalancer而言,提供了动态更新内部服务实例列表的功能。

图片

说完一些核心的组件,以及他们跟ILoadBalancer的关系之后,接下来就来分析一下,ILoadBalancer是在ribbon中是如何使用的。

8、AbstractLoadBalancerAwareClient

ILoadBalancer是一个可以获取到服务实例数据的组件,AbstractLoadBalancerAwareClient,这个是用来执行请求的,我们来看一下这个类的构造。

   public AbstractLoadBalancerAwareClient(ILoadBalancer lb) {      super(lb); } 
/**
* Delegate to {@link #initWithNiwsConfig(IClientConfig)} 
* @param clientConfig
*/
public AbstractLoadBalancerAwareClient(ILoadBalancer lb, IClientConfig clientConfig) {    super(lb, clientConfig);   
}

通过上面可以看出,在构造的时候需要传入一个ILoadBalancer。

AbstractLoadBalancerAwareClient中有一个方法executeWithLoadBalancer,这个是用来执行传入的请求,以负载均衡的方式。

   public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {        LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);try {            return command.submit(                new ServerOperation<T>() {                    @Override                    public Observable<T> call(Server server) {                        URI finalUri = reconstructURIWithServer(server, request.getUri());                        S requestForServer = (S) request.replaceUri(finalUri);                        try {                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));                        }                         catch (Exception e) {                            return Observable.error(e);                        }                    }                })                .toBlocking()                .single();        } catch (Exception e) {            Throwable t = e.getCause();            if (t instanceof ClientException) {                throw (ClientException) t;            } else {                throw new ClientException(e);            }        }            }

这个方法构建了一个LoadBalancerCommand,随后调用了submit方法,传入了一个匿名内部类,这个匿名内部类中有这么一行代码很重要。

URI finalUri = reconstructURIWithServer(server, request.getUri());

这行代码是根据给定的一个Server重构了URI,reconstructURIWithServer干的一件事就是将ServerA服务名替换成真正的服务所在的机器的ip和端口,这样就能发送http请求到ServerA服务所对应的一台服务器了。

之后根据新的地址,调用这个类中的execute方法来执行请求,execute方法是个抽象方法,也就是交给子类实现,子类就可以通过实现这个方法,来发送http请求,实现rpc调用。

那么这台Server是从获取的呢?肯定是通过ILoadBalancer获取的。直接贴出submit方法中核心的一部分代码

Observable<T> o = (server == null ? selectServer() : Observable.just(server))

就是通过selectServer来选择一个Server的,selectServer我就不翻源码了,其实最终还是调用ILoadBalancer的方法chooseServer方法来获取一个服务,之后就会调用上面的说的匿名内部类的方法,重构URI,然后再交由子类的execut方法来实现发送http请求。

所以,通过对AbstractLoadBalancerAwareClient的executeWithLoadBalancer方法,我们可以知道,这个抽象类的主要作用就是通过负载均衡算法,找到一个合适的Server,然后将你传入的请求路径http://ServerA/api/sayHello重新构建成类似http://192.168.1.101:8088/api/sayHello这样,之后调用子类实现的execut方法,来发送http请求,就是这么简单。

到这里其实Ribbon核心组件和执行原理我就已经说的差不多了,再来画一张图总结一下

图片

SpringCloud中使用的核心组件的实现都有哪些

说完了Ribbon的一些核心组件和执行原理之后,我们再来看一下在SpringCloud环境下,这些组件到底是用的哪些实现

Ribbon, 启动!(原理)

启动阶段的任务

  • 你们微服务源码流程够有共性的,一起总结下?

Ribbon的自动装配类为RibbonAutoConfiguration,此处粘贴核心源码:

@Configuration
@RibbonClients
public class RibbonAutoConfiguration {@Autowired(required = false)private List<RibbonClientSpecification> configurations = new ArrayList<>();@Beanpublic SpringClientFactory springClientFactory() {SpringClientFactory factory = new SpringClientFactory();factory.setConfigurations(this.configurations);return factory;}
}

RibbonAutoConfiguration配置类上有个@RibbonClients注解,接下来讲解一下这个注解的作用

@Import(RibbonClientConfigurationRegistrar.class)
public @interface RibbonClients {RibbonClient[] value() default {};Class<?>[] defaultConfiguration() default {};
}

OpenFeign的小伙伴肯定知道,要使用Feign,得需要使用@EnableFeignClients,@EnableFeignClients的作用可以扫描指定包路径下的@FeignClient注解,也可以声明配置类;

RibbonClients的作用也是可以声明配置类,同样也使用了@Import注解来实现,RibbonClientConfigurationRegistrar这个配置类的作用就是往spring容器中注入每个服务的Ribbon组件(@RibbonClient里面可以声明每个服务对应的配置)的配置类和默认配置类,将配置类封装为RibbonClientSpecification注入到spring容器中。

RibbonAutoConfiguration的主要作用就是注入了一堆RibbonClientSpecification,就是每个服务对应的配置类,然后声明了SpringClientFactory这个bean,将配置类放入到里面。

SpringClientFactory跟OpenFeign中的FeignContext很像,SpringClientFactory也继承了NamedContextFactory,实现了配置隔离,同时也在构造方法中传入了每个容器默认的配置类RibbonClientConfiguration。至于什么是配置隔离,我在OpenFeign那篇文章说过,不清楚的小伙伴可以后台回复feign01即可获得文章链接。

配置优先级问题

优先级最高的是springboot启动的时候的容器,因为这个容器是每个服务的容器的父容器,而在配置类声明bean的时候,都有@ConditionalOnMissingBean注解,一旦父容器有这个bean,那么子容器就不会初始化。

优先级第二高的是每个客户端声明的配置类,也就是通过@FeignClient和@RibbonClient的configuration属性声明的配置类

优先级第三高的是@EnableFeignClients和@RibbonClients注解中configuration属性声明的配置类

优先级最低的就是FeignContext和SpringClientFactory构造时传入的配置类

至于优先级怎么来的,其实是在NamedContextFactory中createContext方法中构建AnnotationConfigApplicationContext时按照配置的优先级一个一个传进去的。

RibbonClientConfiguration提供的默认的bean

接下来我们看一下RibbonClientConfiguration都提供了哪些默认的bean

  @Bean@ConditionalOnMissingBeanpublic IClientConfig ribbonClientConfig() {DefaultClientConfigImpl config = new DefaultClientConfigImpl();config.loadProperties(this.name);config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);return config;}

配置类对应的bean,这里设置了ConnectTimeout和ReadTimeout都是1s中。

  @Bean  @ConditionalOnMissingBean  public IRule ribbonRule(IClientConfig config) {    if (this.propertiesFactory.isSet(IRule.class, name)) {      return this.propertiesFactory.get(IRule.class, config, name);    }    ZoneAvoidanceRule rule = new ZoneAvoidanceRule();    rule.initWithNiwsConfig(config);    return rule;  }

IRule,默认是ZoneAvoidanceRule,这个Rule带有过滤的功能,过滤哪些不可用的分区的服务,过滤成功之后,继续采用线性轮询的方式从过滤结果中选择一个出来。至于这个propertiesFactory,可以不用管,这个是默认读配置文件的中的配置,一般不设置,后面看到都不用care。

@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerList<Server> ribbonServerList(IClientConfig config) {if (this.propertiesFactory.isSet(ServerList.class, name)) {return this.propertiesFactory.get(ServerList.class, config, name);}ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();serverList.initWithNiwsConfig(config);return serverList;
}

默认是ConfigurationBasedServerList,也就是基于配置来提供服务实例列表。但是在SpringCloud环境中,服务信息是注册在注册中心的。因此注册中心实现了优先级更高的ServerList。比如Nacos的实现NacosServerList,这里我贴出NacosServerList的bean的声明,在配置类NacosRibbonClientConfiguration中

  @Bean  @ConditionalOnMissingBeanpublic ServerList<?> ribbonServerList(IClientConfig config, NacosDiscoveryProperties nacosDiscoveryProperties) {NacosServerList serverList = new NacosServerList(nacosDiscoveryProperties);    serverList.initWithNiwsConfig(config);    return serverList;}

至于为什么容器选择NacosServerList而不是ConfigurationBasedServerList,主要是因为NacosRibbonClientConfiguration这个配置类是通过@RibbonClients导入的,比SpringClientFactory导入的RibbonClientConfiguration配置类优先级高。

  • @ConditionalOnMissingBean?这类注解还是不要在Spring里总结了,大而全,还是需要的时候临时现场介绍下
  • 是说单独使用ribbon的时候用ConfigurationBasedServerList从配置里读取服务列表,使用注册中心的时候,利用优先级覆盖关系,使用注册中心的列表?多么天才的发明!
  • 服务中心怎么通信自己的?怎么 拉取的?nacos也默认集成ribbon吗?
@Bean  
@ConditionalOnMissingBean
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {    return new PollingServerListUpdater(config); 
}

ServerListUpdater,就是我们剖析的PollingServerListUpdater,默认30s更新一次BaseLoadBalancer内部服务的缓存。

@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,IRule rule, IPing ping, ServerListUpdater serverListUpdater) {if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {return this.propertiesFactory.get(ILoadBalancer.class, config, name);}return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,serverListFilter, serverListUpdater);}

ILoadBalancer,默认是ZoneAwareLoadBalancer,构造的时候也传入了上面声明的的bean,ZoneAwareLoadBalancer这个类继承了DynamicServerListLoadBalancer。

到这里,Ribbon在SpringCloud的配置我们就讲完了,主要就是声明了很多核心组件的bean,最后都设置到ZoneAwareLoadBalancer中。但是,AbstractLoadBalancerAwareClient这个对象的声明我们并没有在配置类中找到,主要是因为这个对象是OpenFeign整合Ribbon的一个入口,至于是如何整合的,这个坑就留给下篇文章吧。

那么在springcloud中,上图就可以加上注册中心。

图片

附录

可能一辈子也没必要看,但如果你真的想看的Ribbon源码地址

在不结合其他微服务组件的情况下,我们也可以单独使用ribbon

<dependency><groupId>com.netflix.ribbon</groupId><artifactId>ribbon</artifactId><version>2.2.2</version>
</dependency>

相关文章:

Ribbon源码

学了feign源码之后感觉&#xff0c;这部分还是按运行流程分块学合适。核心组件什么的&#xff0c;当专业术语学妥了。序章&#xff1a;认识真正のRibbon 但只用认识一点点 之前我们学习Ribbon的简单使用时&#xff0c;都是集成了Eureka-client或者Feign等组件&#xff0c;甚至在…...

Linux下在终端输入密码隐藏方法

Linux系统中&#xff0c;如何将在终端输入密码时将密码隐藏&#xff1f; 最近做简单的登录界面时&#xff0c;不做任何操作的话&#xff0c;在终端输入密码的同时也会显示输入的密码是什么&#xff0c;这样对于隐蔽性和使用都有不好的体验。那么我就想到将密码用字符*隐藏起来…...

【ARM 常见汇编指令学习 3 -- ARM64 无符号位域提取指令 UBFX】

文章目录 ARM64 无符号位域提取指令 上篇文章&#xff1a;ARM 常见汇编指令学习 2 – 存储指令 STP 与 LDP 下篇文章&#xff1a;ARM 常见汇编指令学习 4 – ARM64 比较指令 cbnz 与 b.ne 区别 ARM64 无符号位域提取指令 在代码中如何监控寄存器的某1bit&#xff0c; 或者某几…...

求分享如何批量压缩视频的容量的方法

视频内存过大&#xff0c;不但特别占内存&#xff0c;而且还会使手机电脑出现卡顿的现象&#xff0c;除此之外&#xff0c;如果我们想发送这些视频文件可能还会因为内存太大无法发送。因此&#xff0c;我们可以批量地压缩视频文件的内存大小&#xff0c;今天小编要来分享一招&a…...

ChatGPT 是如何工作的:从预训练到 RLHF

欢迎来到人工智能的未来&#xff1a;生成式人工智能&#xff01;您是否想知道机器如何学习理解人类语言并做出相应的反应&#xff1f;让我们来看看ChatGPT ——OpenAI 开发的革命性语言模型。凭借其突破性的 GPT-3.5 架构&#xff0c;ChatGPT 席卷了世界&#xff0c;改变了我们…...

KafKa脚本操作

所有操作位于/usr/local/kafka_2.12-3.5.1/bin。 rootubuntu2203:/usr/local/kafka_2.12-3.5.1/bin# pwd /usr/local/kafka_2.12-3.5.1/bin rootubuntu2203:/usr/local/kafka_2.12-3.5.1/bin# ls connect-distributed.sh kafka-delegation-tokens.sh kafka-mirror-mak…...

【自动化运维】playbook剧本

目录 一、Ansible 的脚本 playbook 剧本1.1playbooks的组成 二、剧本编写实验2.1定义、引用变量2.2使用远程主机sudo切换用户2.3whenn条件判断2.4迭代 三、Templates 模板四、Tags模板 一、Ansible 的脚本 playbook 剧本 1.1playbooks的组成 &#xff08;1&#xff09;Tasks&…...

java中双引号和单引号的区别

起因 刷题的时候&#xff0c;有判断是否相同的情况&#xff0c;然后我发现单引号和双引号在上的表现不一样&#xff0c;所以记录一下。 解释 在Java中&#xff0c;双引号&#xff08;" "&#xff09;和单引号&#xff08;’ &#xff09;在使用上有很重要的区别&a…...

jenkinsfile指定jenkins流水线的构建号

背景 升级Jenkins过程中不小心导致流水线配置文件job目录丢失, 重新配置流水线后所有流水线构建号码都从1开始构建了, 然而我们的产品关联了jenkins构建号,重新从1 构建会导致各种问题. 解决方案 在Jenkinsfile文件中指定流水线的构建号为一个不存在的数字, 这样就不会冲突了…...

微信小程序:实现提示窗确定,取消执行不同操作(消息提示确认取消)showModal

效果 代码 wx.showModal({title: 提示,content: 是否确认退出,success: function (res) {if (res.confirm) {console.log(用户点击确定)} else if (res.cancel) {console.log(用户点击取消)}}})...

深度学习论文: Q-YOLO: Efficient Inference for Real-time Object Detection及其PyTorch实现

深度学习论文: Q-YOLO: Efficient Inference for Real-time Object Detection及其PyTorch实现 Q-YOLO: Efficient Inference for Real-time Object Detection PDF: https://arxiv.org/pdf/2307.04816.pdf PyTorch代码: https://github.com/shanglianlm0525/CvPytorch PyTorch代…...

解读随机森林的决策树:揭示模型背后的奥秘

一、引言 随机森林[1]是一种强大的机器学习算法&#xff0c;在许多领域都取得了显著的成功。它由多个决策树组成&#xff0c;而决策树则是构建随机森林的基本组件之一。通过深入解析决策树&#xff0c;我们可以更好地理解随机森林模型的工作原理和内在机制。 决策树是一种树状结…...

OceanMind海睿思获评中国信通院“内审数字化产品评测”卓越级(最高级)!

2023年7月27日&#xff0c;由中国内部审计协会、中国通信标准化协会指导&#xff0c;中国信息通信研究院主办的第二届数字化审计论坛在北京成功召开。 大会聚焦内部审计数字化领域先进实践、研究成果、行业发展举措&#xff0c;重磅发布了多项内部审计数字化领域的最新研究和实…...

TPlink云路由器界面端口映射设置方法?快解析内网穿透能实现吗?

有很多网友在问&#xff1a;TPlink路由器端口映射怎么设置&#xff1f;因为不懂端口映射的原理&#xff0c;所以无从下手&#xff0c;下面小编就给大家分享TPlink云路由器界面端口映射设置方法&#xff0c;帮助大家快速入门TP路由器端口映射设置方法。 1.登录路由器管理界面&a…...

css3的filter图片滤镜使用

业务介绍 默认&#xff1a;第一个图标为选中状态&#xff0c;其他三个图标事未选中状态 样式&#xff1a;选中状态是深蓝&#xff0c;未选中状体是浅蓝 交互&#xff1a;鼠标放上去选中&#xff0c;其他未选中&#xff0c;鼠标离开时候保持当前选中状态 实现&#xff1a;目前…...

❤️创意网页:打造炫酷网页 - 旋转彩虹背景中的星星动画

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…...

react常用知识点

React是一个用于构建用户界面的JavaScript库。以下是React常用的知识点&#xff1a; 组件&#xff1a;React将用户界面分解成小而独立的组件&#xff0c;每个组件都有自己的状态和属性&#xff0c;并且可以通过组合这些组件来构建复杂的用户界面。 // 函数组件示例 function We…...

iOS开发-QLPreviewController与UIDocumentInteractionController显示文档

iOS开发-QLPreviewController与UIDocumentInteractionController显示文档 在应用中,我们有时想预览文件, 可以使用QLPreviewController与UIDocumentInteractionController 一、QLPreviewController与UIDocumentInteractionController QLPreviewController是一个 UIViewContr…...

八、用 ChatGPT 帮助排查生产事故

目录 一、实验介绍 二、背景 三、故障排查概述 3.1 生产环境故障排查涉及的角色...

WPF实战学习笔记25-首页汇总

注意&#xff1a;本实现与视频不一致。本实现中单独做了汇总接口&#xff0c;而视频中则合并到国todo接口当中了。 添加汇总webapi接口添加汇总数据客户端接口总数据客户端接口对接3首页数据模型 添加数据汇总字段类 新建文件MyToDo.Share.Models.SummaryDto using System;…...

FreeRTOS源码分析-7 消息队列

目录 1 消息队列的概念和作用 2 应用 2.1功能需求 2.2接口函数API 2.3 功能实现 3 消息队列源码分析 3.1消息队列控制块 3.2消息队列创建 3.3消息队列删除 3.4消息队列在任务中发送 3.5消息队列在中断中发送 3.6消息队列在任务中接收 3.7消息队列在中断中接收 1 消…...

机器学习深度学习——权重衰减

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——模型选择、欠拟合和过拟合 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你…...

【Linux】线程互斥 -- 互斥锁 | 死锁 | 线程安全

引入互斥初识锁互斥量mutex锁原理解析 可重入VS线程安全STL中的容器是否是线程安全的? 死锁 引入 我们写一个多线程同时访问一个全局变量的情况(抢票系统)&#xff0c;看看会出什么bug&#xff1a; // 共享资源&#xff0c; 火车票 int tickets 10000; //新线程执行方法 vo…...

【vue-pdf】PDF文件预览插件

1 插件安装 npm install vue-pdf vue-pdf GitHub&#xff1a;https://github.com/FranckFreiburger/vue-pdf#readme 参考文档&#xff1a;https://www.cnblogs.com/steamed-twisted-roll/p/9648255.html catch报错&#xff1a;vue-pdf组件报错vue-pdf Cannot read properti…...

Flink集群运行模式--Standalone运行模式

Flink集群运行模式--Standalone运行模式 一、实验目的二、实验内容三、实验原理四、实验环境五、实验步骤5.1 部署模式5.1.1 会话模式&#xff08;Session Mode&#xff09;5.1.2 单作业模式&#xff08;Per-Job Mode&#xff09;5.1.3 应用模式&#xff08;Application Mode&a…...

Spring整合JUnit实现单元测试

Spring整合JUnit实现单元测试 一、引言 在软件开发过程中&#xff0c;单元测试是保证代码质量和稳定性的重要手段。JUnit是一个流行的Java单元测试框架&#xff0c;而Spring是一个广泛应用于Java企业级开发的框架。本文将介绍如何使用Spring整合JUnit实现单元测试&#xff0c…...

Spring Boot学习路线1

Spring Boot是什么&#xff1f; Spring Boot是基于Spring Framework构建应用程序的框架&#xff0c;Spring Framework是一个广泛使用的用于构建基于Java的企业应用程序的开源框架。Spring Boot旨在使创建独立的、生产级别的Spring应用程序变得容易&#xff0c;您可以"只是…...

管理类联考——写作——论说文——实战篇——标题篇

角度3——4种材料类型、4个立意对象、5种写作态度 经过审题立意后&#xff0c;我们要根据我们的立意&#xff0c;确定一个主题&#xff0c;这个主题必须通过文章的标题直接表达出来。 标题的基本要求 主题清晰&#xff0c;态度明确 第一&#xff0c;阅卷人看到一篇论说文的标…...

idea中设置maven本地仓库和自动下载依赖jar包

1.下载maven 地址&#xff1a;maven3.6.3 解压缩在D:\apache-maven-3.6.3-bin\apache-maven-3.6.3\目录下新建文件夹repository打开apache-maven-3.6.3-bin\apache-maven-3.6.3\conf文件中的settings.xml编辑&#xff1a;新增本地仓库路径 <localRepository>D:\apache-…...

前缀和差分

前缀和 前缀和&#xff1a;一段序列里的前n项和 给出n个数&#xff0c;在给出q次问询&#xff0c;每次问询给出L、R&#xff0c;快速求出每组数组中一段L至R区间的和 给出一段数组&#xff0c;每次问询为求出l到r区间的和 普通方法&#xff1a;L到R进行遍历&#xff0c;那么…...

烟台搭建网站建设制作/怎样做关键词排名优化

现在很多的朋友都应该见过VR全景&#xff0c;但是不知道VR全景具体是什么&#xff1f;是怎么做的&#xff1f;有什么作用&#xff1f;今天小粉就给大家说一下。VR全景是当今最新颖的宣传展示的手段&#xff0c;代表了科技的更进一步&#xff0c;随着5G的发展&#xff0c;VR全景…...

嘉兴做营销型网站/百度网盘客服24小时电话人工服务

文章目录08. 将有序数组转换为二叉搜索树题目分析完整代码如下跟随carl代码随想录刷题 语言&#xff1a;python 08. 将有序数组转换为二叉搜索树 题目&#xff1a;给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索…...

wordpress https到期/网页生成

安装OpenStack客户端并创建一个云主机 查看创建好的openstack项目中的信息和云主机网络连通性 openstack web界面使用方法 openstack-allinone-使用方法 安装OpenStack客户端并创建一个云主机 安装OpenStack client端&#xff0c;方便后期使用命令行操作openstack [roottest…...

传媒大学附近网站建设公司/b站推广网站入口mmm

本文讲解如何通过ElementTree来操作XML&#xff1b; 1.引入库需要用到3个类&#xff0c;ElementTree&#xff0c;Element以及建立子类的包装类SubElement from xml.etree.ElementTree import ElementTreefrom xml.etree.ElementTree import Elementfrom xml.etree.ElementTree …...

管理培训课程/搜索关键词排名优化技术

setTimeout(methodName, interval); //间隔时间单位为毫秒&#xff0c;表示interval毫秒后执行方法methodName setInterval(methodName, interval); //间隔时间单位为毫秒&#xff0c;表示每隔interval毫秒执行后都会执行一次方法methodName 执行的方法可以带参数&#xff0c;但…...

wordpress smtp插件/免费放单平台无需垫付

2019独角兽企业重金招聘Python工程师标准>>> 在项目中加入附件中的DevExpress.Localization.v10.1.dll引用 winform: 在MDI MainForm 的FormLoad事件中加入以下sources DevExpress.Utils.Localization.AccLocalizer.Active new DevExpress.LocalizationCHS.DevExpr…...