*:first-child {
margin-top: 0 !important;
}

body>*:last-child {
margin-bottom: 0 !important;
}

/* BLOCKS
=============================================================================*/

p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}

/* HEADERS
=============================================================================*/

h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}

h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}

h1 {
font-size: 28px;
color: #000;
}

h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}

h3 {
font-size: 18px;
}

h4 {
font-size: 16px;
}

h5 {
font-size: 14px;
}

h6 {
color: #777;
font-size: 14px;
}

body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}

h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}

/* LINKS
=============================================================================*/

a {
color: #4183C4;
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

/* LISTS
=============================================================================*/

ul, ol {
padding-left: 30px;
}

ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}

ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}

dl {
padding: 0;
}

dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}

dl dt:first-child {
padding: 0;
}

dl dt>:first-child {
margin-top: 0px;
}

dl dt>:last-child {
margin-bottom: 0px;
}

dl dd {
margin: 0 0 15px;
padding: 0 15px;
}

dl dd>:first-child {
margin-top: 0px;
}

dl dd>:last-child {
margin-bottom: 0px;
}

/* CODE
=============================================================================*/

pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}

code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}

pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}

pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}

pre code, pre tt {
background-color: transparent;
border: none;
}

kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}

blockquote>:first-child {
margin-top: 0px;
}

blockquote>:last-child {
margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}

/* IMAGES
=============================================================================*/

img {
max-width: 100%
}
-->

Netty 线程模型

Netty的线程模型主要是基于React,因为考虑到应用场景的不同所以演化出多种版本。

单线程模式

即接收服务请求以及执行IO操作都由一个线程来完成,由于采用的是IO多路复用这类无阻塞IO操作,所以在请求量不大的情况下单线程模式也是可以解决一部分场景问题的。

单接收多工作线程模式

当请求量增大后,原有的一个线程处理所有IO操作变得越来越无法支撑相应的性能指标,所以提到了一个工作线程池的概念,此时接收服务请求还是一个线程,接收请求的线程收到请求后会委托给后面的工作线程池,从线程池中取得一个线程去执行用户请求。

多接收多工作线程模式

当请求量进一步增大后,单一的接收服务请求的线程无法处理所有客户端的连接,所以将接收服务请求的也扩展成线程池,由多个线程同时负责接收客户端的连接。

RPC 业务线程

上面提到的都是Netty自身的线程模型,伴随着请求量的增长而不断发展出来的优化策略。而RPC请求对应用系统来讲最主要还是业务逻辑的处理,而这类业务有可能是计算密集型的也有可以是IO密集型,像大多数应用都伴随着数据库操作,redis或者是连接其它的网络服务等。如果业务请求中有这类耗时的IO操作,推荐将处理业务请求的任务分配给独立的线程池,否则可能会阻塞netty自身的线程。

接收请求线程与工作线程分工

  • 接收请求线程主要负责创建链路,然后将请求委派给工作线程
  • 工作线程负责编码解码读取IO等操作

方案实现

目前我实现的RPC是采用多接收多工作线程模式,在服务端是这样绑定端口的:

public void bind(ServiceConfig serviceConfig) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(this.rpcServerInitializer)
.childOption(ChannelOption.SO_KEEPALIVE,true)
; try {
ChannelFuture channelFuture = bootstrap.bind(serviceConfig.getHost(),serviceConfig.getPort()).sync();
//...
channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) {
throw new RpcException(e);
}
}
finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}

boosGroup就是一组用来接收服务请求的
workerGroup就是一组具体负责IO操作的

增加业务线程只需要将handle的操作进一步委派给线程池即可,这里为了扩展所以需要定义接口:

定义线程池接口

public interface RpcThreadPool {
Executor getExecutor(int threadSize,int queues);
}

实现固定大小线程池

参考了dubbo线程池

@Qualifier("fixedRpcThreadPool")
@Component
public class FixedRpcThreadPool implements RpcThreadPool { private Executor executor; @Override
public Executor getExecutor(int threadSize,int queues) {
if(null==executor) {
synchronized (this) {
if(null==executor) {
executor= new ThreadPoolExecutor(threadSize, threadSize, 0L, TimeUnit.MILLISECONDS,
queues == 0 ? new SynchronousQueue<Runnable>() :
(queues < 0 ? new LinkedBlockingQueue<Runnable>()
: new LinkedBlockingQueue<Runnable>(queues)),
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//...
}
});
}
}
}
return executor;
}
}

小插曲:
记的有一次一朋友突然问java 线程池中的那个coreSize是什么意思?我顿时短路了,因平时也不怎么写多线程,想到平时用的比较多的数据库线程池,里面的参数倒是印象比较深,但就是想不起来有个coreSize。后来才又仔细看了下线程池的一些参数。现在借这个机会又可以多多再看看,以免再次短路。

线程池工厂

当有多个线程池实现时,通过线程池名称来动态选择线程池。

@Component
public class RpcThreadPoolFactory { @Autowired
private Map<String,RpcThreadPool> rpcThreadPoolMap; public RpcThreadPool getThreadPool(String threadPoolName){
return this.rpcThreadPoolMap.get(threadPoolName);
}
}

修改ChannelHandle的channelRead0方法

将方法体包装成Task交给线程池去执行。

@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcRequest rpcRequest) { this.executor.execute(new Runnable() {
@Override
public void run() {
RpcInvoker rpcInvoker=RpcServerInvoker.this.buildInvokerChain(RpcServerInvoker.this);
RpcResponse response=(RpcResponse) rpcInvoker.invoke(RpcServerInvoker.this.buildRpcInvocation(rpcRequest));
channelHandlerContext.writeAndFlush(response);
}
}); }

问题

目前缺乏压测,所以暂时没有明确的数据对比。

源码地址

https://github.com/jiangmin168168/jim-framework

简单RPC框架-业务线程池的更多相关文章

  1. Java实现简单RPC框架(转)

    一.RPC简介 RPC,全称Remote Procedure Call, 即远程过程调用,它是一个计算机通信协议.它允许像本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).H ...

  2. Java 并发编程——Executor框架和线程池原理

    Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务 ...

  3. RPC笔记之初探RPC:DIY简单RPC框架

    一.什么是RPC RPC(Remote Procedure Call)即远程过程调用,简单的说就是在A机器上去调用B机器上的某个方法,在分布式系统中极其常用. rpc原理其实很简单,比较容易理解,在r ...

  4. Java 并发编程——Executor框架和线程池原理

    Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...

  5. 并发新特性—Executor 框架与线程池

    兰亭风雨 · 更新于 2018-11-14 09:00:31 并发新特性-Executor 框架与线程池 Executor 框架简介 在 Java 5 之后,并发编程引入了一堆新的启动.调度和管理线程 ...

  6. 从零开始实现简单 RPC 框架 6:网络通信之 Netty

    网络通信的开发,就涉及到一些开发框架:Java NIO.Netty.Mina 等等. 理论上来说,类似于序列化器,可以为其定义一套统一的接口,让不同类型的框架实现,事实上,Dubbo 就是这么干的. ...

  7. 一个简单RPC框架是怎样炼成的(V)——引入传输层

    开局篇我们说了,RPC框架的四个核心内容 RPC数据的传输. RPC消息 协议 RPC服务注冊 RPC消息处理    接下来处理传输数据.实际应用场景一般都是基于socket.socket代码比較多, ...

  8. Java并发(基础知识)—— Executor框架及线程池

    在Java并发(基础知识)—— 创建.运行以及停止一个线程中讲解了两种创建线程的方式:直接继承Thread类以及实现Runnable接口并赋给Thread,这两种创建线程的方式在线程比较少的时候是没有 ...

  9. 从零开始实现简单 RPC 框架 5:网络通信之序列化

    我们在接下来会开始讲网络通信相关的内容了.既然是网络通信,那必然会涉及到序列化的相关技术. 下面是 ccx-rpc 序列化器的接口定义. /** * 序列化器 */ public interface ...

随机推荐

  1. wifi驱动总结(2)

    1.dhd_module_init(驱动模块初始化函数) → dhd_wifi_platform_register_drv(查找设备,注册驱动) → 1) wifi_ctrlfunc_register ...

  2. VS2010 使用 EF5 框架步骤

    1.安装 nuget , 在nuget里联机查找 EF 5.x DbContext Generator for C# 模版下载安装 2.nuget 控制台执行 Install-Package Enti ...

  3. CoreOS, Kubernetes, etcd

    CoreOS CoreOS Container Linux is the leading container operating system, designed to be managed and ...

  4. Hibernate中调用带有underscore的Column Name

    Hibernate中默认的NamingStrategy不支持调用带有下划线的column name.在hibernate的bean中必须使用camel case.使用ImprovedNamingStr ...

  5. Lua学习(5)——迭代器与泛型for

    1. 迭代器 2. 泛型for语义 所谓迭代器就是一种可以遍历一种集合中所有元素的机制.在lua中,迭代器通常表示为函数,每调用依次函数就返回集合中的下一个元素.泛型for 内部保存了迭代器函数 实际 ...

  6. Asp.Net Core MVC项目实现多语言(Globalization/Localization)

    正好最近手上在给一个Razor MVC项目实现一个多语言功能,叫Globalization也好,Localization也好,whatever.最终要实现的效果呢,就是一键切换全站语言,并且开发的时候 ...

  7. VR问题无关方向,VR全景为您领航,全景智慧城市已势不可当

    2016年,VR绝对是互联网科技圈的一个高频词. 在这一年里,Magic Leap获得阿里领投的近8亿美元的融资,VR公司的商业价值得到认可.Oculus Rift和HTC Vive的VR产品正式发货 ...

  8. c++概括

    c++到底是什么样的语言 在过去的几十年,计算机技术的发展令人吃惊,当前的笔记本电脑的计算速度和存储信息的能力超过了20世纪60年代的大型机.20世纪七十年代,C和Pascal语言引领人们进入结构化编 ...

  9. mybatis中$和#java代码演示

    MyBatis mapper文件中的变量引用方式#{}与${}的差别 内容来源:http://blog.csdn.net/szwangdf/article/details/26714603 默认情况下 ...

  10. python中xrange用法分析

    本文实例讲述了python中xrange用法.分享给大家供大家参考.具体如下: 先来看如下示例: >>> x=xrange(0,8) >>> print x xra ...