thrift-1-2-3

国庆前后玩了一下thrift,下面是一些入门级的体会和心得:

1 在linux 64位下的编译过程,之前已经写过一个文章了:http://edwardpro.iteye.com/blog/1172064

2 客户端体系结构:

thrift在客户端中几个大的部分:

TTransport -> TTProtocol -> {生成的业务代码}

这三者是一个引用关系,简单说:

TTransport:代表了用什么方式和远端通讯,在thrift官方包中我使用的是TSocket,本质上就是通过socket来进行通讯

TTProtocol:协议层,协议层是指说话的语言,比如可以使用json也可以使用SImpleJson,当然效率最高的我想应该是Bin模式,它在每次发送的头部都有一个版本字段的,这应该是我们通常协议设计的一些基础思想。

业务代码:通过描述生成的,简单说就是调用协议层和远程说话,至于怎么说,是打电话呢还是发短信呢就是有TTransport来指挥的,这样说大家应该基本上能记住这玩意了。

生成代码类中有几个比较重要的static class,大家需要知道的:

1) iface 也就是你描述的方法生成的对应接口

2)Client,也就是你可以用来new的客户端对象

3 服务器体系:

服务器体系我没有太多研究也就是知道大概用法,首先需要对你服务接口里的iface做implements,实现功能,如这次开发的实现:

class TestWebThumbServer implements
		thrift.requestWebThumb.Iface {

	@Override
	public WebThumbResult requestSync(String uri) throws TException {
		System.out.println("called by Sync" + uri);
		return null;
	}

	@Override
	public void requestAsync(String uri) throws TException {
		System.out.println("called by Aync" + uri);
	}

}

同样Server也包括这样的流程:

TTransport -> handle(你的业务实现) -> Processor

			serverTransport = new TServerSocket(8811);
			com.taobao.wireless.webthumb.thrift.requestWebThumb.Iface handle = new TestWebThumbServer();
			Processor<requestWebThumb.Iface> processor = new requestWebThumb.Processor<webthumb.thrift.requestWebThumb.Iface>(
					handle);
			TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(
					serverTransport).processor(processor));
			server.serve();

 TTransport: 同样担负着协议部分的重要责任

handle:你的实现

Processor:业务处理线程,用来做一个类似Selector的模式(这个问题后面数落的时候会说)

4 客户端基本使用过程:

1) 根据描述文件生成最初的代码

2) 接下来就是写代码了,其实说起来是很容易的,但是并不是这样的,因为你万事得考虑一个效率和易用性:

先说易用性:

易用性在我的实现里采用了代理模式,考虑调用代码基本上每个方法调用都是一个模型,因此为了使用方便不重复劳动,因此使用了代理模式,当然了代理模式个人还是很清醒低认识到这玩意对jvm的性能是有伤害的,不过这个实现思路比较简单,当然代码量是不小的,因为我还是用的jdk原生的反射,这些构造函数到方法一步步的处理代码还是很多的,而且有很多异常要判断。

然后在代码里你只需要声明一个虚拟的接口,这个接口生成服务就可以了比如:

public interface WebthumbServiceInternal {
	
	@ClassName(name="requestWebThumb$Client")
	@MethodName(name="requestAsync")
	public void request(String uri);
}

 接下来是性能:

首先我们看到了TSocket这个transport是一个block io设计,所以为了更高效率执行和避免服务器被搞死,我做了一个以服务器为单位的线程池(Executor)来完成这项工作,但在使用时发现一个问题,本来以为连接可以不断开,但实际上在多线程下访问同一个socket,即使你控制了线程还是不行:

会返回一个叫:broken pipe的错误,可以确认线程执行没问题,没有出现污染。经过查阅资料,是说linux下的socket连接会禁止多线程访问,这也就是当你手工连续执行两个方法的时候,是可以的,但是当你用单线程来控制就不行了(因为不同的runnable是属于不同的线程的)

因此要解决这个问题就需要使用nio了,我使用的是netty,netty里是通过channel来控制的,channel本质上看起来更像是一个资源会话。所以当我们使用非阻塞的时候发现很多代码要变了:

1) 需要建立一个接收缓冲池

2)channel本身不需要更多的线程管理了,可以通过设置option来解决

3)执行的时候不需要再自己控制所谓的executor了,因为netty帮你做了

当然经过了一个晚上的编码,直到天亮,直到看到只有ip4s没有传说中的ip5的时候还是有一个诡异的问题没有解决,当我通过bootstrape连接上去的时候,很奇怪,总是会被服务器踢掉,导致后续的channel就算自己connect也不行了(案例来说应该可以的,但是就是不行),这个问题还没搞定,应该是用netty上有点问题,对netty的pipe也要更多研究下,后面有结果再向大家汇报。

接下来我想讲讲自己对thrift的一些感觉:

优点:

1 发现thrift的通讯是双向的,在Transport中可以制定out和in不同的流,这两者可以分开指定,意味着这玩意已经不再局限在我们所看到的单向rpc模式,而是可以通过自己双手改造成一个双向服务器,而这个对于一些压力非常高的请求就非常必要,比如我们平时在用的tair,就是这样的设计形态(当然它没有oneway是蛮suck的)

2 设计层次我前面讲过了,不重复,我觉得在可扩展性上还是不错的,很好用,接口设计都比较清晰,一看源代码就很清楚。

缺点:

1 bug太多了,我经过两个晚上已经发现的bug有:

Tserver的强壮性很差,特别是我故意发送一些不对的字节流时会出现但是又不是100%,这个看起来应该是processor的问题了,不过因为netty没搞定,心情上没去深入搭理这个问题。

Protocol中的Json协议有问题,会引起server端的oom,这个是100%复现的(我使用的是thrift 0.7.0,其他版本为测试不作结论),同样也是因为netty没去查这个thrift的bug。

2 自有代码性能不好,这个前面也说了,thrift本身没有实现nio下的持久链接调用这个当今的企业应用环境这个基本上就没有什么实用性了,虽然自己可以写,但是还是希望官方支持下,技术都有小懒的毛病^^

总体来说thrift生成的代码没有太多值得说的地方,包括其生成的接口描述我觉得也是比较混乱,这个也是所有描述型的一个短版,以前soap时代的生成器也大多是这个毛病,不可想象如果生成一个很复杂的服务会是什么情况,所以服务生成真的是有好有坏,如果是内部服务其实效率第一了,这种方案我认为肯定不可取,如果是外部服务在某些情况下是可以考虑的,毕竟这样可以偷懒不少事情,当然这个实际上是根据你的协议设计的,如果协议本身比较复杂的确还是需要的,不然写这个代码姚折腾半天。但是我觉得比较好的方法还是用代理模式,在像java这样的语言里做好框架,然后用代理让客户调用还是很不错的,至少我觉得比生成代码可能要更方便,因为你也得考虑生成代码万一有问题,这个解决起来就费劲了。

好了关于thrift就说这些,下次解决了netty的问题再来唠叨

猜你喜欢

转载自edwardpro.iteye.com/blog/1186402
今日推荐