上节说了关于通用请求代理,实际上对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. k64 datasheet学习笔记26--Oscillator (OSC)

    1.前言 OSC模块是一个晶体振荡器. 该模块使用晶体或谐振器与外部连接,为MCU产生一个参考时钟. 主要为下图红色框住的部分 2.特性和模式 Supports 32 kHz crystals (Lo ...

  2. saltstack 入门

    1.Saltstack是什么? saltstack 是一个异构平台基础设施管理工具,具有远程执行.配置管理.云管理.只需花费数分钟就可以运行起来,扩展性足以支撑上万台服务器,速度快,服务器之间秒级通讯 ...

  3. AngularJS 作用域(Scope)

    AngularJS作用域(Scope) Scope作用域是应用在视图和控制器之间的纽带,Scope是一个对象包含可用的方法和属性,Scope可以应用在试图和控制器上. $scope是针对当前的cont ...

  4. Python——plot可视化数据,作业8(python programming)

    subject1k和subject1v的形状相同 # -*- coding: utf-8 -*- import scipy.io as sio raw_K = sio.loadmat('Subject ...

  5. noj算法 踩气球 回溯法

    描述: 六一儿童节,小朋友们做踩气球游戏,气球的编号是1-100,两位小朋友各踩了一些气球,要求他们报出自己所踩气球的编号的乘积.现在需要你编一个程序来判断他们的胜负,判断的规则是这样的:如果两人都说 ...

  6. HDU 2196 Compute --树形dp

    Computer Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  7. 浮点数运算的精度问题:以js语言为例

    在 JavaScript 中整数和浮点数都属于 Number 数据类型,所有数字都是以 64 位浮点数形式储存,即便整数也是如此. 所以我们在打印 1.00 这样的浮点数的结果是 1 而非 1.00  ...

  8. INotifyPropertyChanged 接口

    INotifyPropertyChanged 接口 用于向客户端(通常是执行绑定的客户端)发出某一属性值已更改的通知. 例如,考虑一个带有名为 FirstName 属性的 Person 对象.若要提供 ...

  9. js为什么是单线程的?10分钟了解js引擎的执行机制

    深入理解JS引擎的执行机制 1.JS为什么是单线程的? 为什么需要异步? 单线程又是如何实现异步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4.说说setT ...

  10. CSS3 box-sizing的作用

    设置CSS盒模型为标准模型或IE模型.标准模型的宽度只包括content,二IE模型包括border和padding box-sizing属性可以为三个值之一: content-box,默认值,bor ...