上节说了关于通用请求代理,实际上对spring的bean引用都是通过koalasClientProxy来实现的,那么在代理方法中才是我们实际的发送逻辑,咱们先看一下原生的thrift请求是什么样的。

public void startClient(String userName) {
TTransport transport = null;
try {
//transport = new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT);
transport = new TFramedTransport(new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT));
// 协议要和服务端一致
TProtocol protocol = new TBinaryProtocol(transport);
//TProtocol protocol = new TCompactProtocol(transport);
// TProtocol protocol = new TJSONProtocol(transport);
Client client = new Client(protocol);
transport.open();
String result = client.sayHello(userName);
System.out.println("Thrify client result =: " + result);
} catch (TTransportException e) {
e.printStackTrace();
} catch (TException e) {
e.printStackTrace();
} finally {
if (null != transport) {
transport.close();
}
}
}

用同步调用的例子来说明,首先需要new 一个TSocket对象,这个对象其实就是thrift维护的socket对象,内部封装维护了Socket模型,相信大家对原生Socket已经很了解了,这次不在过多阐述,熟悉Socket的朋友一定知道每次需要调用socket.open的时候,需要和远程server进行三个握手来建立通信,三次通信的代价是巨大的,对于跨异地调用或者是跨机房调用都是巨大的开销,我们不可能每次通信都和远程server进行三次握手,那么我们可以将已经握手的TCP连接放入连接池中,每次从连接池中获取到socket然后在进行调用,使用完毕之后再放入连接池,原理和mysql连接池DBCP和阿里的德鲁伊等等一个道理关于连接池相关的内容大家可以参照我git代码中的AbstractBaseIcluster相关实现即可。

我们将socket对象缓存到缓存池中,每次请求都是TCP复用,这样将极大的提升请求速度,这也是作为企业级RPC不可或缺的一部分。

看一下核心实现

 @Override
public Object invoke(MethodInvocation invocation) throws InvocationTargetException, IllegalAccessException { Method method = invocation.getMethod ();
String methodName = method.getName ();
Object[] args = invocation.getArguments (); Class<?>[] parameterTypes = method.getParameterTypes ();
if (method.getDeclaringClass () == Object.class) {
try {
return method.invoke ( this, args );
} catch (IllegalAccessException e) {
LOG.error ( e.getMessage (), e );
return null;
}
}
if ("toString".equals ( methodName ) && parameterTypes.length == 0) {
return this.toString ();
}
if ("hashCode".equals ( methodName ) && parameterTypes.length == 0) {
return this.hashCode ();
}
if ("equals".equals ( methodName ) && parameterTypes.length == 1) {
return this.equals ( args[0] );
} boolean serviceTop =false; Transaction transaction=null;
if(TraceThreadContext.get () ==null){
serviceTop=true;
transaction = Cat.newTransaction("Service", method.getDeclaringClass ().getName ().concat ( "." ).concat ( methodName ).concat ( ".top" )); MessageTree tree = Cat.getManager().getThreadLocalMessageTree();
String messageId = tree.getMessageId(); if (messageId == null) {
messageId = Cat.createMessageId();
tree.setMessageId(messageId);
} String childId = Cat.getProducer().createRpcServerId("default"); String root = tree.getRootMessageId(); if (root == null) {
root = messageId;
}
Cat.logEvent(CatConstants.TYPE_REMOTE_CALL, "", Event.SUCCESS, childId); KoalasTrace koalasTrace = new KoalasTrace ( );
koalasTrace.setChildId (childId );
koalasTrace.setParentId ( messageId);
koalasTrace.setRootId ( root );
TraceThreadContext.set (koalasTrace);
} else{
KoalasTrace currentKoalasTrace = TraceThreadContext.get ();
String child_Id = Cat.getProducer().createRpcServerId("default");
Cat.logEvent(CatConstants.TYPE_REMOTE_CALL, "", Event.SUCCESS, child_Id);
currentKoalasTrace.setChildId ( child_Id );
}
try {
TTransport socket = null;
int currTryTimes = 0;
while (currTryTimes++ < retryTimes) {
ServerObject serverObject = icluster.getObjectForRemote ();
if (serverObject == null) throw new IllegalStateException("no server list to use :" + koalasClientProxy.getServiceInterface ().getName ());
GenericObjectPool<TTransport> genericObjectPool = serverObject.getGenericObjectPool ();
try {
long before = System.currentTimeMillis ();
socket = genericObjectPool.borrowObject ();
long after = System.currentTimeMillis ();
LOG.debug ( "get Object from pool with {} ms", after - before );
} catch (Exception e) {
if (socket != null)
genericObjectPool.returnObject ( socket );
LOG.error ( e.getMessage (), e );
if(transaction!=null)
transaction.setStatus ( e );
throw new IllegalStateException("borrowObject error :" + koalasClientProxy.getServiceInterface ().getName ());
} Object obj = koalasClientProxy.getInterfaceClientInstance ( socket, serverObject.getRemoteServer ().getServer () ); if (obj instanceof TAsyncClient) {
((TAsyncClient) obj).setTimeout ( asyncTimeOut );
if (args.length < 1) {
genericObjectPool.returnObject ( socket );
throw new IllegalStateException ( "args number error" );
} Object argslast = args[args.length - 1];
if (!(argslast instanceof AsyncMethodCallback)) {
genericObjectPool.returnObject ( socket );
throw new IllegalStateException ( "args type error" );
} AsyncMethodCallback callback = (AsyncMethodCallback) argslast;
ReleaseResourcesKoalasAsyncCallBack releaseResourcesKoalasAsyncCallBack = new ReleaseResourcesKoalasAsyncCallBack ( callback, serverObject, socket );
args[args.length - 1] = releaseResourcesKoalasAsyncCallBack; }
try {
Object o = method.invoke ( obj, args );
if (socket instanceof TSocket) {
genericObjectPool.returnObject ( socket ); }
if(transaction!=null)
transaction.setStatus ( Transaction.SUCCESS );
return o;
} catch (Exception e) {
Throwable cause = (e.getCause () == null) ? e : e.getCause (); boolean ifreturn = false;
if (cause instanceof TApplicationException) {
if (((TApplicationException) cause).getType () == 6666) {
LOG.info ( "the server{}--serverName【{}】 thread pool is busy ,retry it!", serverObject.getRemoteServer (),koalasClientProxy.getServiceInterface ().getName () );
if (socket != null) {
genericObjectPool.returnObject ( socket );
ifreturn = true;
}
Thread.yield ();
if (retryRequest)
continue;
} if (((TApplicationException) cause).getType () == 9999) {
LOG.error ( "rsa error with service--{}--serverName【{}】", serverObject.getRemoteServer (),koalasClientProxy.getServiceInterface ().getName () );
if (socket != null) {
genericObjectPool.returnObject ( socket );
}
if(transaction!=null)
transaction.setStatus ( cause );
throw new IllegalStateException("rsa error with service" + serverObject.getRemoteServer ().toString ()+koalasClientProxy.getServiceInterface ().getName () );
} if (((TApplicationException) cause).getType () == 6699) {
LOG.error ( "this client is not rsa support,please get the privateKey and publickey with server--{}--serverName【{}】", serverObject.getRemoteServer (),koalasClientProxy.getServiceInterface ().getName () );
if (socket != null) {
genericObjectPool.returnObject ( socket );
}
if(transaction!=null)
transaction.setStatus ( cause );
throw new IllegalStateException("this client is not rsa support,please get the privateKey and publickey with server" + serverObject.getRemoteServer ().toString ()+koalasClientProxy.getServiceInterface ().getName ());
} if (((TApplicationException) cause).getType () == TApplicationException.INTERNAL_ERROR) {
LOG.error ( "this server is error please take the error log with server--{}--serverName【{}】the remote server error message data【{}】", serverObject.getRemoteServer (),koalasClientProxy.getServiceInterface ().getName (),((TApplicationException) cause).getMessage () );
if (socket != null) {
genericObjectPool.returnObject ( socket );
}
if(transaction!=null)
transaction.setStatus ( cause );
throw new IllegalStateException("this server is error please take the error log with server" + serverObject.getRemoteServer ()+koalasClientProxy.getServiceInterface ().getName ());
} if (((TApplicationException) cause).getType () == TApplicationException.MISSING_RESULT) {
if (socket != null) {
genericObjectPool.returnObject ( socket );
}
return null;
}
} if (cause instanceof RSAException) {
LOG.error ( "this client privateKey or publicKey is error,please check it! --{}--serverName【{}】", serverObject.getRemoteServer (),koalasClientProxy.getServiceInterface ().getName () );
if (socket != null) {
genericObjectPool.returnObject ( socket );
}
if(transaction!=null)
transaction.setStatus ( cause );
throw new IllegalStateException("this client privateKey or publicKey is error,please check it!" + serverObject.getRemoteServer ()+ koalasClientProxy.getServiceInterface ().getName ());
} if(cause instanceof OutMaxLengthException){
LOG.error ( (cause ).getMessage (),cause );
if (socket != null) {
genericObjectPool.returnObject ( socket );
}
if(transaction!=null)
transaction.setStatus ( cause );
throw new IllegalStateException("to big content!" + serverObject.getRemoteServer ()+ koalasClientProxy.getServiceInterface ().getName ());
} if (cause.getCause () != null && cause.getCause () instanceof ConnectException) {
LOG.error ( "the server {}--serverName【{}】 maybe is shutdown ,retry it!", serverObject.getRemoteServer (),koalasClientProxy.getServiceInterface ().getName () );
try {
if (socket != null) {
genericObjectPool.returnObject ( socket );
ifreturn = true;
} if (retryRequest)
continue;
} catch (Exception e1) {
LOG.error ( "invalidateObject error!", e1 );
}
} if (cause.getCause () != null && cause.getCause () instanceof SocketTimeoutException) {
LOG.error ( "read timeout SocketTimeoutException,retry it! {}--serverName【{}】", serverObject.getRemoteServer (),koalasClientProxy.getServiceInterface ().getName () );
if (socket != null) {
try {
genericObjectPool.invalidateObject ( socket );
ifreturn = true;
} catch (Exception e1) {
LOG.error ( "invalidateObject error ,", e );
if(transaction!=null)
transaction.setStatus ( e1 );
throw new IllegalStateException("SocketTimeout and invalidateObject error" + serverObject.getRemoteServer () + koalasClientProxy.getServiceInterface ().getName ());
}
}
if (retryRequest)
continue;
} if(cause instanceof TTransportException){
if(((TTransportException) cause).getType () == TTransportException.END_OF_FILE){
LOG.error ( "TTransportException,END_OF_FILE! {}--serverName【{}】", serverObject.getRemoteServer (),koalasClientProxy.getServiceInterface ().getName () );
if (socket != null) {
try {
genericObjectPool.invalidateObject ( socket );
} catch (Exception e1) {
LOG.error ( "invalidateObject error", e );
if(transaction!=null)
transaction.setStatus ( e1 );
throw new IllegalStateException("TTransportException and invalidateObject error" + serverObject.getRemoteServer () + koalasClientProxy.getServiceInterface ().getName ());
}
}
if(transaction!=null)
transaction.setStatus ( cause );
throw new IllegalStateException("the remote server error!" + serverObject.getRemoteServer () + koalasClientProxy.getServiceInterface ().getName ());
}
if(cause.getCause ()!=null && cause.getCause () instanceof SocketException){
if(genericObjectPool.isClosed ()){
LOG.warn ( "serverObject {} is close!,retry it",serverObject );
if (retryRequest)
continue;
}
}
} if (socket != null && !ifreturn)
genericObjectPool.returnObject ( socket );
LOG.error ( "invoke server error,server ip -【{}】,port -【{}】--serverName【{}】", serverObject.getRemoteServer ().getIp (), serverObject.getRemoteServer ().getPort (),koalasClientProxy.getServiceInterface ().getName () );
if(transaction!=null)
transaction.setStatus ( cause );
throw e;
}
}
IllegalStateException finallyException = new IllegalStateException("error!retry time out of:" + retryTimes + "!!! " + koalasClientProxy.getServiceInterface ().getName () );
if(transaction!=null)
transaction.setStatus ( finallyException );
throw finallyException;
} finally {
if(transaction!=null)
transaction.complete ();
if(serviceTop)
TraceThreadContext.remove ();
}
}

首先是线加入cat埋点,生成服务链路。这个地方先不用关注,接下来在重试循环体中来实现发送逻辑,当连接超时异常和服务端拒绝异常等异常时会进行重试

                    socket = genericObjectPool.borrowObject ();

获取socket连接

                Object obj = koalasClientProxy.getInterfaceClientInstance ( socket, serverObject.getRemoteServer ().getServer () );
 public Object getInterfaceClientInstance(TTransport socket,String server) {

        if (!async) {
Class<?> clazz = getSynClientClass ();
try {
if (synConstructor == null) {
synConstructor = clazz.getDeclaredConstructor ( TProtocol.class );
}
TTransport transport = new TKoalasFramedTransport ( socket, maxLength_ );
if(this.getPrivateKey ()!=null && this.getPublicKey () != null){
((TKoalasFramedTransport) transport).setRsa ( (byte) 1 );
((TKoalasFramedTransport) transport).setPrivateKey ( this.privateKey );
((TKoalasFramedTransport) transport).setPublicKey ( this.publicKey );
} TProtocol protocol = new KoalasBinaryProtocol ( transport ); return synConstructor.newInstance ( protocol ); } catch (NoSuchMethodException e) {
logger.error ( "the clazz can't find the Constructor with TProtocol.class" );
} catch (InstantiationException e) {
logger.error ( "get InstantiationException", e );
} catch (IllegalAccessException e) {
logger.error ( "get IllegalAccessException", e );
} catch (InvocationTargetException e) {
logger.error ( "get InvocationTargetException", e );
}
} else {
if (null == asyncClientManagerList) {
synchronized (this) {
if (null == asyncClientManagerList) {
asyncClientManagerList = new ArrayList<> ();
for (int i = 0; i < asyncSelectorThreadCount; i++) {
try {
asyncClientManagerList.add(new TAsyncClientManager());
} catch (IOException e) {
e.printStackTrace ();
}
}
}
}
}
Class<?> clazz = getAsyncClientClass (); if (asyncConstructor == null) {
try {
asyncConstructor = clazz.getDeclaredConstructor ( TProtocolFactory.class, TAsyncClientManager.class, TNonblockingTransport.class );
} catch (NoSuchMethodException e) {
e.printStackTrace ();
}
} try {
return asyncConstructor.newInstance ( new KoalasBinaryProtocol.Factory (), asyncClientManagerList.get (socket.hashCode () % asyncSelectorThreadCount), socket );
} catch (InstantiationException e) {
logger.error ( "get InstantiationException", e );
} catch (IllegalAccessException e) {
logger.error ( "get IllegalAccessException", e );
} catch (InvocationTargetException e) {
logger.error ( "get InvocationTargetException", e );
} }
return null;
}

获取Thrift发送对象,也就是原生thrift代码中的xxxxx.client,它才是最终的发送对象,然后反射调用服务端,获取结果后返回给调用方,这样一个client端的同步调用逻辑就全部完成了

https://gitee.com/a1234567891/koalas-rpc

koalas-RPC 个人作品,提供大家交流学习,有意见请私信,欢迎拍砖。客户端采用thrift协议,服务端支持netty和thrift的TThreadedSelectorServer半同步半异步线程模型,支持动态扩容,服务上下线,权重动态,可用性配置,页面流量统计等,持续为个人以及中小型公司提供可靠的RPC框架技术方案

更多学习内容请加高级java QQ群:825199617

JAVA RPC (七) 手把手从零教你写一个生产级RPC之client请求的更多相关文章

  1. JAVA RPC (六) 之手把手从零教你写一个生产级RPC之client的代理

    首先对于RPC来讲,最主要的无非三点[SERVER IO模型].[序列化协议].[client连接池复用],之前的博客大家应该对thrift有一个大致的了解了,那么我们现在来说一说如何将thrift的 ...

  2. 只有20行Javascript代码!手把手教你写一个页面模板引擎

    http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...

  3. 半个小时教你写一个装(bi)逼(she)之地图搜租房

    半个小时教你写一个装(bi)逼(she)之地图搜租房 首先需要一个Python3环境,怎么准备我就不多说了,实在不会的出门右转看一下廖雪峰老师的博客. HTML部分 代码来自:高德API+Python ...

  4. 【vps】教你写一个属于自己的随机图API

    [vps]教你写一个自己的随机图API 前言 刚刚开始使用halo博客的时候,我就发现halo博客系统是可以使用随机图当背景的,所以也是使用了网上一些比较火的随机图API. 在上次发现了各种图片API ...

  5. 手把手教你写一个java的orm(五)

    生成sql:where 上一篇里我们实现了生成insert的sql,下面要开始实现update,delete,select的sql语句了.但是这些语句有一个比较麻烦的地方是:它们一般后面都会有wher ...

  6. 手把手教你写一个java的orm(一)

    写之前的说明 其实吧. 这个东西已经写好了,地址在:https://github.com/hjx601496320/JdbcPlus 这系列文章算是我写的过程的总结吧.(恩系列,说明我可能会写好久,╮ ...

  7. 手把手教你写一个RPC

    1.1 RPC 是什么 定义:RPC(Remote Procedure Call Protocol)--远程过程调用协议 ,RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数 ...

  8. 教你写个简单到的 Redis Client 框架 - .NET Core

    目录 1,关于 Redis RESP 定义数据类型 2,定义异步消息状态机 3,定义命令发送模板 4,定义 Redis Client 5,实现简单的 RESP 解析 6,实现命令发送客户端 7,如何使 ...

  9. 手把手教你写一个java的orm(完)

    生成sql:select 上一篇讲了怎样生成一个sql中where的一部分,之后我们要做事情就简单很多了,就只要像最开始一样的生成各种sql语句就好了,之后只要再加上我们需要的条件,一个完整的sql就 ...

随机推荐

  1. 论文笔记:Deep feature learning with relative distance comparison for person re-identification

    这篇论文是要解决 person re-identification 的问题.所谓 person re-identification,指的是在不同的场景下识别同一个人(如下图所示).这里的难点是,由于不 ...

  2. [Kubernetes]yaml文件详解

    应前一段时间夸下的海口:[Kubernetes]如何使用yaml文件使得可以向外暴露服务,说过要写一篇关于yaml文件详解的文章出来的,今天来总结一下.yaml文件用在很多地方,但是这里以介绍在Kub ...

  3. Lua中的迭代器与泛型for

    [前言] 迭代器就是一种可以遍历一种集合中所有元素的机制,在Lua中,通常将迭代器表示为函数.每调用一次函数,就返回集合中的“下一个”元素.每个迭代器都需要在每次成功调用之后保存一些状态,这样才能知道 ...

  4. 【转载】IIS出现“HTTP 错误 500.0,C:\php\php-cgi.exe - FastCGI 进程意外退出”解决方法

    昨天给大家介绍了在windows+iis的平台上搭建支持php+mysql平台的文章,教程步骤都是笔者一步一个操作然后一个记录介绍给大家的,实机演练,教程绝对切实可用,但是不同的同学在不同的环境下按照 ...

  5. Windows系统中python3.7安装数据可视化模块Matplotlib、numpy的各种方法汇总

    安装环境:Windows10 64位Python3.7 32位 确保已经安装PIP工具命令窗口输入PIP出现以下窗口说明PIP已经成功安装 方法1:(1)在Matplotlib的官网下载电脑对应的版本 ...

  6. SQL Server2012如何导出sql脚本并且还原数据库

    一  备份数据库 1  选择某一个数据库,右键依次选择:任务==>生成脚本: 2  选择要编写脚本的数据库对象,注意此处可以选择特定的数据库对象,我们可以选择我们需要备份的数据表. 3   在当 ...

  7. Codeforces 1097E. Egor and an RPG game 构造

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF1097E.html 题解 首先我们求出 $k = f(n) = \max\{x|\frac{x(x+1)}2 ...

  8. B - Housewife Wind-树链剖分-树状数组

    思路:边权转化到点权上,统一把每一条边的边权集中到深度较深的点上去. #include<stdio.h> #include<iostream> #include<cstr ...

  9. react的Virtual DOM

    一.Virtual DOMVirtual DOM是一个JavaScript对象,v8引擎使得js可以高效运行,而直接操作DOM很慢.Virtual DOM本质上就是在JS和DOM之间做了一个缓存.可以 ...

  10. eclipse 中 导入git项目无法导入的问题

    研发在git上打了一个分支,需要重新导入分支项目.此时发现与之前相同模式导入失败,不起作用. 解决: 需要在Git Repositories中对应项目下找到.project 文件并进行修改,修改项目名 ...