简介

  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. C#并行编程(3):并行循环

    初识并行循环 并行循环主要用来处理数据并行的,如,同时对数组或列表中的多个数据执行相同的操作. 在C#编程中,我们使用并行类System.Threading.Tasks.Parallel提供的静态方法 ...

  2. 谁记录了mysql error log中的超长信息(记pt-stalk一个bug的定位过程)

    [问题] 最近查看MySQL的error log文件时,发现有很多服务器的文件中有大量的如下日志,内容很长(大小在200K左右),从记录的内容看,并没有明显的异常信息. 有一台测试服务器也有类似的问题 ...

  3. BeagleBone Black的内核移植-从零开始制作镜像

    很多年前做的针对BeagleBone Black开发板的镜像制作,因为当时涉及到非标准化的,所以把基本的kernel.uboot和跟文件系统rootfs都做了一遍,文中只做移植,不作定制化的修改. 如 ...

  4. Project_Lemon测评系统安装经验

    历经千辛万苦才在我自己的Linux上装好了Lemon 因为毕竟没有什么使用Linux的经验然后踩了不少坑,同时为了所以就有了这篇文章. 本教程大部分都基于Linux,若有需要Windows下的帮助请看 ...

  5. BZOJ.2816.[ZJOI2012]网络(LCT)

    题目链接 BZOJ 洛谷 对每种颜色维护一个LCT,保存点之间的连接关系. 修改权值A[x]和所有Max[x]都要改: 修改边的颜色先枚举所有颜色,看是否在某种颜色中有边,然后断开.(枚举一遍就行啊 ...

  6. swoole深入学习 4. process

    swoole-1.7.2增加了一个进程管理模块,用来替代PHP的pcntl扩展.pcntl是php新增的一个多进程扩展,用来实现多进程,但是有很多不完善的地方,swoole 就完善了这些地方,而且使得 ...

  7. mac下搭建node+koa2项目

    1.安装koa sudo npm install koa-generator -g (必须加上  sudo ,否则会报没有权限的错误) 提示输入密码: koa2 node001 npm i 启动:no ...

  8. C1驾考总结

    C1学车经验总结       科目1:理论考试,要点:驾考宝典上有关科目一的题全部做一遍,然后使用模拟测试,连续能够三次测试通过95分以上,说明差不多了.其中要特别注意,多去回顾几次做错过的题目.   ...

  9. stdlib库中qsort函数的使用

    qsort :功 能: 使用快速排序例程进行排序  用 法: void qsort(void *base, int nelem, int width, int (*fcmp)(const void * ...

  10. JVM的Client模式与Server模式

    概述 JVM有两种运行模式Server与Client.两种模式的区别在于,Client模式启动速度较快,Server模式启动较慢:但是启动进入稳定期长期运行之后Server模式的程序运行速度比Clie ...