一、概述

RPC(Remote Procedure Call)即 远程过程调用,说的这么抽象,其实简化理解就是一个节点如何请求另一节点所提供的服务。在文章 微服务调用链追踪中心搭建 一文中模拟出来的调用链:ServiceA ---> ServiceB ---> ServiceC 就是一个远程调用的例子,只不过这篇文章里是通过 RestTemplate这种 同步调用方式,利用的是HTTP协议在应用层完成的,这种方法虽然奏效,但有时效率并不高。而RPC可以不依赖于应用层协议,可以直接基于TCP进行远程调用,在传输层中即可完成通信,因此更适于某些对效率要求更高的场景。由于RPC调用方式依赖于客户端与服务端之间建立Socket连接来实现二进制数据通信,底层会比较复杂,所以一些RPC框架应运而生来封装这种复杂性,让开发者将精力聚焦于业务之上。常见的RPC框架包括:Thrift、gRPC、Finagle、Dubbo等等,从本文开始作者将选一些实践一下,本文主要记录作者对于Thrift框架的实践过程。

Thrift是Apache的项目,它结合了功能强大的软件堆栈和代码生成引擎,可以在诸多语言之间提供无缝支持。

心动不如行动吧!

二、实验环境

  • Mac OS X 10.13.2

  • SpringBoot 2.0.1

  • Thrift 0.11.0

  • IDE:IntelliJ IDEA 2018.01

为了便于读者理解,我先将下文内容总结一下,包含7点:

  • Thrift环境搭建

  • IDEA中Thrift插件配置

  • 创建 Thrift 项目并编译(目的:定义RPC接口)

  • 开发Thrift API接口

  • 开发RPC服务端

  • 开发RPC客户端

  • RPC通信实际实验

三、Thrift环境搭建

  • 方法一:原生安装方式,通过官方提供的步骤一步一步来安装

参考这里:Mac上Thrift官方安装教程

  • 方法二:使用 brew 工具(推荐)
brew install thrift

四、IDEA中Thrift插件配置

方法一:直接在IDEA界面中配置

打开IDEA的插件中心,搜索 Thrift 即可安装

方法二:手动下载Thrift插件安装

就像文章 《SpringBoot优雅编码之:Lombok加持》(https://www.jianshu.com/p/c88b0f17f62a) 一文中在IDEA中安装Lombok插件一样,有时由于网络原因,方法一不奏效时插件装不上,此时可以手动下载插件并安装。

可以去如下地址下载Thrift插件:http://plugins.jetbrains.com/plugin/7331-thrift-support

然后去IDEA中 Install plugin from disk... 选择下载的zip包安装,然后重启IDE即可

安装完成的成功标志是 Compiler 中出现了 Thrift编译器!如下图所示:

五、创建 Thrift 项目并编译(定义RPC接口)

  • 第一步:创建Thrift项目并配置

IDE 很智能地在 New Project 时提供 Thrift项目创建选项:

项目创建完成以后,在 Project Settings 中设置好 Facets 的 Thrift配置,如下图所示,这里我们添加一个 Java的Generator

在弹出的对话框中配置好 Output folder 路径,该路径用于存放由 thrift文件 转化而成的 java源文件:

OK,Thrift项目就绪了!

  • 第二步:创建thrift接口文件

这里创建一个thrift接口文件: RPCDateService.thrift

thrift文件的写法我不赘述,跟gRPC一样有其自己的语法,namespace是最后生成的接口文件的包名

namespace java com.hansonwang99.thrift.interfaceservice RPCDateService{    string getDate(1:string userName)}

在该接口文件中,我们定义了一个 提供日期的Service,让客户端能通过该接口查询到服务器当前的时间

  • 第三步:编译Thrift源文件生成Java接口类

右击.thrift源文件,点击 Recompile 'xxx.thrift' 即可完成 thrift接口文件 ---> java接口文件 的转换

输出的Java接口文件生成于上文中配置的 output 中,其 包结构=上文.thrift文件中的namespace ,其包结构如下图所示,该Java接口十分重要,后续会用于实现Client和Server之间的RPC调用。

六、开发Thrift API接口

我们创建一个Maven项目:ThriftAPI,其包含的的就是上文由自定义Thrift接口生成的Java接口: RPCDateService.java 文件,该文件将用于后面的RPC服务端和RPC客户端的代码实现!

  • pom.xml中添加thrift依赖
        <dependencies>            <dependency>                <groupId>org.apache.thrift</groupId>                <artifactId>libthrift</artifactId>                <version>0.11.0</version>            </dependency>        </dependencies>
  • 添加RPCDateService.java

将上文 第五步 由 RPCDateService.thrift 生成的 RPCDateService.java 原样拷贝到该Maven项目中即可,代码结构如下:

再次强调,该 ThriftAPI项目 会服务于下文即将要创建的RPC服务端和RPC客户端

七、开发RPC服务端

我们是利用SpringBoot来实现RPC服务端

  • pom.xml中添加依赖

这里除了自动添加好的SpringBoot依赖外,需要额外添加的就是上文的 ThriftAPI依赖 了

<dependency>        <groupId>com.hansonwang99</groupId>        <artifactId>ThriftAPI</artifactId>         <version>1.0-SNAPSHOT</version></dependency>
  • 创建Controller并实现RPC接口
    @Controller    public class RPCDateServiceImpl implements RPCDateService.Iface {        @Override        public String getDate(String userName) throws TException {            Date now=new Date();            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("今天是"+"yyyy年MM月dd日 E kk点mm分");            String nowTime = simpleDateFormat.format( now );            return "Hello " + userName + "\n" + nowTime;        }    }

这里将服务器当前时间以字符串形式返回给调用端!

  • 编写RPCThriftServer:用于启动RPC服务器
    @Component    public class RPCThriftServer {        protected final Logger logger = LoggerFactory.getLogger(this.getClass());        @Value("${thrift.port}")        private int port;        @Value("${thrift.minWorkerThreads}")        private int minThreads;        @Value("${thrift.maxWorkerThreads}")        private int maxThreads;        private TBinaryProtocol.Factory protocolFactory;        private TTransportFactory transportFactory;        @Autowired        private RPCDateServiceImpl rpcDateService;        public void init() {            protocolFactory = new TBinaryProtocol.Factory();            transportFactory = new TTransportFactory();        }        public void start() {            RPCDateService.Processor processor = new RPCDateService.Processor<RPCDateService.Iface>( rpcDateService );            init();            try {                TServerTransport transport = new TServerSocket(port);                TThreadPoolServer.Args tArgs = new TThreadPoolServer.Args(transport);                tArgs.processor(processor);                tArgs.protocolFactory(protocolFactory);                tArgs.transportFactory(transportFactory);                tArgs.minWorkerThreads(minThreads);                tArgs.maxWorkerThreads(maxThreads);                TServer server = new TThreadPoolServer(tArgs);                logger.info("thrift服务启动成功, 端口={}", port);                server.serve();            } catch (Exception e) {                logger.error("thrift服务启动失败", e);            }        }    }
  • 创建SpringBootApplication
    @SpringBootApplication    public class RPCThriftServerApplication {        private static RPCThriftServer rpcThriftServer;        public static void main(String[] args) {            ApplicationContext context = SpringApplication.run(RPCThriftServerApplication.class, args);            try {                rpcThriftServer = context.getBean(RPCThriftServer.class);                rpcThriftServer.start();            } catch (Exception e) {                e.printStackTrace();            }        }    }
  • 添加配置文件application.properties
    thrift.port=6666    thrift.minWorkerThreads=10    thrift.maxWorkerThreads=100

我们让thrift服务起在6666端口!

  • 启动RPC服务端服务

八、开发RPC客户端

这里同样用SpringBoot来实现RPC客户端!

  • pom.xml中添加依赖

    此处同RPC服务端依赖,不赘述

  • 编写RPCThriftClient:用于发出RPC调用

这里包含两个文件: RPCThriftClient.java 和 RPCThriftClientConfig.java

RPCThriftClient.java如下:

public class RPCThriftClient {    private RPCDateService.Client client;    private TBinaryProtocol protocol;    private TSocket transport;    private String host;    private int port;    public String getHost() {        return host;    }    public void setHost(String host) {        this.host = host;    }    public int getPort() {        return port;    }    public void setPort(int port) {        this.port = port;    }    public void init() {        transport = new TSocket(host, port);        protocol = new TBinaryProtocol(transport);        client = new RPCDateService.Client(protocol);    }    public RPCDateService.Client getRPCThriftService() {        return client;    }    public void open() throws TTransportException {        transport.open();    }    public void close() {        transport.close();    }}

而 RPCThriftClientConfig.java是利用config生成bean

@Configurationpublic class RPCThriftClientConfig {    @Value("${thrift.host}")    private String host;    @Value("${thrift.port}")    private int port;    @Bean(initMethod = "init")    public RPCThriftClient rpcThriftClient() {        RPCThriftClient rpcThriftClient = new RPCThriftClient();        rpcThriftClient.setHost(host);        rpcThriftClient.setPort(port);        return rpcThriftClient;    }}
  • 编写Restful的Controller作为调用入口
    @RestController    @RequestMapping("/hansonwang99")    public class RPCThriftContoller {        protected final Logger logger = LoggerFactory.getLogger(this.getClass());        @Autowired        private RPCThriftClient rpcThriftClient;        @RequestMapping(value = "/thrift", method = RequestMethod.GET)        public String thriftTest(HttpServletRequest request, HttpServletResponse response) {            try {                rpcThriftClient.open();                return rpcThriftClient.getRPCThriftService().getDate("hansonwang99");            } catch (Exception e) {                logger.error("RPC调用失败", e);                return "error";            } finally {                rpcThriftClient.close();            }        }    }
  • 创建SpringBootApplication
    @SpringBootApplication    public class RPCThriftClientApplication {        public static void main(String[] args) {            SpringApplication.run(RPCThriftClientApplication.class, args);        }    }
  • 添加配置文件application.properties
    thrift.host=localhost    thrift.port=6666    server.port=9999
  • 启动RPC客户端服务

九、RPC通信实验

我们浏览器输入: localhost:9999/hansonwang99/thrift 即可查看客户端从服务端取回的服务器当前时间,说明RPC通信过程打通!

十、后记

本文实验代码已开源:https://gitee.com/hansonwang99/SpringBoot_RPC_Thrift

作者一些其他容器化、微服务化方面的文章:

  • 微服务调用链追踪中心搭建

  • 利用K8S技术栈打造个人私有云连载文章

  • Docker容器可视化监控中心搭建

  • 利用ELK搭建Docker容器化应用日志中心

  • Spring Boot应用监控实战

欢迎关注作者公众号CodeSheep,分享更多 务实、能看懂、可复现 的原创文

©著作权归作者所有:来自51CTO博客作者mb5fdcae83766b7的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 面试官:高并发下重启服务,接口调用老是超时,你有什么解决办法?
  2. 老大甩给我 30G 文件,让小黑哥几天内全部导入到数据库
  3. Ansible 之 配置文件解析
  4. TFTP不能传输大于32MB的文件?
  5. 如何实时主动监控你的网站接口是否挂掉并及时报警
  6. 使用Flask在服务器实现一个API接口。
  7. MySQL+Flask,在本地实现一个API接口。
  8. widnows 下如何使用 ping加时间戳,来ping探测多个域名并生成文件
  9. 项目里文件名永远不要用中文!永远不要!

随机推荐

  1. 在单个测试中断言多个条件,还是分成多个测
  2. 在PHP中如何取得两个日期时间相减的结果,
  3. 函数不返回“functions.php”中的值
  4. 教你如何利用php.exe运行php文件
  5. php之简单的文件管理(基本功能)
  6. 如何检查时间偏移是否在夏令时?
  7. PHP源码安装出现virtual memory exhauste
  8. 当excel表格单元格的格式是日期格式非文
  9. php数字操作,高精度函数,保留计算结果小数
  10. php通过$_SERVER[&#39;HTTP_USER_AGENT&#