分布式全局ID设计

随着互联网行业的发展,数据越来越多,在分布式系统中往往我们需要设计一个能满足业务的全局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进行timesequenceid赋值
    * @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 就可以看到返回的结果了

猜你喜欢

转载自blog.csdn.net/qq_18603599/article/details/80350574