简介

  Apache Thrift是Facebook开源的跨语言的RPC通信框架,目前已经捐献给Apache基金会管理,由于其跨语言特性和出色的性能,在很多互联网公司得到应用,有能力的公司甚至会基于thrift研发一套分布式服务框架,增加诸如服务注册、服务发现等功能。

  RPC即Remote Procedure Call,翻译为远程过程调用。任何RPC协议的实现终极目标都是让使用者在调用远程方法的时候就像是调用本地方法一样简单,从而提高使用远程服务的效率。

  现代互联网架构多数基于SOA思想而搭建,即面向服务化的架构。服务提供方称为Provider,服务的使用方称为Consumer,有时也把服务提供方称为Server端,使用方称为Client端,即典型的CS模型。这里的远程调用,主要指跨进程的调用,Provider和Consumer可能是同一机器的不同进程,也可能在不同的机器,通过网络相互通信,大部分情况下两者会部署在不同的物理机器上,这种情况下由于网络通信的开销就会对RPC框架的性能要求极高。

下面分别从服务端和客户端的视角来介绍Thrift在RPC中的应用。

服务端(Server)

服务端需要发布一个服务给别人使用,首先要约定好服务的接口,包括以下几个部分:

  • 服务的名称
  • 服务使用时的参数
  • 返回结果

Thrift自己规定了一套接口定义语言(IDL)来描述服务,用后缀为.thrift的文件来描述,比如我们要提供一个打招呼的服务,传入姓名,然后返回一段友好的语句,Thrift文件HelloService.thrift的内容如下:

namespace java com.yuanwhy.service
service HelloService{
string sayHello(1:string name)
}

  Thrift文件定义好之后,就约定好了接口的使用方式,但是仍然还不能使用,需要我们用thrift命令来生成对应的编程语言的文件,比如用一下命令来生成HelloService.class的Java文件。

thrift -r --gen java HelloService.thrift 

命令行参数 -r 代表递归生成里面引用的其他文件, --gen 后面跟生成的目标语言,最后跟上thrift文件。

  生成的Java文件里会有一个接口HelloService.Iface,这就是一个普通的Java Interface,服务端要提供该接口的实现,实现之前需要引用libthrift的jar包,比如我们这么实现:

package com.yuanwhy.service;

import org.apache.thrift.TException;
public class HelloServiceImpl implements HelloService.Iface { @Override
public String sayHello(String name) throws TException {
return "Hello, " + name + "!";
}
}

  这样,服务端就实现了服务的逻辑部分,但是要让别人在网络上真正可用,我们还得把这个服务发布出去,发布的方式就是借助Socket编程,监听一个对外服务的端口,这也是网络通讯的基本套路。利用Thrift提供的API,在HelloServiceProvider类中启动Thrift服务:

package com.yuanwhy.demo;

import com.yuanwhy.service.HelloService;
import com.yuanwhy.service.HelloServiceImpl;
import org.apache.thrift.TProcessor;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException; public class HelloServiceProvider {
/**
* 启动 Thrift 服务器
*
* @param args
*/
public static void main(String[] args) {
try {
// 设置服务端口为 7911
TServerSocket serverTransport = new TServerSocket(7911);
TProcessor processor = new HelloService.Processor(new HelloServiceImpl());
TServer server = new TSimpleServer(new TServer.Args(serverTransport).processor(processor));
System.out.println("Start server on port 7911...");
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
}
}
}  

运行main函数之后,服务端就会监听7911端口,开始对外提供sayHello的服务了。

客户端(Client)

  客户端想要使用服务端通过thrift发布的服务,只需要遵循面向接口编程的基本思想,引用Java接口,用thrift的API连接服务端即可,比如HelloServiceConsumer类中这么使用sayHello服务:

package com.yuanwhy.demo;

import com.yuanwhy.service.HelloService;
import org.apache.thrift.TApplicationException;
import org.apache.thrift.TEnum;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport; /**
* Created by zeze on 2018/03/23.
*/
public class HelloServiceConsumer {
public static void main(String[] args) {
TTransport transport = new TSocket("0.0.0.0", 7911);
try {
transport.open();
TProtocol protocol = new TBinaryProtocol(transport);
HelloService.Client client = new HelloService.Client(protocol);
System.out.println(client.sayHello("yuanwhy"));
transport.close();
} catch (TApplicationException e) { if (e.getType() == TApplicationException.MISSING_RESULT) {
System.out.println("null");
} } catch (TException e){ }
}
}

  客户端运行结果会打印Hello, yuanwhy!,表明服务调用成功。仔细观察一下客户端的代码会发现,基本和普通的Socket编程没有太大的区别,只是又被thrift做了一层的封装,让我们可以按照约定的接口直接像client.sayHello("yuanwhy")这样调用远程服务。

  注意:客户端如果是在不同的Java项目中调用服务,只需要服务端把thrift文件或者生成的Java接口文件以API的方式提供出来即可,客户端绝对不需要引用HelloServiceImpl实现类,因为目的就是让逻辑在服务端实现,对客户端透明。

  另外,当服务端返回null时,客户端会抛一个Type为TApplicationException.MISSING_RESULT的异常出来,如果不处理就会影响客户端正常的流程。这一点可以在Thrift的生成代码中看出来:

public String recv_sayHello() throws org.apache.thrift.TException
{
sayHello_result result = new sayHello_result();
receiveBase(result, "sayHello");
if (result.isSetSuccess()) {
return result.success;
}
throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "sayHello failed: unknown result");
}

总结

  以上对Thrift的使用只做了个简单的介绍,真正在项目中使用Thrift还会涉及很多,比如各种Thrift数据结构的使用,在对Thrift接口进行升级过程中struct的字段最好保留原有字段顺序以达到兼容目的,还比如客户端应该建立连接池机制,而不是每次调用服务时都去新建一次TCP连接等等。

  理解Thrift基本的运行模式,对后续更深入研究RPC实现机制会有更好的帮助, 更多Thrift数据结构及其使用方法,可以直接访问官网https://thrift.apache.org 来学习。

【Java】分布式RPC通信框架Apache Thrift 使用总结的更多相关文章

  1. RPC通信框架——RCF介绍

    现有的软件中用了大量的COM接口,导致无法跨平台,当然由于与Windows结合的太紧密,还有很多无法跨平台的地方.那么为了实现跨平台,支持Linux系统,以及后续的分布式,首要任务是去除COM接口. ...

  2. RPC通信框架——RCF介绍(替换COM)

    阅读目录 RPC通信框架 为什么选择RCF 简单的性能测试 参考资料 总结 现有的软件中用了大量的COM接口,导致无法跨平台,当然由于与Windows结合的太紧密,还有很多无法跨平台的地方.那么为了实 ...

  3. RPC通信框架——RCF介绍

    现有的软件中用了大量的COM接口,导致无法跨平台,当然由于与Windows结合的太紧密,还有很多无法跨平台的地方.那么为了实现跨平台,支持Linux系统,以及后续的分布式,首要任务是去除COM接口. ...

  4. 美团分布式服务通信框架及服务治理系统OCTO

     一.什么是OCTO 定义: OCTO是美团的分布式服务通信框架及服务治理系统,属于公司级基础设施,目前尚未开源. 目标: 为公司所有业务提供统一的服务通信框架,使业务具备良好的服务运营能力,轻松实现 ...

  5. 基于netty轻量的高性能分布式RPC服务框架forest<下篇>

    基于netty轻量的高性能分布式RPC服务框架forest<上篇> 文章已经简单介绍了forest的快速入门,本文旨在介绍forest用户指南. 基本介绍 Forest是一套基于java开 ...

  6. 基于netty轻量的高性能分布式RPC服务框架forest<上篇>

    工作几年,用过不不少RPC框架,也算是读过一些RPC源码.之前也撸过几次RPC框架,但是不断的被自己否定,最近终于又撸了一个,希望能够不断迭代出自己喜欢的样子. 顺便也记录一下撸RPC的过程,一来作为 ...

  7. RSF 分布式 RPC 服务框架的分层设计

    RSF 是个什么东西? 一个高可用.高性能.轻量级的分布式服务框架.支持容灾.负载均衡.集群.一个典型的应用场景是,将同一个服务部署在多个Server上提供 request.response 消息通知 ...

  8. SpringBoot2+Netty打造通俗简版RPC通信框架(升级版)

    背景         上篇文章我简单的介绍了自己打造的通俗简版RPC通信框架,这篇是对简版的增强~         如果大家对此项目还感兴趣的话,可到码云上瞄瞄:Netty-RPC         上 ...

  9. SpringBoot2+Netty打造通俗简版RPC通信框架

    2019-07-19:完成基本RPC通信! 2019-07-22:优化此框架,实现单一长连接! 2019-07-24:继续优化此框架:1.增加服务提供注解(带版本号),然后利用Spring框架的在启动 ...

随机推荐

  1. Burp Suite之爬网模块(二)

    Spider功能 Burp Spider爬网介绍 Burp Spider 是一个映射 web 应用程序的工具.它使用多种智能技术对一个应用程序的内容和功能进行全面的清查. 通过跟踪 HTML 和 Ja ...

  2. class.forName的作用?

    调用该访问 返回一个以字符串指定类名的类的对象. 返回字节码,返回字节码的方式有几种: ①:这份字节码曾经被加载过已经存在java虚拟机中了直接返回. ②:java虚拟机中还没有这份字节码,用类加载器 ...

  3. Progressive web app理念及发展前景

    前一段时间微信推出微信小程序进行公测,着实火了一把,博得了大众的眼球,不明真相的吃瓜观众们纷纷围观,所谓的“微信小程序”,通俗的讲就是一种不需要下载安装即可使用的应用程序,脱离于app商店依托于浏览器 ...

  4. CAD常用的快捷键命令

    CAD常用的快捷键命令大全,主要包括快捷键命令的键盘操作和所对应的快捷命令,方便查找和使用,对cad初学者能够快速提高绘图速度.图文并茂,非常实用. 一:常用功能键 F1: 获取帮助 F2: 实现作图 ...

  5. 学JAVA二十一天,自定义数组

    今天就说一下自定义数组,至于要怎么用,我也不知道,反正逼格挺高的. 闲话不多说,开始: 首先,自定义数组首先要创建一个类,用来做自定义数组的类型. public class User{ private ...

  6. Oracle INTERVAL

    转自:http://www.cnblogs.com/ungshow/archive/2009/04/11/1433747.html INTERVAL DAY TO SECOND数据类型 Oracle语 ...

  7. GOCN每日新闻(2017-08-12)

    1.为什么我们从 Python 转向 Go 2.Go 实现常见数据结构 3.3年半,50万行 Go 代码 4.Go 游戏引擎 oka 5.Go 是隐藏学习曲线的语言

  8. 版本视图找不到数据 EDITIONING VIEW

    Oracle database 12 以后的版本,特别在EBS R12.2.X加入了版本视图这种技术,跟MOAC有点像. CREATE OR REPLACE FORCE EDITIONING VIEW ...

  9. Java中部分常见语法糖

    语法糖(Syntactic Sugar),也称糖衣语法,指在计算机语言中添加的某种语法,这种语法对语言本身功能来说没有什么影响,只是为了方便程序员的开发,提高开发效率.说白了,语法糖就是对现有语法的一 ...

  10. (转)Render Path

    Render Path定义Render Path,就是采取的光照流程. Render Path设置可以在Edit-> Project Settings->Player 里设定,见下图.也可 ...