随着互联网行业的发展,数据越来越多,在分布式系统中往往我们需要设计一个能满足业务的全局ID
以下部分内容是来源于书中的知识,在我学习的过程中进行了整合。目前有以下几种方案可以实现
基于时间戳
比如流水号规则如下:XX-YYYYMMDD-N位随机数,这也是企业级应用开发常用的规则。此流水号对人比较友好,可识别性高,但容量受后面随机数的限制,且数据量越大,生成时难度越高。前三部分每天的流水号基本固定,后面的N位随机数生成后,需要校验此前不存在,可依赖redis的set机制,每天的随机数都写到一个set集合中[set容易达42亿之多,完全够用],重新生成后要与set集合作比对,以确保其唯一性。一天内不重复,再结合确定日期来保证其唯一性。
N位随机数生成时,可基于系统时间戳,再与一个大数取模生成。
UUID/GUID
最简单直接暴力的方式,虽然能够保证ID的唯一性,但是,它无法满足业务系统需要的很多其他特性,例如:时间粗略有序性,可反解和可制造型。另外,UUID产生的时候使用完全的时间数据,性能比较差,并且UUID比较长,占用空间大,间接导致数据库性能下降,更重要的是,UUID并不具有有序性。系统容量较小的时候可以采用,变大后不建议采用此方式。
Twitter-Snowflake
GitHub 地址:https://github.com/twitter/snowflake
Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。
它的语言是基于scala不太好扩展成分布式
基于redis的分布式ID生成器
GitHub 地址:https://github.com/hengyunabc/redis-id-generator
依赖redis的EVAL,EVALSHA两个命令,利用redis的lua脚本执行功能,在每个节点上通过lua脚本生成唯一ID。 生成的ID是64位的:
- 使用41 bit来存放时间,精确到毫秒,可以使用41年。
- 使用12 bit来存放逻辑分片ID,最大分片ID是4095
- 使用10 bit来存放自增长ID,意味着每个节点,每毫秒最多可以生成1024个ID
Redis提供了TIME命令,可以取得redis服务器上的秒数和微秒数。因些lua脚本返回的是一个四元组。
second, microSecond, partition, seq
客户端要自己处理,生成最终ID。
以上这些方式都是开源的,可以直接使用,但是其实我们也是可以设计一套自己的实现方案,主要根据snowflake的部分思想而来的额
我们要考虑一下问题:
1 全局ID的产生机制
2 ID是否可以翻转
3 服务的高可用
根据实际需要,可以提供秒级生产和毫秒级生产,前者每秒可以达到百万ID,后者可以产生1024个ID,在实际环境中往往选择前者,下面看具体类的实现。
整体项目结构:
我们首先bean包下面的两个类:
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.bean; import java.io.Serializable; /** * 生成ID的基本信息 * 这个根据实际业务需求可以进行扩展字段 * 比如 也可以增加一些特定的业务标志 */ public class TraceId implements Serializable{ private static final long serialVersionUID = 1L; //机器ID private long machineId; //序列ID private long sequenceId; //产生时间戳 private long time; //生成方法类型 private long genMethod; //ID选择类型 秒级和毫秒级 private long type; //版本标志 private long version; public long getMachineId() { return machineId; } public void setMachineId(long machineId) { this.machineId = machineId; } public long getSequenceId() { return sequenceId; } public void setSequenceId(long sequenceId) { this.sequenceId = sequenceId; } public long getTime() { return time; } public void setTime(long time) { this.time = time; } public long getGenMethod() { return genMethod; } public void setGenMethod(long genMethod) { this.genMethod = genMethod; } public long getType() { return type; } public void setType(long type) { this.type = type; } public long getVersion() { return version; } public void setVersion(long version) { this.version = version; } public TraceId(){ } public TraceId(long machineId, long sequenceId, long time, long genMethod, long type, long version) { super(); this.machineId = machineId; this.sequenceId = sequenceId; this.time = time; this.genMethod = genMethod; this.type = type; this.version = version; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("["); sb.append("machine=").append(machineId).append(","); sb.append("seq=").append(sequenceId).append(","); sb.append("time=").append(time).append(","); sb.append("genMethod=").append(genMethod).append(","); sb.append("type=").append(type).append(","); sb.append("version=").append(version).append("]"); return sb.toString(); }
这个类提供的位信息保存,所以需要懂得java位运算知识,如果不懂的需要先去了解下
public class TraceIdMeta { / private byte machineIdBits; private byte sequenceIdBits; private byte timeBits; private byte genMethodBits; private byte typeBits; private byte versionBits; public TraceIdMeta(long machineIdBits, long sequenceIdBits, long timeBits, long genMethodBits, long typeBits, long versionBits) { super(); // long类型转换为byte this.machineIdBits = (byte) machineIdBits; this.sequenceIdBits = (byte) sequenceIdBits; this.timeBits = (byte) timeBits; this.genMethodBits = (byte) genMethodBits; this.typeBits = (byte) typeBits; this.versionBits = (byte) versionBits; } public byte getMachineIdBits() { return machineIdBits; } public void setMachineIdBits(byte machineIdBits) { this.machineIdBits = machineIdBits; } public byte getSequenceIdBits() { return sequenceIdBits; } public void setSequenceIdBits(byte sequenceIdBits) { this.sequenceIdBits = sequenceIdBits; } public byte getTimeBits() { return timeBits; } public void setTimeBits(byte timeBits) { this.timeBits = timeBits; } public byte getGenMethodBits() { return genMethodBits; } public void setGenMethodBits(byte genMethodBits) { this.genMethodBits = genMethodBits; } public byte getTypeBits() { return typeBits; } public void setTypeBits(byte typeBits) { this.typeBits = typeBits; } public byte getVersionBits() { return versionBits; } public void setVersionBits(byte versionBits) { this.versionBits = versionBits; } // 提供相关位运算计算 // 事后需要好好探索 /** * 计算支持机器的台数 这个结果是1023 */ public long getMachineBitsMask() { return -1l ^ -1l << machineIdBits; } /** * 能产生的最大序列ID个数 秒级内1048575 毫秒内1023 * * @return */ public long getSeqBitsMask() { return -1l ^ -1l << sequenceIdBits; } public long getTimeBitsMask() { return -1L ^ -1L << timeBits; } public long getGenMethodBitsStartPos() { return machineIdBits + sequenceIdBits + timeBits; } public long getGenMethodBitsMask() { return -1l ^ -1l << genMethodBits; } public long getTypeBitsStartPos() { return machineIdBits + sequenceIdBits + timeBits + genMethodBits; } public long getVersionBitsStartPos(){ return machineIdBits + sequenceIdBits + timeBits + genMethodBits + typeBits; } public long getTypeBitsMask() { return -1l ^ -1l << typeBits; } public long getSeqBitsStartPos() { return machineIdBits; } public long getTimeBitsStartPos(){ return machineIdBits + timeBits; } public long getVersionBitsMask(){ return -1L ^ -1L << versionBits; } public static void main(String[] args) { long a = 1420041600000L; System.out.println(new Long(a).intValue()); } }
定义生成ID的两种类型,最大值和最小细粒度
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.enums; /** * @author 18011618 * */ public enum TraceIdType { //最大值-秒级内生成ID-可以达到几十万级别 MAX_PEAK("max-peak"), //最小值-毫秒级内生成ID -最大是1024 MIN_GRANULARITY("min-granularity"); private String name; TraceIdType(String name){ this.name = name; } /** * 返回枚举类型的标志值 * @return */ public long value() { switch (this) { case MAX_PEAK: return 0; case MIN_GRANULARITY: return 1; default: return 0; } } @Override public String toString() { return this.name; } /** * 返回枚举类型 * @param name * @return */ public static TraceIdType parse(String name) { if ("min-granularity".equals(name)) return MIN_GRANULARITY; else if ("max-peak".equals(name)) return MAX_PEAK; return null; } /** * 返回枚举类型 * @param type * @return */ public static TraceIdType parse(long type) { if (type == 1) return MIN_GRANULARITY; else if (type == 0) return MAX_PEAK; return null; } }
根据上面的枚举类型实例化两种不同的位运算信息,用工厂进行封装,方便隔离和使用
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.factory; import trace_service.bean.TraceIdMeta; import trace_service.enums.TraceIdType; /** * @author 18011618 *构建traceid的位数据类型 *最大峰值型 *最小细粒度型 */ public class TraceIdMetaFactory { private static TraceIdMeta maxPeak = new TraceIdMeta(10, 20, 30, 2, 1, 1); private static TraceIdMeta minGranularity = new TraceIdMeta(10, 10, 40, 2, 1, 1); /** * 根据枚举类型实例化ID创造类型 * @param type * @return */ public static TraceIdMeta getTraceIdMeta(TraceIdType type) { if (TraceIdType.MAX_PEAK.equals(type)) { return maxPeak; } else if (TraceIdType.MIN_GRANULARITY.equals(type)) { return minGranularity; } return null; } }
接下来定义服务的核心接口,具体方法见接口定义
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.service; import java.util.Date; import trace_service.bean.TraceId; /** * @author 18011618 * ID服务接口 */ public interface TraceIdService { /** * 生成全局ID * @return */ public long genTraceId(); /** * 反转ID * @return */ public TraceId explainTraceId(long traceId); /** * 反转时间 * @param time * @return */ public Date transTime(long time);
和 为什么不放在一个接口,主要是考虑接口隔离性,后面可以使用多继承,使用起来更加灵活
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.service; /** * @author 18011618 * 根据条件制造ID */ public interface TraceIdMakeService { /** * 根据条件制造全局ID * @param time * @param seq * @return */ public long makeId(long time, long seq); /** * 根据条件制造全局ID * @param time * @param seq * @return */ public long makeId(long time, long seq, long machine); /** * 根据条件制造全局ID * @param time * @param seq * @return */ public long makeId(long genMethod, long time, long seq, long machine); /** * 根据条件制造全局ID * @param time * @param seq * @return */ public long makeId(long type, long genMethod, long time,long seq, long machine); /** * 根据条件制造全局ID * @param time * @param seq * @return */ public long makeId(long version, long type, long genMethod,long time, long seq, long machine); }
我们先跳过实现类,来分析其他的实现机制
& 如何获取机器的ID,这个是核心属性之一,通常我们都会想到,通过属性配置文件,数据库保存,同步到ZK等等
在这里为了方便演示,只实现了最简单的通过属性配置文件,其它的方式,大家可以自己实现,只要实现接口里面的一个方法就可以了
具体实现的包模块:
看接口定义:
package trace_service.provider; /** * 获取机器ID * @author 18011618 * */ public interface MachineIdProvider { public long getMachineId(); }
看其中的一个实现:
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.provider.impl; import trace_service.provider.MachineIdProvider; /** * @author 18011618 * 基于属性配置文件 通常用于测试 */ public class PropertyMachineIdProvider implements MachineIdProvider{ private long machineId; public long getMachineId() { return machineId; } public void setMachineId(long machineId){ this.machineId = machineId; } }
如果大家要有多种实现方式,这里可以使用策略模式+简单工厂进行封装,不要让客户端接触选择的具体细节,ok
有了机器ID,再获取一个时间戳,我们就可以获取到一个完整的TraceId
接下来再看具体实现,实际环境中,因为是在分布式中使用,所以我们肯定考虑同步机制来实现,常用的同步机制有3中方式
& 基于方法同步
& 基于锁同步
& 基于CAS原子操作
同样这里为了方便演示,只选择其中一种实现:
首先看接口:
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.populator; import trace_service.bean.TraceId; import trace_service.bean.TraceIdMeta; import trace_service.enums.TraceIdType; /** * @author 18011618 * */ public interface TraceIdPopulator { /** * 给traceid进行time和sequenceid赋值 * @param traceId * @param traceIdType */ public void traceIdPopulator(TraceId traceId,TraceIdMeta traceIdMeta); }
重置接口:
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.populator; /** * @author 18011618 * */ public interface TraceIdResetPopulator { public void resetTraceId(); }
我们看接口的模板实现类:
package trace_service.populator; import trace_service.bean.TraceId; import trace_service.bean.TraceIdMeta; import trace_service.enums.TraceIdType; import trace_service.util.TimeUtils; /** * @author 18011618 * */ public abstract class AbstractTraceIdPopulator implements TraceIdPopulator,TraceIdResetPopulator { //初始化序列ID protected long sequenceId = 0; //初始化上一次时间戳 protected long lastTimeStamp = -1l; public AbstractTraceIdPopulator(){ super(); } public void resetTraceId() { sequenceId = 0; lastTimeStamp = -1l; } public void traceIdPopulator(TraceId traceId,TraceIdMeta idMeta) { //获取当前时间戳 long timeStamp = TimeUtils.genTime(TraceIdType.parse(traceId.getType())); //验证时间戳 TimeUtils.validateTimestamp(lastTimeStamp, timeStamp); //如果当前时间戳是同一个时间戳 那就累加序列ID if (timeStamp == lastTimeStamp) { sequenceId++; sequenceId &= idMeta.getSeqBitsMask(); if (sequenceId==0) { //如果当前时间戳用完了 需要阻塞到下一个时间戳 TimeUtils.tillNextTimeUnit(lastTimeStamp, TraceIdType.parse(traceId.getType())); } } else{ //初始化 lastTimeStamp = timeStamp; sequenceId = 0; } //设置序列ID traceId.setSequenceId(sequenceId); //设置时间戳 traceId.setTime(timeStamp); } }
后面的子类根据需要加上对应的同步方法即可:比如下面这种基于锁的机制
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.populator.impl; import trace_service.bean.TraceId; import trace_service.bean.TraceIdMeta; import trace_service.populator.AbstractTraceIdPopulator; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author 18011618 * 基于锁的机制 */ public class LockTraceIdPopulator extends AbstractTraceIdPopulator { private Lock lock = new ReentrantLock(); @Override public void resetTraceId() { super.resetTraceId(); } @Override public void traceIdPopulator(TraceId traceId, TraceIdMeta idMeta) { lock.lock(); try { super.traceIdPopulator(traceId, idMeta); } catch (Exception e) { e.printStackTrace(); } finally{ lock.unlock(); } } }
其它两种方式可以自行实现,如果有多种实现方式,可以把选择参数加载在JVM启动参数里面,通过获取到的参数选择不同的实现
上面的实现细节,一定要考虑验证机制,因为如果不验证的话,当机器出现了回拨就会发生数据错乱。
OK,所有的属性都获取到了,接下来就需要提供转换机制,可以把一个完成的对象转换成一个Long类型的ID,也可以根据需要把Long类型的ID转换为一个对象,下面我们看接口和对应的实现:
首先看接口定义:
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.convert; import trace_service.bean.TraceId; /** * @author 18011618 * 提供对ID进行相关转换机制 */ public interface TraceIdConvert { /** * 把一个ID最终转化成一个long类型的数值 * @param traceId * @return */ public long convert(TraceId traceId); /** * 把一个long类型的数转换为对象 * @param traceId * @return */ public TraceId convert(long traceId); }
再看具体实现:
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.convert.impl; import trace_service.bean.TraceId; import trace_service.bean.TraceIdMeta; import trace_service.convert.TraceIdConvert; import trace_service.enums.TraceIdType; import trace_service.factory.TraceIdMetaFactory; /** * @author 18011618 * 实现traceid转换 */ public class TraceIdConvertImpl implements TraceIdConvert { private TraceIdMeta traceIdMeta; public TraceIdConvertImpl() { } //基于枚举类型 public TraceIdConvertImpl(TraceIdType traceIdType) { this(TraceIdMetaFactory.getTraceIdMeta(traceIdType)); } //基于元数据 public TraceIdConvertImpl(TraceIdMeta traceIdMeta) { this.traceIdMeta = traceIdMeta; } @Override public long convert(TraceId traceId) { return doConvert(traceId, traceIdMeta); } /** * 对象转换为long类型 * @param traceId * @param traceIdMeta * @return */ private long doConvert(TraceId traceId, TraceIdMeta traceIdMeta) { long ret = 0; ret |= traceId.getMachineId(); ret |= traceId.getSequenceId() << traceIdMeta.getSeqBitsStartPos(); ret |= traceId.getTime() << traceIdMeta.getTimeBitsStartPos(); ret |= traceId.getGenMethod() << traceIdMeta.getGenMethodBitsStartPos(); ret |= traceId.getType() << traceIdMeta.getTypeBitsStartPos(); ret |= traceId.getVersion() << traceIdMeta.getVersionBitsStartPos(); return ret; } @Override public TraceId convert(long traceId) { return doConvert(traceId, traceIdMeta); } /** * long类型ID转换为对象 * @param traceId * @param idMeta * @return */ private TraceId doConvert(long traceId, TraceIdMeta idMeta) { TraceId ret = new TraceId(); ret.setMachineId(traceId & idMeta.getMachineBitsMask()); ret.setSequenceId((traceId) & idMeta.getSeqBitsMask()); ret.setTime((traceId >>> idMeta.getTimeBitsStartPos()) & idMeta.getTimeBitsMask()); ret.setGenMethod((traceId >>> idMeta.getGenMethodBitsStartPos()) & idMeta.getGenMethodBitsMask()); ret.setType((traceId >>> idMeta.getTypeBitsStartPos()) & idMeta.getTypeBitsMask()); ret.setVersion((traceId >>> idMeta.getVersionBitsStartPos()) & idMeta.getVersionBitsMask()); return ret; } }
这两个方法都是完全依赖于位运算而实现的...,
接下来我们再看之间的服务接口对应的实现,对于接口而言我们同样提供了一个抽象类来进行功能的封装,把需要子类实现的方法
封装成抽象...
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.service; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import trace_service.bean.TraceId; import trace_service.bean.TraceIdMeta; import trace_service.convert.TraceIdConvert; import trace_service.convert.impl.TraceIdConvertImpl; import trace_service.enums.TraceIdType; import trace_service.provider.MachineIdProvider; import trace_service.util.TimeUtils; /** * @author 18011618 * */ public abstract class AbstractTraceIdService implements TraceIdMakeService,TraceIdService { protected final Logger log = LoggerFactory.getLogger(this.getClass()); protected long machineId = -1; protected long genMethod = 0; protected long type = 0; protected long version = 0; protected TraceIdType idType; protected TraceIdMeta idMeta; protected TraceIdConvert idConverter; protected MachineIdProvider machineIdProvider; public AbstractTraceIdService() { idType = TraceIdType.MAX_PEAK; } public AbstractTraceIdService(String type) { idType = TraceIdType.parse(type); } public AbstractTraceIdService(TraceIdType type) { idType = type; } public long getMachineId() { return machineId; } public void setMachineId(long machineId) { this.machineId = machineId; } public long getGenMethod() { return genMethod; } public void setGenMethod(long genMethod) { this.genMethod = genMethod; } public long getType() { return type; } public void setType(long type) { this.type = type; } public long getVersion() { return version; } public void setVersion(long version) { this.version = version; } public TraceIdType getIdType() { return idType; } public void setIdType(TraceIdType idType) { this.idType = idType; } public TraceIdMeta getIdMeta() { return idMeta; } public void setIdMeta(TraceIdMeta idMeta) { this.idMeta = idMeta; } public TraceIdConvert getIdConverter() { return idConverter; } public void setIdConverter(TraceIdConvert idConverter) { this.idConverter = idConverter; } public MachineIdProvider getMachineIdProvider() { return machineIdProvider; } public void setMachineIdProvider(MachineIdProvider machineIdProvider) { this.machineIdProvider = machineIdProvider; } public Logger getLog() { return log; } /** * 生成全局ID */ public long genTraceId() { TraceId id = new TraceId(); //设置其他基本属性 id.setMachineId(machineId); id.setType(type); id.setVersion(version); id.setGenMethod(genMethod); //设置序列和时间戳 populateId(id); //转换为long类型 long traceId = idConverter.convert(id); if (log.isTraceEnabled()) { log.trace(String.format("Id: %s => %d", id, traceId)); } return traceId; } public TraceId explainTraceId(long traceId) { return idConverter.convert(traceId); } public Date transTime(long time) { if (idType == TraceIdType.MAX_PEAK) { return new Date(time * 1000 + TimeUtils.EPOCH); } else if (idType == TraceIdType.MIN_GRANULARITY) { return new Date(time + TimeUtils.EPOCH); } return null; } public long makeId(long time, long seq) { return makeId(time, seq, machineId); } public long makeId(long time, long seq, long machine) { return makeId(genMethod, time, seq, machine); } public long makeId(long genMethod, long time, long seq, long machine) { return makeId(type, genMethod, time, seq, machine); } public long makeId(long type, long genMethod, long time, long seq,long machine) { return makeId(version, type, genMethod, time, seq, machine); } public long makeId(long version, long type, long genMethod, long time,long seq, long machine) { TraceIdType traceIdType = TraceIdType.parse(type); TraceId id = new TraceId(machine, seq, time, genMethod, type, version); return new TraceIdConvertImpl(traceIdType) .convert(id); } //定义抽象方法 留给具体的子类来实现 protected abstract void populateId(TraceId id); }
接下来看最终的实现类:
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.service.impl; import trace_service.bean.TraceId; import trace_service.convert.impl.TraceIdConvertImpl; import trace_service.enums.TraceIdType; import trace_service.factory.TraceIdMetaFactory; import trace_service.populator.TraceIdPopulator; import trace_service.populator.impl.LockTraceIdPopulator; import trace_service.service.AbstractTraceIdService; /** * @author 18011618 * */ public class TraceIdServiceImpl extends AbstractTraceIdService { public TraceIdPopulator getTraceIdPopulator() { return traceIdPopulator; } public void setTraceIdPopulator(TraceIdPopulator traceIdPopulator) { this.traceIdPopulator = traceIdPopulator; } private TraceIdPopulator traceIdPopulator; public TraceIdServiceImpl() { super(); initPopulatorId(); } public TraceIdServiceImpl(String type) { super(type); initPopulatorId(); } public TraceIdServiceImpl(TraceIdType traceIdType) { super(traceIdType); initPopulatorId(); } public void init() { this.machineId = machineIdProvider.getMachineId(); if (machineId < 0) { log.error("The machine ID is not configured properly so that Vesta Service refuses to start."); throw new IllegalStateException( "The machine ID is not configured properly so that Vesta Service refuses to start."); } if (this.idMeta == null) { setIdMeta(TraceIdMetaFactory.getTraceIdMeta(idType)); setType(idType.value()); } else { if (this.idMeta.getTimeBits() == 30) { setType(0); } else if (this.idMeta.getTimeBits() == 40) { setType(1); } else { throw new RuntimeException( "Init Error. The time bits in IdMeta should be set to 30 or 40!"); } } setIdConverter(new TraceIdConvertImpl(this.idMeta)); } public void initPopulatorId() { traceIdPopulator = new LockTraceIdPopulator(); } @Override protected void populateId(TraceId id) { traceIdPopulator.traceIdPopulator(id, this.idMeta); } }
代码注释写的很清楚了,就不啰嗦了...
OK,至此服务就算实现了,最后我们要考虑如何和spring结合,这里要注意,因为我们希望在spring一启动的时候就能加载这个
service的实例(包括各种依赖),所以这里定一个工厂bean实现spring提供的FactoryBean,配置对应的init方法,从而达到初始化对象
具体看代码实现:
/** * @author 贾红平 @date @version 版本标识 */ package trace_service.factory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.FactoryBean; import trace_service.provider.impl.PropertyMachineIdProvider; import trace_service.service.TraceIdService; import trace_service.service.impl.TraceIdServiceImpl; /** * @author 18011618 * 针对用户选择不同 创建不同的服务实例 */ public class TraceIdServiceFactoryBean implements FactoryBean<TraceIdService> { protected final Logger log = LoggerFactory.getLogger(this.getClass()); public enum Type { PROPERTY, IP_CONFIGURABLE, DB } private Type providerType; private long machineId; private String ips; private String dbUrl; private String dbName; private String dbUser; private String dbPassword; private long genMethod = -1; private long type = -1; private long version = -1; private TraceIdService traceIdService; @Override public TraceIdService getObject() throws Exception { return traceIdService; } /** * 在spring初始化的时候,就根据这个方法来实例化对应的bean * 配置在spring中的init-method = init() */ public void init() { if (providerType == null) { log.error("The type of Id service is mandatory."); throw new IllegalArgumentException( "The type of Id service is mandatory."); } switch (providerType) { case PROPERTY: traceIdService = constructPropertyIdService(machineId); break; case IP_CONFIGURABLE: traceIdService = constructIpConfigurableIdService(ips); break; case DB: traceIdService = constructDbIdService(dbUrl, dbName, dbUser, dbPassword); break; } } // 通过配置构建服务 private TraceIdService constructPropertyIdService(long traceId) { log.info("Construct Property TraceIdService machineId {}", machineId); PropertyMachineIdProvider propertyMachineIdProvider = new PropertyMachineIdProvider(); propertyMachineIdProvider.setMachineId(machineId); TraceIdServiceImpl idServiceImpl = new TraceIdServiceImpl(); idServiceImpl.setMachineIdProvider(propertyMachineIdProvider); if (genMethod != -1) idServiceImpl.setGenMethod(genMethod); if (type != -1) idServiceImpl.setType(type); if (version != -1) idServiceImpl.setVersion(version); idServiceImpl.init(); return idServiceImpl; } // 基于IP配置列表 private TraceIdService constructIpConfigurableIdService(String ips) { log.info("Construct Ip Configurable TraceIdService ips {}", ips); IpConfigurableMachineIdProvider ipConfigurableMachineIdProvider = new IpConfigurableMachineIdProvider( ips); TraceIdServiceImpl idServiceImpl = new TraceIdServiceImpl(); idServiceImpl.setMachineIdProvider(ipConfigurableMachineIdProvider); if (genMethod != -1) idServiceImpl.setGenMethod(genMethod); if (type != -1) idServiceImpl.setType(type); if (version != -1) idServiceImpl.setVersion(version); idServiceImpl.init(); return idServiceImpl; } // 基于DB配置 private TraceIdService constructDbIdService(String dbUrl, String dbName, String dbUser, String dbPassword) { return null; } @Override public Class<?> getObjectType() { return TraceIdService.class; } public Type getProviderType() { return providerType; } public void setProviderType(Type providerType) { this.providerType = providerType; } public long getMachineId() { return machineId; } public void setMachineId(long machineId) { this.machineId = machineId; } public String getIps() { return ips; } public void setIps(String ips) { this.ips = ips; } public String getDbUrl() { return dbUrl; } public void setDbUrl(String dbUrl) { this.dbUrl = dbUrl; } public String getDbName() { return dbName; } public void setDbName(String dbName) { this.dbName = dbName; } public String getDbUser() { return dbUser; } public void setDbUser(String dbUser) { this.dbUser = dbUser; } public String getDbPassword() { return dbPassword; } public void setDbPassword(String dbPassword) { this.dbPassword = dbPassword; } public long getGenMethod() { return genMethod; } public void setGenMethod(long genMethod) { this.genMethod = genMethod; } public long getType() { return type; } public void setType(long type) { this.type = type; } public long getVersion() { return version; } public void setVersion(long version) { this.version = version; } public TraceIdService getTraceIdService() { return traceIdService; } public void setTraceIdService(TraceIdService traceIdService) { this.traceIdService = traceIdService; } public boolean isSingleton() { return true; } }
最后增加spring配置文件
代码写完了,我们的写个测试类来测试一下行不行吧:
测试类:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import trace_service.bean.TraceId; import trace_service.service.TraceIdService; /** * @author 18011618 * */ public class TraceIdServiceTest { public static void main(String[] args) { String path ="spring/trace-service-sample.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(path); final TraceIdService traceIdService = (TraceIdService)context.getBean("traceIdService"); for (int i = 0; i < 100; i++) { new Thread(){ @Override public void run() { long traceId = traceIdService.genTraceId(); System.out.println("traceId:"+traceId); TraceId id = traceIdService.explainTraceId(traceId); System.out.println("ID:"+id); } }.start(); } } }
当然在真实环境中,我们需要把服务进行封装提供给别人用,一般都是http形式的,所以接下来继续完善如何把上面的服务封装成http,其实很简单可以通过spring mvc机制就OK了,当时我们要考虑性能问题,所以这里介绍使用netty进行底层服务的封装:
这里如果不太熟悉netty的,需要先去了解一下:当然我会把解释写的清楚点:实现思路
就是通过用户访问的URL截取后面的api,然后根据对应的api调用对应service的方法,获取到对应的结果,然后把结果通过
response机制返回给客户端
我们主要看handler是如何解析的:
package trace_rest_netty.handler; import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH; import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.util.CharsetUtil; import java.net.URI; import java.nio.charset.Charset; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import trace_service.service.TraceIdService; /** * @author 18011618 * */ @Sharable @SuppressWarnings("all") public class TraceIdServiceHandler extends ChannelInboundHandlerAdapter { private static final String ID = "id"; private static final String VERSION = "version"; private static final String TYPE = "type"; private static final String GENMETHOD = "genMethod"; private static final String MACHINE = "machine"; private static final String TIME = "time"; private static final String SEQ = "seq"; // api请求 private static final String ACTION_GENID = "/genid"; TraceIdService traceIdService; // 实例化服务实例 public TraceIdServiceHandler() { ApplicationContext ac = new ClassPathXmlApplicationContext("spring/trace_netty_main.xml"); traceIdService = (TraceIdService) ac.getBean("traceIdService"); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if(msg instanceof HttpRequest){ HttpRequest httpRequest = (HttpRequest) msg; URI uri = new URI(httpRequest.getUri()); long id = -1; long time = -1; long version = -1; long type = -1; long genmethod = -1; long machine = -1; long seq = -1; QueryStringDecoder decoder = new QueryStringDecoder(uri); Map<String, List<String>> uriAttributes = decoder.parameters(); //获取参数并转化 for (Entry<String, List<String>> attr : uriAttributes.entrySet()) { for (String attrVal : attr.getValue()) { if (ID.equals(attr.getKey())) { id = Long.parseLong(attrVal); } else if (TIME.equals(attr.getKey())) { time = Long.parseLong(attrVal); } else if (VERSION.equals(attr.getKey())) { version = Long.parseLong(attrVal); } else if (TYPE.equals(attr.getKey())) { type = Long.parseLong(attrVal); } else if (GENMETHOD.equals(attr.getKey())) { genmethod = Long.parseLong(attrVal); } else if (MACHINE.equals(attr.getKey())) { machine = Long.parseLong(attrVal); } else if (SEQ.equals(attr.getKey())) { seq = Long.parseLong(attrVal); } } } StringBuffer sbContent = new StringBuffer(); //sbContent.append("10010100"); if (ACTION_GENID.equals(uri.getPath())) { //获取到结果 long idl = traceIdService.genTraceId(); sbContent.append(idl); } //组成成响应 FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(sbContent.toString().getBytes(Charset.forName("UTF-8")))); response.headers().set(CONTENT_TYPE, "text/plain"); response.headers().set(CONTENT_LENGTH, response.content().readableBytes()); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } //如果是httpcontent if(msg instanceof HttpContent){ HttpContent content = (HttpContent) msg; ByteBuf buf = content.content(); System.out.println(buf.toString(CharsetUtil.UTF_8)); buf.release(); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.err.println("sorry,http happend error!"); cause.printStackTrace(); ctx.close(); } }
接下来再看看服务端的代码:
package trace_rest_netty.server; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import trace_rest_netty.initalizer.TraceIdServiceInitalizer; /** * @author 18011618 * 通过netty服务启动应用 */ public class TraceIdServiceNettyServer { //主线程 private static EventLoopGroup bossEventLoopGroup; //工作线程 private static EventLoopGroup workEventLoopGroup; //服务端 private static ServerBootstrap serverBootstrap; /** * 静态化 */ static{ bossEventLoopGroup = new NioEventLoopGroup(); workEventLoopGroup = new NioEventLoopGroup(); serverBootstrap = new ServerBootstrap(); //指定服务端的channel serverBootstrap.channel(NioServerSocketChannel.class); //服务端指定主线程和工作者线程 serverBootstrap.group(bossEventLoopGroup, workEventLoopGroup); //指定日志大小 serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024); //添加业务handler serverBootstrap.childHandler(new TraceIdServiceInitalizer()); } public static void run(int port){ try { //异步获取结果 ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); //关闭通道才能拿到返回值 channelFuture.channel().closeFuture().sync(); } catch (Throwable e) { e.printStackTrace(); } finally{ //最后关闭相关线程 bossEventLoopGroup.shutdownGracefully(); workEventLoopGroup.shutdownGracefully(); } } public static void main(String[] args) { run(8888); } }
最后看一下初始化编解码器和业务handler被添加到channelpiplie中:
package trace_rest_netty.initalizer; import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpServerCodec; import trace_rest_netty.handler.TraceIdServiceHandler; /** * @author 18011618 * */ public class TraceIdServiceInitalizer extends ChannelInitializer<SocketChannel>{ private TraceIdServiceHandler handler = new TraceIdServiceHandler(); @Override protected void initChannel(SocketChannel ch) throws Exception { //添加一个默认decoder options ch.pipeline().addLast(new HttpServerCodec()); ch.pipeline().addLast(handler); } }ok 现在只要启动上面的那个
TraceIdServiceNettyServer 然后就可以通过http形式访问了,比如http://locahost:8080/genTraceId 就可以看到返回的结果了