RPC框架核心学习内容

tech2022-07-31  145

RPC框架核心学习内容

线程池1. 核心参数2. workQueue1. 无界队列(linkedBlockingQueue)2. 有界队列(ArrayBolckingQueue、PriorityBlockingQueue)3. 同步移交队列(SynchronousQueue) 3. 拒绝策略1. AbortPolicy 中止策略2. DiscardPolicy 抛弃策略3. DiscardOldestPolicy抛弃旧任务策略4. CallerRunsPolicy 调用者运行 动态代理1. JDK动态代理2. CGLib动态代理3. 区别 ByteBufByteBufferByteBuf

线程池

1. 核心参数

corePoolSIze :核心池大小,未达到此参数会创建新的线程进行执行。达到此参数限制会放入等待队列中(小于maximunPoolSize)。

maximumPoolSize:最大能达到的数量限制,阻塞队列满且达到最大线程数的时候会根据抛弃策略进行丢弃。

keepAliveTime:当线程数大于核心时,多于的空闲线程最多存活时间。

workQueue:当线程舒服超过核心线程数的时候用于保存任务的队列。三种类型:无界队列、有界队列、同步移交。

threadFactory:执行程序创建新线程时使用的工厂。

rejectedExecutionHandler:拒绝策略。提供四种拒绝策略。

2. workQueue

1. 无界队列(linkedBlockingQueue)

其实并不能算无限,这个队列的默认大小是Integer.MAXVALUE。newFixedThreadPool会采用此种队列。当任务耗时较长的时候,会无限在队列中创建等待任务,堆积大量的请求。可能会溢出造成OOM。大量任务被添加到这个无界队列中,会导致cpu和内存飙升导致服务器挂掉。

2. 有界队列(ArrayBolckingQueue、PriorityBlockingQueue)

FIFO原则的 ArrayBolckingQueue 和优先级队列 PriorityBlockingQueue(优先级由任务的 Comparator 决定)。 线程池较小、有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是会限制系统吞吐量,可能会导致部分任务被丢失。

3. 同步移交队列(SynchronousQueue)

不是一种真正的队列,时一种线程间的移交机制。要将一个队列放入一种,必须要另一个线程正在等待接受这个元素。只有在使用无界线程池(newCachedThreadPool)或者由饱和策略的时候才建议使用该队列。 newCachedThreadPool 的概念就是需要一个线程接受才能使任务顺利进入队列,否则的话就需要新启一个线程(刚开始的时候也就是会新创建线程后才会开始进行处理)。最大线程大小是Integer.MAXVALUE。任务太多时会创建大量的线程,可能会造成OOM。

所以阿里的规范里面不建议使用Executor,建议使用ThreadPoolExecutor自己创建。

3. 拒绝策略

1. AbortPolicy 中止策略

默认饱和策略。饱和时会抛出 RejectedExecutionException(RuntimeException),可捕获异常处理。

2. DiscardPolicy 抛弃策略

不做任何处理,直接抛弃任务

3. DiscardOldestPolicy抛弃旧任务策略

抛弃最老任务。抛弃掉队列头部的元素,再尝试提交任务。 如果使用的是优先级队列会导致优先级最高的任务被抛弃,不建议配合使用。

4. CallerRunsPolicy 调用者运行

使用调用者的线程来运行此任务。运行此任务期间无法提交新的任务。

动态代理

1. JDK动态代理

使用反射进行创建,被代理类需实现 InvocationHandler 接口

Method method = serviceClass.getMethod(methodName, parameterTypes); method.setAccessible(true); return method.invoke(serviceBean, parameters);

2. CGLib动态代理

Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。 这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。

FastClass serviceFastClass = FastClass.create(serviceClass); // 其实这里里面也是使用的 index 来调用的,两者没有区别 FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes); return serviceFastMethod.invoke(serviceBean, parameters); //fastClass 使用methodIndex调用 int methodIndex = serviceFastClass.getIndex(methodName, parameterTypes); return serviceFastClass.invoke(methodIndex, serviceBean, parameters); // test2 是 test 的 fastClass class Test{ public void f(){ System.out.println("f method"); } public void g(){ System.out.println("g method"); } } class Test2{ public Object invoke(int index, Object o, Object[] ol){ Test t = (Test) o; switch(index){ case 1: t.f(); return null; case 2: t.g(); return null; } return null; } public int getIndex(String signature){ switch(signature.hashCode()){ case 3078479: return 1; case 3108270: return 2; } return -1; } }

3. 区别

1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。 2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。 3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。

ByteBuf

ByteBuffer

这个是nio原生api中的内容,主要有四个内容:

position:当前的下表位置,表示下一个读/写操作的起始位置。limit:结束标记下标,下一个读/写操作时的(最大)结束位置。capacity:ByteBuffer的容量。mark:自定义的标记位置

写入的时候position会表示当前的写入位置。 通过flip切换的读写模式的时候,会将position放入头部,将limit放入position之前的位置。 如果未读取完,会把没有读取的拷贝到最开始,然后接着后面的位置开始写入。

缺点: 只有一个position指针标记位置,所以使用的时候读写切换需要调用 flip() 方法。使用不方便。 不支持动态扩容,如果之前创建的大小不满足我们的使用需求,则需要手动的重新创建。

ByteBuf

对比 ByteBuffer 只有一个position 指针,ByteBuf 有两个指针 readIndex 和 writeIndex,分别时读指针和写指针,可以直接进行读写。 如果超过容量大小,会自动扩容。(阈值4M)

最新回复(0)