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开发实现一个简单区块链.通过这一开发过程,你将理解区块链技术是什么:区块链就是一个分布式数据库,存储结构是一个不断增长的链表,链表中包含着许多有序的记录. 然而,在通常情况下,当 ...
随机推荐
- Codeforces Round #546 (Div. 2) E - Nastya Hasn't Written a Legend
这题是一个贼搞人的线段树 线段树维护的是 区间和a[i - j] 首先对于update的位置可以二分查找 其次update时候的lazy比较技巧 比如更新的是 l-r段,增加的是c 那么这段的值为: ...
- 【Alpha】功能规格说明书
更新说明:从用户需求分析中剥离有关用户场景分析部分,加入功能规格说明书. Github地址:https://github.com/buaase/Phylab-Web/blob/master/docs/ ...
- numpy 读取txt为array 一行搞定
vec = np.genfromtxt('wiki.ch.text.vector', skip_header=1, delimiter=' ', dtype=None)skip_header=1是跳过 ...
- 重温redis命令
redis是已知的性能最快的key-value 数据库. 1.key相关命令 exists key :检查指定的key是否存在 1表示存在 0表示不存在 del key1,key2,key3....: ...
- PAT乙级(Basic Level)练习题-NowCoder数列总结
题目描述 NowCoder最近在研究一个数列: F(0) = 7 F(1) = 11 F(n) = F(n-1) + F(n-2) (n≥2) 他称之为NowCoder数列.请你帮忙确认一下数列中第n ...
- mysql & java & spring transaction isolation level
mysql /*SESSION LEVEL*/ select @@tx_isolation; /*GLOBAL LEVEL*/ select @@global.tx_isolation; select ...
- confluence
Confluence Confluence是一个专业的wiki程序.它是一个知识管理的工具,通过它可以实现团队成员之间的协作和知识共享. Confluence不是一个开源软件,非商业用途可以免费使用. ...
- java自定义注解学习(二)_注解详解
上篇文章,我们简单的实现了一个自定义注解,相信大家对自定义注解有了个简单的认识,这篇,这样介绍下注解中的元注解和内置注解 整体图示 内置注解 @Override 重写覆盖 这个注解大家应该经常用到,主 ...
- JVM学习笔记(四):类加载机制
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 一.类加载的时机1. 类从被加载到虚拟机内存 ...
- Spring中ClassPathXmlApplication与FileSystemXmlApplicationContext的区别以及ClassPathXmlApplicationContext 的具体路径
一.ClassPathXmlApplicationContext 的具体路径 String s[] = System.getProperty("java.class.path"). ...