对于健壮的服务,其中的数据类型必须一种机制来对其进行版本管理,尤其是它可以在不中断服务(或者更坏的情况,出现段错误)的前提下,增加或删除一个对象中的字段,或者改变一个函数的参数列表。

字段标识符

Thrift的版本管理是通过字段标识符来实现的。对于每个被Thrift编码的结构的域头,都有一个唯一的字段标识符,这个字段标识符和它的类型说明符构成了对这个字段独一无二的识别。

Thrift定义语言支持字段标识符的自动分配,但是好的程序实践中是明确指出字段标识符。字段标识符使用如下方式指定:

struct Example {
1:i32 number = 0,
2:i64 bigNumber,
3:double decimals,
4:string name = "thrifty"
}

为了避免人工和自动分配的标识符冲突,忽略了标识符的字段自动从-1递减分配字段标识符,并且Thrift定义语言只支持人工分配正的标识符。

当数据正在被反序列化的时候,产生的代码能够使用这些字段标识符来恰当的识别字段,并判断这个标识符是否在它的定义文件中和一个字段对齐。如果一个字段标识符不被识别,产生的代码可以用类型说明符去跳过这个不可知的字段而不产生任何错误。同样,这个也可以归结为这样一个事实:所有的数据类型都是自划界的。

字段标识符也能够在函数列表中被指定。事实上,参数列表在后端不仅作为结构被呈现,而是在编译器前段分享了相同的代码。这是为了允许安全的修改方法的参数。

service StringCache {
void set(1:i32 key, 2:string value),
string get(1:i32 key) throws(1:KeyNotFound knf),
void delete(1:i32 key)
}

对于每个结构,都选择指定字段标识符的语法。结构可以看成一个字典,标识符是主键,强类型名的字段是值。

Isset

当遇到一个未期望的字段,能够被安全的忽略和丢弃。当一个预期的字段未被找到,必须有一些方法来告知开发者该字段未出现。这是通过内部结构isset来实现的,这个结构位于定义对象内部。本质上说,每个thrift结构内部的isset对象为每个字段包含了一个布尔值,以此指示这个字段是否出现在结构中。当一个阅读器接受一个结构,它应该在直接操作它之前检查这个是否置位。

以上面Example为例,每个字段都有一个isset方法,生成的Java代码如下:

/**
* Autogenerated by Thrift Compiler (0.11.0)
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
* @generated
*/
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2018-06-18")
public class Example implements org.apache.thrift.TBase<Example, Example._Fields>, java.io.Serializable, Cloneable, Comparable<Example> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("Example"); private static final org.apache.thrift.protocol.TField NUMBER_FIELD_DESC = new org.apache.thrift.protocol.TField("number", org.apache.thrift.protocol.TType.I32, (short)1);
private static final org.apache.thrift.protocol.TField BIG_NUMBER_FIELD_DESC = new org.apache.thrift.protocol.TField("bigNumber", org.apache.thrift.protocol.TType.I64, (short)2);
private static final org.apache.thrift.protocol.TField DECIMALS_FIELD_DESC = new org.apache.thrift.protocol.TField("decimals", org.apache.thrift.protocol.TType.DOUBLE, (short)3);
private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)4); private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new ExampleStandardSchemeFactory();
private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new ExampleTupleSchemeFactory(); public int number; // required
public long bigNumber; // required
public double decimals; // required
public java.lang.String name; // required /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
public enum _Fields implements org.apache.thrift.TFieldIdEnum {
NUMBER((short)1, "number"),
BIG_NUMBER((short)2, "bigNumber"),
DECIMALS((short)3, "decimals"),
NAME((short)4, "name"); private static final java.util.Map<java.lang.String, _Fields> byName = new java.util.HashMap<java.lang.String, _Fields>(); static {
for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) {
byName.put(field.getFieldName(), field);
}
} /**
* Find the _Fields constant that matches fieldId, or null if its not found.
*/
public static _Fields findByThriftId(int fieldId) {
switch(fieldId) {
case 1: // NUMBER
return NUMBER;
case 2: // BIG_NUMBER
return BIG_NUMBER;
case 3: // DECIMALS
return DECIMALS;
case 4: // NAME
return NAME;
default:
return null;
}
} /**
* Find the _Fields constant that matches fieldId, throwing an exception
* if it is not found.
*/
public static _Fields findByThriftIdOrThrow(int fieldId) {
_Fields fields = findByThriftId(fieldId);
if (fields == null) throw new java.lang.IllegalArgumentException("Field " + fieldId + " doesn't exist!");
return fields;
} /**
* Find the _Fields constant that matches name, or null if its not found.
*/
public static _Fields findByName(java.lang.String name) {
return byName.get(name);
} private final short _thriftId;
private final java.lang.String _fieldName; _Fields(short thriftId, java.lang.String fieldName) {
_thriftId = thriftId;
_fieldName = fieldName;
} public short getThriftFieldId() {
return _thriftId;
} public java.lang.String getFieldName() {
return _fieldName;
}
} // isset id assignments
private static final int __NUMBER_ISSET_ID = 0;
private static final int __BIGNUMBER_ISSET_ID = 1;
private static final int __DECIMALS_ISSET_ID = 2;
private byte __isset_bitfield = 0;
public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
static {
java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
tmpMap.put(_Fields.NUMBER, new org.apache.thrift.meta_data.FieldMetaData("number", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32)));
tmpMap.put(_Fields.BIG_NUMBER, new org.apache.thrift.meta_data.FieldMetaData("bigNumber", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I64)));
tmpMap.put(_Fields.DECIMALS, new org.apache.thrift.meta_data.FieldMetaData("decimals", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.DOUBLE)));
tmpMap.put(_Fields.NAME, new org.apache.thrift.meta_data.FieldMetaData("name", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
metaDataMap = java.util.Collections.unmodifiableMap(tmpMap);
org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(Example.class, metaDataMap);
} public Example() {
this.number = 0; this.name = "thrifty"; } public Example(
int number,
long bigNumber,
double decimals,
java.lang.String name)
{
this();
this.number = number;
setNumberIsSet(true);
this.bigNumber = bigNumber;
setBigNumberIsSet(true);
this.decimals = decimals;
setDecimalsIsSet(true);
this.name = name;
} /**
* Performs a deep copy on <i>other</i>.
*/
public Example(Example other) {
__isset_bitfield = other.__isset_bitfield;
this.number = other.number;
this.bigNumber = other.bigNumber;
this.decimals = other.decimals;
if (other.isSetName()) {
this.name = other.name;
}
} public Example deepCopy() {
return new Example(this);
} @Override
public void clear() {
this.number = 0; setBigNumberIsSet(false);
this.bigNumber = 0;
setDecimalsIsSet(false);
this.decimals = 0.0;
this.name = "thrifty"; } public int getNumber() {
return this.number;
} public Example setNumber(int number) {
this.number = number;
setNumberIsSet(true);
return this;
} public void unsetNumber() {
__isset_bitfield = org.apache.thrift.EncodingUtils.clearBit(__isset_bitfield, __NUMBER_ISSET_ID);
} /** Returns true if field number is set (has been assigned a value) and false otherwise */
public boolean isSetNumber() {
return org.apache.thrift.EncodingUtils.testBit(__isset_bitfield, __NUMBER_ISSET_ID);
} public void setNumberIsSet(boolean value) {
__isset_bitfield = org.apache.thrift.EncodingUtils.setBit(__isset_bitfield, __NUMBER_ISSET_ID, value);
} public long getBigNumber() {
return this.bigNumber;
} public Example setBigNumber(long bigNumber) {
this.bigNumber = bigNumber;
setBigNumberIsSet(true);
return this;
} public void unsetBigNumber() {
__isset_bitfield = org.apache.thrift.EncodingUtils.clearBit(__isset_bitfield, __BIGNUMBER_ISSET_ID);
} /** Returns true if field bigNumber is set (has been assigned a value) and false otherwise */
public boolean isSetBigNumber() {
return org.apache.thrift.EncodingUtils.testBit(__isset_bitfield, __BIGNUMBER_ISSET_ID);
} public void setBigNumberIsSet(boolean value) {
__isset_bitfield = org.apache.thrift.EncodingUtils.setBit(__isset_bitfield, __BIGNUMBER_ISSET_ID, value);
} public double getDecimals() {
return this.decimals;
} public Example setDecimals(double decimals) {
this.decimals = decimals;
setDecimalsIsSet(true);
return this;
} public void unsetDecimals() {
__isset_bitfield = org.apache.thrift.EncodingUtils.clearBit(__isset_bitfield, __DECIMALS_ISSET_ID);
} /** Returns true if field decimals is set (has been assigned a value) and false otherwise */
public boolean isSetDecimals() {
return org.apache.thrift.EncodingUtils.testBit(__isset_bitfield, __DECIMALS_ISSET_ID);
} public void setDecimalsIsSet(boolean value) {
__isset_bitfield = org.apache.thrift.EncodingUtils.setBit(__isset_bitfield, __DECIMALS_ISSET_ID, value);
} public java.lang.String getName() {
return this.name;
} public Example setName(java.lang.String name) {
this.name = name;
return this;
} public void unsetName() {
this.name = null;
} /** Returns true if field name is set (has been assigned a value) and false otherwise */
public boolean isSetName() {
return this.name != null;
} public void setNameIsSet(boolean value) {
if (!value) {
this.name = null;
}
} public void setFieldValue(_Fields field, java.lang.Object value) {
switch (field) {
case NUMBER:
if (value == null) {
unsetNumber();
} else {
setNumber((java.lang.Integer)value);
}
break; case BIG_NUMBER:
if (value == null) {
unsetBigNumber();
} else {
setBigNumber((java.lang.Long)value);
}
break; case DECIMALS:
if (value == null) {
unsetDecimals();
} else {
setDecimals((java.lang.Double)value);
}
break; case NAME:
if (value == null) {
unsetName();
} else {
setName((java.lang.String)value);
}
break; }
} public java.lang.Object getFieldValue(_Fields field) {
switch (field) {
case NUMBER:
return getNumber(); case BIG_NUMBER:
return getBigNumber(); case DECIMALS:
return getDecimals(); case NAME:
return getName(); }
throw new java.lang.IllegalStateException();
} /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
public boolean isSet(_Fields field) {
if (field == null) {
throw new java.lang.IllegalArgumentException();
} switch (field) {
case NUMBER:
return isSetNumber();
case BIG_NUMBER:
return isSetBigNumber();
case DECIMALS:
return isSetDecimals();
case NAME:
return isSetName();
}
throw new java.lang.IllegalStateException();
} @Override
public boolean equals(java.lang.Object that) {
if (that == null)
return false;
if (that instanceof Example)
return this.equals((Example)that);
return false;
} public boolean equals(Example that) {
if (that == null)
return false;
if (this == that)
return true; boolean this_present_number = true;
boolean that_present_number = true;
if (this_present_number || that_present_number) {
if (!(this_present_number && that_present_number))
return false;
if (this.number != that.number)
return false;
} boolean this_present_bigNumber = true;
boolean that_present_bigNumber = true;
if (this_present_bigNumber || that_present_bigNumber) {
if (!(this_present_bigNumber && that_present_bigNumber))
return false;
if (this.bigNumber != that.bigNumber)
return false;
} boolean this_present_decimals = true;
boolean that_present_decimals = true;
if (this_present_decimals || that_present_decimals) {
if (!(this_present_decimals && that_present_decimals))
return false;
if (this.decimals != that.decimals)
return false;
} boolean this_present_name = true && this.isSetName();
boolean that_present_name = true && that.isSetName();
if (this_present_name || that_present_name) {
if (!(this_present_name && that_present_name))
return false;
if (!this.name.equals(that.name))
return false;
} return true;
} @Override
public int hashCode() {
int hashCode = 1; hashCode = hashCode * 8191 + number; hashCode = hashCode * 8191 + org.apache.thrift.TBaseHelper.hashCode(bigNumber); hashCode = hashCode * 8191 + org.apache.thrift.TBaseHelper.hashCode(decimals); hashCode = hashCode * 8191 + ((isSetName()) ? 131071 : 524287);
if (isSetName())
hashCode = hashCode * 8191 + name.hashCode(); return hashCode;
} @Override
public int compareTo(Example other) {
if (!getClass().equals(other.getClass())) {
return getClass().getName().compareTo(other.getClass().getName());
} int lastComparison = 0; lastComparison = java.lang.Boolean.valueOf(isSetNumber()).compareTo(other.isSetNumber());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetNumber()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.number, other.number);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = java.lang.Boolean.valueOf(isSetBigNumber()).compareTo(other.isSetBigNumber());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetBigNumber()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.bigNumber, other.bigNumber);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = java.lang.Boolean.valueOf(isSetDecimals()).compareTo(other.isSetDecimals());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetDecimals()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.decimals, other.decimals);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = java.lang.Boolean.valueOf(isSetName()).compareTo(other.isSetName());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetName()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.name, other.name);
if (lastComparison != 0) {
return lastComparison;
}
}
return 0;
} public _Fields fieldForId(int fieldId) {
return _Fields.findByThriftId(fieldId);
} public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
scheme(iprot).read(iprot, this);
} public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
scheme(oprot).write(oprot, this);
} @Override
public java.lang.String toString() {
java.lang.StringBuilder sb = new java.lang.StringBuilder("Example(");
boolean first = true; sb.append("number:");
sb.append(this.number);
first = false;
if (!first) sb.append(", ");
sb.append("bigNumber:");
sb.append(this.bigNumber);
first = false;
if (!first) sb.append(", ");
sb.append("decimals:");
sb.append(this.decimals);
first = false;
if (!first) sb.append(", ");
sb.append("name:");
if (this.name == null) {
sb.append("null");
} else {
sb.append(this.name);
}
first = false;
sb.append(")");
return sb.toString();
} public void validate() throws org.apache.thrift.TException {
// check for required fields
// check for sub-struct validity
} private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
try {
write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
} catch (org.apache.thrift.TException te) {
throw new java.io.IOException(te);
}
} private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException {
try {
// it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor.
__isset_bitfield = 0;
read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
} catch (org.apache.thrift.TException te) {
throw new java.io.IOException(te);
}
} private static class ExampleStandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory {
public ExampleStandardScheme getScheme() {
return new ExampleStandardScheme();
}
} private static class ExampleStandardScheme extends org.apache.thrift.scheme.StandardScheme<Example> { public void read(org.apache.thrift.protocol.TProtocol iprot, Example struct) throws org.apache.thrift.TException {
org.apache.thrift.protocol.TField schemeField;
iprot.readStructBegin();
while (true)
{
schemeField = iprot.readFieldBegin();
if (schemeField.type == org.apache.thrift.protocol.TType.STOP) {
break;
}
switch (schemeField.id) {
case 1: // NUMBER
if (schemeField.type == org.apache.thrift.protocol.TType.I32) {
struct.number = iprot.readI32();
struct.setNumberIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
case 2: // BIG_NUMBER
if (schemeField.type == org.apache.thrift.protocol.TType.I64) {
struct.bigNumber = iprot.readI64();
struct.setBigNumberIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
case 3: // DECIMALS
if (schemeField.type == org.apache.thrift.protocol.TType.DOUBLE) {
struct.decimals = iprot.readDouble();
struct.setDecimalsIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
case 4: // NAME
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.name = iprot.readString();
struct.setNameIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
default:
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
iprot.readFieldEnd();
}
iprot.readStructEnd(); // check for required fields of primitive type, which can't be checked in the validate method
struct.validate();
} public void write(org.apache.thrift.protocol.TProtocol oprot, Example struct) throws org.apache.thrift.TException {
struct.validate(); oprot.writeStructBegin(STRUCT_DESC);
oprot.writeFieldBegin(NUMBER_FIELD_DESC);
oprot.writeI32(struct.number);
oprot.writeFieldEnd();
oprot.writeFieldBegin(BIG_NUMBER_FIELD_DESC);
oprot.writeI64(struct.bigNumber);
oprot.writeFieldEnd();
oprot.writeFieldBegin(DECIMALS_FIELD_DESC);
oprot.writeDouble(struct.decimals);
oprot.writeFieldEnd();
if (struct.name != null) {
oprot.writeFieldBegin(NAME_FIELD_DESC);
oprot.writeString(struct.name);
oprot.writeFieldEnd();
}
oprot.writeFieldStop();
oprot.writeStructEnd();
} } private static class ExampleTupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory {
public ExampleTupleScheme getScheme() {
return new ExampleTupleScheme();
}
} private static class ExampleTupleScheme extends org.apache.thrift.scheme.TupleScheme<Example> { @Override
public void write(org.apache.thrift.protocol.TProtocol prot, Example struct) throws org.apache.thrift.TException {
org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot;
java.util.BitSet optionals = new java.util.BitSet();
if (struct.isSetNumber()) {
optionals.set(0);
}
if (struct.isSetBigNumber()) {
optionals.set(1);
}
if (struct.isSetDecimals()) {
optionals.set(2);
}
if (struct.isSetName()) {
optionals.set(3);
}
oprot.writeBitSet(optionals, 4);
if (struct.isSetNumber()) {
oprot.writeI32(struct.number);
}
if (struct.isSetBigNumber()) {
oprot.writeI64(struct.bigNumber);
}
if (struct.isSetDecimals()) {
oprot.writeDouble(struct.decimals);
}
if (struct.isSetName()) {
oprot.writeString(struct.name);
}
} @Override
public void read(org.apache.thrift.protocol.TProtocol prot, Example struct) throws org.apache.thrift.TException {
org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot;
java.util.BitSet incoming = iprot.readBitSet(4);
if (incoming.get(0)) {
struct.number = iprot.readI32();
struct.setNumberIsSet(true);
}
if (incoming.get(1)) {
struct.bigNumber = iprot.readI64();
struct.setBigNumberIsSet(true);
}
if (incoming.get(2)) {
struct.decimals = iprot.readDouble();
struct.setDecimalsIsSet(true);
}
if (incoming.get(3)) {
struct.name = iprot.readString();
struct.setNameIsSet(true);
}
}
} private static <S extends org.apache.thrift.scheme.IScheme> S scheme(org.apache.thrift.protocol.TProtocol proto) {
return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) ? STANDARD_SCHEME_FACTORY : TUPLE_SCHEME_FACTORY).getScheme();
}
}

案例分析:

版本不匹配可能发生在如下四种情况中:

1.已添加字段,旧客户端,新服务器

旧客户端没有发送新字段,新服务器识别到那个新字段未置位,执行对于旧数据请求的默认操作。

2.已删除字段,旧客户端,新服务器

旧客户端发送已被删除的字段,新服务器简单忽略这个字段。

3.已添加字段,新客户端,旧服务器

新客户端发送了旧服务器不能识别的字段,旧服务器简单的忽略,并按正常请求处理。

4.已删除字段,新客户端,旧服务器

最危险的情况,对于丢失的字段,旧服务器不大可能有默认的合适动作执行。这种情况,推荐在升级客户端之前升级服务器端。

Thrift版本管理的更多相关文章

  1. 和 Thrift 的一场美丽邂逅

    一. 与 Thrift 的初识 也许大多数人接触 Thrift 是从序列化开始的.每次搜索 “java序列化” + “方式”.“对比” 或 “性能” 等关键字时,搜索引擎总是会返回一大堆有关各种序列化 ...

  2. Apache thrift RPC 双向通信

    在上一篇介绍Apache thrift 安装和使用,写了一个简单的demo,讲解thrift服务的发布和客户端调用,但只是单向的客户端发送消息,服务端接收消息.而客户端却得不到服务器的响应. 在不涉及 ...

  3. common-pool2 学习:thrift连接池的另一种实现

    对象池是一种很实用的技术,经典的例子就是数据库连接池.去年曾经从零开始写过一个thrift客户端连接池.如果不想重造轮子,可以直接在apache开源项目commons-pool的基础上开发. 步骤: ...

  4. thrift:swift项目笔记

    先声明:此swift不是Apple公司的那个swift开发语言,而是facebook的另一个开源项目. facebook的thrift IDL文件,如果默认用thrift -gen java生成jav ...

  5. dubbo/dubbox 增加原生thrift及avro支持

    (facebook) thrift / (hadoop) avro / (google) probuf(grpc)是近几年来比较抢眼的高效序列化/rpc框架,dubbo框架虽然有thrift的支持,但 ...

  6. linux系统下使用流行的版本管理工具 Git

    前几天被版本管理困扰了好久,主要是因为 没法回到之前的版本,新版本又出了问题真的很尴尬. 终于决定使用目前网上很火的版本管理工具-------Git 历史啥的就不说了,说些有用的. 我用的是oschi ...

  7. 版本管理工具svn简介

    svn简介 SVN是一种C/S架构的版本管理软件 , 能够帮助我们保存开发过程中各个文件的所有历史版本, 你因此可以方便的找回软件的任何一个历史状态., 日常开发中经常用到. 安装使用 1.  在 u ...

  8. Thrift的TJsonProtocol协议分析

    Thrift协议实现目前有二进制协议(TBinaryProtocol),紧凑型二进制协议(TCompactProtocol)和Json协议(TJsonProtocol). 前面的两篇文字从编码和协议原 ...

  9. Thrift的TBinaryProtocol二进制协议分析

    先上张图,说明一下thrift的二进制协议是什么东东. 报文格式编码: bool类型: 一个字节的类型,两个字节的字段编号,一个字节的值(true:1,false:0). Byte类型: 一个字节的类 ...

随机推荐

  1. org.springframework.web.util.WebUtils.isSameOrigin(WebUtils.java:816)

    Nginx反向代理WebSocket时报这个错,普通的http请求没问题,ws请求报错 可能原因: 1.你用了4.2.5.RELEASE版本或者4.2.6.RELEASE,升级到4.2.7.RELEA ...

  2. linux下chmod使用

    用法 chmod使用语法 $ chmod [options] mode[,mode] file1 [file2 ...] 使用ls命令的查看文件或目录的属性 $ ls -l file 八进制语法 ch ...

  3. 树莓派3中编译Opencv3.4.10

    一.命令 -dev libv4l-dev libavcodec-dev libavformat-dev libswscale-dev cd wget http://sourceforge.net/pr ...

  4. .Net学习资料

    1.博客系列文章 (1)设计模式 吕震宇 设计模式 张逸:晴窗笔记 Design & Pattern 梦幻Dot Net  .Net设计模式 李会军          .NET设计模式系列文章 ...

  5. CentOS7.4安装配置mysql8 TAR免安装版

    下载mysql: https://dev.mysql.com/downloads/mysql/ 解压tar.xz文件:先 xz -d mysql-8.0.15-linux-glibc2.12-x86_ ...

  6. 【iCore4 双核心板_ARM】例程十:RTC实时时钟实验——显示时间和日期

    实验现象: 核心代码: int main(void) { /* USER CODE BEGIN 1 */ RTC_TimeTypeDef sTime; RTC_DateTypeDef sDate; ; ...

  7. 使用Fidder将生成环境代码映射到本地(文件夹)

    匹配生产URL正则表达式 regex:http://www.a.com/statics/js/(.*) 本地路径配置 D:\web\statics\js\$1

  8. Java知多少(69)面向字节的输入输出流

    字节流以字节为传输单位,用来读写8位的数据,除了能够处理纯文本文件之外,还能用来处理二进制文件的数据.InputStream类和OutputStream类是所有字节流的父类. InputStream类 ...

  9. Docker入门学习总结

    1. 什么是Docker Docker是一种虚拟化技术,其在容器的基础上进一步封装了文件系统.网络互联.进程隔离等等,从而极大地简化了容器的创建和维护.Docker使用 Google 公司推出的 Go ...

  10. An SPI class of type org.apache.lucene.codecs.PostingsFormat with name 'Lucene50' does not exist. You need to add the corresponding JAR file supporting this SPI to your classpath. The current classp

    背景介绍: 当ES中guava库与hive等组件的库冲突时,对Elasticsearch库进行shade,relocate解决库冲突问题. 当使用"org.apache.maven.plug ...