• RPC远程协议之Thrift入门


    在上一篇文章《RPC远程协议之原理分析》中,我介绍了RPC的工作原理及欲实现RPC框架功能应该做哪些事情,因为要做的事情太多,完全由开发人员研发实现,不是很现实,所以市面上出现了诸多RPC快捷框架,目前主流的有Facebook的Thrift、谷歌的gRPC,以及Dubbo,但就性能角度考虑,Thrift相对好些,并且是跨语言的,所以这里先以Thrift的介绍开始。对于Facebook,我们现阶段只需要知道它是一个高性能的、支持跨语言平台的远程服务调用框架,并且作为很多企业实现分布式系统架构的服务调用实现的基础,以及该怎样去使用它来快速搭建服务调用功能。

     

    l  数据类型

    l  准备条件

    l  例子验证

     

     

    一、数据类型

    我们知道,Thrift是跨语言的RPC开源框架,那么它应该有自己的消息数据类型,而不是其它任何一门语言的数据类型,否则就不能支持其它语言,那么就看下它支持的几种定制类型,具体如下:

    1、基础类型

    为什么Thrift的数据类型都是有符号?因为很多语言都不支持无符号的数据类型,所以Thrift为了满足大部分语言特征,所以没必要加入无符号的数据类型。

     

    2、特殊类型

    binary,未经编码的字节流类型,主要针对字符串类型的字节流化,提供与java语言更好的互操作性。

     

    3、结构类型

    struct,定义普通的OOP类型,但不支持继承特性。

     

    4、容器类型

    list,一种有序的列表集合类型,如:对应java的List

    set,一种无序的唯一值集合类型,如:对应java的Set

    map,一种离散的键值对集合类型,如:对应java的Map

     

    5、服务类型

    service,定义对外提供的服务,如:供客户端使用的服务接口。

     

    6、异常类型

    exception,一种Thrift本身定制异常,与其它语言无缝结合的异常类型。

     

    在下面的例子会演示基础类型、容器类型、结构类型,以及服务类型的使用,供读者参考。

     

    二、准备条件

    1、C/S双端

    这里的C/S双端指的是客户端和服务端,实际使用时,客户端与服务端进程往往不在同一个节点中,比如:分布式环境,所以客户端和服务端一般是分离的,客户端需要引用由服务端所生成的服务代码,来完成远程调用。但在这里,我们将客户端与服务端代码均放在同一个项目中,所以客户端不需要额外引入即可使用调用,当然两端也分别在不同的进程中运行通信。

     

    2、编译环境

    下载地址:

    http://thrift.apache.org/download

     

    如果是mac osx则可以使用brew install thrift自动安装;

    如果是linux系统,则可使用apt-get install thrift-compiler安装均可。

    安装后,可以使用thrift –version查看版本,如果正常显示,则安装完成。

     
    3、依赖添加

    我这里采用maven来加载和管理thrift依赖包,并且使用最新版本0.11.0,具体配置如下:

    <dependency>
      <groupId>org.apache.thrift</groupId>
      <artifactId>libthrift</artifactId>
      <version>0.11.0</version>
    </dependency>

     

    三、例子验证

    在这里,我就不以典型的helloworld为例,而是以根据用户ID获取用户基本信息和该用户的订单为例,详细介绍下Thrift的几种数据类的使用方法。

     

    1、编写IDL文件(user.thrift)

    # defaine the namespace
    namespace java com.cwteam

    # define the struct
    struct User {
        1:i32 uid,
        2:string name,
        3:i16 sex,
        4:list<Order> orderList
    }

    struct Order {
        1:string oid,
        2:string oname,
        3:double price,
        4:i32 number,
        5:string createAt
    }

    # define the service
    service UserService {
        # Get the user's order list by uid
        User getUserOrders(1:i32 uid)
        # Other operation follow here
        # ...
    }


    2、生成语言文件(UserService.java)

    使用Thrift提供的编辑工具生成,切换到user.thrift文件所在目录,我的结构如下:

     

    也就是user.thrift存放在main下,切换到main下,使用thrift命令编译生成语言文件,如下所示:

    #thrift--gen java user.thrift

     
    生成后的stub文件如下样子:

     

    3、业务接口实现(UserHandler.java)

    /**
     * 用户服务接口,由服务端负责实现
     */
    public class UserHandler implements UserService.Iface {

        public User getUserOrders(int uid) throws TException {
            User user = new User();      // 模拟实现用户及订单查询
            user.setUid(uid);
            user.setName("David Lang");
            user.setSex((short)1);

            List<Order> orders = new ArrayList<Order>();
            Order order = new Order();
            order.setOid("NO1112321");
            order.setOname("《Thrift进阶与提高》");
            order.setPrice(99.99);
            order.setNumber(1);
            order.setCreateAt("2017-04-04");
            orders.add(order);
            Order order2 = new Order();
            order2.setOid("NO1112322");
            order2.setOname("《RPC进阶与提高》");
            order2.setPrice(88.88);
            order2.setNumber(2);
            order2.setCreateAt("2017-04-05");
            orders.add(order2);

            user.setOrderList(orders);
            return user;
        }

    }

     

    4、服务端实例(UserServer.java)

    /**
     * 将业务处理逻辑UserHandler作为具体的业务
     * 处理器,传递给Thrift服务器,执行处理逻辑.
     */
    public class UserServer {
        private static final int port = 9081;
        private static UserHandler handler;
        private static UserService.Processor processor;

        /**
         * 启动服务端
         * processor为控制调用逻辑
         */
        public static void start(UserService.Processor processor) {
            try {
                // 阻塞方式,基于ServerSocket
                TServerTransport serverTransport = new TServerSocket(port);
                TServer server = new TSimpleServer(new TServer.Args(serverTransport).processor(processor));
                System.out.println("Starting simple server ...");
                // 启动服务
                server.serve();
            } catch(Exception e) {
                e.printStackTrace();
            }
        }

        public static void main(String[] args) {
            handler = new UserHandler();
            processor = new UserService.Processor(handler);
            start(processor);
        }

    }

     

    5、客户端实例(UserClient.java)

    /**
     * 远程调用服务接口,获取用户信息及订单列表
     */
    public class UserClient {
        private static final int port = 9081;
        private static final String addr = "localhost";

        private static UserService.Client client;
        private static TTransport transport;

        /**
         * 创建TTransport
         */
        private static TTransport createTTransport() {
            TTransport transport = new TSocket(addr,port);
            return transport;
        }

        /**
         * 开启TTransport
         */
        private static void openTTransport(TTransport transport) throws TTransportException {
            if(null == transport) {
                return;
            }
            transport.open();
        }

        /**
         * 关闭TTransport
         */
        private static void closeTTransport(TTransport transport) {
            if(null == transport) {
                return;
            }
            transport.close();
        }

        /**
         * 创建客户端实体
         */
        private static UserService.Client createClient(TTransport transport) {
            if(null == transport) {
                return null;
            }

            // 编码协议指定(这里是二进制方式传递)
            TProtocol protocol = new TBinaryProtocol(transport);
            if(null == protocol) {
                return null;
            }

            // 设定编码协议
            UserService.Client client = new UserService.Client(protocol);
            return client;
        }

        public static void main(String[] args) {
            try {
                transport = createTTransport();
                openTTransport(transport);
                client = createClient(transport);

                // 调用远程服务
                if(null == client) {
                    System.out.println("客户端生成失败,不能调用服务 ...");
                    return;
                }
                User user = client.getUserOrders(10000021);
                System.out.println(user);
            } catch(TException e1) {
                e1.printStackTrace();
            }
        }

    }

     

    6、例子运行结果

    启动服务端:

     

    启动客户端:

     

     

    好了,Thrift入门就介绍到这里,读者也会发现这里所实现的服务调用方式为阻塞的,并且是非多线程的。

    原文:https://blog.csdn.net/why_2012_gogo/article/details/79432630  

  • 相关阅读:
    去掉myeclipse的预览窗口
    tomcat访问
    传值:web.xml传递参数 即在Servlet中获取web.xml里的值
    URI、URL、请求、响应、常见状态代码
    为什么使用HttpServlet?http协议特点、servlet
    HackerRank Ice Cream Parlor
    HackerRank and MiniMax
    HackerRank Extra long factorials
    Longest Increasing Common Subsequence (LICS)
    UVa 12505 Searching in sqrt(n)
  • 原文地址:https://www.cnblogs.com/ajing2018/p/10254283.html
Copyright © 2020-2023  润新知