200行代码实现RPC框架
之前因为项目需要,基于zookeeper和thrift协议实现了一个简单易用的RPC框架,核心代码不超过200行。
zookeeper主要作用是服务发现,thrift协议作为通信传输协议, 基于commons pool2构建连接池。
大家感兴趣的话可以参考,具体代码如下:
/**
* @author zhangkai
* 抽象的thrift client,内置socket连接池以及线程池,提供同步阻塞式调用和超时调用
* 具体thrift client需要继承该类并实现其中的抽象方法并按照需要重写相关方法
*/
public abstract class AbstractThriftClient {
private final static int MAX_FRAME_SIZE = 1024 * 1024 * 1024;
private final static int MIN_FRAME_SIZE = 1024; protected ThreadPoolExecutor executor;
protected AbstractThriftClient client = this;
protected ClientConfig clientConfig;
protected CuratorFramework zkClient;
protected List<TConnectionPool> shardInfos = Lists.newArrayList(); /**
* AbstractThriftClient的构造函数
* 初始化线程池、连接池以及服务发现机制
*/
protected AbstractThriftClient(ClientConfig clientConfig) {
int processors = Runtime.getRuntime().availableProcessors();
this.executor = new ThreadPoolExecutor(processors * 5, processors * 10, 60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(processors * 100),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
this.clientConfig = clientConfig;
this.zkClient = CuratorFrameworkFactory.builder()
.connectString(clientConfig.getZkAddrs())
.retryPolicy(new ExponentialBackoffRetry(500, 4)).build();
this.zkClient.start();
buildConnPool();
} /**
* 唯一需要上层实现的抽象类
* 该方法接收封装好的RPCRequest
* 调用真实的RPC请求
* 将RPC服务返回的结果打包成RPCResponse
* 上层的具体thrift client实例需要实现该方法
*/
protected abstract RPCResponse doService(RPCRequest rpcRequest, TProtocol protocol) throws Exception; /**
* 从连接池中选择连接的方法,
* 上层可以重写该方法,实现自己的hash规则
*/
protected int hashRule(RPCRequest request){
Random rand = new Random();
return rand.nextInt(shardInfos.size());
} /**
* processRequest方法处理流程:
* 1、从连接池中获取连接
* 2、创建相应的Transport协议结构
* 3、调用doService方法获取RPC的返回结果
* @param rpcRequest
* @return
*/
protected RPCResponse processRequest(RPCRequest rpcRequest){
String serviceName = rpcRequest.getServiceName();
RPCResponse response = new RPCResponse();
if(serviceName == null){
LogUtils.warn("serviceName can not be null");
response.setCode(RPCResponse.FAILED);
return response;
}
TConnectionPool connPool = getConnPool(rpcRequest);
if(connPool == null){
response.setCode(RPCResponse.FAILED);
return response;
}
TSocket socket = connPool.getSocket();
try {
TTransport transport = new TFastFramedTransport(socket, MIN_FRAME_SIZE, MAX_FRAME_SIZE);
if (!transport.isOpen()) {
transport.open();
}
TProtocol protocol = new TBinaryProtocol(transport);
return this.doService(rpcRequest, protocol);
} catch (Exception e) {
LogUtils.error("", e);
connPool.removeSocket(socket);
response.setCode(RPCResponse.FAILED);
return response;
} finally {
if (socket.isOpen()) {
connPool.returnSocket(socket);
}
}
} protected RPCResponse sendRequest(RPCRequest request){
if(clientConfig.getRequestTimeout() <= 0){
return this.processRequest(request);
}else{
return this.processRequestTimeout(request, clientConfig.getRequestTimeout());
}
} private TConnectionPool getConnPool(RPCRequest request){
if(shardInfos.size() <= 0){
LogUtils.warn("no valid node available");
return null;
}
int index = hashRule(request);
return shardInfos.get(index % shardInfos.size());
} private RPCResponse processRequestTimeout(RPCRequest request, int timeout){
RPCRequestTask rpcRequestTask = new RPCRequestTask(request);
Future<RPCResponse> future = executor.submit(rpcRequestTask); try {
RPCResponse response = future.get(clientConfig.getRequestTimeout(), TimeUnit.MILLISECONDS);
return response;
} catch (InterruptedException e) {
LogUtils.warn("[ExecutorService]The current thread was interrupted while waiting: ", e);
RPCResponse response = new RPCResponse();
response.setCode(RPCResponse.FAILED);
return response;
} catch (ExecutionException e) {
LogUtils.warn("[ExecutorService]The computation threw an exception: ", e);
RPCResponse response = new RPCResponse();
response.setCode(RPCResponse.FAILED);
return response;
} catch (TimeoutException e) {
LogUtils.warn("[ExecutorService]The wait " + this.clientConfig.getRequestTimeout() + " timed out: ", e);
RPCResponse response = new RPCResponse();
response.setCode(RPCResponse.FAILED);
return response;
} catch(Exception e){
LogUtils.warn("[ExecutorService] failed", e);
RPCResponse response = new RPCResponse();
response.setCode(RPCResponse.FAILED);
return response;
}
} private class RPCRequestTask implements Callable<RPCResponse> {
private RPCRequest rpcRequest; public RPCRequestTask(RPCRequest request) {
this.rpcRequest = request;
} @Override
public RPCResponse call() {
return client.processRequest(rpcRequest);
}
}; private void buildConnPool(){
try{
List<String> nodes = zkClient
.getChildren()
.usingWatcher(new Watcher(){
@Override
public void process(WatchedEvent event) {
if(event.getType() == EventType.NodeChildrenChanged){
buildConnPool();
}
}})
.forPath(clientConfig.getZkNamespace());
List<TConnectionPool> currShardInfos = Lists.newArrayList();
for(String node : nodes){
String path = clientConfig.getZkNamespace() + "/" + node;
byte[] dataArray = zkClient.getData().forPath(path);
String dataStr = new String(dataArray);
RegistryInfo info = JsonUtils.fromJson(dataStr, RegistryInfo.class);
TServerInfo server = new TServerInfo(info.getIp(), info.getPort());
currShardInfos.add(new TConnectionPool(server));
}
this.shardInfos = currShardInfos;
}catch(Exception e){
LogUtils.error("build conn pool failed", e);
}
}
}
完整的代码和demo可以参考:https://github.com/zhangkai253/simpleRPC
200行代码实现RPC框架的更多相关文章
- 200行代码,7个对象——让你了解ASP.NET Core框架的本质
原文:200行代码,7个对象--让你了解ASP.NET Core框架的本质 2019年1月19日,微软技术(苏州)俱乐部成立,我受邀在成立大会上作了一个名为<ASP.NET Core框架揭秘&g ...
- 200 行代码实现基于 Paxos 的 KV 存储
前言 写完[paxos 的直观解释]之后,网友都说疗效甚好,但是也会对这篇教程中一些环节提出疑问(有疑问说明真的看懂了 ),例如怎么把只能确定一个值的 paxos 应用到实际场景中. 既然 Talk ...
- 不到 200 行代码,教你如何用 Keras 搭建生成对抗网络(GAN)【转】
本文转载自:https://www.leiphone.com/news/201703/Y5vnDSV9uIJIQzQm.html 生成对抗网络(Generative Adversarial Netwo ...
- 200行代码实现Mini ASP.NET Core
前言 在学习ASP.NET Core源码过程中,偶然看见蒋金楠老师的ASP.NET Core框架揭秘,不到200行代码实现了ASP.NET Core Mini框架,针对框架本质进行了讲解,受益匪浅,本 ...
- 200行代码实现简版react🔥
200行代码实现简版react
- SpringBoot,用200行代码完成一个一二级分布式缓存
缓存系统的用来代替直接访问数据库,用来提升系统性能,减小数据库复杂.早期缓存跟系统在一个虚拟机里,这样内存访问,速度最快. 后来应用系统水平扩展,缓存作为一个独立系统存在,如redis,但是每次从缓存 ...
- 200行代码,7个对象——让你了解ASP.NET Core框架的本质
2019年1月19日,微软技术(苏州)俱乐部成立,我受邀在成立大会上作了一个名为<ASP.NET Core框架揭秘>的分享.在此次分享中,我按照ASP.NET Core自身的运行原理和设计 ...
- 200行代码,7个对象——让你了解ASP.NET Core框架的本质[3.x版]
2019年1月19日,微软技术(苏州)俱乐部成立,我受邀在成立大会上作了一个名为<ASP.NET Core框架揭秘>的分享.在此次分享中,我按照ASP.NET Core自身的运行原理和设计 ...
- JavaScript开发区块链只需200行代码
用JavaScript开发实现一个简单区块链.通过这一开发过程,你将理解区块链技术是什么:区块链就是一个分布式数据库,存储结构是一个不断增长的链表,链表中包含着许多有序的记录. 然而,在通常情况下,当 ...
随机推荐
- C++学习之 类
1.类规范 类声明:包括数据成员.成员函数(共有接口)的声明 类方法定义 C++程序员将接口(类)放在头文件中,将实现放在源代码文件中 类设计尽量将共有接口和实现细节分开,数据隐藏(将数据放在私有部分 ...
- PAT甲题题解-1016. Phone Bills (25)-模拟、排序
博主欢迎转载,但请给出本文链接,我尊重你,你尊重我,谢谢~http://www.cnblogs.com/chenxiwenruo/p/6789229.html特别不喜欢那些随便转载别人的原创文章又不给 ...
- CSAPP lab2 二进制拆弹 binary bombs phase_1
这个实验从开始到完成大概花了三天的时间,由于我们还没有学习编译原理.汇编语言等课程,为了完成这个实验我投机取巧了太多,看了网上很多的解题方法,为了更加深入学习编译反编译,觉得需要从头开始好好梳理一下. ...
- 圆桌的项目Alpha冲刺——测试
测试工作安排 作为一个测试计划来讲,核心的三个要素是时间,资源,范围.时间就是什么时候做以及要花多久做,资源就是你要调用的人力.机器等资源,范围是你要测试的东西以及测试重点. 时间:每天完成相应的模块 ...
- jmeter 获取执行脚本的路径
需求:向jmeter.jmx 的路径下 写日志 : import org.apache.jmeter.services.FileServer; import com.bzj.utils.*; Stri ...
- CentOS下面磁盘扩容处理
1. 给虚拟机增加一块硬盘: 过程不表 2. 增加了硬盘之后需要重启一下 查看磁盘 ls /dev/sd* 3. 使用 gdisk 处理磁盘 注意 这里面fdisk 貌似没法处理成 LVM 必须使用 ...
- WebSocket 时时双向数据,前后端(聊天室)
https://blog.csdn.net/lecepin/article/details/54632749 实例内容 今天主要说一下微信的WebSocket接口以及在小程序中的使用. WebSock ...
- 防止短时间js 重复执行
function debounce(fn, delay) { // 持久化一个定时器 timer let timer = null; // 闭包函数可以访问 timer return function ...
- SQL 优化经验总结34条
我们要做到不但会写SQL,还要做到写出性能优良的SQL,以下为笔者学习.摘录.并汇总部分资料与大家分享! (1) 选择最有效率的表名顺序(只在基于规则的优化器中有效): ORACLE 的解析器按照 ...
- POJ 2584 T-Shirt Gumbo
T-Shirt Gumbo Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 3689 Accepted: 1755 Des ...