简介

  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. 使用 Spring Boot 搭建一套增删改查(无多余代码)

    前言 这是我学习 Spring Boot 的第三篇文章,终于可以见到效果了.错过的同学可以看看之前的文章 我们为什么要学习 Spring Boot Spring Boot 入门详细分析 在入门的基础上 ...

  2. Windows环境下 PHP调用R脚本

    写在前面的: 由于是windows平台实现的,只要保证脚本命令能在cmd控制台运行,则可以在php中利用system()实现. 注意事项: (1).保证system的路径中无汉字和空格 !!  (亲身 ...

  3. python有序字典OrderedDict()

    转python创建有序字典OrderedDict # -*- coding:utf-8 -*- """ python有序字典 需导入模块collections " ...

  4. 文件压缩 ZipOutputStream类

    先举一个压缩单个文件的例子吧: [例子1] import java.io.File; import java.io.FileInputStream; import java.io.FileOutput ...

  5. IT常用英文术语解释发音

    associations 联系; 协会( association的名词复数 ); 联合; (思想.感觉.记忆等的) 联想; 按色赛诶神 == desktop ˈdesktɒp  的思克头铺 英[ˈde ...

  6. jQuery.serialize() 提交form表单

    serialize()函数用于序列化一组表单元素,将表单内容编码为用于提交的字符串. serialize()函数常用于将表单内容序列化,以便用于AJAX提交. 该函数主要根据用于提交的有效表单控件的n ...

  7. BeautifulSoup库的使用方法

    from bs4 import BeautifulSoup import lxml html = ''' <html><head><title>The Dormou ...

  8. 常用类及 LeetCode 每日一题

    1 日期时间类 在 Java 语言中,是通过时间戳来表示时间的.所谓的时间戳,在 Java 中就是指当前时间距离历元(1970-01-01 00:00:00)的时间间隔,单位是毫秒,所以 Java 中 ...

  9. CocosCreator弹窗处理

    目前我所用的也就两种方法, 放置一个几近透明的sprite,作为遮罩,防止弹窗出现后,作为背景的UI上的按钮类的响应: 2,将上述遮罩作为弹窗的背景或者弹窗的子对象[此时,需要作为子对象的第一个,防止 ...

  10. Qt.常用代码整理

    窗口全屏化(此方法只对顶级窗口有效,对子窗口无效) window.showFullScreen() 字符串处理 Qt还提供了一种方便的字符串组合方式,使用QString::arg()函数,此函数的重载 ...