之前因为项目需要,基于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框架的更多相关文章

  1. 200行代码,7个对象——让你了解ASP.NET Core框架的本质

    原文:200行代码,7个对象--让你了解ASP.NET Core框架的本质 2019年1月19日,微软技术(苏州)俱乐部成立,我受邀在成立大会上作了一个名为<ASP.NET Core框架揭秘&g ...

  2. 200 行代码实现基于 Paxos 的 KV 存储

    前言 写完[paxos 的直观解释]之后,网友都说疗效甚好,但是也会对这篇教程中一些环节提出疑问(有疑问说明真的看懂了 ),例如怎么把只能确定一个值的 paxos 应用到实际场景中. 既然 Talk ...

  3. 不到 200 行代码,教你如何用 Keras 搭建生成对抗网络(GAN)【转】

    本文转载自:https://www.leiphone.com/news/201703/Y5vnDSV9uIJIQzQm.html 生成对抗网络(Generative Adversarial Netwo ...

  4. 200行代码实现Mini ASP.NET Core

    前言 在学习ASP.NET Core源码过程中,偶然看见蒋金楠老师的ASP.NET Core框架揭秘,不到200行代码实现了ASP.NET Core Mini框架,针对框架本质进行了讲解,受益匪浅,本 ...

  5. 200行代码实现简版react🔥

    200行代码实现简版react

  6. SpringBoot,用200行代码完成一个一二级分布式缓存

    缓存系统的用来代替直接访问数据库,用来提升系统性能,减小数据库复杂.早期缓存跟系统在一个虚拟机里,这样内存访问,速度最快. 后来应用系统水平扩展,缓存作为一个独立系统存在,如redis,但是每次从缓存 ...

  7. 200行代码,7个对象——让你了解ASP.NET Core框架的本质

    2019年1月19日,微软技术(苏州)俱乐部成立,我受邀在成立大会上作了一个名为<ASP.NET Core框架揭秘>的分享.在此次分享中,我按照ASP.NET Core自身的运行原理和设计 ...

  8. 200行代码,7个对象——让你了解ASP.NET Core框架的本质[3.x版]

    2019年1月19日,微软技术(苏州)俱乐部成立,我受邀在成立大会上作了一个名为<ASP.NET Core框架揭秘>的分享.在此次分享中,我按照ASP.NET Core自身的运行原理和设计 ...

  9. JavaScript开发区块链只需200行代码

    用JavaScript开发实现一个简单区块链.通过这一开发过程,你将理解区块链技术是什么:区块链就是一个分布式数据库,存储结构是一个不断增长的链表,链表中包含着许多有序的记录. 然而,在通常情况下,当 ...

随机推荐

  1. CocoStuff—基于Deeplab训练数据的标定工具【二、用已提供的标注数据跑通项目】

    一.说明 本文为系列博客第二篇,主要讲述笔者在使用该团队提供已经标注好的COCO数据集进行训练的过程. 由于在windows中编译Caffe和Deeplab特别的麻烦,笔者并没有去探索,后续可能会去尝 ...

  2. RN热更新

    说白了集成RN业务,就是集成RN离线包,解析并渲染.所以,RN热更新的根本原理就是更换js bundle文件和资源文件,并重新加载,新的内容就完美的展示出来了. 目前市场上出现的3种热更新模式如下:仅 ...

  3. Final发布 视频展示

    1.视频链接 视频地址:http://v.youku.com/v_show/id_XMzk1OTYyNjE0NA==.html?spm=a2hzp.8244740.0.0 杨老师粉丝群——弹球学成语项 ...

  4. 重温jsp③

    Jsp详细   九大内置对象 Out jsp的输出流,用来向客户端响应 page 当前jsp对象!他的引用类型是object,即真身中有如下代码:object page=this: Session h ...

  5. 电梯V1.0

    电梯V1.0 GitHub仓库地址 Problem 一栋3层的大楼(楼层编号0-2),设有一台无限载重的电梯,初始时电梯停在0层.电梯移动1层的耗时为1,在某一层停靠的耗时为1(时间初始为0).电梯不 ...

  6. C#简述(三)

    详细请参考:http://www.runoob.com/csharp/csharp-string.html 1.C# 字符串(String) 在 C# 中,可以使用字符数组来表示字符串,但是,更常见的 ...

  7. Android map转json格式,附上Jackson包下载地址,导入过程

    android中的map转json,需要下载jackson包,下载地址: http://www.java2s.com/Code/Jar/j/Downloadjacksonall199jar.htm 下 ...

  8. Python爬虫利器:BeautifulSoup库

    Beautiful Soup parses anything you give it, and does the tree traversal stuff for you. BeautifulSoup ...

  9. Sublime Text添加插入带当前时间说明

    Sublime Text添加插入带当前时间说明   作者:木尘 日期:2014-11-25 插件实现插入带时间功能的说明: 1. 创建插件: Tools → New Plugin: import da ...

  10. 阿里云视频直播PHP-SDK接入教程

    阿里云视频直播PHP-SDK接入教程 阿里云 视频直播 配置 及 PHP-SDK 接入教程        准备工作        域名管理        配置鉴权        地址生成器及DEMO演 ...