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. LeetCode:15. 3Sum(Medium)

    1. 原题链接 https://leetcode.com/problems/3sum/description/ 2. 题目要求 数组S = nums[n]包含n个整数,请问S中是否存在a,b,c三个整 ...

  2. linux手动安装flash插件

    下载好之后,将解压的文件 1,将libflashplayer.so拷到firefox的插件目录/usr/lib/firefox/browser/plugin/ sudo cp libflashplay ...

  3. What to do when Enterprise Manager is not able to connect to the database instance (ORA-28001)

    摘自:http://dbtricks.com/?p=34 If you are trying to connect to the Oracle enterprise Manger and you ge ...

  4. 【APUE】Chapter3 File I/O

    这章主要讲了几类unbuffered I/O函数的用法和设计思路. 3.2 File Descriptors fd本质上是非负整数,当我们执行open或create的时候,kernel向进程返回一个f ...

  5. Jmeter使用时异常问题解决

    1.执行jmeter请求时,响应数据中出现乱码异常(如图) 解决方案: 打开E:\apache-jmeter-4\bin\jmeter.properries(jmeter安装目录),查找到语句行:#s ...

  6. 基于Ubuntu搭建Linux路由器

    开源,几乎代表了无所不能的意思,最近又因为它玩Hi了... 因业务发展,需要临时接入300MB的专线和千兆路由器,而公司现有的路由器却是百兆的,出于成本考虑,只能不想更换新的路由器,在网上查了一下可以 ...

  7. zabbix从入门到精通

    第1章 zabbix监控 1.1 为什么要监控 在需要的时刻,提前提醒我们服务器出问题了 当出问题之后,可以找到问题的根源   网站/服务器 的可用性 1.1.1 网站可用性 在软件系统的高可靠性(也 ...

  8. 3.爬虫 urlib库讲解 总结

    urllib库的总结: 用ProcessOn(安利这个软件,够用了)根据前面的几节内容做了个思维导图. urllib库一共有四个模块: request:它是最基本的模块,可以用来模拟发送请求 erro ...

  9. spring mvc:实现给Controller函数传入map参数

    [1]前端js调用示例: ...fillOrDiffer?inMapJson={"2016-08-31 0:00:00":0.1,"2016-08-31 0:15:00& ...

  10. # ML学习小笔记—Gradien Descent

    关于本课程的相关资料http://speech.ee.ntu.edu.tw/~tlkagk/courses_ML17.html 根据前面所为,当我们得到Loss方程的时候,我们希望求得最优的Loss方 ...