1.OC底层-alloc探索
648 2023-04-03 03:04:09
最近看到一些招聘说明有关于thrift
的要求:
作为一个面向简历编程的程序员,多年来使用的都是springmvc
下的http
调用,不免对这个thrift
有了一丝好奇,于是thrift
的学习就提上了日程。
百度百科的定义如下:
Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务。它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为“大规模跨语言服务开发”而开发的。
关于那些大而泛的概念,本文就不多介绍了,直接进入thfrit的学习吧。
Thrift 脚本可定义的数据类型包括以下几种类型:
基本类型:
结构体类型:
容器类型:
异常类型:
服务类型:
Thrift可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为**文本(text)和二进制(binary)**传输协议。为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这需要根据项目/产品中的实际需求。常用协议有以下几种:
TBinaryProtocol
:二进制编码格式进行数据传输TCompactProtocol
:高效率的、密集的二进制编码格式进行数据传输TJSONProtocol
: 使用JSON文本的数据编码协议进行数据传输TSimpleJSONProtocol
:只提供JSON只写的协议,适用于通过脚本语言解析常用的传输层有以下几种:
TSocket
:使用阻塞式I/O进行传输,是最常见的模式TNonblockingTransport
:使用非阻塞方式,用于构建异步客户端TFramedTransport
:使用非阻塞方式,按块的大小进行传输,类似于Java中的NIOTSimpleServer
:单线程服务器端,使用标准的阻塞式I/OTThreadPoolServer
:多线程服务器端,使用标准的阻塞式I/OTNonblockingServer
:单线程服务器端,使用非阻塞式I/OTHsHaServer
:半同步半异步服务器端,基于非阻塞式IO读写和多线程工作任务处理TThreadedSelectorServer
:多线程选择器服务器端,对THsHaServer
在异步IO模型上进行增强简单了解完上面的概念后,接下来我们将通过一个实例来演示thrift的使用。
使用thrift前,需要安装thrift命令行工具,该工具用来将编写的thfirt
文件编译成指定的源码文件。
在 Mac 上用如下命令安装:
$ brew install thrift
安装成功后,查看版本:
$ thrift -vThrift version 0.14.1
这里我们编写两个服务。
namespace java com.attempt.thrift02.gen.serviceservice HelloService { string hello(1: string text);}
这里定义了一个HelloService
,里面仅有一个hello(String)
方法
namespace java com.attempt.thrift02.gen.vostruct QueryResult { 1: required i32 code; // 请求的code 必选 2: optional string msg; // 请求返回信息,可选}
这里定义了一个对象实体,里面有两个属性:code
与msg
,用来接收返回参数。
namespace java com.attempt.thrift02.gen.service// 引入另一文件include "QueryResult.thrift"service QueryService { // QueryResult在另一个文件中,使用方式为 文件名.对象名 QueryResult.QueryResult query(1: i32 code);}
这是另一个Service
:QueryService
,该service
中只有一个方法:query(...)
,该方法返回对象为QueryResult
,由于该对象在QueryResult.thrift
文件中,因此需要使用include
命令引入,并且在引用时,需要使用文件名.对象名
的方式。
在命令行中执行如下命令(指定文件的源码文件为java):
$ thrift -r --gen java QueryService.thrift$ thrift -r --gen java HelloService.thrift
在QueryService.thrift
中引用了QueryResult.thrift
文件,因此只需生成QueryService.thrift
,QueryResult.thrift
就可自动生成。
生成的文件如下:
我们将上一步生成的java代码复制到src/main/java
下,目录结构如下:
除了复制生成的文件外,还需要在项目中引入thrift 的 jar 包,jar包的GAV如下:
<dependency> <groupId>org.apache.thrift</groupId> <artifactId>libthrift</artifactId> <version>0.14.1</version></dependency>
package com.attempt.thrift02.serviceImpl;import com.attempt.thrift02.gen.service.HelloService;import org.apache.thrift.TException;/** * {这里添加描述} * * @author chengyan * @date 2021-05-06 11:50 上午 */public class HelloServiceImpl implements HelloService.Iface { @Override public String hello(String text) throws TException { return "hello, " + text + " !"; }}
HelloServiceImpl
是我们自己编写的类,它实现了HelloService.Iface
,在对应的方法中编写我们自己的业务逻辑,而HelloService.Iface
是由thrift
生成的类。
package com.attempt.thrift02.serviceImpl;import com.attempt.thrift02.gen.service.QueryService;import com.attempt.thrift02.gen.vo.QueryResult;import org.apache.thrift.TException;/** * {这里添加描述} * * @author chengyan * @date 2021-05-06 11:51 上午 */public class QueryServiceImpl implements QueryService.Iface { @Override public QueryResult query(int code) throws TException { QueryResult result = new QueryResult(); if (code == 1) { result.code = 1; result.msg = "success"; } else { result.code = 0; result.msg = "fail"; } return result; }}
同样地,QueryServiceImpl
是我们自己编写的类,它实现了QueryService.Iface
,在对应的方法中编写我们自己的业务逻辑,而QueryService.Iface
是由thrift
生成的类。
接下来就是实现各服务对应的客户端了。
server:
package com.attempt.thrift02.server;import com.attempt.thrift02.gen.service.HelloService;import com.attempt.thrift02.serviceImpl.HelloServiceImpl;import org.apache.thrift.server.TServer;import org.apache.thrift.server.TSimpleServer;import org.apache.thrift.transport.TServerSocket;import org.apache.thrift.transport.TServerTransport;/** * {这里添加描述} * * @author chengyan * @date 2021-05-06 11:53 上午 */public class HelloServer { private static final int SERVER_PORT = 8090; public static void main(String[] args) { try { HelloService.Processor processor = new HelloService.Processor<>( new HelloServiceImpl()); TServerTransport transport = new TServerSocket(SERVER_PORT); TServer server = new TSimpleServer(new TServer.Args(transport) .processor(processor)); System.out.println("Starting the simple server..."); server.serve(); } catch (Exception e) { e.printStackTrace(); } }}
client:
package com.attempt.thrift02.client;import com.attempt.thrift02.gen.service.HelloService;import org.apache.thrift.protocol.TBinaryProtocol;import org.apache.thrift.protocol.TProtocol;import org.apache.thrift.transport.TSocket;import org.apache.thrift.transport.TTransport;/** * {这里添加描述} * * @author chengyan * @date 2021-05-06 11:52 上午 */public class HelloClient { private static final int SERVER_PORT = 8090; public static void main(String[] args) { TTransport transport = null; try { transport = new TSocket("localhost", SERVER_PORT); transport.open(); TProtocol protocol = new TBinaryProtocol(transport); HelloService.Client client = new HelloService.Client(protocol); String result = client.hello("thrift world"); System.out.println("result=" + result); } catch (Exception e) { e.printStackTrace(); } finally { if(null != transport) { transport.close(); } } }}
启动 server:
运行 client:
server:
package com.attempt.thrift02.server;import com.attempt.thrift02.gen.service.QueryService;import com.attempt.thrift02.serviceImpl.QueryServiceImpl;import org.apache.thrift.server.TServer;import org.apache.thrift.server.TSimpleServer;import org.apache.thrift.transport.TServerSocket;import org.apache.thrift.transport.TServerTransport;/** * {这里添加描述} * * @author chengyan * @date 2021-05-06 11:53 上午 */public class QueryServer { private static final int SERVER_PORT = 8091; public static void main(String[] args) { try { QueryService.Processor processor = new QueryService.Processor<>( new QueryServiceImpl()); TServerTransport transport = new TServerSocket(SERVER_PORT); TServer server = new TSimpleServer(new TServer.Args(transport) .processor(processor)); System.out.println("Starting the simple server..."); server.serve(); } catch (Exception e) { e.printStackTrace(); } }}
client:
package com.attempt.thrift02.client;import com.attempt.thrift02.gen.service.QueryService;import com.attempt.thrift02.gen.vo.QueryResult;import org.apache.thrift.protocol.TBinaryProtocol;import org.apache.thrift.protocol.TProtocol;import org.apache.thrift.transport.TSocket;import org.apache.thrift.transport.TTransport;/** * {这里添加描述} * * @author chengyan * @date 2021-05-06 11:53 上午 */public class QueryClient { private static final int SERVER_PORT = 8091; public static void main(String[] args) { TTransport transport = null; try { transport = new TSocket("localhost", SERVER_PORT); transport.open(); TProtocol protocol = new TBinaryProtocol(transport); QueryService.Client client = new QueryService.Client(protocol); QueryResult result = client.query(1); System.out.println("query result=" + result); } catch (Exception e) { e.printStackTrace(); } finally { if(null != transport) { transport.close(); } } }}
启动 server:
运行 client:
以上同一个项目中有两个thrift服务,我们是开两个端口对外提供服务,在实际生产环境,thrift服务可能会更多,这种时候我们能不能只开一个jvm进程来处理呢?thrift 提供了TMultiplexedProcessor
来解决这个问题,使用方法如下:
server:
package com.attempt.thrift02.server;import com.attempt.thrift02.gen.service.HelloService;import com.attempt.thrift02.gen.service.QueryService;import com.attempt.thrift02.serviceImpl.HelloServiceImpl;import com.attempt.thrift02.serviceImpl.QueryServiceImpl;import org.apache.thrift.TMultiplexedProcessor;import org.apache.thrift.TProcessor;import org.apache.thrift.server.THsHaServer;import org.apache.thrift.server.TNonblockingServer;import org.apache.thrift.server.TServer;import org.apache.thrift.server.TSimpleServer;import org.apache.thrift.server.TThreadPoolServer;import org.apache.thrift.transport.TNonblockingServerSocket;import org.apache.thrift.transport.TServerSocket;import org.apache.thrift.transport.TServerTransport;import org.apache.thrift.transport.TTransportException;/** * {这里添加描述} * * @author chengyan * @date 2021-05-06 12:45 下午 */public class MultipleServer { private static final int SERVER_PORT = 8093; public static void main(String[] args) { try { TMultiplexedProcessor processor = new TMultiplexedProcessor(); // 注册 helloService processor.registerProcessor("helloService", new HelloService.Processor<>(new HelloServiceImpl())); // 注册 queryService processor.registerProcessor("queryService", new QueryService.Processor<>(new QueryServiceImpl())); TServer server = getSimpleServer(SERVER_PORT, processor); System.out.println("Starting the simple server..."); server.serve(); } catch (Exception e) { e.printStackTrace(); } } /** 简单的单线程服务模型,一般用于测试 */ public static TServer getSimpleServer(int port, TProcessor processor) throws TTransportException { TServerTransport transport = new TServerSocket(port); TServer server = new TSimpleServer(new TServer.Args(transport) .processor(processor)); return server; }}
client:
服务端使用了TMultiplexedProcessor
,客户端也需要使用:
package com.attempt.thrift02.client;import com.attempt.thrift02.gen.service.HelloService;import com.attempt.thrift02.gen.service.QueryService;import org.apache.thrift.TException;import org.apache.thrift.protocol.TBinaryProtocol;import org.apache.thrift.protocol.TMultiplexedProtocol;import org.apache.thrift.protocol.TProtocol;import org.apache.thrift.transport.TSocket;import org.apache.thrift.transport.TTransport;/** * {这里添加描述} * * @author chengyan * @date 2021-05-06 12:46 下午 */public class MultipleClient { private static final int SERVER_PORT = 8093; public static void main(String[] args) { TTransport transport = null; try { transport = new TSocket("localhost", SERVER_PORT); transport.open(); TProtocol protocol = new TBinaryProtocol(transport); // helloService TMultiplexedProtocol helloService = new TMultiplexedProtocol( protocol, "helloService"); HelloService.Client client = new HelloService.Client(helloService); System.out.println(client.hello("thrift world")); // queryService TMultiplexedProtocol helloProtocol = new TMultiplexedProtocol( protocol, "queryService"); QueryService.Client queryClient = new QueryService.Client(helloProtocol); System.out.println(queryClient.query(1)); } catch (TException e) { e.printStackTrace(); } finally { transport.close(); } }}
启动 server:
启动 client:
关于 thrift 的使用介绍就到这里了,下篇我们来分析源码的实现。
参考:
限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。
本文首发于微信公众号 Java技术探秘,如果您喜欢本文,欢迎关注该公众号,让我们一起在技术的世界里探秘吧!