Thrift版本管理
对于健壮的服务,其中的数据类型必须一种机制来对其进行版本管理,尤其是它可以在不中断服务(或者更坏的情况,出现段错误)的前提下,增加或删除一个对象中的字段,或者改变一个函数的参数列表。
字段标识符
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版本管理的更多相关文章
- 和 Thrift 的一场美丽邂逅
一. 与 Thrift 的初识 也许大多数人接触 Thrift 是从序列化开始的.每次搜索 “java序列化” + “方式”.“对比” 或 “性能” 等关键字时,搜索引擎总是会返回一大堆有关各种序列化 ...
- Apache thrift RPC 双向通信
在上一篇介绍Apache thrift 安装和使用,写了一个简单的demo,讲解thrift服务的发布和客户端调用,但只是单向的客户端发送消息,服务端接收消息.而客户端却得不到服务器的响应. 在不涉及 ...
- common-pool2 学习:thrift连接池的另一种实现
对象池是一种很实用的技术,经典的例子就是数据库连接池.去年曾经从零开始写过一个thrift客户端连接池.如果不想重造轮子,可以直接在apache开源项目commons-pool的基础上开发. 步骤: ...
- thrift:swift项目笔记
先声明:此swift不是Apple公司的那个swift开发语言,而是facebook的另一个开源项目. facebook的thrift IDL文件,如果默认用thrift -gen java生成jav ...
- dubbo/dubbox 增加原生thrift及avro支持
(facebook) thrift / (hadoop) avro / (google) probuf(grpc)是近几年来比较抢眼的高效序列化/rpc框架,dubbo框架虽然有thrift的支持,但 ...
- linux系统下使用流行的版本管理工具 Git
前几天被版本管理困扰了好久,主要是因为 没法回到之前的版本,新版本又出了问题真的很尴尬. 终于决定使用目前网上很火的版本管理工具-------Git 历史啥的就不说了,说些有用的. 我用的是oschi ...
- 版本管理工具svn简介
svn简介 SVN是一种C/S架构的版本管理软件 , 能够帮助我们保存开发过程中各个文件的所有历史版本, 你因此可以方便的找回软件的任何一个历史状态., 日常开发中经常用到. 安装使用 1. 在 u ...
- Thrift的TJsonProtocol协议分析
Thrift协议实现目前有二进制协议(TBinaryProtocol),紧凑型二进制协议(TCompactProtocol)和Json协议(TJsonProtocol). 前面的两篇文字从编码和协议原 ...
- Thrift的TBinaryProtocol二进制协议分析
先上张图,说明一下thrift的二进制协议是什么东东. 报文格式编码: bool类型: 一个字节的类型,两个字节的字段编号,一个字节的值(true:1,false:0). Byte类型: 一个字节的类 ...
随机推荐
- Ubuntu上安装git和创建工作区和提交文件!!!
1.安装git: sudo apt-get install git 2.创建工作区: 创建一个文件夹,sudo mkdir 文件文件夹.告诉git这是个工作区文件夹,sudo git init 文件夹 ...
- java中常用jar包
commons-io.jar:可以看成是java.io的扩展,用来帮助进行IO功能开发.它包含三个主要的领域:Utilityclasses-提供一些静态方法来完成公共任务.Filters-提供文件过滤 ...
- Zabbix agent 在windows上安装部署
Zabbix agent 在windows上安装部署 1.下载与解压 地址: http://www.zabbix.com/downloads/2.4.4/zabbix_agents_2.4.4.win ...
- python3用BeautifulSoup抓取图片地址
# -*- coding:utf-8 -*- #python 2.7 #XiaoDeng #http://tieba.baidu.com/p/2460150866 #抓取图片地址 from bs4 i ...
- 更新ruby:Error running 'requirements_osx_brew_update_system ruby-2.4.1报错解决
更新ruby时,报错: Failed to update Homebrew, follow instructions here: https://github.com/Homebrew/homebre ...
- 修改PHP上传文件大小设置
问题: 上传MV到服务器发现有最大文件限制: 50M 怎么修改呢? 度娘了一把, 修改php.ini文件的upload_max_filesize = 100M 及 post_max_size = 10 ...
- java 字符串中参数化符号${}的解析
我们在很多地方都能看到代表参数意义的符号${},可能我们在写一些框架的时候,有时候也需要用到这个符号,但他们是如何精确解析的?或者说需要我们自已写的时候,如何写?我们先来看以下的几个场景: 1.字符串 ...
- 【Android】Android传感器
1.加速度传感器2.磁场传感器3.方向传感器4.陀螺仪传感器5.重力传感器6.线性加速度传感器7.温度传感器8.光线传感器9.距离传感器10.压力传感器11.计步传感器 首先先查看测试的安卓机拥有的传 ...
- 使用Python解析JSON详解
为大家介绍如何使用 Python 语言来编码和解码 JSON 对象. JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写. JSON 函数 ...
- [svc]linux的inode和block-软硬链接
磁盘结构 容量 磁盘结构 磁盘容量 = 一个柱面大小柱面的总数 = 磁头数量每个磁道上的扇区数一个扇区大小柱面总数 存储容量=磁头数 × 磁道(柱面)数 × 每道扇区数 × 每扇区字节数 了解-什么是 ...