zoukankan      html  css  js  c++  java
  • RPC框架实践之:Apache Thrift

    一、概述

    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

    brew install thrift


    四、IDEA中Thrift插件配置

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

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

    利用插件中心安装Thrift

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

    就像文章 SpringBoot优雅编码之:Lombok加持 一文中在IDEA中安装Lombok插件一样,有时由于网络原因,方法一不奏效时插件装不上,此时可以手动下载插件并安装。

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

    手动下载Thrift插件

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

    Install plugin from disk

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

    成功安装Thrift插件


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

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

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

    Thrift项目创建选项

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

    添加一个Java的Generator

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

    配置好Output folder

    OK,Thrift项目就绪了!

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

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

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

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

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

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

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

    Recompile 'xxx.thrift'

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

    输出的.java文件


    六、开发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代码结构

    再次强调,该 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 + "
    " + 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服务端启动成功


    八、开发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

    @Configuration
    public 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客户端启动成功


    九、RPC通信实验

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

    客户端成功从服务端取回结果

  • 相关阅读:
    前端微服务
    日志系统的设计
    js解决数据精度问题
    前端取不到header 里面的内容问题
    sql server 数据库查询 json 数据乱码
    IDEA 根据 DCEVM + Hotswap Agent 实现项目热部署
    Redis 常用命令
    Spring Data JPA native query 分页
    十八年开发经验分享(06)递归程序构造
    十八年开发经验分享(07)递归程序设计
  • 原文地址:https://www.cnblogs.com/weiguoaa/p/9039106.html
Copyright © 2011-2022 走看看