TProtocol

    TProtocol定义了消息怎么进行序列化和反序列化的。

    TProtocol的类结构图如下:

    

    TBinaryProtocol:二进制编码格式;

    TCompactProtocol:高效率,密集的二进制编码格式,使用了zigzag压缩算法,使用了类似于ProtocolBuffer的Variable-Length Quantity (VLQ) 编码方式;

    TTupleProtocol:继承于TCompactProtocol,C++中不支持,Java中支持;

    TJSONProtocol:JSON格式;

    TProtocolFactory接口:只有getProtocol一个方法,用于通过一个TTransport对象创建一个TProtocol对象;TProtocolFactory接口的实现类都在TProtocol子类中。

    TProtocol

      TProtocol是所有消息协议的父类,定义了公用的抽象方法。

public abstract class TProtocol {
@SuppressWarnings("unused")
private TProtocol() {}
protected TTransport trans_;//Transport
protected TProtocol(TTransport trans) {
trans_ = trans;
}
public TTransport getTransport() {
return trans_;
}
/**
* Writing methods.写方法
*/
//TMessage表示一个请求,发送请求时调用的方法
public abstract void writeMessageBegin(TMessage message) throws TException;
public abstract void writeMessageEnd() throws TException;
//TStruct表示一个对象,写对象时调用的方法
public abstract void writeStructBegin(TStruct struct) throws TException;
public abstract void writeStructEnd() throws TException;
//TField表示一个字段,写字段时调用的方法
public abstract void writeFieldBegin(TField field) throws TException;
public abstract void writeFieldEnd() throws TException;
//写字段结束标志调用的方法
public abstract void writeFieldStop() throws TException;
//写TMap时调用的方法
public abstract void writeMapBegin(TMap map) throws TException;
public abstract void writeMapEnd() throws TException;
//写TList时调用的方法
public abstract void writeListBegin(TList list) throws TException;
public abstract void writeListEnd() throws TException;
//写TSet时调用的方法
public abstract void writeSetBegin(TSet set) throws TException;
public abstract void writeSetEnd() throws TException;
//String和基本数据类型的写
public abstract void writeBool(boolean b) throws TException;
public abstract void writeByte(byte b) throws TException;
public abstract void writeI16(short i16) throws TException;
public abstract void writeI32(int i32) throws TException;
public abstract void writeI64(long i64) throws TException;
public abstract void writeDouble(double dub) throws TException;
public abstract void writeString(String str) throws TException;
//将buf中的数据写出
public abstract void writeBinary(ByteBuffer buf) throws TException; /**
* Reading methods.读方法
*/
public abstract TMessage readMessageBegin() throws TException;
public abstract void readMessageEnd() throws TException;
public abstract TStruct readStructBegin() throws TException;
public abstract void readStructEnd() throws TException;
public abstract TField readFieldBegin() throws TException;
public abstract void readFieldEnd() throws TException;
public abstract TMap readMapBegin() throws TException;
public abstract void readMapEnd() throws TException;
public abstract TList readListBegin() throws TException;
public abstract void readListEnd() throws TException;
public abstract TSet readSetBegin() throws TException;
public abstract void readSetEnd() throws TException;
public abstract boolean readBool() throws TException;
public abstract byte readByte() throws TException;
public abstract short readI16() throws TException;
public abstract int readI32() throws TException;
public abstract long readI64() throws TException;
public abstract double readDouble() throws TException;
public abstract String readString() throws TException;
public abstract ByteBuffer readBinary() throws TException; public void reset() {}
public Class<? extends IScheme> getScheme() {
return StandardScheme.class;
}
}

    TBinaryProtocol

      TBinaryProtocol是二进制编码。

public class TBinaryProtocol extends TProtocol {
private static final TStruct ANONYMOUS_STRUCT = new TStruct();
//版本号
protected static final int VERSION_MASK = 0xffff0000;//掩码
protected static final int VERSION_1 = 0x80010000;//版本号 protected boolean strictRead_ = false;//是否严格读
protected boolean strictWrite_ = true;//是否严格写 protected int readLength_;
protected boolean checkReadLength_ = false; //工厂类
public static class Factory implements TProtocolFactory {
protected boolean strictRead_ = false;
protected boolean strictWrite_ = true;
protected int readLength_;
public Factory() {
this(false, true);
}
public Factory(boolean strictRead, boolean strictWrite) {
this(strictRead, strictWrite, 0);
}
public Factory(boolean strictRead, boolean strictWrite, int readLength) {
strictRead_ = strictRead;
strictWrite_ = strictWrite;
readLength_ = readLength;
}
//通过TTransport实例获取一个TBinaryProtocol实例
public TProtocol getProtocol(TTransport trans) {
TBinaryProtocol proto = new TBinaryProtocol(trans, strictRead_, strictWrite_);
if (readLength_ != 0) {
proto.setReadLength(readLength_);
}
return proto;
}
}
public TBinaryProtocol(TTransport trans) {
this(trans, false, true);
}
public TBinaryProtocol(TTransport trans, boolean strictRead, boolean strictWrite) {
super(trans);
strictRead_ = strictRead;
strictWrite_ = strictWrite;
}
/**
* 写方法
*/
//发送一个请求,最终转换为对基本数据类型的写
public void writeMessageBegin(TMessage message) throws TException {
if (strictWrite_) {//
int version = VERSION_1 | message.type;//版本号和消息类型与运算
writeI32(version);//调用writeI32写版本号
writeString(message.name);//调用writeString写方法名
writeI32(message.seqid);//调用writeI32写序列号
} else {
writeString(message.name);
writeByte(message.type);
writeI32(message.seqid);
}
}
public void writeMessageEnd() {}
public void writeStructBegin(TStruct struct) {}
public void writeStructEnd() {}
//写字段方法,最终转换为对基本数据类型的写
public void writeFieldBegin(TField field) throws TException {
writeByte(field.type);
writeI16(field.id);
}
public void writeFieldEnd() {}
public void writeFieldStop() throws TException {
writeByte(TType.STOP);
}
//写Map
public void writeMapBegin(TMap map) throws TException {
writeByte(map.keyType);//写Key类型
writeByte(map.valueType);//写value类型
writeI32(map.size);//写map大小
}
public void writeMapEnd() {}
//写List
public void writeListBegin(TList list) throws TException {
writeByte(list.elemType);//写元素类型
writeI32(list.size);//写list大小
}
public void writeListEnd() {}
//写Set
public void writeSetBegin(TSet set) throws TException {
writeByte(set.elemType);//写元素类型
writeI32(set.size);//写Set大小
}
public void writeSetEnd() {}
//写bool转换为写writeByte
public void writeBool(boolean b) throws TException {
writeByte(b ? (byte)1 : (byte)0);
}
private byte [] bout = new byte[1];
public void writeByte(byte b) throws TException {
bout[0] = b;
trans_.write(bout, 0, 1);
}
private byte[] i16out = new byte[2];
public void writeI16(short i16) throws TException {
i16out[0] = (byte)(0xff & (i16 >> 8));
i16out[1] = (byte)(0xff & (i16));
trans_.write(i16out, 0, 2);
}
private byte[] i32out = new byte[4];
public void writeI32(int i32) throws TException {
i32out[0] = (byte)(0xff & (i32 >> 24));
i32out[1] = (byte)(0xff & (i32 >> 16));
i32out[2] = (byte)(0xff & (i32 >> 8));
i32out[3] = (byte)(0xff & (i32));
trans_.write(i32out, 0, 4);
}
private byte[] i64out = new byte[8];
public void writeI64(long i64) throws TException {
i64out[0] = (byte)(0xff & (i64 >> 56));
i64out[1] = (byte)(0xff & (i64 >> 48));
i64out[2] = (byte)(0xff & (i64 >> 40));
i64out[3] = (byte)(0xff & (i64 >> 32));
i64out[4] = (byte)(0xff & (i64 >> 24));
i64out[5] = (byte)(0xff & (i64 >> 16));
i64out[6] = (byte)(0xff & (i64 >> 8));
i64out[7] = (byte)(0xff & (i64));
trans_.write(i64out, 0, 8);
}
//写Double转换为writeI64
public void writeDouble(double dub) throws TException {
writeI64(Double.doubleToLongBits(dub));
}
//写String
public void writeString(String str) throws TException {
try {
byte[] dat = str.getBytes("UTF-8");//转换为字节数组
writeI32(dat.length);//写数组长度
trans_.write(dat, 0, dat.length);//写数据
} catch (UnsupportedEncodingException uex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
//写ByteBuffer
public void writeBinary(ByteBuffer bin) throws TException {
int length = bin.limit() - bin.position();
writeI32(length);
trans_.write(bin.array(), bin.position() + bin.arrayOffset(), length);
} /**
* Reading methods.读方法
*/
//读一个请求,与写请求对应
public TMessage readMessageBegin() throws TException {
int size = readI32();//读取消息的头部(4字节),可能是版本号和消息类型的组合,也可能直接是消息方法名
if (size < 0) {
//如果小于0,就是二进制为第一位以1开头,说明是带有版本号的
//校验版本号是否正确
int version = size & VERSION_MASK;
if (version != VERSION_1) {
throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in readMessageBegin");
}
//三个参数依次为方法名、消息类型、消息序列号
return new TMessage(readString(), (byte)(size & 0x000000ff), readI32());
} else {
if (strictRead_) {
throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?");
}
//readStringBody(size)为方法名,readByte()为消息类型,readI32()为消息序列号
return new TMessage(readStringBody(size), readByte(), readI32());
}
}
public void readMessageEnd() {}
public TStruct readStructBegin() {
return ANONYMOUS_STRUCT;
}
public void readStructEnd() {}
public TField readFieldBegin() throws TException {
byte type = readByte();
short id = type == TType.STOP ? 0 : readI16();
return new TField("", type, id);
}
public void readFieldEnd() {}
public TMap readMapBegin() throws TException {
return new TMap(readByte(), readByte(), readI32());
}
public void readMapEnd() {}
public TList readListBegin() throws TException {
return new TList(readByte(), readI32());
}
public void readListEnd() {}
public TSet readSetBegin() throws TException {
return new TSet(readByte(), readI32());
}
public void readSetEnd() {}
public boolean readBool() throws TException {
return (readByte() == 1);
}
private byte[] bin = new byte[1];
public byte readByte() throws TException {
if (trans_.getBytesRemainingInBuffer() >= 1) {
byte b = trans_.getBuffer()[trans_.getBufferPosition()];
trans_.consumeBuffer(1);
return b;
}
readAll(bin, 0, 1);
return bin[0];
}
private byte[] i16rd = new byte[2];
public short readI16() throws TException {
byte[] buf = i16rd;
int off = 0;
if (trans_.getBytesRemainingInBuffer() >= 2) {
buf = trans_.getBuffer();
off = trans_.getBufferPosition();
trans_.consumeBuffer(2);
} else {
readAll(i16rd, 0, 2);
}
return
(short)
(((buf[off] & 0xff) << 8) |
((buf[off+1] & 0xff)));
}
private byte[] i32rd = new byte[4];
public int readI32() throws TException {
byte[] buf = i32rd;
int off = 0;
if (trans_.getBytesRemainingInBuffer() >= 4) {
buf = trans_.getBuffer();
off = trans_.getBufferPosition();
trans_.consumeBuffer(4);
} else {
readAll(i32rd, 0, 4);
}
return
((buf[off] & 0xff) << 24) |
((buf[off+1] & 0xff) << 16) |
((buf[off+2] & 0xff) << 8) |
((buf[off+3] & 0xff));
}
private byte[] i64rd = new byte[8];
public long readI64() throws TException {
byte[] buf = i64rd;
int off = 0;
if (trans_.getBytesRemainingInBuffer() >= 8) {
buf = trans_.getBuffer();
off = trans_.getBufferPosition();
trans_.consumeBuffer(8);
} else {
readAll(i64rd, 0, 8);
}
return
((long)(buf[off] & 0xff) << 56) |
((long)(buf[off+1] & 0xff) << 48) |
((long)(buf[off+2] & 0xff) << 40) |
((long)(buf[off+3] & 0xff) << 32) |
((long)(buf[off+4] & 0xff) << 24) |
((long)(buf[off+5] & 0xff) << 16) |
((long)(buf[off+6] & 0xff) << 8) |
((long)(buf[off+7] & 0xff));
}
public double readDouble() throws TException {
return Double.longBitsToDouble(readI64());
}
public String readString() throws TException {
int size = readI32();
if (trans_.getBytesRemainingInBuffer() >= size) {
try {
String s = new String(trans_.getBuffer(), trans_.getBufferPosition(), size, "UTF-8");
trans_.consumeBuffer(size);
return s;
} catch (UnsupportedEncodingException e) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
return readStringBody(size);
}
public String readStringBody(int size) throws TException {
try {
checkReadLength(size);
byte[] buf = new byte[size];
trans_.readAll(buf, 0, size);
return new String(buf, "UTF-8");
} catch (UnsupportedEncodingException uex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
public ByteBuffer readBinary() throws TException {
int size = readI32();
checkReadLength(size);
if (trans_.getBytesRemainingInBuffer() >= size) {
ByteBuffer bb = ByteBuffer.wrap(trans_.getBuffer(), trans_.getBufferPosition(), size);
trans_.consumeBuffer(size);
return bb;
}
byte[] buf = new byte[size];
trans_.readAll(buf, 0, size);
return ByteBuffer.wrap(buf);
}
private int readAll(byte[] buf, int off, int len) throws TException {
checkReadLength(len);
return trans_.readAll(buf, off, len);
}
public void setReadLength(int readLength) {
readLength_ = readLength;
checkReadLength_ = true;
}
protected void checkReadLength(int length) throws TException {
if (length < 0) {
throw new TException("Negative length: " + length);
}
if (checkReadLength_) {
readLength_ -= length;
if (readLength_ < 0) {
throw new TException("Message length exceeded: " + length);
}
}
}
}

      其中TMessage表示一个请求,看一下TMessage的结构。

public final class TMessage {
public TMessage() {
this("", TType.STOP, 0);
}
public TMessage(String n, byte t, int s) {
name = n;
type = t;
seqid = s;
}
public final String name;//方法名
public final byte type;//消息类型
public final int seqid;//消息序列号
@Override
public String toString() {
return "<TMessage name:'" + name + "' type: " + type + " seqid:" + seqid + ">";
}
@Override
public boolean equals(Object other) {
if (other instanceof TMessage) {
return equals((TMessage) other);
}
return false;
}
public boolean equals(TMessage other) {
return name.equals(other.name) && type == other.type && seqid == other.seqid;
}
}

      消息类型定义如下:

public final class TMessageType {
public static final byte CALL = 1;//客户端请求
public static final byte REPLY = 2;//服务端响应
public static final byte EXCEPTION = 3;//服务端返回异常
public static final byte ONEWAY = 4;//单向RPC,客户端请求不要求服务端响应
}

      TField表示一个字段,TField结构如下:

public class TField {
public TField() {
this("", TType.STOP, (short)0);
}
public TField(String n, byte t, short i) {
name = n;
type = t;
id = i;
}
public final String name;//字段名
public final byte type;//字段类型
public final short id;//该字段在对象中的序号,与Thrift文件中的序号一致
public String toString() {
return "<TField name:'" + name + "' type:" + type + " field-id:" + id + ">";
}
public boolean equals(TField otherField) {
return type == otherField.type && id == otherField.id;
}
}

      Thrift定义的数据类型

public final class TType {
public static final byte STOP = 0;
public static final byte VOID = 1;
public static final byte BOOL = 2;
public static final byte BYTE = 3;
public static final byte DOUBLE = 4;
public static final byte I16 = 6;
public static final byte I32 = 8;
public static final byte I64 = 10;
public static final byte STRING = 11;
public static final byte STRUCT = 12;
public static final byte MAP = 13;
public static final byte SET = 14;
public static final byte LIST = 15;
public static final byte ENUM = 16;
}

  TProcessor

    TProcessor是服务端Thrift框架转入用户逻辑的关键。先看一下类结构图。TProcessor对TServer中一次请求的InputProtocol和OutputTProtocol进行操作,也就是从InputProtocol中读出Client的请求数据,向OutputProtcol中写入用户逻辑的返回值。

    

    TProcessor接口:只有一个抽象方法process();

    TBaseProcessor类:实现了TProcessor接口,给出了process()方法的具体实现;

    Processor类:通过thrift文件自动生成的代码,继承了TBaseProcessor类,在Processor类内部为每一个方法生成了一个类,如sayHello类;

    TProcessorFactory类:一个工厂类,返回一个TProcessor单例;

    ProcessFunction类:处理函数抽象类。

    HelloService类:由thrift文件自动生成的类,包含内部类Processor类、参数类、返回结果类等等。

    

    TProcessor

public interface TProcessor {
public boolean process(TProtocol in, TProtocol out)
throws TException;
}

    TBaseProcessor

public abstract class TBaseProcessor<I> implements TProcessor {
private final I iface;//业务逻辑实现的接口,接口中的方法即thrift文件中定义的方法
private final Map<String,ProcessFunction<I, ? extends TBase>> processMap;//处理方法Map,key为方法名,Value为方法对象
protected TBaseProcessor(I iface, Map<String, ProcessFunction<I, ? extends TBase>> processFunctionMap) {
this.iface = iface;
this.processMap = processFunctionMap;
}
@Override
public boolean process(TProtocol in, TProtocol out) throws TException {
TMessage msg = in.readMessageBegin();//读出客户端发的请求
ProcessFunction fn = processMap.get(msg.name);//根据方法名从processMap中找到处理方法
if (fn == null) {
//如果找不到该方法,返回异常
TProtocolUtil.skip(in, TType.STRUCT);
in.readMessageEnd();
TApplicationException x = new TApplicationException(TApplicationException.UNKNOWN_METHOD, "Invalid method name: '"+msg.name+"'");
out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));
x.write(out);
out.writeMessageEnd();
out.getTransport().flush();
return true;
}
fn.process(msg.seqid, in, out, iface);//处理请求
return true;
}
}

    ProcessFunction

      Thrift将方法抽象为ProcessFunction类,每一个方法都会生成一个ProcessFunction类的子类。

public abstract class ProcessFunction<I, T extends TBase> {
private final String methodName;//方法名
public ProcessFunction(String methodName) {
this.methodName = methodName;
}
//处理请求
public final void process(int seqid, TProtocol iprot, TProtocol oprot, I iface) throws TException {
T args = getEmptyArgsInstance();//获取一个参数实例(TBase子类的参数实例),由子类实现
try {
args.read(iprot);//从iprot中读取参数,具体实现由thrift文件自动生成(见sayHello_args类)
} catch (TProtocolException e) {
//读取参数异常,返回异常
iprot.readMessageEnd();
TApplicationException x = new TApplicationException(TApplicationException.PROTOCOL_ERROR, e.getMessage());
oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.EXCEPTION, seqid));
x.write(oprot);
oprot.writeMessageEnd();
oprot.getTransport().flush();
return;
}
iprot.readMessageEnd();
TBase result = getResult(iface, args);//获取处理结果,此时调用业务逻辑,具体实现由thrift文件自动生成(见sayHello类)
oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.REPLY, seqid));
result.write(oprot);//将result写到oprot,具体实现由thrift文件自动生成(见sayHello_result类)
oprot.writeMessageEnd();
oprot.getTransport().flush();
}
//获取处理结果,调用业务逻辑,由子类实现
protected abstract TBase getResult(I iface, T args) throws TException;
//获取一个参数实例,由子类实现
protected abstract T getEmptyArgsInstance();
public String getMethodName() {
return methodName;
}
}

    Processor

  public static class Processor<I extends Iface> extends org.apache.thrift.TBaseProcessor<I> implements org.apache.thrift.TProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(Processor.class.getName());
public Processor(I iface) {
super(iface, getProcessMap(new HashMap<String, org.apache.thrift.ProcessFunction<I, ? extends org.apache.thrift.TBase>>()));
}
protected Processor(I iface, Map<String, org.apache.thrift.ProcessFunction<I, ? extends org.apache.thrift.TBase>> processMap) {
super(iface, getProcessMap(processMap));
}
//初始化processMap,在Processor初始化时会调用该方法
private static <I extends Iface> Map<String, org.apache.thrift.ProcessFunction<I, ? extends org.apache.thrift.TBase>> getProcessMap(Map<String, org.apache.thrift.ProcessFunction<I, ? extends org.apache.thrift.TBase>> processMap) {
processMap.put("sayHello", new sayHello());
return processMap;
}
//每个方法生成一个类
private static class sayHello<I extends Iface> extends org.apache.thrift.ProcessFunction<I, sayHello_args> {
public sayHello() {
super("sayHello");
}
//获取空参数实例
protected sayHello_args getEmptyArgsInstance() {
return new sayHello_args();
}
//获取返回结果
protected sayHello_result getResult(I iface, sayHello_args args) throws org.apache.thrift.TException {
sayHello_result result = new sayHello_result();
result.success = iface.sayHello(args.paramJson);//调用业务逻辑接口的sayHello方法,
return result;
}
}
}

  

  最后总结一下TProcessor的处理流程:

    1)TServer接收到请求后,调用TProcessor的process(TProtocol in, TProtocol out)方法进行处理;

    2)TProcessor通过in.readMessageBegin()获取客户端请求,并根据请求方法名找到对应的ProcessFunction实例;

    3)调用ProcessFunction的process方法;

      首先从inTProtocol中读取参数,

      然后通过调用getResult(iface, args)方法调用业务逻辑,获取到返回结果,

      最后将返回结果写入到outTProtocol。

    

   

参考资料

  Apache Thrift设计概要

  Thrift源码分析(二)-- 协议和编解码

  Thrift RPC详解

RPC-Thrift(三)的更多相关文章

  1. Thrift RPC实战(三) thrift序列化揭秘

    本文主要讲解Thrift的序列化机制, 看看thrift作为数据交换格式是如何工作的? 1.构造应用场景: 1). 首先我们先来定义下thrift的简单结构. 1 2 3 4 5 namespace ...

  2. 应答流式RPC 请求流式RPC 向流式RPC 流式RPC的三种具体形式

    https://mp.weixin.qq.com/s/pWwSfXl71GQZ3KPmAHE_dA 用Python进行gRPC接口测试(二) 大帆船 搜狗测试 2020-02-07   上期回顾:用P ...

  3. Hadoop概念学习系列之谈谈RPC(三十三)

    RPC-------->远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数 ...

  4. Go gRPC教程-服务端流式RPC(三)

    前言 上一篇介绍了简单模式RPC,当数据量大或者需要不断传输数据时候,我们应该使用流式RPC,它允许我们边处理边传输数据.本篇先介绍服务端流式RPC. 服务端流式RPC:客户端发送请求到服务器,拿到一 ...

  5. 老王讲自制RPC框架.(三.ZOOKEEPER)

    (#)定义Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务.状态同步服务.集群管理.分布式应用配 ...

  6. C#使用Thrift作为RPC框架入门(三)之三层架构

    前言 这是我们讲解Thrift框架的第三篇文章,前两篇我们讲了Thrift作为RPC框架的基本用法以及架构的设计.为了我们更好的使用和理解Thrift框架,接下来,我们将来学习一下Thrift框架提供 ...

  7. 轻量级RPC设计与实现第三版

    在前两个版本中,每次发起请求一次就新建一个netty的channel连接,如果在高并发情况下就会造成资源的浪费,这时实现异步请求就十分重要,当有多个请求线程时,需要设计一个线程池来进行管理.除此之外, ...

  8. Thrift的TCompactProtocol紧凑型二进制协议分析

    Thrift的紧凑型传输协议分析: 用一张图说明一下Thrift的TCompactProtocol中各个数据类型是怎么表示的. 报文格式编码: bool类型: 一个字节. 如果bool型的字段是结构体 ...

  9. 从0到1:全面理解RPC远程调用

    上一篇关于 WSGI 的硬核长文,不知道有多少同学,能够从头看到尾的,不管你们有没有看得很过瘾,反正我是写得很爽,总有一种将一样知识吃透了的错觉. 今天我又给自己挖坑了,打算将 rpc 远程调用的知识 ...

  10. Thrift 原理与使用实例

    一.Thrift 框架介绍 1.前言 Thrift是一个跨语言的服务部署框架,最初由Facebook于2007年开发,2008年进入Apache开源项目.Thrift通过一个中间语言(IDL, 接口定 ...

随机推荐

  1. Unity3d工具方法小集

    1.获取垂直水平方向上的输入: float moveHorizontal = Input.GetAxis("Horizontal"); float moveVertical = I ...

  2. rails 中 preload、includes、Eager load、Joins 的区别

    Rails 提供了四种不同加载关联数据的方法.下面就来介绍一下. 一.Preload Preload 是以附加一条查询语句来加载关联数据的 User.preload(:posts).to_a # =& ...

  3. vuex模块相互调用

    https://segmentfault.com/a/1190000009434398

  4. BZOJ 3998 TJOI2015 弦论 后缀自动机+DAG上的dp

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3998 题意概述:对于一个给定长度为N的字符串,求它的第K小子串是什么,T为0则表示不同位置 ...

  5. URAL 1936 Roshambo(求期望)

    Description Bootstrap: Wondering how it's played? Will: It's a game of deception. But your bet inclu ...

  6. NO7——二分

    int binsearch(int *t,int k,int n) {//t为数组,k是要查找的数,n为长度,此为升序 ,high = n,mid; while(low<=high) { mid ...

  7. hadoop自定义数据类型

    统计某手机数据库的每个手机号的上行数据包数量和下行数据包数量 数据库类型如下: 数据库内容如下: 下面自定义类型SimLines,类似于平时编写的model import java.io.DataIn ...

  8. java--List转换成json格式

    方法一 首先导入jar包,json-rpc-1.0.jar public class List2Json { public static JSONArray ProLogList2Json(List& ...

  9. oracle dg 备库不同步主库数据

    今天遇到一个数据库同步问题,主库被关闭,重启主库后,备库不能正常同步主库数据.只有当手动切换归档日志的时候,备库才能和主库一致. 这个问题的解决方法: 重启备库,重新应用归档日志. 操作步骤如下: / ...

  10. lincode-58-四数之和

    58-四数之和 给一个包含n个数的整数数组S,在S中找到所有使得和为给定整数target的四元组(a, b, c, d). 注意事项 四元组(a, b, c, d)中,需要满足a <= b &l ...