首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往
24 篇文章
1
[享学Netflix] 三十七、源生Ribbon介绍 --- 客户端负载均衡器
2
[享学Netflix] 三十八、Ribbon核心API源码解析:ribbon-core(一)IClient请求客户端
3
[享学Netflix] 三十九、Ribbon核心API源码解析:ribbon-core(二)IClientConfig配置详解
4
[享学Netflix] 四十、Ribbon核心API源码解析:ribbon-core(三)RetryHandler重试处理器
5
[享学Netflix] 四十一、Ribbon核心API源码解析:ribbon-core(四)ClientException客户端异常
6
[享学Netflix] 四十二、Ribbon的LoadBalancer五大组件之:IPing心跳检测
7
[享学Netflix] 四十三、Ribbon的LoadBalancer五大组件之:ServerList服务列表
8
[享学Netflix] 四十四、netflix-statistics详解,手把手教你写个超简版监控系统
9
[享学Netflix] 四十五、Ribbon服务器状态:ServerStats及其断路器原理
10
[享学Netflix] 四十六、Ribbon负载均衡策略服务器状态总控:LoadBalancerStats
11
[享学Netflix] 四十七、Ribbon多区域选择
12
[享学Netflix] 四十八、Ribbon服务器过滤逻辑的基础组件:AbstractServerPredicate
13
[享学Netflix] 四十九、Ribbon的LoadBalancer五大组件之:服务列表过滤器
14
[享学Netflix] 五十、Ribbon的LoadBalancer五大组件之:ServerListUpdater
15
[享学Netflix] 五十一、Ribbon的LoadBalancer五大组件之:IRule(一)轮询和加权轮询
16
[享学Netflix] 五十二、Ribbon的LoadBalancer五大组件之:IRule(二)应用于大规模集群的可配置规则
17
[享学Netflix] 五十三、Ribbon的LoadBalancer五大组件之:IRule(三)随机和重试,所有IRule实现总结
18
[享学Netflix] 五十四、Ribbon启动连接操作:IPrimeConnection检测Server是否能够提供服务
19
[享学Netflix] 五十五、Ribbon负载均衡器执行上下文:LoadBalancerContext
20
[享学Netflix] 五十六、Ribbon负载均衡器ILoadBalancer(一):BaseLoadBalancer
21
[享学Netflix] 五十七、Ribbon负载均衡器ILoadBalancer(二):ZoneAwareLoadBalancer具备区域意识、动态服务列表的负载均衡器
22
[享学Netflix] 五十八、Ribbon负载均衡命令:LoadBalancerCommand(一)基础类打点
23
[享学Netflix] 五十九、Ribbon负载均衡命令:LoadBalancerCommand(二)执行目标请求
24
[享学Netflix] 六十、Ribbon具有负载均衡能力的客户端:AbstractLoadBalancerAwareClient
清单首页ribbon文章详情

[享学Netflix] 四十一、Ribbon核心API源码解析:ribbon-core(四)ClientException客户端异常

计算机科学领域的所有问题都可以通过其他方式间接解决。

代码下载地址:https://github.com/f641385712/netflix-learning

前言

关于Ribbon核心包ribbon-core的API前3篇已经介绍完了,本篇收收尾,介绍其内置的几个“工具”,因为在实践过程中也会使用到,如好用的线程调度工具ScheduledThreadPoolExectuorWithDynamicSize等,所以本文就过一把。


正文

ScheduledThreadPoolExectuorWithDynamicSize

它是对JDK源生的任务调度线程池的java.util.concurrent.ScheduledThreadPoolExecutor的一个扩展:它能让你的线程池的coreSize核心数动态实时生效。

代码语言:javascript
复制
public class ScheduledThreadPoolExectuorWithDynamicSize extends ScheduledThreadPoolExecutor {
	...
	// 注意此处使用的DynamicIntProperty,因此具有动态性,且是实时的哦
	// ThreadFactory:生产Thread,用于任务执行
	public ScheduledThreadPoolExectuorWithDynamicSize(final DynamicIntProperty corePoolSize, ThreadFactory threadFactory) {
		super(corePoolSize.get(), threadFactory);

		// 通过DynamicIntProperty的回调机制实现coreSize的动态调整
		corePoolSize.addCallback(() -> {setCorePoolSize(corePoolSize.get());});
		...
	}
}

可见,它能做到改了后立即生效,这是通过DynamicIntProperty的callback回调机制实现的。

说明:这里的立即也是有时间差的,依托于DynamicIntProperty的动态轮询机制:delayMillis=60000默认是60s轮询一次文件的变化,具体值可参考com.netflix.config.FixedDelayPollingScheduler

代码示例

代码语言:javascript
复制
@Test
public void fun6() throws InterruptedException {
    // =========准备一个动态属性==========
    DynamicIntProperty poolCoreSize = DynamicPropertyFactory.getInstance().getIntProperty("myThreadPoolCoreSize", 2);
    ThreadFactory threadFactory = new ThreadFactory() {
        private AtomicInteger count = new AtomicInteger();

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName("myThreadPrefix-" + count.incrementAndGet());
            thread.setDaemon(true);
            return thread;
        }
    };

    ScheduledThreadPoolExectuorWithDynamicSize exectuor = new ScheduledThreadPoolExectuorWithDynamicSize(poolCoreSize, threadFactory);

    // 启动3个定时任务(默认coreSize是2哦)
    for (int i = 1; i <= 3; i++) {
        int index = i;
        exectuor.scheduleAtFixedRate(() -> {
            String currThreadName = Thread.currentThread().getName();
            int corePoolSize = exectuor.getCorePoolSize();
            System.out.printf("我是%s号任务,线程名是[%s],线程池核心数是:%s\n", index, currThreadName, corePoolSize);
        }, index, 3, TimeUnit.SECONDS);
    }

    // 阻塞主线程
    TimeUnit.MINUTES.sleep(100);
}

准备配置文件config.properties

代码语言:javascript
复制
myThreadPoolCoreSize=2

运行程序10秒钟,我改动配置文件的值为myThreadPoolCoreSize=3,控制台打印:

代码语言:javascript
复制
...
我是1号任务,线程名是[myThreadPrefix-1],线程池核心数是:2
我是2号任务,线程名是[myThreadPrefix-2],线程池核心数是:2
我是3号任务,线程名是[myThreadPrefix-1],线程池核心数是:2
我是1号任务,线程名是[myThreadPrefix-2],线程池核心数是:2
我是2号任务,线程名是[myThreadPrefix-1],线程池核心数是:2
17:23:36.690 [pollingConfigurationSource] DEBUG com.netflix.config.AbstractPollingScheduler - Polling started
17:23:36.691 [pollingConfigurationSource] DEBUG com.netflix.config.DynamicPropertyUpdater - updating property key [myThreadPoolCoreSize], value [3]
我是3号任务,线程名是[myThreadPrefix-2],线程池核心数是:3
我是1号任务,线程名是[myThreadPrefix-1],线程池核心数是:3
我是2号任务,线程名是[myThreadPrefix-3],线程池核心数是:3
我是3号任务,线程名是[myThreadPrefix-2],线程池核心数是:3
我是1号任务,线程名是[myThreadPrefix-1],线程池核心数是:3
我是2号任务,线程名是[myThreadPrefix-3],线程池核心数是:3
...

动态改变核心数成功:开始仅有两个线程执行3个任务,改变后有3个线程分别去执行3个任务了,这种动态性支持在线上是非常有意义的。

本例子强依赖于对Netflix Archaius动态配置库的掌握,关于它的全文讲解可参考本系列前面文章有非常详细的讲解。另外,要想文件修改生效,请务必重新编译config.properties配置文件。


Resources

资源加载工具类。

代码语言:javascript
复制
public abstract class Resources {

	// 有且仅有一个方法
	public static URL getResource(String resourceName) {
		URL url = null;
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		...
			//1、使用ClassLoader从context classpath里加载资源
			url = loader.getResource(resourceName);
			//2、从system classpath下加载资源
			url = ClassLoader.getSystemResource(resourceName);
			
			//3、通过文件系统加载资源
			resourceName = URLDecoder.decode(resourceName, "UTF-8");
			url = (new File(resourceName)).toURI().toURL();
		...
		return url;
	}
}

使用示例:

代码语言:javascript
复制
@Test
public void fun7(){
    URL resource = Resources.getResource("config.properties");
    System.out.println(resource);
}

因为config.properties就在工程的classpath下面,所以它会被loader.getResource(resourceName)这句代码找到(其实ClassLoader.getSystemResource(resourceName)也能定位到此文件)。


ClientException

它是一个异常类型(非Runtime异常),客户端Client的执行过程中抛出的均是此种异常。比如你工作中常见的异常信息:

代码语言:javascript
复制
com.netflix.client.ClientException: Load balancer does not have available server for client

它表示Client在执行时,使用Loadbalancer计算时木有可用的Server了。

代码语言:javascript
复制
public class ClientException extends Exception{

    protected int errorCode;
    protected String message; // 错误消息
    protected Object errorObject; // 错误实体
    protected ErrorType errorType = ErrorType.GENERAL;
}

该异常保存了出错误时的错误状态码、错误消息、错误实体等。当然最最最为重要的当属这个错误类型枚举:

代码语言:javascript
复制
ClientException:

    public enum ErrorType{
        GENERAL, 
        CONFIGURATION, 
        NUMBEROF_RETRIES_EXEEDED, 
        NUMBEROF_RETRIES_NEXTSERVER_EXCEEDED, 
        SOCKET_TIMEOUT_EXCEPTION, 
        READ_TIMEOUT_EXCEPTION,
        UNKNOWN_HOST_EXCEPTION,
        CONNECT_EXCEPTION,
        CLIENT_THROTTLED,
        SERVER_THROTTLED,
        NO_ROUTE_TO_HOST_EXCEPTION,
        CACHE_MISSING;
	}
  • GENERAL:不知道啥错误类型就用它。比如:
    • Unable to execute RestClient request for URI:
    • Load balancer does not have available server for client:
    • Invalid Server for :
    • Request contains no HOST to talk to
  • CONFIGURATION:解析配置时抛错。
    • Unable to InitializeAndAssociateNFLoadBalancer set for RestClient:
    • Unable to get an instance of CommonClientConfigKey.NIWSServerListFilterClassName. Configured class:
  • NUMBEROF_RETRIES_EXEEDED:同一台服务超过重试数量。
    • Number of retries exceeded max 1 retries, while making a call for:
  • NUMBEROF_RETRIES_NEXTSERVER_EXCEEDED:超过在nextServer的重试数量。
  • SOCKET_TIMEOUT_EXCEPTION:链接超时。
    • Unable to execute RestClient request for URI:
  • READ_TIMEOUT_EXCEPTION:读取超时。
    • Unable to execute RestClient request for URI:
  • UNKNOWN_HOST_EXCEPTION:不明主机host(异常类型UnknownHostException)。
  • CONNECT_EXCEPTION:连接失败(异常类型ConnectException)。
  • NO_ROUTE_TO_HOST_EXCEPTION:路由失败(异常类型NoRouteToHostException)。
  • CLIENT_THROTTLED:Client抛出的异常。比如返回状态是4xx
  • SERVER_THROTTLED:服务端抛出的异常。比如返回状态码是5xx
  • CACHE_MISSING:未命中缓存。

这些异常类型先混个脸熟,在讲述负载均衡执行Client时会再次遇到~


总结

关于ribbon-core包下的所有API就全部介绍完了,任何组件的core包一般都是最重要的,它具有概念最核心、接口最抽象等特点,基本上理解了core包就能答题掌握框架是干什么用的,如何工作的。但是,但是,但是,Ribbon稍显特殊,因为接下来讲解的ribbon-loadbalance内容才是它的重难点,敬请持续关注。

下一篇
举报
领券