zoukankan      html  css  js  c++  java
  • [Thrift]学习使用Thrift

    Thrift

    在接触到Thrift之前我只接触过阿里的Dubbo,但是Dubbo不支持跨语言,所以我在找一门跨语言的RPC框架,最近接触到的有gRPC,Thrift,自己也用Netty实现了一下简单的调用,不过那充其量只是一个玩具。这次打算把之前自己项目里的HTTP调用换掉,换成Thrift。这里来记录一下Thrift的学习。

    简介

    Thrift是Facebook开发的,现在已经是APACHE的开源项目了,它是一种接口描述语言和二进制通讯协议,用来定义和创建跨语言的服务,也正因为跨语言这个特点,用它来做开发的时候,需要使用它的一个自带工具来生成自己需要的代码。这和以往的非跨语言的RPC框架还是有一点不同的。

    Thrift是一套完整的栈,什么是完整的栈了,其实只要用过了就知道,它里面有TProtocol,TTransport这样的类,也就是可以设置传输协议而不需要重新进行编码。除此之外,在服务端,也做了阻塞,非阻塞以及多线程这样的机制。

    • Thrift支持的通讯协议
      • TBinaryProtocol 一种简单的二进制格式,之所以简单,是因为并没有对空间效率进行优化,但比文本处理起来更快,不易调试。
      • TCompactProtocol 顾名思义,是一种带压缩的更紧凑的二进制格式,处理起来更高效。
      • TDebugProtocol 一种人类可以理解的文本协议,可以用来协助调试。
      • TDenseProtocol 和第二个相似,将传输数据的原信息剥离。
      • TJSONProtocol 使用JSON格式。
      • TSimpleJSONProtocol 一种只写协议,不能被Thrift解析,适合用脚本语言来解析。
    • Thrift支持的传输协议
      • TFileTransport 该协议会写文件。
      • TFramedTransport 当使用一个非阻塞服务器的时候,需要使用这个协议,顾名思义按帧来发送数据。
      • TMemoryTransport 使用阻塞的套接字来进行传输。
      • TZlibTransport 用zlib进行压缩,用于连接另一个传输协议。
    • Thrift支持的服务模型
      • TNonblockingServer 一个多线程的服务器,顾名思义是非阻塞的,上面的TFRamedTransport必须和这个模型一块使用。
      • TSimpleServer 一个单线程的服务器,是阻塞的。
      • TThreadPoolServer 阻塞的多线程服务器
      • THsHaServer 把读写任务放到线程池处理,半同步半异步的处理模式,半同步是在处理IO时间上,比如Socket的读写,连接,半异步是用于Handler对RPC的同步处理。

    demo

    定义接口和实体

    需要用Thrift的IDL来定义一个简单的实体,代表一个学生,此次demo主要就是实现学生信息的查和保存,信息都存在HashMap里。

    //指明代码生成之后所处的文件路径,相当于java里的package
    namespace java cn.izzer.thriftdemo.common
    
    //将shrift的数据类型格式转换为java习惯的格式
    typedef i16 short
    typedef i32 int
    typedef i64 long
    typedef string String
    typedef bool boolean
    
    //定义对象
    struct Student {
        1:optional String name,
        2:optional int age,
        3:optional String address
    }
    
    //定义异常
    exception DataException {
        //optional 可选 非必传
        1:optional int code,
        2:optional String message,
        3:optional String dateTime
    }
    
    //定义后台业务接口
    service StudentService {
        //根据名称获取学生信息 返回一个学生对象  抛出DataException异常
        //required 必传项
        Student getStudentByName(1:required String name) throws (1:DataException dataException),
    
        //保存一个学生信息 无返回 抛出DataException异常
        void save(1:required Student student) throws (1:DataException dataException)
    }
    

    用Thrift工具生成代码

    (https://mirror.bit.edu.cn/apache/thrift/0.13.0/thrift-0.13.0.exe

    使用命令:

    thrift-0.13.0.exe --gen java <path>/Student.thrift
    

    在当前路径下就会产生thrift生成的代码。这个包作为公共模块即可。

    服务端

    实现上面的生成的StudentService.Iface的接口,初始化的方法里放了两个对象来做测试。

    @Service
    public class StudentServiceImpl implements cn.izzer.thriftdemo.common.StudentService.Iface {
    
        private HashMap<String,Student> studentMap;
    
        @PostConstruct
        public void init(){
            studentMap = new HashMap<>();
            Student s1 = new Student();
            s1.setName("thyin");
            s1.setAddress("Shanghai");
            s1.setAge(22);
            Student s2 = new Student();
            s2.setName("abc");
            s2.setAddress("Shanghai");
            s2.setAge(22);
            studentMap.put(s1.getName(),s1);
            studentMap.put(s2.getName(),s2);
        }
    
    
        @Override
        public Student getStudentByName(String name) {
            return studentMap.get(name);
        }
    
        @Override
        public void save(Student student) throws DataException, TException {
            studentMap.put(student.getName(),student);
        }
    }
    
    

    然后就是Thrift的服务编写。

    @Component
    public class ThriftServer {
    
        @Value("${thrift.port}")
        private Integer port;
    
        @Value("${thrift.min-thread-pool}")
        private Integer minThreadPool;
    
        @Value("${thrift.max-thread-pool}")
        private Integer maxThreadPool;
    
        @Autowired
        private StudentServiceImpl studentService;
    
        public void start(){
            try {
                TNonblockingServerSocket serverSocket = new TNonblockingServerSocket(port);
                THsHaServer.Args args = new THsHaServer.Args(serverSocket)
                        .minWorkerThreads(minThreadPool)
                        .maxWorkerThreads(maxThreadPool);
                StudentService.Processor<StudentServiceImpl> processor = new StudentService.Processor<StudentServiceImpl>(studentService);
                //二进制压缩协议
                args.protocolFactory(new TCompactProtocol.Factory());
                args.transportFactory(new TFramedTransport.Factory());
                args.processorFactory(new TProcessorFactory(processor));
                TServer server = new THsHaServer(args);
                System.out.println("Thrift Server Started at port:"+port);
                server.serve();
            }catch (TTransportException ex){
                ex.printStackTrace();
            }
        }
    }
    

    端口和IP设置还是不能忘。

    thrift:
      port: 7001
      min-thread-pool: 100
      max-thread-pool: 200
    

    在Springboot启动类里启动Thrift服务。

    SpringBootApplication
    public class ServerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ServerApplication.class, args);
        }
    
        @Bean(initMethod = "start")
        public ThriftServer init(){
            ThriftServer server = new ThriftServer();
            return server;
        }
    }
    

    客户端

    定义StudentService接口,然后实现。

    public interface StudentService {
        Student getStudentByName(String name);
        void save(Student student);
    }
    @Service
    public class StudentServiceImpl implements StudentService {
    
        @Autowired
        private ThriftClient client;
    
        @Override
        public Student getStudentByName(String name) {
            try {
                client.open();
                Student student = client.getService().getStudentByName(name);
                System.out.println("获取用户成功,用户信息为:"+student);
                return student;
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                client.close();
            }
            return null;
        }
    
        @Override
        public void save(Student student) {
            try {
                client.open();
                client.getService().save(student);
                System.out.println("客户端保存用户信息成功,信息为:"+student);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                client.close();
            }
        }
    }
    

    定义客户端操作的API。

    public class ThriftClient {
    
        private Integer port;
    
        private String host;
    
        private TTransport transport;
    
        private TProtocol protocol;
    
        private StudentService.Client client;
    
        private void initClient(){
            transport = new TFastFramedTransport(new TSocket(host,port),1000);
            protocol = new TCompactProtocol(transport);
            client = new StudentService.Client(protocol);
        }
    
        public void setPort(Integer port) {
            this.port = port;
        }
    
        public void setHost(String host) {
            this.host = host;
        }
    
        public StudentService.Client getService(){
            return client;
        }
    
        public void open() throws TTransportException{
            if(null!=transport&&!transport.isOpen()){
                transport.open();
            }
        }
    
        public void close(){
            if(null!=transport&&!transport.isOpen()){
                transport.close();
            }
        }
    }
    

    然后用Spring来进行管理。

    @Configuration
    public class ThrifClientConfig {
    
        @Value("${thrift.port}")
        private Integer port;
    
        @Value("${thrift.host}")
        private String host;
    
        @Bean(initMethod = "initClient")
        public ThriftClient init(){
            ThriftClient client = new ThriftClient();
            client.setHost(host);
            client.setPort(port);
            return client;
        }
    }
    

    thrift的端口和ip设置,这里我使用HTTP接口去调用客户端,所以多一个HTTP服务的端口

    thrift:
      port: 7001
      host: localhost
    server:
      port: 8001
    

    最后就是业务类的编写了,也没啥太复杂的。

    @Controller
    @RequestMapping("/rpc")
    public class StudentController {
    
        @Autowired
        private StudentService studentService;
    
        @RequestMapping("/get/{name}")
        @ResponseBody
        public Student getStudent(@PathVariable("name")String name){
            return studentService.getStudentByName(name);
        }
    
        @RequestMapping("/save/{name}/{age}")
        @ResponseBody
        public String saveStudent(@PathVariable("name")String name,@PathVariable("age")Integer age){
            Student student = new Student();
            student.setName(name);
            student.setAge(age);
            studentService.save(student);
            return "Success";
        }
    
    }
    

    测试

    服务端客户端都启动之后,就可以通过POSTMAN或者浏览器来进行调用了。

    先调用获取信息接口。

    avatar

    可以看到请求正常执行。那再来试试保存。

    avatar

    再来获取被保存的人的信息。

    avatar

  • 相关阅读:
    Android 异步请求通用类
    Android 异步下载
    Eclipse 使用 VS Emulator for android 调试环境配置 步骤
    android ListView 可缩放,支持左右上下手势
    安卓中自定义控件引用
    java中的可释放资源定义,类似c#中的using
    java 实现自定义事件
    c# android 全局捕获未处理异常
    java android 捕获未处理异常
    java 中异常处理示例并捕获完整异常内容
  • 原文地址:https://www.cnblogs.com/Yintianhao/p/14260089.html
Copyright © 2011-2022 走看看