前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Ribbon 负载均衡器 LoadBalancer 源码解析

Ribbon 负载均衡器 LoadBalancer 源码解析

作者头像
Java技术编程
修改于 2020-05-21 04:42:46
修改于 2020-05-21 04:42:46
2.2K00
代码可运行
举报
文章被收录于专栏:Java技术编程Java技术编程
运行总次数:0
代码可运行

本文首发于个人公众号 Java 技术大杂烩,欢迎关注

前言

什么是负载均衡?简单来说一个应用,后台有多个服务来支撑,即利用多台服务器提供单一服务,当某个服务挂了或者负载过高的时候,负载均衡器能够选择其他的服务来处理请求,用来提高应用的高可用性和高并发行;此外,当用户请求过来的时候,负载均衡器会将请求后台内网服务器,内网服务器将请求的响应返回给负载平衡器,负载平衡器再将响应发送到用户,这样可以阻止了用户直接访问后台服务器,使得服务器更加安全。

维基百科-负载均衡 https://zh.wikipedia.org/wiki/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1

Ribbon 负载均衡器

Ribbon 的负载均衡器是通过 LoadBalancerClient 来实现的,在应用启动的时候,LoadBalancerClient 默认会从 EurekaClient 获取服务列表,并将服务注册列表缓存在本地,当调用 LoadBalancerClientchoose() 方法的时候, 根据负载均衡策略 IRule 来选择一个可用的服务,从而实现负载均衡。

当然,LoadBalancerClient 也可以不从 EurekaClient 中获取服务列表,这时需要自己维护一个服务注册列表信息,具体操作如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ribbon:
  eureka:
    enabled: false

stores:
  ribbon:
    listOfServers: baidu.com, google.com

Ribbon 负载均衡器流程图

主要流程:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1. 当应用启动的时候,ILoadBalancer 从 EurekaClient 获取服务列表
2. 然后每 10 秒 向 EurekaClient 发送一次心跳检测,如果注册列表发生了变化,则更新获取重新获取
3. LoadBalancerClient 调用 choose() 方法来选择服务的时候,会调用 ILoadBalancer 的  chooseServer() 来获取一个可以的服务,
4. 在 ILoadBalancer 进行获取服务的时候,会根据负载均衡策略 IRule 来进行选择
5. 返回可用的服务

Ribbon 负载均衡器实现原理

下面就来看看每个类的实现原理

RibbonLoadBalancerClient

RibbonLoadBalancerClient 它是 Ribbon 负载均衡实现的一个重要的类,最终的负载均衡的请求处理都由它来执行,先来看下它的类图:

它实现了 LoadBalancerClient 接口,而 LoadBalancerClient 接口实现了 ServiceInstanceChooser 接口

ServiceInstanceChooser

该接口用来从负载均衡器中获取一个可用的服务,只有一个方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface ServiceInstanceChooser {
    /**
     * @param serviceId:服务ID
     * @return 可用的服务实例
     */
    ServiceInstance choose(String serviceId);
}
LoadBalancerClient

表示负载均衡的客户端,是一个接口,继承了 ServiceInstanceChooser 接口 ,共有三个方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface LoadBalancerClient extends ServiceInstanceChooser {
    /**
     * 执行请求
     * @param serviceId :用于查找 LoadBalancer的服务ID
     * @param request:允许实现执行前后操作
     */
    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

    /**
     * 执行请求
     * @param serviceId :用于查找 LoadBalancer的服务ID
     * @param serviceInstance :执行请求的服务
     * @param request:允许实现执行前后操作
     */
    <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;

    /**
     * 创建具有真实主机和端口的正确URI,有些系统使用带有逻辑服务名的URL作为主机,调用该方法将会使用 host:port 来替换逻辑服务名
     * @param instance :用于重建URI的服务实例
     * @param original :具有逻辑服务名的URL
     * @return A reconstructed URI.
     */
    URI reconstructURI(ServiceInstance instance, URI original);
}
RibbonLoadBalancerClient 实现如下:

主要看从 ServiceInstanceChooserLoadBalancerClient 中实现的接口方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class RibbonLoadBalancerClient implements LoadBalancerClient {

    // 工厂:主要用来创建客户端,创建负载均衡器,进行客户端配置等
    // 对于每一个客户端名称都会创建一个Spring ApplicationContext,可以从中获取需要的bean
    private SpringClientFactory clientFactory;

    protected ILoadBalancer getLoadBalancer(String serviceId) {
        return this.clientFactory.getLoadBalancer(serviceId);
    }
    ..................
}


public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {

    // 获取客户端
    public <C extends IClient<?, ?>> C getClient(String name, Class<C> clientClass) {
        return getInstance(name, clientClass);
    }

    // 获取负载均衡器
    public ILoadBalancer getLoadBalancer(String name) {
        return getInstance(name, ILoadBalancer.class);
    }

    //获取客户端配置
    public IClientConfig getClientConfig(String name) {
        return getInstance(name, IClientConfig.class);
    }

    // 获取 RibbonLoadBalancerContext
    public RibbonLoadBalancerContext getLoadBalancerContext(String serviceId) {
        return getInstance(serviceId, RibbonLoadBalancerContext.class);
    }

    // 获取对应的bean
    public <T> T getInstance(String name, Class<T> type) {
        AnnotationConfigApplicationContext context = getContext(name);
        .....
        return context.getBean(type);
        .....
    }
}
choose() 方法

该方法主要用来获取一个可用的服务实例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public ServiceInstance choose(String serviceId, Object hint) {
        Server server = getServer(getLoadBalancer(serviceId), hint);
        if (server == null) {
            return null;
        }
        // RibbonServer 实现了 ServiceInstance
        return new RibbonServer(serviceId, server, isSecure(server, serviceId),
                serverIntrospector(serviceId).getMetadata(server));
    }

    // 根据服务ID获取负载均衡器,会调用 SpringClientFactory 的方法进行获取
    protected ILoadBalancer getLoadBalancer(String serviceId) {
        return this.clientFactory.getLoadBalancer(serviceId);
    }

    // 根据负载均衡器来获取可用的服务
    protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
        if (loadBalancer == null) {
            return null;
        }
        return loadBalancer.chooseServer(hint != null ? hint : "default");
    }

最后会调用 ILoadBalancer.chooseServer 来获取可用服务,后面再来说 ILoadBalancer 。

execute() 方法

该方法执行请求

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public <T> T execute(String serviceId, ServiceInstance serviceInstance,
            LoadBalancerRequest<T> request) throws IOException {
        Server server = null;
        if (serviceInstance instanceof RibbonServer) {
            server = ((RibbonServer) serviceInstance).getServer();
        }

        RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);

        // 状态记录器,记录着服务的状态
        RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

        ...........
        T returnVal = request.apply(serviceInstance);
        statsRecorder.recordStats(returnVal);
        return returnVal;
        ...........
    }

    // apply 方法调用如下,最终返回 ClientHttpResponse
    public ListenableFuture<ClientHttpResponse> intercept(final HttpRequest request,
            final byte[] body, final AsyncClientHttpRequestExecution execution)
            throws IOException {
        final URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        return this.loadBalancer.execute(serviceName,
                new LoadBalancerRequest<ListenableFuture<ClientHttpResponse>>() {
                    @Override
                    public ListenableFuture<ClientHttpResponse> apply(
                            final ServiceInstance instance) throws Exception {
                        HttpRequest serviceRequest = new ServiceRequestWrapper(request,
                                instance, AsyncLoadBalancerInterceptor.this.loadBalancer);
                        return execution.executeAsync(serviceRequest, body);
                    }
                });
    }

以上就是负载均衡器流程图左边部分的原理,接下来看下右边的部分

ILoadBalancer

通过上面的分析,负载均衡器获取一个可用的服务,最终会调用 ILoadBalancerchooseServer 方法,下面就来看下 ILoadBalancer 的实现原理

首先来看下 ILoadBalancer 的整体类图:

在上面的类图中,主要的逻辑是在 BaseLoadBalancer 中实现,而 DynamicServerListLoadBalancer 主要提供动态获取服务列表的能力。

ILoadBalancer

首先来看下 ILoadBalancer,它表示一个负载均衡器接口,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface ILoadBalancer {
    // 添加服务
    public void addServers(List<Server> newServers);

    //获取服务
    public Server chooseServer(Object key);

    //标记某个服务下线
    public void markServerDown(Server server);

    //获取状态为UP的所有可用服务列表
    public List<Server> getReachableServers();

    //获取所有服务列表,包括可用的和不可用的
    public List<Server> getAllServers();
}
AbstractLoadBalancer

实现 ILoadBalancer接口,提供一些默认实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class AbstractLoadBalancer implements ILoadBalancer {
    public enum ServerGroup{ALL, STATUS_UP, STATUS_NOT_UP}

    public Server chooseServer() {
        return chooseServer(null);
    }
    public abstract List<Server> getServerList(ServerGroup serverGroup);
    // 获取状态
    public abstract LoadBalancerStats getLoadBalancerStats();    
}
IClientConfigAware

客户端配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface IClientConfigAware {
    public abstract void initWithNiwsConfig(IClientConfig clientConfig);
}
BaseLoadBalancer

负载均衡器的主要实现逻辑在该类中,会根据负载均衡策略 IRule 来获取可用的服务,会通过 IPing 来检测服务的可用性,此外,该类中从 EurkaClient 获取到服务列表后,会在该类中保存下来,会维护所有的服务列表和可用的服务列表。

首先来看下它的一些属性,然后再来看每个对应的方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class BaseLoadBalancer extends AbstractLoadBalancer implements
        PrimeConnections.PrimeConnectionListener, IClientConfigAware {

    // 默认的负载均衡策略:轮询选择服务实例
    private final static IRule DEFAULT_RULE = new RoundRobinRule();
    protected IRule rule = DEFAULT_RULE;

    // 默认 ping 的策略,会调用 IPing 来检测服务是否可用
    private final static SerialPingStrategy DEFAULT_PING_STRATEGY = new SerialPingStrategy();
    protected IPingStrategy pingStrategy = DEFAULT_PING_STRATEGY;
    protected IPing ping = null;

    // 所有服务列表
    protected volatile List<Server> allServerList = Collections.synchronizedList(new ArrayList<Server>());
    // 状态为 up 的服务列表
    protected volatile List<Server> upServerList = Collections.synchronizedList(new ArrayList<Server>());

    // 锁
    protected ReadWriteLock allServerLock = new ReentrantReadWriteLock();
    protected ReadWriteLock upServerLock = new ReentrantReadWriteLock();

    // 定时任务,去 ping 服务是否可用
    protected Timer lbTimer = null;
    // ping 的时间间隔,10秒
    protected int pingIntervalSeconds = 10;
    // ping 的最大次数
    protected int maxTotalPingTimeSeconds = 5;

    // 负载均衡器的状态
    protected LoadBalancerStats lbStats;
    // 客户端配置
    private IClientConfig config;

    // 服务列表变化监听器
    private List<ServerListChangeListener> changeListeners = new CopyOnWriteArrayList<ServerListChangeListener>();
    // 服务状态变化监听器
    private List<ServerStatusChangeListener> serverStatusListeners = new CopyOnWriteArrayList<ServerStatusChangeListener>();

    // 构造方法,使用默认的配置来创建负载均衡器,还有其他重载的构造方法,可用根据需要来创建负载均衡器
    public BaseLoadBalancer() {
        this.name = DEFAULT_NAME;
        this.ping = null;
        setRule(DEFAULT_RULE);
        setupPingTask();
        lbStats = new LoadBalancerStats(DEFAULT_NAME);
    }

   .....................
}

在上面的属性中,Ribbon 提供了一些默认的配置: IClientConfig 表示客户端的配置,实现类为 DefaultClientConfigImpl,在该类中配置了默认的值:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DefaultClientConfigImpl implements IClientConfig {

    // ping 的默认策略 DummyPing
    public static final String DEFAULT_NFLOADBALANCER_PING_CLASSNAME = "com.netflix.loadbalancer.DummyPing"; // DummyPing.class.getName();
    public static final String DEFAULT_NFLOADBALANCER_RULE_CLASSNAME = "com.netflix.loadbalancer.AvailabilityFilteringRule";
    public static final String DEFAULT_NFLOADBALANCER_CLASSNAME = "com.netflix.loadbalancer.ZoneAwareLoadBalancer";
    public static final int DEFAULT_MAX_TOTAL_TIME_TO_PRIME_CONNECTIONS = 30000;
    public static final int DEFAULT_MAX_RETRIES_PER_SERVER_PRIME_CONNECTION = 9;

   .............................................
}

IRule 表示负载均衡策略,即如何去选择服务实例,默认为 RoundRobinRule,即通过轮询的方式选择服务。Ribbon 默认提供的有 7 种。 IPing 表示检测服务是否可用策略,Ribbon 也提供了很多策略,共有 5 种,默认为 DummyPing

关于 IRule 和 IPing 的策略,后面会专门进行研究。

BaseLoadBalancer 中,除了提供一个无参的构造方法(使用的是默认的配置)外,还提供了很多重载的构造方法,下面来看下根据客户端的配置来创建BaseLoadBalancer :

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 根据客户端配置来创建 BaseLoadBalancer
public BaseLoadBalancer(IClientConfig config) {
    initWithNiwsConfig(config);
}

@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
    // 负载均衡策略
    String ruleClassName = (String) clientConfig.getProperty(CommonClientConfigKey.NFLoadBalancerRuleClassName);
    // ping 策略
    String pingClassName = (String) clientConfig.getProperty(CommonClientConfigKey.NFLoadBalancerPingClassName);
    IRule rule = (IRule) ClientFactory.instantiateInstanceWithClientConfig(ruleClassName, clientConfig);
    IPing ping = (IPing) ClientFactory.instantiateInstanceWithClientConfig(pingClassName, clientConfig);
    // 状态
    LoadBalancerStats stats = createLoadBalancerStatsFromConfig(clientConfig);
    // 初始化配置
    initWithConfig(clientConfig, rule, ping, stats);
}

void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping, LoadBalancerStats stats) {
    this.config = clientConfig;
    String clientName = clientConfig.getClientName();
    this.name = clientName;

    // ping 的周期
    int pingIntervalTime = Integer.parseInt(clientConfig.getProperty(CommonClientConfigKey.NFLoadBalancerPingInterval,Integer.parseInt("30")));
    // 最大 ping 的次数
    int maxTotalPingTime = Integer.parseInt(clientConfig.getProperty(CommonClientConfigKey.NFLoadBalancerMaxTotalPingTime,Integer.parseInt("2")));

    setPingInterval(pingIntervalTime);
    setMaxTotalPingTime(maxTotalPingTime);
    setRule(rule);
    setPing(ping);

    setLoadBalancerStats(stats);
    rule.setLoadBalancer(this);
    if (ping instanceof AbstractLoadBalancerPing) {
        ((AbstractLoadBalancerPing) ping).setLoadBalancer(this);
    }
    .................
    // 注册监控/可忽略
    init();
}

在上面的构造方法中,可用根据客户端配置的信息来创建一个BaseLoadBalancer,如客户端可以配置负载均衡策略,ping的策略,ping的时间间隔和最大次数等。

判断服务的可用性

在 Ribbon 中,负载均衡器多久才去更新获取服务列表呢?在 BaseLoadBalancer 类中,有一个 setupPingTask 方法,在该方法内部,会创建 PingTask 定时任务去检测服务的可用性,而 PingTask 又会创建 Pinger 对象,在 Pinger对象的 runPinger() 方法中,会根据ping策略即 pingerStrategypingServers(ping, allServer) 来判断服务的可用性,主要逻辑如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void setupPingTask() {
    if (canSkipPing()) {
        return;
    }
    // 如果已经有了定时任务,则取消
    if (lbTimer != null) {
        lbTimer.cancel();
    }
    // 第二个参数为true,表示它是一个deamon线程
    lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name, true);
    // 创建 PingTask, 它继承于 TimerTask,定时执行 run 方法
    lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
    ......
}
class PingTask extends TimerTask {
    public void run() {
        // 默认 pingStrategy = new SerialPingStrategy()
        new Pinger(pingStrategy).runPinger();
    }
}

public void runPinger() throws Exception {
    // 如果正在ping,则返回
    if (!pingInProgress.compareAndSet(false, true)) { 
        return; // Ping in progress - nothing to do
    }
    // 所有的服务,包括不可用的服务
    Server[] allServers = null;
    boolean[] results = null;

    Lock allLock = null;
    Lock upLock = null;

    try {

        allLock = allServerLock.readLock();
        allLock.lock();
        allServers = allServerList.toArray(new Server[allServerList.size()]);
        allLock.unlock();
        // 所有服务的数量
        int numCandidates = allServers.length;
        // 所有服务ping的结果
        results = pingerStrategy.pingServers(ping, allServers);

        // 状态可用的服务列表 
        final List<Server> newUpList = new ArrayList<Server>();
        // 状态改变的服务列表
        final List<Server> changedServers = new ArrayList<Server>();

        for (int i = 0; i < numCandidates; i++) {
            // 最新的状态
            boolean isAlive = results[i];
            Server svr = allServers[i];
            // 老的状态
            boolean oldIsAlive = svr.isAlive();
            // 更新状态
            svr.setAlive(isAlive);

            // 如果状态改变了,则放到集合中,会进行重新拉取
            if (oldIsAlive != isAlive) {
                changedServers.add(svr);
            }
            // 状态可用的服务
            if (isAlive) {
                newUpList.add(svr);
            }
        }
        upLock = upServerLock.writeLock();
        upLock.lock();
        upServerList = newUpList;
        upLock.unlock();
        // 变态改变监听器
        notifyServerStatusChangeListener(changedServers);
    } finally {
        // ping 完成
        pingInProgress.set(false);
    }
}

// 检测服务的状态
@Override
public boolean[] pingServers(IPing ping, Server[] servers) {
    int numCandidates = servers.length;
    boolean[] results = new boolean[numCandidates];

    for (int i = 0; i < numCandidates; i++) {
        results[i] = false;
        if (ping != null) {
            results[i] = ping.isAlive(servers[i]);
        }
    }
    return results;
}

在上面的逻辑中,Ribbon 每10秒向EurekaClient发送 ping 来判断服务的可用性,如果服务的可用性发生了改变或服务的数量和之前的不一致,则会更新或重新拉取服务。有了这些服务之后,会根据负载均衡策略IRule 来选择一个可用的服务。

根据负载均衡策略 IRule 来选择一个可用的服务

在前文说到 Ribbon 客户端RibbonLoadBalancerClient 选择服务的时候,最终会调用 ILoadBalancer.chooseServer() 来选择服务,接下来就来看下这个方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public Server chooseServer(Object key) {
    .......
    //rule= new RoundRobinRule()
    return rule.choose(key);
    ....
}

关于 Ribbon 的负载均衡策略 IRule, Ribbon 提供了 7 种,后面再来分析,现在只需要知道通过 IRule 来选择服务就可以了。

初始化获取所有服务列表

在上面的分析中,Ribbon 会每10秒定时的去检测服务的可用性,如果服务状态发生了变化则重新获取,之后,再根据负载均衡策略 IRule 来选择一个可用的服务;但是,在初始化的时候,是从哪里获取服务列表呢?下面就来分析这个问题

BaseLoadBalancer 有个子类 DynamicServerListLoadBalancer,它具有使用动态获取服务器列表的功能。即服务器列表在运行时可能会更改。此外,还可以通过条件来过滤掉不符合所需条件的服务。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
    // 是否正在进行服务列表的更新
    protected AtomicBoolean serverListUpdateInProgress = new AtomicBoolean(false);
    // 服务列表
    volatile ServerList<T> serverListImpl;
    // 服务过滤器
    volatile ServerListFilter<T> filter;
}
初始化时获取所有服务

DynamicServerListLoadBalancer中,有个 restOfInit 方法,在初始化时进行调用,在该方法中,会从 Eureka客户端中拉取所有的服务列表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void restOfInit(IClientConfig clientConfig) {
    .............
    updateListOfServers();
    ........
}

public void updateListOfServers() {
    List<T> servers = new ArrayList<T>();
    if (serverListImpl != null) {
        // 获取所有服务列表
        servers = serverListImpl.getUpdatedListOfServers();
        // 根据条件过滤服务
        if (filter != null) {
            servers = filter.getFilteredListOfServers(servers);
        }
    }
    updateAllServerList(servers);
}

protected void updateAllServerList(List<T> ls) {
    if (serverListUpdateInProgress.compareAndSet(false, true)) {
        try {
            for (T s : ls) {
                s.setAlive(true); // 状态设置为可用
            }
            setServersList(ls);
            super.forceQuickPing(); // 强制检测服务状态
        } finally {
            serverListUpdateInProgress.set(false);
        }
    }
}

获取所有服务列表 servers = serverListImpl.getUpdatedListOfServers(); 最终会调用 DiscoveryEnabledNIWSServerList的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
servers = serverListImpl.getUpdatedListOfServers();

public List<DiscoveryEnabledServer> getUpdatedListOfServers(){
    return obtainServersViaDiscovery();
}

private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
    List<DiscoveryEnabledServer> serverList = new ArrayList<DiscoveryEnabledServer>();
    ........
    // 通过 eurekaClient 来获取注册的服务列表 
    EurekaClient eurekaClient = eurekaClientProvider.get();
    if (vipAddresses!=null){
        for (String vipAddress : vipAddresses.split(",")) {

            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);
                }
            }
            ......
        }
    }
    return serverList;
}

通过上面方法的分析,Ribbon 最终会通过 EurekaClient 来获取服务列表的,而 EurekaClient的实现类是 DiscoveryClient,而在 Eureka 中,DiscoveryClient 类具有服务的注册,发现,续约,获取服务列表等功能。

此外,该类中还可以通过过滤器来获取不符合条件的服务。

以上就是 Ribbon 负载均衡器的一个实现原理。最后再来看下流程图,加深印象:

关于负载均衡策略 IRule 和 Ping 策略,下篇文章进行研究。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java技术大杂烩 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
教你一招 | Python: 函数参数魔法
函数参数 在 Python 中,定义函数和调用函数都很简单,但如何定义函数参数和传递函数参数,则涉及到一些套路了。总的来说,Python 的函数参数主要分为以下几种: 必选参数 默认参数 可变参数 关键字参数 必选参数 必选参数可以说是最常见的了,顾名思义,必选参数就是在调用函数的时候要传入数量一致的参数,比如: >>> def add(x, y): # x, y 是必选参数 ... print x + y ... >>> add() # 啥都没传,不行
CDA数据分析师
2018/02/05
7860
Robot Framework(11)- 用户关键字的详解
https://www.cnblogs.com/poloyy/category/1770899.html
小菠萝测试笔记
2020/06/09
6950
Robot Framework(11)- 用户关键字的详解
函数的不定长参数
# *args:位置参数,收集成元组 def func1(*args): print(args) for i in args: print(i) func1('python', 28, 'man', 'meiguo') # **kwargs :收集关键字参数,合并字典 def func2(**kwargs): print(kwargs) for key, value in kwargs.items(): print(key)
汪凡
2018/05/29
7280
Python3 * 和 ** 运算符
你可以将不定数量的参数传递给一个函数。不定的意思是:预先并不知道, 函数使用者会传递多少个参数给你, 所以在这个场景下使用这两个关键字。其实并不是必须写成 *args 和 **kwargs。  *(星号) 才是必须的. 你也可以写成 *ar  和 **k 。而写成 *args 和**kwargs 只是一个通俗的命名约定。
py3study
2020/01/13
5220
Python函数签名的参数设计以及=None的重要性
位置参数(Positional Arguments):最常见的参数类型,按照位置传递。
运维开发王义杰
2023/09/19
5940
Python函数签名的参数设计以及=None的重要性
python基础3
 交换: a,b=b,a 相当于定义了一个元组t=(b,a) 然后将t[0]的值给了a,t[1]的值给了b ####字典#### 定义用花括号 集合定义若为空的话,会默认为字典,所以集合不能为空 子典只能通过关键字来查找值,因为字典是key-value(关键字-值),因此不能通过值来查找关键字 In [1]: dic = {"user1":"123","user2":"234","user3":"789"} In [3]: dic["234"] --------------------------------------------------------------------------- KeyError                                  Traceback (most recent call last) <ipython-input-3-2845b64d96b1> in <module>() ----> 1 dic["234"] KeyError: '234' 字典是一个无序的数据类型,因此也不能进行索引和切片等操作。 In [1]: dic = {"user1":"123","user2":"234","user3":"789"} In [2]: dic["user1"] Out[2]: '123' In [5]: dic["user2"] Out[5]: '234' In [7]: user = ['user1','user2','user3'] In [8]: passwd = ['123','234','456'] In [9]: zip(user,passwd) Out[9]: [('user1', '123'), ('user2', '234'), ('user3', '456')] In [10]: 当你有一个用户名单和密码,若使用列表的类型,判断用户是否和密码一致时,就比较麻烦,而使用字典时,只需通过关键子就可以返回相对应的值,(如上例子:当定义一个子典当你搜索user1时,字典类型就会返回该关键字对应的密码,此时只需判断该密码是否匹配即可) ####字典的基本操作### In [17]: dic. dic.clear       dic.items       dic.pop         dic.viewitems dic.copy        dic.iteritems   dic.popitem     dic.viewkeys dic.fromkeys    dic.iterkeys    dic.setdefault  dic.viewvalues dic.get         dic.itervalues  dic.update       dic.has_key     dic.keys        dic.values 字典添加 In [12]: dic Out[12]: {'user1': '123', 'user2': '234', 'user3': '789'} In [13]: dic["westos"]='linux' In [14]: dic Out[14]: {'user1': '123', 'user2': '234', 'user3': '789', 'westos': 'linux'} In [15]: dic["hello"]='world' In [16]: dic            ####由此可以看出字典是无序的,在添加时,并不会按照顺序往后添加#### Out[16]: {'hello': 'world',  'user1': '123',  'user2': '234',  'user3': '789',  'westos': 'linux'} In [17]: 字典更新 In [22]: dic Out[22]: {'hello': 'world', 'user1': '123', 'user2': '234', 'user3': '789'} In [23]: dic["user1"]="redhat"        ###可直接通过赋值对关键字进行更新### In [24]: dic Out[24]: {'hello': 'world', 'user1': 'redhat', 'user2': '234', 'user3': '789'} ###或者通过dic.update更新### In [25]: dic Out[25]: {'hello': 'world', 'user1': 'redhat', 'user2': '234', 'user3': '789'} In [26]: help(di
py3study
2020/01/03
4690
Python3 系列之 可变参数和关键字
对于可变参数可以联想到 C# 中的可变参数。可变参数是一个数量不确定的列表集合,可以是 list 类型,也可以是 tuple 类型 我们定义如下代码段:
py3study
2020/01/19
5400
Python学习笔记4——函数
Help on built-in function print in module builtins:
py3study
2020/01/19
3580
Python-函数
def 函数名([形式参数]):     函数体                    函数要执行的程序     return 返回值        如果没有return返回,默认返回值为None;
py3study
2020/01/13
3650
python学习笔记3.1-函数
本文介绍了Python中函数的定义、调用、参数以及lambda表达式的用法。函数是封装特定功能的代码块,可以通过调用进行重复使用,节省代码量并提高代码的可读性。函数的参数分为位置参数、可变参数和关键字参数。lambda表达式是一种匿名函数,语法简洁,常用于函数式编程中。
锦小年
2018/01/02
6260
week04_python函数、参数及参数
        由若干语句组成的语句块、函数名称、参数列表构成,它是组织代码的最小单元;
py3study
2020/01/14
5640
Python 学习之 def 函数
当我们定义了一个 function(),并不意味着我们要调用它,所以我们需要运行这个 function(),一个简单的办法就是在 input 框中输入 function()。
Python技术与生活认知的分享
2018/08/01
6500
Python 学习之 def 函数
Python中函数的介绍
函数名:函数名是函数的标识符,用于唯一标识函数。在定义函数时,需要给函数一个名字,以便后续调用和引用。函数名应遵循命名规则,例如以字母或下划线开头,由字母、数字或下划线组成。命名规范可参考官网的PEP 8风格,地址如下:
小博测试成长之路
2023/09/01
3090
Python中函数的介绍
Python 函数引入
由若干语句组成的语句块,函数名称,参数列表构成,它是组织代码的最小单元,完成一定功能。
江小白
2019/06/08
9420
Python函数参数之全面讲解
Python函数参数 Python函数参数 本文主要介绍Python的函数参数,各种形式的参数。建议动手试试,可以加深理解。 函数参数 定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了。对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂的逻辑被封装起来,调用者无需了解。 Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以
1846122963
2018/03/09
1.4K0
python基础教程:函数(2)
上一节我们学习了函数的定义和调用,理解了基本的函数知识。本节进一步学习函数相关的更多内容,深入了解函数,包括:默认参数、关键字参数、位置参数、变量的作用域等等。
一墨编程学习
2019/05/15
6600
Python 函数 —— 定义,参数,参
    数学定义:y = f(x), y是x的函数,x是自变量。 y = f(x0,x1,x2,...,xn)
py3study
2020/01/10
1.2K0
Python - 函数形参之必填参数、缺省参数、可变参数、关键字参数的详细使用
声明函数时,当同时存在必填参数和缺省参数,形参的顺序必须是 (必填参数 , 缺省参数),不能缺省参数在前
小菠萝测试笔记
2020/06/09
3.6K0
Python - 函数形参之必填参数、缺省参数、可变参数、关键字参数的详细使用
Python基础入门_4函数
第四篇内容,这次介绍下函数的基本用法,包括函数的定义、参数的类型、匿名函数、变量作用域以及从模块导入函数的方法,目录如下所示:
kbsc13
2019/08/16
1.1K0
Python入门-函数
此处的函数区别于我们数学上的函数,在编程世界中,函数(Functions)是指可重复使用的程序片段。它们允许你为某个代码块赋予名字,允许你 通过这一特殊的名字在你的程序任何地方来运行代码块,并可重复任何次数。这就是所谓的调用函数。我们已经使用过了许多内置的函数,例如 len 和 range 。
py3study
2020/01/03
4830
相关推荐
教你一招 | Python: 函数参数魔法
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • 前言
  • Ribbon 负载均衡器
  • Ribbon 负载均衡器流程图
    • 主要流程:
  • Ribbon 负载均衡器实现原理
    • RibbonLoadBalancerClient
    • ServiceInstanceChooser
    • LoadBalancerClient
    • RibbonLoadBalancerClient 实现如下:
      • choose() 方法
      • execute() 方法
  • ILoadBalancer
    • ILoadBalancer
    • AbstractLoadBalancer
    • IClientConfigAware
    • BaseLoadBalancer
  • 判断服务的可用性
  • 根据负载均衡策略 IRule 来选择一个可用的服务
  • 初始化获取所有服务列表
    • 初始化时获取所有服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档