如何在centos7桌面版安装 Ubuntu 中安装 Unity Tweak Tool

这是一个简单小巧的Java RPC框架,适用于Java平台内、为系统之间的交互提供了、高性能、低延迟的方案。适合在集群数量偏少的情况下使用(50台以下集群环境)。当然、它也可以在大型集群环境下使用,由于未引入Zookeeper支持,所以它在大型集群环境下不够成熟,例如服务发现以及监控都没有做,但是作为RPC框架来用已经足够,至少比使用rest、webservice等性能高得多,也比直接使用thrift、avro等方便的多。
为了让它保持小巧、简单,所以不打算引入Zookeeper支持。我认为50台server组成的集群,已经可以满足绝大部分需求,所以简单、小巧、高性能才是最重要的。如果你认为简单不重要,或者成熟度是最重要的,那么淘宝的Dubbo在等着你。
背景以及需求
心血来潮,在公司很无聊,这才是主要原因。 其次是我们要对系统模块进行拆分,从原系统移出,那么就需要寻找一个远程调用工具。1、基于HTTP(rest、webservice等) 主要性能很差,其次是难以支持高并发,而且组装HTTP请求也比较麻烦,难以形成规范,所以此项被pass。2、基于thrift,性能虽好,但是使用起来非常麻烦,需要频繁的生成代码,而且对Client开发者要求较高,需要自己写连接池,长连接无法使用LVS还要写负载均衡和容错。而且thrift的服务端需要将业务逻辑全部放在一个接口(一个接口就需要发布一个服务,占用一个线程池),这将是个很恶心的事,所以也被pass。
正因为以上两点,所以我打算自己写一个框架。要求是:简单小巧、依赖少、高性能、高并发、支持集群、负载均衡、容错。无学习成本,源码简单可定制修改,我认为这些才是最主要的。
如果你也在寻找这样一个框架,那么很值得看一下。
我做完了这个框架,没多久便发现了Dubbo,在看了Dubbo的设计以后,惊喜的发现此框架和Dubbo的核心功能几乎一样。
说起来很简单,就是框架会在Client端代理一个接口,调用这个接口的方法,将发送远程请求,参数序列化传递到远程Server端,Server端处理业务逻辑,完成后、将返回结果序列化给Client端,作为被调用方法的返回值。因此整个过程对用户是透明的。
项目底层使用thrift,这是为了使用thrift的各种Server模型,以此来支持高并发,低延迟。没有使用Netty,原因是Netty较重,延迟要比thrift稍高一些,Netty适合处理高吞吐的异步IO,对于RPC的同步调用没有好处。Netty并不适合。您不用担心thrift性能有问题,也不用担心thrift框架太重,我做过测试,性能和直接使用soket通信几乎不相上下,thrift框架的代码特别少,仅仅是对soket的简单封装,框架非常轻便。
序列化工具使用kryo,这也是性能的关键,您也可以自己去查一下kryo相关资料,这里就不说他了,序列化结果很小,速度很快就是了。
框架依赖 thrift、kryo、commons-pool、spring-beans(其中kryo可以自行替换为您喜欢的序列化工具)
集群支持随机负载均衡,轮询负载均衡(您也可以自己写负载均衡实现),优雅停机(kill pid不要加-9),容错(集群某几台挂掉并不影响服务)
线程模型 以ThriftThreadPoolServer、ThriftTThreadedSelectorServer 两种为主,具体细节参考thrift(您也可以自己实现Server)
服务端:/appchina-rpc-test/src/main/java/main/Server.java
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("/application-server.xml");ctx.registerShutdownHook();ctx.start();
/appchina-rpc-test/src/main/resources/application-server.xml
&bean id="servicePublisher" class="com.appchina.rpc.thrift.remote.base.ThriftServicePublisher"&
&property name="definitions"&
&!-- 需要发布的服务列表 --&
&!-- ServiceDefinition 定义了服务的信息 --&
&bean class="com.appchina.rpc.remote.ServiceDefinition"&
&!-- 可选,用于区分不同实现类 --&
&property name="serviceName" value="addServiceImpl"&&/property&
&!-- 发布服务的接口 --&
&property name="interfaceClass" value="com.appchina.rpc.test.api.AddService"&&/property&
&!-- 发布服务实现类 --&
&property name="implInstance"&
&bean class="com.appchina.rpc.test.impl.AddServiceImpl" /&
&/property&
&/property&
&bean class="com.appchina.rpc.thrift.server.ThriftThreadPoolServer"&
&property name="processor" ref="servicePublisher"&&/property&
&property name="port" value="9090"&&/property&
&property name="minWorkerThreads" value="100"&&/property&
&property name="workerThreads" value="500"&&/property&
&property name="security" value="true"&&/property&
&property name="stopTimeoutVal" value="3000"&&/property&
&property name="clientTimeout" value="10000"&&/property&
&property name="allowedFromTokens"&
&entry key="DONGJIAN" value="DSIksduiKIOYUIOkYIOhIOUIOhjklYUI"&&/entry&
&/property&
客户端:/appchina-rpc-test/src/main/java/main/Client.java
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("/application-client.xml");AddService addService = (AddService) ctx.getBean("addService");addService.add(100);
/appchina-rpc-test/src/main/resources/application-client.xml
&bean id="factoryProvider" class="com.appchina.rpc.thrift.cluster.ThriftClientFactoryProvider"&
&!-- server列表 --&
&property name="hostPorts"&
&value&127.0.0.1:9090&/value&
&value&127.0.0.1:9191&/value&
&/property&
&!-- 请求超时,根据业务设置 --&
&property name="timeout" value="60000"&&/property&
&!-- 连接超时,超过这个时间无法创建连接的server将被设置为暂时无效,恢复时设置为有效
&property name="connectionTimeout" value="10000"&&/property&
&!-- 如果服务端是NIO,需要启用此配置 --&
&property name="framed" value="false"&&/property&
&!-- 安全选项 --&
&property name="from" value="DONGJIAN"&&/property&
&property name="token" value="DSIksduiKIOYUIOkYIOhIOUIOhjklYUI"&&/property&
&!-- 关于集群的相关配置 --&
&bean id="client" class="com.appchina.rpc.base.cluster.client.DistributeClient"&
&property name="factoryProvider" ref="factoryProvider"&&/property&
&!-- 负载均衡实现类 --&
&property name="loadBalance"&
&bean class="com.appchina.rpc.base.cluster.RoundrobinLoadBalance"&&/bean&
&/property&
&!-- 心跳频率,用于检测Server可用性的间隔 --&
&property name="heartbeat" value="1000"&&/property&
&!-- 处理心跳的最大线程数,一般1个线程足够 --&
&property name="maxHeartbeatThread" value="1"&&/property&
&!-- 连接池耗完直接重试,重试其他池子的次数,因此、maxWait的时间可能叠加 --&
&property name="retry" value="3"&&/property&
&!-- 由于被借走时间不一样,可能导致单个池子不够用,建议这个值大一些,可以通过maxIdle来限制长连接数量 --&
&property name="maxActive" value="300"&&/property&
&!-- 最大空闲,当池内连接大于maxIdle,每次returnObject都会销毁连接,maxIdle保证了长连接的数量 --&
&property name="maxIdle" value="200"&&/property&
&!-- 最小空闲 --&
&property name="minIdle" value="5"&&/property&
&!-- 等待连接池的时间 --&
&property name="maxWait" value="1000"&&/property&
&!-- 连接使用多久之后被销毁 --&
&property name="maxKeepMillis" value="-1"&&/property&
&!-- 连接使用多少次之后被销毁 --&
&property name="maxSendCount" value="-1"&&/property&
&bean id="addService" class="com.appchina.rpc.thrift.remote.base.ThriftRemoteProxyFactory"&
&property name="serviceName" value="addService"&&/property&
&property name="proxyInterface" value="com.appchina.rpc.test.api.AddService"&&/property&
&property name="client" ref="client"&&/property&
git:/dongjian1029/java-rpc-thrift.git
阅读(...) 评论() &1716人阅读
对Thrift的一点点理解
这是一篇学习Thrift的笔记,包含了这样几点内容:
简单介绍Thrift
怎样使用Thrift
Thrift整体架构
Thrift中的知识点
  struct可以设置默认值
  thrift中的序列化机制
  thrift中的版本控制
简单介绍Thrift
  它是一款RPC通信框架,采用C/S架构,且拥有高效的序列化机制。要使用Thrift,首先我们需要在远端服务器上开启Thrift服务,之后,服务器端进程保持睡眠状态,直到客户端代码的调用。
  Thrift应用广泛的一个主要原因是它支持多种主流的语言,且使用它的用户不需要关注服务器和客户端是怎样实现通信,怎样实现序列化的,只需要去考虑怎样实现自己需要的业务逻辑。
  Thrift使用接口语言定义数据结构和服务,包含了最常用的数据类型,并一一对应各种语言的基本类型,还可以定义枚举和异常等等。
怎样使用Thrift
  Thrift把它定义的相当简洁,以致于我们的使用过程也是异常的方便,简单来说,使用Thrift的过程只是需要以下的四个步骤:
  1. 设计需要交互的数据格式(struct、enum等等)和具体的服务(service),定义thrift接口描述文件,也就是后缀名是 .thrift
  2. 利用thrift工具(我使用的是比较老的版本0.5.0),根据之前定义的接口文件生成目标语言文件(在这次的笔记中客户端代码和服务器代码都是使用java语言)
  3. 实现服务(service)代码,并把实现的业务逻辑设定为thrift服务器的处理层,选择端口,服务器启动监听,等待客户端的连接请求
  4. 客户端使用相同的端口连接服务器请求服务
下面简单的介绍下thrift接口描述语言(IDL)的类型:
IDL包含基础类型、结构、容器、异常和服务这样几种类型:
  基础类型 : 包括了 bool,byte、i16,i32,i64,double,string,每一种都对应各种语言的基础类型
  结构 : 在thrift中定义为struct,它类似于C语言中的结构体,是基础类型的集合体,每一个结构都会生成一个单独的类,在java中类似于pojo
  容器 : thrift中定义了常用的三种容器 – list,set,map,在Java中各自的对应实现是 ArrayList、HashSet、HashMap,其中模板类型可以是基础类型或者结构类型
  异常 : 异常的定义类似于struct,只是换成了exception
  服务 : 服务类似于java中的接口,需要对服务中的每一个方法签名定义返回类型、参数声明、抛出的异常,对于方法抛出的异常,除了自己声明的之外,每个方法还都会抛出TException,对于返回值是void类型的方法,我们可以在方法签名的前面加上oneway标识符,将这个方法标记为异步的模式,即调用之后会立即返回
  下面,为了更好的理解怎样使用thrift,以及怎样使用IDL中的类型,我将举一个例子,当然,这个例子只是为了演示过程,并没有过多的设计,可能会存在一些并不实用的逻辑。
  怎样开始写这个例子呢?对呀,就按照之前介绍的Thrift过程的四个步骤就可以了:
定义接口描述文件(.thrift)
  qinyi_student_model.thrift
    定义学生信息和学校信息的数据结构
namespace java com.qinyi.thrift_study.thrift_example
enum Sex {
struct StudentInfo {
1: required string
2: required S
3: required i32
4: optional list&string&
5: required map&string, i64&
struct School {
1: required string
2: required list&StudentInfo&
3: optional string
  可以看到,我们使用namespace定义文件的命名空间,由于目标代码是java语言,所以namespace java之后的声明代表的就是包名,struct结构中每一个属性前都有一个数字id标识,这个一旦定义了,最好不要去更改,具体的原因下文会有具体说明,属性类型前有required/optional声明,代表这个属性是必须要设置的或者可以选择不设置,如果这个属性被声明为required,但是在代码中没有set,thrift会认为这是一个异常,当然,我们可以对属性设置默认值,就是声明的时候赋值就可以了。文件开始的部分使用的java风格的注释,这也是可选的,thrift支持c,c++,shell,java风格的注释,怎样注释根据个人习惯就好。
    qinyi_student_exception.thrift
     定义异常
* qinyi student thrift exception
namespace java com.qinyi.thrift_study.thrift_example
exception StudentException {
1: required i64 errorC
2: require
3: optional string causeI
  我们可以看到,异常的定义和上面文件中的struct是极为相似的。
    qinyi_student_service.thrift
     定义服务
namespace java com.qinyi.thrift_study.thrift_example
include "qinyi_student_model.thrift"
include "qinyi_student_exception.thrift"
service StudentService {
bool addStudentToSchool(1: qinyi_student_model.StudentInfo student) throws (1: qinyi_student_exception.StudentException ex);
list&qinyi_student_model.StudentInfo& getStudentInfoByName(1: string name) throws (1: qinyi_student_exception.StudentException ex);
void printStudentInfo(1: qinyi_student_model.StudentInfo student) throws (1: qinyi_student_exception.StudentException ex);
void printStudentsInfo(1: list&qinyi_student_model.StudentInfo& students) throws (1: qinyi_student_exception.StudentException ex);
  如果你熟悉C语言的话,对include肯定不会陌生,thrift中也可以这样引用其他的thrift文件,而且include之后需要是双引号,在文件中对于引用其他thrift文件的字段也都要使用全名。
使用thrift工具利用IDL生成目标代码
  正如之前所述,这是thrift过程的第二个步骤,这里,为了便于操作,我们写一个shell脚本吧:
#!/bin/bash
thrift_home="{your_thrift_home}/thrift_version/bin"
thrift_file="{your_thrift_idl_files}"
${thrift_home}/thrift --gen java ${thrift_file}/qinyi_student_exception.thrift
${thrift_home}/thrift --gen java ${thrift_file}/qinyi_student_model.thrift
${thrift_home}/thrift --gen java ${thrift_file}/qinyi_student_service.thrift
    由于目标语言是java,且在thrift脚本中定义了命名空间,所以,运行上面的脚本之后,生成的目录结构会是这样:
  /gen-java/com/qinyi/thrift_study/thrift_example
实现服务业务逻辑并开始服务监听
  接下来的第三步是实现接口中的业务逻辑,并等待客户端调用这些业务逻辑,比较简单,业务逻辑实现文件是 : StudentServiceImpl.java
* Created by qinyi on 10/2/15.
public class StudentServiceImpl implements StudentService.Iface {
public boolean addStudentToSchool(StudentInfo student) throws StudentException, TException {
if (null == student) {
throw new StudentException().setErrorCode(-1)
.setDescription("addStudentToSchool(StudentInfo student) error")
.setCauseInfo("student is null");
List&StudentInfo& students = SchoolMock.getInstance().getStudents();
students.add(student);
SchoolMock.getInstance().setStudents(students);
return true;
public List&StudentInfo& getStudentInfoByName(String name) throws StudentException, TException {
if (null == name) {
throw new StudentException().setErrorCode(-1)
.setDescription("getStudentInfoByName(String name) error")
.setCauseInfo("name is null");
List&StudentInfo& students = SchoolMock.getInstance().getStudents();
List&StudentInfo& results = new ArrayList&StudentInfo&();
for (StudentInfo student : students) {
if (student.getName().equals(name)) {
results.add(student);
public void printStudentInfo(StudentInfo student) throws StudentException, TException {
if (null == student) {
throw new StudentException().setErrorCode(-1)
.setDescription("printStudentInfo(StudentInfo student) error")
.setCauseInfo("student is null");
StringBuilder builder = new StringBuilder();
builder.append("name : ").append(student.getName()).append("\n");
if (student.getSex().getValue() == 1) {
builder.append("sex : boy").append("\n");
builder.append("sex : girl").append("\n");
builder.append("age : ").append(student.getAge()).append("\n");
if (student.isSetHobby()) {
for (String hobby : student.getHobby()) {
builder.append("hobby : ").append(hobby).append("\n");
builder.append("id : ").append(student.getNumber().get(student.getName())).append("\n");
System.out.println(builder.toString());
public void printStudentsInfo(List&StudentInfo& students) throws StudentException, TException {
if (null == students) {
throw new StudentException().setErrorCode(-1)
.setDescription("printStudentsInfo(List&StudentInfo& students) error")
.setCauseInfo("students is null");
for (StudentInfo student : students) {
printStudentInfo(student);
  正如之前所述,所有的服务方法除了抛出我们自定义的异常之外,还都会抛出TException这个检查异常,其中这里使用了一个SchoolMock的对象可以获取到一个School对象,来完成模拟的业务逻辑,这里也给出实现代码:
  SchoolMock.java
* Created by qinyi on 10/2/15.
public class SchoolMock {
private static S
private SchoolMock() {
public static synchronized School getInstance() {
if (null == school) {
school = new School();
school.setName("school");
school.setDescription("this is just a mock school");
school.setStudents(new ArrayList&StudentInfo&());
  接下来,我们服务器端需要做最后一步工作,开启服务器端的监听,实现文件是 : StudentThriftServer.java,由于代码中已经做了很多注释,所以,不去过多的解释:
* Created by qinyi on 10/2/15.
public class StudentThriftServer {
public static final int SERVER_PORT = 9527;
public static void main(String[] args) throws TException{
serverTransport : 设置服务器的端口
tProcessor : 关联处理器的服务实现类
server : 设定服务器 (TSimpleServer -
单线程服务器端使用标准的堵塞式I/O,只适合测试开发使用)
server.serve() : 开启服务,一般是处于睡眠状态,直到客户端的请求到来
这里开启 Server 服务使用的方法是旧的API接口,这里用的 thrift 是0.5.0的
TServerSocket serverTransport = new TServerSocket(SERVER_PORT);
TProcessor tProcessor = new StudentService.Processor(new StudentServiceImpl());
单线程服务器端使用标准的堵塞式I/O
TServer server = new TSimpleServer(tProcessor, serverTransport);
System.out.println("Start server on port 9527...");
server.serve();
thrift0.6.1以后的版本(如果我没查错的话)中,Tserver抽象类中定义了一个内部静态类 Args,用户串联软件栈(传输层、协议层、处理层)
public static class Args extends AbstractServerArgs&Args& {
*   public Args(TServerTransport transport) {
*     super(transport);
新的接口中开启 thrift 服务的接口调用大概是这样:
Args 串联了: 传输层、协议层、处理层
* TProcessor tprocessor = new StudentService.Processor&StudentService.Iface&(new StudentServiceImpl());
* TServerSocket serverTransport = new TServerSocket(SERVER_PORT);
* TServer.Args tArgs = new TServer.Args(serverTransport);
* tArgs.processor(tprocessor);
* tArgs.protocolFactory(new TBinaryProtocol.Factory());
* TServer server = new TSimpleServer(tArgs);
* System.out.println("Start server on port 9527...");
* server.serve();
  没错,开启服务器端的代码就是这些,非常的简单,因为thrift做了很多的工作,我们需要的仅仅是填充我们想要的业务逻辑和各个层的实现方式就OK啦。
  最后,只剩下客户端连接获取请求了。
客户端连接服务器请求服务
   客户端的实现也非常的简单,我们只需要获得一个thrift为我们定义好的Client,然后调用需要的业务逻辑就可以了,这里的实现代码是 : StudentThriftClient.java
* Created by zhanghu on 10/2/15.
public class StudentThriftClient {
private static final String SERVER_IP = "127.0.0.1";
private static final int SERVER_PORT = 9527;
private static final int TIMEOUT = 5000;
private static TT
private static StudentService.C
传输层使用的是堵塞式 I/O 进行传输
transport = new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT);
定义内存和网络传输格式之间的映射
binary: 相当简单的二进制编码:将filed和对应的value合并在一起简单的二进制编码TBinaryProtocol
TProtocol protocol = new TBinaryProtocol(transport);
client = new StudentService.Client(protocol);
private static void mockConstructStudent() throws TException, StudentException {
* 构造对象需要注意的事项:
* 1.如果在 thrift 脚本文件中定义的字段是 required,那么就一定需要 set,否则会报错
* 2.如果在 thrift 脚本文件中定义的字段是 optional,那么可以不用去 set
StudentInfo student1 = new StudentInfo();
student1.setName("qinyi");
student1.setNumber(new HashMap&String, Long&() {{
put("qinyi", L);
student1.setAge(25);
student1.setSex(Sex.Boy);
student1.setHobby(new ArrayList&String&(Arrays.asList("ping pong", "swimming", "tai qiu")));
StudentInfo student2 = new StudentInfo();
student2.setName("brucezhang");
student2.setNumber(new HashMap&String, Long&() {{
put("brucezhang", L);
student2.setAge(18);
student2.setSex(Sex.Boy);
client.addStudentToSchool(student1);
client.addStudentToSchool(student2);
下面的调用会抛出异常:
本例中打印的异常消息如下:
StudentException(errorCode:-1, description:addStudentToSchool(StudentInfo student) error, causeInfo:student is null)
client.addStudentToSchool(null);
private static void mockGetService() throws TException, StudentException {
mockConstructStudent();
client.printStudentsInfo(client.getStudentInfoByName("qinyi"));
client.printStudentsInfo(client.getStudentInfoByName("brucezhang"));
public static void main(String[] args) throws TException{
transport : 设置传输通道
protocol : 使用二进制的传输协议
client : 创建客户端
transport.open() : 打开传输通道
transport.close() : 关闭传输通道
transport.open();
mockGetService();
} catch (StudentException e) {
System.out.println(e.getMessage());
e.printStackTrace();
transport.close();
  代码中对重要的位置进行了说明,这里不做过多的解释了。
  这样,我们就完成了thrift过程的四个步骤,接下来,可以开始测试RPC过程了,首先,我们需要运行服务器端代码,会看到控制台会打印出一条输出:Start server on port 9527,之后,运行客户端代码,等待客户端进程终结,我们回到服务器端的控制台,可以看到业务逻辑中定义的输出。
  哈哈,也许你不明白为什么我要把输出放在服务器端,而不是客户端,似乎不是正确的逻辑思维,没错,这里要解释下,只是因为方便,顺手就写在了服务器端,实际中的应用一定是方法返回客户端的查询结果,然后客户端这边自己做解析工作。
Thrift整体架构
  其实写这个部分难免有些心有余而力不足,这个部分是整个thrift框架的组成,我对它的理解也只是基础中的基础,不过,由于是学习笔记,还是记录在这里吧。
  Thrift是由四层架构组成的,这样设计的优点是可以自由的选择每一层的实现方式应对不同的服务需求,比如我在上面的例子中服务器端采用的是单线程阻塞式IO模型(这个只是Thrift实现的玩具,生产过程不可能会使用这种服务模式),你也可以根据需要换成其他的实现模型,而且代码部分的变动也是微乎其微的,分离的架构设计使得每一层之间都是透明的,不用考虑底层的实现,只需要一个接口就可以完成调用。下面,我将从最底层开始粗略的介绍Thrift中的每一层。
TTransport层
  传输层使用TCP、Http等协议实现,它包含了各种socket调用中的方法,如open,close,read,write。由于是框架中的最后一层,所以,最重要的实现部分当然是数据的读出和写入(read 和 write),它有阻塞和非阻塞的实现方式。
TProtocol层
  协议层是定义数据会以怎样的形式到达传输层。它首先对IDL中的各个数据结构进行了定义,且对每一种类型都定义了read和write方法。我们需要在服务器端和客户端声明相同的实现协议来作为内存和网络传输格式之间的映射。
  常用的协议有 TBinaryProtocol:它定义了数据会以二进制的形式传输,它是最简单的实现协议,同时也是最常用的实现协议,非常的高效;TCompactProtocol:它的名字叫做压缩二进制协议,与TBinaryProtocol相比,它会采用压缩算法对数据进行再压缩,减少实际传输的数据量,提高传输效率。
TProcessor层
  处理层就是服务器端定义的处理业务逻辑,它的主要代码是**Service.java文件中的Iface接口和Processor类。
  Iface接口:这个接口中的所有方法都是用户定义在IDL文件中的service的方法,它需要抛出TException这个检查异常,服务器端需要定义相应的实现类去 implements **.Iface 接口,完成服务的业务逻辑。
  Processor类:这个类中定义了一个processMap,里面包含了service中定义的方法,服务器端在构造这个Processor对象的时候,唯一需要做的就是把实现service(Iface)的对象作为参数传递给Processor的构造函数。
  server是Thrift框架中的最高层,它创建并管理下面的三层,同时提供了客户端调用时的线程调度逻辑。
  服务层的基类是TServer,它相当于一个容器,里面包含了TProcessor,TTransport,TProtocol,并实现对它们的管理和调度。TServer有多种实现方式,对于本例中使用的是TSimpleServer,这是一个单线程阻塞式IO模型,实际的生产中大多用到的是TThreadSelectorServer – 多线程非阻塞式IO模型。
Thrift中的知识点
struct可以设置默认值
  以我们之前定义的School举例,我们还可以这样定义struct School:
struct School {
1: required string name = "school";
2: required list&StudentInfo&
3: optional string description = "this is just a mock school";
  这样,我们就可以不需要在构造School对象的时候设置这两个字段了,当然,前提是这个默认值是你想要的。这个功能的好处是,当有多个required字段,且这些字段往往都是不变的,我们在定义对象的时候也必须要去一一设置这些字段,如果忘记了设置某一个,那么还会引起thrift抛出异常,会非常的麻烦,但是,如果我们在定义IDL文件的时候考虑了这些默认值,在构造对象的时候就不会遇到那些问题啦!
thrift中的序列化机制
  之前,曾经提到过struct中每一个属性的前面都要有一个数字id,且定义好了之后最好不要改变,这里对它进行解释。为了更好的说明问题,我们举一个例子吧,假设我们的程序中需要定义一个School结构,它包含两个字段(string name, string address),就好像下面这样:
struct School {
1: required string
2: required string
  之后,我们利用thrift工具生成了目标代码(里面包含序列化),之后,我们这样构造这个School:
School school = new School();
school.setName("大连理工大学").setAddress("凌工路2号");
  然后,我们重新定义School(thrift文件):
struct School {
2: required string
1: required string
  然后重新生成目标代码,并编写下面的过程:
System.out.println(school.getName());
System.out.println(school.getAddress());
  问题来了,我们会得到什么样的输出呢?也许,你已经猜到了,名字和地址反过来了,并不是像我们之前定义的那样,要知道为什么,就需要了解thrift是怎样对对象进行序列化的。
  thrift中的struct定义最终是需要实现序列化的,它需要用到的信息是属性前面的id和类型,序列化存储过程会形成这样的映射关系:
  name : value —— id + type : value
  所以,属性的名字是不重要的,实际过程是不需要的,所以,我们用对象去获取属性值的过程就是映射关系的一个反过程,根据id和type获取相应的value,那么,为什么会得到相反的结果就清晰了。
  所以,在实际的应用中,如果已经定义好了struct中的字段,增加没有问题,只需要定义不同的id数值就可以了,尽量不要去改变原来属性的id,也不要去删除不再需要的字段,以免导致原来的id使用重复,序列化的时候会导致结果混乱。
thrift中的版本控制
  这是设计thrift脚本文件的一个技巧,是针对序列化机制而言的,即struct。我们还是以举例的形式来进行说明,假设我们需要设计一个School结构(怎么老是School,不是不喜欢学校嘛?),里面包含了学生信息和教师信息(通常会写在两个不同的struct中,这里只是为了说明问题),它看起来就好像下面这样:
struct School {
1: required list&string& student_
2: required map&string, i16& student_
5: required list&string& teacher_
6:required map&string, i16& teacher_
  看起来怪怪的,为什么没有id是3,4的属性字段呢?这是因为,如果我们的需求变化了,比如学生信息中需要增加一个考试分数(score)的字段,那么,根据上一个版本IDL的设计,可以实现“无缝接入”,就好像下面这样:
struct School {
1: required list&string& student_
2: required map&string, i16& student_
3: required map&string, set&i16&&
5: required list&string& teacher_
6:required map&string, i16& teacher_
  这样的id设计会随着以后信息的增加而不会导致模糊不清的语义,尽管我们可以随便定义每个字段的id,不过,更好的做法是顺序定义各个字段的id,并相应的根据需要设定一些保留的字段,以备版本升级的时候使用,这样的用法在HBase,MySQL等数据库建表也是非常常见的。
  学习Thrift的时间还不长,加上本人反应愚钝,水平有限,对新鲜事物的理解能力稍差,不过,乐于分享,对人类友善是本性使然,懂得分享的乐趣,才能更好的编程,无分享,不编程。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1860997次
积分:25333
积分:25333
排名:第194名
原创:500篇
转载:316篇
译文:43篇
评论:1046条
技术 | 交流 | 娱乐 可以加下博主的群--
<font color="#FF901
大家没事可以在群里闲聊Android, IOS,C/C++, Linux等技术问题,心得,学习体会等
PS:博主仍是计算机行业的一名小学生
文章:52篇
阅读:90060
文章:38篇
阅读:92270
文章:32篇
阅读:54813
文章:33篇
阅读:53057
文章:21篇
阅读:61027
阅读:43762
阅读:53212
文章:56篇
阅读:240151
文章:20篇
阅读:38542
文章:15篇
阅读:33113
文章:30篇
阅读:159102
文章:28篇
阅读:138720

我要回帖

更多关于 魔秀桌面手机版安装 的文章

 

随机推荐