[享学Ribbon] 二十四、Ribbon具有负载均衡能力的客户端:AbstractLoadBalancerAwareClient

梦里梦外; 2023-07-17 09:56 11阅读 0赞

当你想在你的代码中找到一个错误时,这很难;当你认为你的代码是不会有错误时,这就更难了。

–> 返回Netflix OSS套件专栏汇总 <–
代码下载地址:https://github.com/f641385712/netflix-learning

目录

    • 前言
    • 正文
      • AbstractLoadBalancerAwareClient
        • 初始化方法
        • buildLoadBalancerCommand() 构建负载均衡命令
        • executeWithLoadBalancer() 带有负载均衡能力的执行
      • ClientFactory
    • 总结
  • 关注A哥

前言

Ribbon不仅仅是负载均衡,负载均衡只是它的一个最核心、最出名的模块而已。在聊ribbon-core的时候我们知道它有个核心API是IClient,它表示发送一个请求得到一个响应,不规定发送方式、协议等。

因为Ribbon最核心的功能就是负载均衡,因此本文我们将了解到它这个具有负载均衡能力的客户端:AbstractLoadBalancerAwareClient,它所在的jar是:ribbon-loadbalancer


正文

AbstractLoadBalancerAwareClient

因为我们不太可能把Ribbon当其它用,而只用作负载均衡,因此你可以简单粗暴理解为:AbstractLoadBalancerAwareClient是所有的客户端实现的顶级父类,而实际上也确实如此。

需要注意的是,该抽象类不仅实现了接口IClient,并且还继承自LoadBalancerContext,所以它自己不仅仅是个Client,还拥有着负载均衡上下文。

该抽象类无任何成员属性,提供了一些方法:


初始化方法

它提供两个构造器用于初始化。

  1. public abstract class AbstractLoadBalancerAwareClient<S extends ClientRequest, T extends IResponse> extends LoadBalancerContext implements IClient<S, T>, IClientConfigAware {
  2. public AbstractLoadBalancerAwareClient(ILoadBalancer lb) {
  3. super(lb);
  4. }
  5. public AbstractLoadBalancerAwareClient(ILoadBalancer lb, IClientConfig clientConfig) {
  6. super(lb, clientConfig);
  7. }
  8. }

作为一个和负载均衡相关的Client,负载均衡器ILoadBalancer是必不可少的喽。


buildLoadBalancerCommand() 构建负载均衡命令

Ribbon执行所有的请求都是基于命令模式去执行的,因此均需要包装成一个LoadBalancerCommand命令:

  1. AbstractLoadBalancerAwareClient
  2. // 抽象方法:提供一个RequestSpecificRetryHandler重试处理器
  3. // 因为重试方案父类定不了:有些是超时重试,有些是异常重试,因此交给子类去决定为好
  4. // 但请保证是RequestSpecificRetryHandler的子类:因为它已经帮你实现了写基本逻辑
  5. // 一般使用包装器模式,给RequestSpecificRetryHandler.fallback赋值了就好
  6. public abstract RequestSpecificRetryHandler getRequestSpecificRetryHandler(S request, IClientConfig requestConfig);
  7. // 毕竟LoadBalancerCommand的属性众多,默认只给其设置必要的属性,其它的交给调用者去个性化吧
  8. // 比如常用的:增加监听器来监听必要的执行过程
  9. protected void customizeLoadBalancerCommandBuilder(S request, IClientConfig config, LoadBalancerCommand.Builder<T> builder) {
  10. // 空实现,交给子类去定制
  11. }
  12. // request请求对象,提供URI(注意不是URL,因为不一定是网络请求)
  13. protected LoadBalancerCommand<T> buildLoadBalancerCommand(S request, IClientConfig config) {
  14. // 得到重试处理器:因为重试处理器对LoadBalancerCommand的行为特别重要
  15. RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, config);
  16. LoadBalancerCommand.Builder<T> builder = LoadBalancerCommand.<T>builder()
  17. .withLoadBalancerContext(this)
  18. .withRetryHandler(handler)
  19. .withLoadBalancerURI(request.getUri());
  20. customizeLoadBalancerCommandBuilder(request, config, builder);
  21. return builder.build();
  22. }

它留下一个抽象方法和一个钩子方法给AbstractLoadBalancerAwareClient去完成定制化~


executeWithLoadBalancer() 带有负载均衡能力的执行

它不是接口方法:因为接口方法不具备负载均衡的能力。但是它是更为重要的方法:包装了execute()接口方法,放在LoadBalancerCommand里执行从而就具有负载均衡的能力了。

  1. AbstractLoadBalancerAwareClient
  2. // 注意:接口方法只有execute,这是在外层套了一个负载均衡器,均由负载均衡的能力
  3. public T executeWithLoadBalancer(S request) throws ClientException {
  4. return executeWithLoadBalancer(request, null);
  5. }
  6. public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
  7. // 构建一个执行命令:command
  8. LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
  9. // 提交目标操作/目标请求 -> 执行目标方法
  10. return command.submit(server -> {
  11. // 根据LB选中的Server,构建出一个最终的URI
  12. // 因为你的URI可能没有host、port等是不完整的
  13. URI finalUri = reconstructURIWithServer(server, request.getUri());
  14. // 给request重新制定一个新的URI
  15. S requestForServer = (S) request.replaceUri(finalUri);
  16. // execute执行目标方法:一般是发送http请求,当然这不是一定的~
  17. return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
  18. })
  19. // 阻塞的,顺序执行 使用RxJava是为了编程方便、优美
  20. .toBlocking()
  21. .single();
  22. }
  23. }

说明:为了代码结构清晰,内嵌的很多try…catch均省略了

该方法的核心要义是:使用LoadBalancerCommand包装execute目标方法,从而使得其具有了负载均衡的能力。因此在实际应用中:请勿直接调用execute方法,而是使用更加上层、功能更强的executeWithLoadBalancer()方法。


如果仅是在ribbon-loadbalancer这个jar内,IClient体系有且仅有这一个子类AbstractLoadBalancerAwareClient,并且它还是抽象类。由于负载均衡器它并不限定具体协议,比如http、tcp、udp等都是可以做负载均衡的,所以在此jar内并无任何具体的Client实现类

但是在Spring Cloud环境下,一片繁荣:

在这里插入图片描述
在这里插入图片描述
本文并不会介绍Spring Cloud里对它的实现,而是把它放在和Spring Cloud整合的相关章节中。


ClientFactory

既然Client如此重要,为了快速、方便的得到一个Client实例,Ribbon提供了ClientFactory这个工厂类。该类用于快速创建IClient客户端、ILoadBalancerIClientConfig等的工厂。并且它内部还维护了全局的Map,缓存来提高获取效率。

  1. public class ClientFactory {
  2. // key是ClientName value是Client实例
  3. private static Map<String, IClient<?,?>> simpleClientMap = new ConcurrentHashMap<>();
  4. // key是Lb的名称 value是LB实例
  5. private static Map<String, ILoadBalancer> namedLBMap = new ConcurrentHashMap<>();
  6. // key是ClientName value是该client对应的配置(含默认配置)
  7. private static ConcurrentHashMap<String, IClientConfig> namedConfig = new ConcurrentHashMap<>();
  8. // ==========工具方法们=========
  9. // 反射创建一个实例,并且调用其initWithNiwsConfig()方法把config传递给它
  10. public static Object instantiateInstanceWithClientConfig(String className, IClientConfig clientConfig) {
  11. ...
  12. IClientConfigAware obj = (IClientConfigAware) clazz.newInstance();
  13. obj.initWithNiwsConfig(clientConfig);
  14. return obj;
  15. }
  16. // 反射创建一个clientConfigClass类型的配置。IClientConfig接口的自带实现仅有DefaultClientConfigImpl
  17. // 注意config.loadProperties(name)方法会被调用哦(配置会被加载进来)
  18. // 最后放进缓存(缓存里就返回缓存里的)
  19. public static IClientConfig getNamedConfig(String name, Class<? extends IClientConfig> clientConfigClass) {
  20. IClientConfig config = namedConfig.get(name);
  21. if (config != null) {
  22. return config;
  23. }
  24. config = (IClientConfig) clientConfigClass.newInstance();
  25. config.loadProperties(name);
  26. ...
  27. return config;
  28. }
  29. public static IClientConfig getNamedConfig(String name) {
  30. return getNamedConfig(name, DefaultClientConfigImpl.class);
  31. }
  32. ... // 创建`ILoadBalancer`实例的方法几乎一模一样,略
  33. // 提供名称和客户端配置的实用程序方法来创建客户端和负载均衡器(如果在客户端配置中启用)
  34. // InitializeNFLoadBalancer默认配置值是true,开启负载均衡器的
  35. public static synchronized IClient<?, ?> registerClientFromProperties(String restClientName, IClientConfig clientConfig) throws ClientException {
  36. IClient<?, ?> client = null;
  37. ILoadBalancer loadBalancer = null;
  38. // 如果同名的Client已经创建过了,在调用此方法就抛错,而并非把缓存里的返回给你
  39. if (simpleClientMap.get(restClientName) != null) {
  40. throw new ClientException(ClientException.ErrorType.GENERAL, "A Rest Client with this name is already registered. Please use a different name");
  41. }
  42. ... // 反射创建Client、LB的实例
  43. simpleClientMap.put(restClientName, client);
  44. return client;
  45. }
  46. // 它木有传入配置:所以全部使用外部化配置
  47. public static synchronized IClient getNamedClient(String name) {
  48. return getNamedClient(name, DefaultClientConfigImpl.class);
  49. }
  50. public static synchronized IClient getNamedClient(String name, Class<? extends IClientConfig> configClass) {
  51. if (simpleClientMap.get(name) != null) {
  52. return simpleClientMap.get(name);
  53. }
  54. return createNamedClient(name, configClass);
  55. }
  56. }

其中ClientFactory.instantiateInstanceWithClientConfig()方法是最为通用的:它可以实例化帮你实例化任何实例,包括五大核心组件等。它的优点是初始化完成后自动帮你调用initWithNiwsConfig()方法完成属性赋值~


总结

关于Ribbon具有负载均衡能力的客户端:AbstractLoadBalancerAwareClient就先介绍到这,虽然本文木有提及具体实现类和给出代码示例,但是只要理解了它(其实核心是LoadBalancerCommand)你会觉得其它都是小儿科,这在后面讲解整合时将会继续说明。

至此,关于Ribbon最最最核心部分(包含core和loadbalancer)就全部介绍完了,虽然此项目目前已经停更,但停更不停用,目前仍然是主流的(甚至是唯一的)客户端负载均衡器。学习了它不仅可以用在Spring Cloud体系下运用自如,对于理解dubbo等框架负载均衡机制都易如反掌,另外据我了解深入了解Ribbon,以及有能力使用它来实现多区域部署的人并不多,因此若你掌握了这不就是你升职加薪的砝码了麽~

当然,这不是Ribbon系列的全部,后面还要讲整合、在Spring Cloud下的实战。有了强大的理论做支撑,实战讲解将非常的快。


关注A哥






































Author A哥(YourBatman)
个人站点 www.yourbatman.cn
E-mail yourbatman@qq.com
微 信 fsx641385712
活跃平台
公众号 BAT的乌托邦(ID:BAT-utopia)
知识星球 BAT的乌托邦
每日文章推荐 每日文章推荐

BAT的乌托邦

往期精选

  • [享学Ribbon] 一、源生Ribbon介绍 — 客户端负载均衡器
  • [享学Ribbon] 二、Ribbon核心API源码解析:ribbon-core(一)IClient请求客户端
  • [享学Ribbon] 三、Ribbon核心API源码解析:ribbon-core(二)IClientConfig配置详解
  • [享学Ribbon] 四、Ribbon核心API源码解析:ribbon-core(三)RetryHandler重试处理器
  • [享学Ribbon] 五、Ribbon核心API源码解析:ribbon-core(四)ClientException及常用工具
  • [享学Ribbon] 六、Ribbon的LoadBalancer五大组件之:IPing心跳检测
  • [享学Ribbon] 七、Ribbon的LoadBalancer五大组件之:ServerList服务列表
  • [享学Ribbon] 八、netflix-statistics详解,手把手教你写个超简版监控系统
  • [享学Ribbon] 九、Ribbon服务器状态:ServerStats及其断路器原理
  • [享学Ribbon] 十、Ribbon负载均衡策略服务器状态总控:LoadBalancerStats
  • [享学Ribbon] 十一、Ribbon多区域选择:ZoneAvoidanceRule.getAvailableZones()获取可用区
  • [享学Ribbon] 十二、Ribbon服务器过滤逻辑的基础组件:AbstractServerPredicate
  • [享学Ribbon] 十三、Ribbon的LoadBalancer五大组件之:ServerListFilter服务列表过滤器
  • [享学Ribbon] 十四、Ribbon的LoadBalancer五大组件之:ServerListUpdater服务列表更新器
  • [享学Ribbon] 十五、Ribbon的LoadBalancer五大组件之:IRule(一)轮询和加权轮询
  • [享学Ribbon] 十六、Ribbon的LoadBalancer五大组件之:IRule(二)应用于大规模集群的可配置规则
  • [享学Ribbon] 十七、Ribbon的LoadBalancer五大组件之:IRule(三)随机和重试,所有IRule实现总结
  • [享学Ribbon] 十八、Ribbon启动连接操作:IPrimeConnection检测Server是否能够提供服务
  • [享学Ribbon] 十九、Ribbon负载均衡器执行上下文:LoadBalancerContext
  • [享学Ribbon] 二十、Ribbon负载均衡器ILoadBalancer(一):BaseLoadBalancer
  • [享学Ribbon] 二十一、Ribbon负载均衡器ILoadBalancer(二):ZoneAwareLoadBalancer具备区域意识、动态服务列表的负载均衡器
  • [享学Ribbon] 二十二、Ribbon负载均衡命令:LoadBalancerCommand(一)基础类打点
  • [享学Ribbon] 二十三、Ribbon负载均衡命令:LoadBalancerCommand(二)执行目标请求

发表评论

表情:
评论列表 (有 0 条评论,11人围观)

还没有评论,来说两句吧...

相关阅读