• Netty章节十一:Thrift多语言案例


    本案例使用JavaPython两种语言进行互相调用测试Thrift

    IDL文件(.thrift文件)

    #定义命名空间: namespace 语言名 路径
    namespace java thrift.generated
    namespace py py.thrift.generated
    
    #定义类型别名
    typedef i16 short
    typedef i32 int
    typedef i64 long
    typedef bool boolean
    typedef string String
    
    #定义一个消息/对象/结构体  关键字struct
    struct Person{
        1:optional String username,
        2:optional int age,
        3:optional boolean married
    }
    /*说明:
        唯一标记数:修饰符 数据类型 属性名
      修饰符: 默认就是optional
           required 必须的,必须存在,必须赋值
           optional 可选的,可以不使用
    */
    
    #定义一个异常,数据传递时/方法调用是可能出现的异常 关键字exception
    #服务端如果出现异常的话直接抛给客户端,让客户端catch处理
    exception DataException{
        1:optional String message;
        2:optional String callStack;
        3:optional String date;
    }
    
    #定义服务接口 关键字service
    #定义一系列方法,就是客户端于服务端进行交互所调用的方法,具体实现由服务端完成
    service PersonService{
        //返回值 方法名(参数) throws (异常)
        Person getPersonByUsername(1:required String username) throws (1:DataException dataException),
        void savePersion(1:required Person person) throws (1:DataException dataException)
    }
    

    使用thrift编译器生成编译文件

    生成Java代码

    thrift --gen java src/thrift/data.thrift
    

    生成Python代码

    thrift --gen py src/thrift/data.thrift
    

    Java所有实现

    引入依赖

    使用包管理器使用Gradle

    org.apache.thrift:libthrift:0.13.0
    

    编写接口实现类

    实际开发中放在服务端

    //thrift生成的接口文件的实现类
    public class PersonServiceImpl implements PersonService.Iface {
    
        @Override
        public Person getPersonByUsername(String username) throws DataException, TException {
            System.out.println("Got Client Param:" + username);
    
            Person person = new Person().setUsername(username)
                    .setAge(18).setMarried(false);
            return person;
        }
    
        @Override
        public void savePersion(Person person) throws DataException, TException {
            System.out.println("Got Client Param:");
            System.out.println(person.getUsername());
            System.out.println(person.getAge());
            System.out.println(person.isMarried());
        }
    }
    

    服务器端

    /*
    	服务端  实现client远程调用Server方法,这个结果说明了方法还是在Server端。
     	Client调用方法,方法走了一遍,但其实还是在server端走,最后的结果通过网络传输到Client
       	方法体里的打印值,还是在Server端打印。只有Client端自己打印的值,才会出现在Client中。
    */
    public class ThriftServer {
        public static void main(String[] args) throws Exception {
            //非阻塞的socket  绑定端口号8899,表示客户端与服务端建立的连接
            TNonblockingServerSocket socket = new TNonblockingServerSocket(8899);
            //高可用的server,并设置工作线程的最大值和最小值  arg作用就是构建一系列信息
            THsHaServer.Args arg = new THsHaServer.Args(socket)
                    .minWorkerThreads(2).maxWorkerThreads(4);
            //设置处理器(Processor),将实现接口作为泛型,因为客户端那边调用的就是这个,
            //所以后面传输的也是这个对象new PersonServiceImpl()
            PersonService.Processor<PersonServiceImpl> processor =
                    new PersonService.Processor<>(new PersonServiceImpl());
    
            //设置协议工厂
            //协议层:表示数据传输格式,这里TCompactProtocol(二进制压缩协议)表示压缩格式,速率很快
            arg.protocolFactory(new TCompactProtocol.Factory());
            //传输层:表示数据的传输方式,这里TFramedTransport是以frame为单位传输,非阻塞式传输
            arg.transportFactory(new TFramedTransport.Factory());
            arg.processorFactory(new TProcessorFactory(processor));
    
            //启动server  支持的服务模型:THsHaServer半同步,半异步Server
            TServer server = new THsHaServer(arg);
    
            System.out.println("Thrift Server Started!");
    
            //一个异步死循环,永远不会退出
            server.serve();
        }
    }
    

    客户端

    public class ThriftClient {
        public static void main(String[] args) {
            //传输层/传输协议:要和服务端的传输协议保持一致,设置地址,端口号,和超时时间,是一个连接/socket
            TTransport transport = new TFramedTransport(
                    new TSocket("localhost",8899),600);
            //协议层设置,设置数据传输格式,传入传输层,要与服务端保持一致
            TProtocol protocol = new TCompactProtocol(transport);
            //获得thrift自动生成的Client对象,可以与服务端进行远程调用的对象
            PersonService.Client client = new PersonService.Client(protocol);
            try {
                //打开socket
                transport.open();
                //关键:client本来就没有getPersonByUsername方法,这是通过网络传输调用
                Person person = client.getPersonByUsername("星空");
    
                System.out.println(person.getUsername());
                System.out.println(person.getAge());
                //对于boolean型,不是get,而是is开头.但是set都一样
                System.out.println(person.isMarried());
    
                System.out.println("------------------");
    
                Person person1 = new Person().setUsername("测试")
                    .setAge(18).setMarried(false);
                client.savePersion(person1);
    
            }catch (Exception e){
                throw new RuntimeException(e.getMessage(),e);
            }finally {
                //最后关闭transport
                transport.close();
            }
        }
    }
    

    Python所有实现

    本项目的Python代码基于Python2

    pip安装

    Archlinux安装pip

    sudo pacman -S python-pip
    

    python-pip包会吧当前系统存在的python版本对应的pip都装上

    使用pip或pip2安装thrift源码

    pip2 install thrift
    

    如果是python专用软件则会自动被扫描到thrift,如果使用vscode指定pip安装的文件目录,会更容易识别

    例如:

     "python.autoComplete.extraPaths": [
            "/home/sakura/.local/lib/python2.7/site-packages/"  //这里填自己的
      ],
    

    查看pip安装包安装路经

    如果不知道pip安装的位置可以运行python2/python3,然后import要查看路径的包,再输入包的名称,即可查看安装路径了

    ❯ python2                                                               
    Python 2.7.18 (default, Apr 23 2020, 22:32:06) 
    [GCC 9.3.0] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import thrift
    >>> thrift
    <module 'thrift' from '/home/sakura/.local/lib/python2.7/site-packages/thrift/__init__.pyc'>
    

    编写接口实现类

    # -*- coding:utf-8 -*-
    
    from py.thrift.generated import *
    
    class PersonServiceImpl:
        def getPersonByUsername(self,username):
            print "Python Got client param:" + username
    
            person = ttypes.Person()
            person.username = username
            person.age = 18
            person.married = False
    
            return person
        
        def savePersion(self,person):
            print "Python Got client param:"
    
            print person.username
            print person.age
            print person.married
    

    服务端

    # -*- coding: utf-8 -*-
    
    from py.thrift.generated import PersonService
    from PersonServiceImpl import PersonServiceImpl
    
    from thrift import Thrift
    from thrift.transport import TSocket
    from thrift.transport import TTransport
    from thrift.protocol import TCompactProtocol
    from thrift.server import TServer
    import sys
    
    #修改字符编码 解决thrift无法传输中文问题
    reload(sys)
    sys.setdefaultencoding('utf-8')
    
    
    try:
        personServiceHandler = PersonServiceImpl()
        #设置Processor
        processor = PersonService.Processor(personServiceHandler)
        #绑定端口号
        serverSocket = TSocket.TServerSocket(port=8899)
        #服务端使用Factory 客户端使用Transport
        transportFactory = TTransport.TFramedTransportFactory()
        #服务端使用Factory 客户端使用Protocol
        protocolFactory = TCompactProtocol.TCompactProtocolFactory()
    
        server = TServer.TSimpleServer(processor,serverSocket,transportFactory,protocolFactory)
        
        server.serve()
    
    except Thrift.TException, ex:
        print '%s' % ex.message
    

    客户端

    # -*- coding: utf-8 -*-
    
    from thrift import Thrift
    from thrift.transport import TSocket
    from thrift.transport import TTransport
    from thrift.protocol import TCompactProtocol
    import sys
    
    #修改字符编码 解决thrift无法传输中文问题
    reload(sys)
    sys.setdefaultencoding('utf-8')
    
    from py.thrift.generated import *
    try:
        #设置连接 指定地址以及端口号
        tSocket = TSocket.TSocket('localhost',8899)
        #设置超时时间
        tSocket.setTimeout(600)
        #设置数据传输格式,设为TFramedTransport并传入tSocket
        transport = TTransport.TFramedTransport(tSocket)
        #设置数据的传输方式,设为TCompactProtocol并传入transport
        protocol = TCompactProtocol.TCompactProtocol(transport)
        #创建client 于服务端交互的接口
        client = PersonService.Client(protocol)
        #开启连接
        transport.open()
    
        #调用相应的方法
        person = client.getPersonByUsername("三木")
    
        print person.username
        print person.age
        print person.married
    
        print "------------"
    
        newPerson = ttypes.Person()
        newPerson.username = "星空"    
        newPerson.age = 18
        newPerson.married = True
    
        client.savePersion(newPerson)
        #关闭连接
        transport.close()
        
    
    except Thrift.TException, tx:
        print '%s' % tx.message
    
    

    测试

    Python作为服务端,Java作为客户端
    服务端打印:

    Python Got client param:星空
    Python Got client param:
    测试
    18
    False
    

    客户端打印:

    星空
    18
    false
    ------------------
    
  • 相关阅读:
    计算机网络基础1
    jmeter 之系统参数根据条件修改
    jmeter 之变量传递
    ant 执行jmeter脚本
    ant安装报错:ANT_HOME is set incorrectly or ant could not be located. Please set ANT_HOME.
    数据类型的转换
    你真的了解JavaScript的数据类型吗?
    js的一些常识
    数组扁平化
    vue.config.js
  • 原文地址:https://www.cnblogs.com/mikisakura/p/12983570.html
Copyright © 2020-2023  润新知