[享学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核心数动态实时生效。

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

代码示例

@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

myThreadPoolCoreSize=2

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

...
我是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

资源加载工具类。

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;
	}
}

使用示例:

@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的执行过程中抛出的均是此种异常。比如你工作中常见的异常信息:

com.netflix.client.ClientException: Load balancer does not have available server for client

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

public class ClientException extends Exception{

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

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

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内容才是它的重难点,敬请持续关注。
分隔线

声明

原创不易,码字不易,多谢你的点赞、收藏、关注。把本文分享到你的朋友圈是被允许的,但拒绝抄袭。你也可【左边扫码/或加wx:fsx641385712】邀请你加入我的 Java高工、架构师 系列群大家庭学习和交流。
往期精选

发布了377 篇原创文章 · 获赞 572 · 访问量 51万+
App 阅读领勋章
微信扫码 下载APP
阅读全文

java.net.NoRouteToHostException: 没有到主机的路由

10-06

虚拟机环境:nginx+fastDFS+fastDFS-nginx-module 可以在虚拟机中上传文件,在浏览器上也可以查看上传的文件。 @Test public void testUpload() throws Exception { ClientGlobal.init("/home/yazhou/Document/java/taotao/clint.conf"); TrackerClient trackerClient =new TrackerClient(); TrackerServer trackerServer =trackerClient.getConnection(); StorageServer storageServer =null; StorageClient storageClient =new StorageClient(trackerServer,storageServer); String[] strings= storageClient.upload_file("/home/yazhou/Pictures/BG/1352113505895.jpg","jpg",null); for (String string:strings){ System.out.println(string); } } java.net.NoRouteToHostException: 没有到主机的路由 at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:589) at org.csource.fastdfs.ClientGlobal.getSocket(ClientGlobal.java:107) at org.csource.fastdfs.StorageServer.<init>(StorageServer.java:45) at org.csource.fastdfs.TrackerClient.getStoreStorage(TrackerClient.java:158) at org.csource.fastdfs.StorageClient.newWritableStorageConnection(StorageClient.java:1938) at org.csource.fastdfs.StorageClient.do_upload_file(StorageClient.java:703) at org.csource.fastdfs.StorageClient.upload_file(StorageClient.java:164) at org.csource.fastdfs.StorageClient.upload_file(StorageClient.java:132) at org.csource.fastdfs.StorageClient.upload_file(StorageClient.java:114) at com.taotao.fastcast.FastTest.testUpload(FastTest.java:19) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: Age of Ai 设计师: meimeiellie

分享到微信朋友圈

×

扫一扫,手机浏览