产生Id

标签: id | 发表时间:2015-10-10 12:33 | 作者:mxdxm
出处:http://www.iteye.com
public class IdWorker {
    // worker编号位数
    private static final long WORKER_BITS = 6L;

    // worker编号最大值,决定支持的部署节点数量
    private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_BITS);

    // 毫秒内自增位数,每毫秒最大序号支持65535
    private static final long SEQUENCE_BITS = 16L;

    // worker编号偏移量
    private static final long WORKER_SHIFT = SEQUENCE_BITS;

    // 时间偏移量
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_BITS;

    // 序号掩码
    private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);

    // 毫秒基线:2015-01-01 00:00:00
    private static final long BASE_TIMESTAMP = 1420041600000L;

    private final long workerId;
    private volatile long sequence = 0L;
    private volatile long lastTimestamp = -1L;

    /**
     * 从环境变量中获取worker编号,每个部署环境编号不能重复
     */
    public IdWorker() {
        this(Misc.parseLong(System.getProperty("app.workerId"), 0L));
    }

    /**
     * 每个部署环境编号不能重复
     * 
     * @param workerId Worker编号
     */
    public IdWorker(long workerId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(String.format("Worker Id can't be greater than %d or less than 0",
                    MAX_WORKER_ID));
        }
        this.workerId = workerId;
    }

    /**
     * 获取下一个ID
     * 
     * @return
     */
    public synchronized long next() {
        long ts = System.currentTimeMillis();
        if (lastTimestamp == ts) {
            sequence = (sequence + 1) & SEQUENCE_MASK;

            // 当前毫秒的序号已经用完,取下一个毫秒
            if (sequence == 0) {
                ts = nextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        if (ts < lastTimestamp) {
            throw new RuntimeException(String.format(
                    "Clock moved backwards. Refusing to generate id for %d millseconds", lastTimestamp - ts));
        }

        lastTimestamp = ts;
        return ((ts - BASE_TIMESTAMP) << TIMESTAMP_SHIFT) | (workerId << WORKER_SHIFT) | sequence;
    }

    // FIXME:当机器时间被调早时,会导致CPU过高。此处可以考虑进行阻塞
    private long nextMillis(final long lastTimestamp) {
        long ts = System.currentTimeMillis();
        while (ts <= lastTimestamp) {
            ts = System.currentTimeMillis();
        }
        return ts;
    }

}

 

import java.io.File;
import java.security.MessageDigest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import com.assess.lang.AppException;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

public final class TextUtil {
    public static final int NO_TRIM = 0;
    public static final int TRIM_TO_NULL = 1;
    public static final int TRIM_TO_BLANK = 2;

    /**
     * 创建文件目录时使用的质数,默认每个文件夹下使用
     */
    private static final int SEED = 8999;
    private static final char[] CHARS_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
            .toCharArray();
    private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();

    private TextUtil() {
    }

    /**
     * 将日期(时间)格式化成指定格式的字符串
     * 
     * @param date 日期(时间)对象
     * @param pattern 格式化模式
     * @return
     */
    public static String formatTime(Date date, String pattern) {
        return new SimpleDateFormat(pattern).format(date);
    }

    /**
     * 将日期(时间)字符串解析成日期时间对象
     * 
     * @param val 日期(时间)字符串
     * @param pattern 时间模式
     * @return 返回日期对象,如果解析失败,则返回null
     */
    public static Date parseTime(String val, String pattern) {
        try {
            return new SimpleDateFormat(pattern).parse(val);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 使用单个字符分割字符串
     * 
     * @param value
     * @param delim
     * @return
     */
    public static String[] split(String value, char delim) {
        return split(value, delim, TRIM_TO_BLANK);
    }

    /**
     * 使用单个字符分割字符串
     * 
     * @param value
     * @param delim
     * @param trimFlag Trim方式
     * @return
     */
    public static String[] split(String value, char delim, int trimFlag) {
        final int end = value.length();
        final List<String> res = new ArrayList<String>();

        int start = 0;
        for (int i = 0; i < end; i++) {
            if (value.charAt(i) == delim) {
                if (start == i) {
                    res.add("");
                } else {
                    res.add(value.substring(start, i));
                }
                start = i + 1;
            }
        }

        if (start == 0) {
            res.add(value);
        } else if (start != end) {
            res.add(value.substring(start, end));
        } else {
            for (int i = res.size() - 1; i >= 0; i--) {
                if (res.get(i).isEmpty()) {
                    res.remove(i);
                } else {
                    break;
                }
            }
        }

        String[] ret = res.toArray(new String[res.size()]);
        if (trimFlag > 0) {
            for (int i = 0; i < ret.length; ++i) {
                if (trimFlag == TRIM_TO_NULL) {
                    ret[i] = trimToNull(ret[i]);
                } else {
                    ret[i] = trimToEmpty(ret[i]);
                }
            }
        }
        return ret;
    }

    /**
     * 对字符串进行MD5编码后转为16进制字符串
     * 
     * @param src
     * @return
     */
    public static String md5(String src, String salt) {
        return toHexString(digest("MD5", src, salt));
    }

    /**
     * 对对象进行MD5编码后转为16进制字符串
     * 
     * @param src
     * @return
     */
    public static String md5(Object obj, String salt) {
        return toHexString(digest("MD5", toBytes(obj), salt));
    }

    /**
     * 对字符串进行SHA-256编码后转为16进制字符串
     * 
     * @param src
     * @return
     */
    public static String sha256(String src, String salt) {
        return toHexString(digest("SHA-256", src, salt));
    }

    /**
     * 对字符串进行SHA-256编码后转为16进制字符串
     * 
     * @param src
     * @return
     */
    public static byte[] bSha256(String src, String salt) {
        return digest("SHA-256", src, salt);
    }

    /**
     * 合并两个字节数组
     * 
     * @param x
     * @param y
     * @return
     */
    public static byte[] merge(byte[] x, byte[] y) {
        byte[] b = new byte[x.length + y.length];

        System.arraycopy(x, 0, b, 0, x.length);
        System.arraycopy(y, 0, b, x.length, y.length);

        return b;
    }

    /**
     * 转换字节数组为16进制字串
     * 
     * @param b 字节数组
     * @return 16进制字串
     */
    public static String toHexString(byte[] b) {
        StringBuilder buf = new StringBuilder(b.length * 2);
        int n = 0;
        for (int i = 0; i < b.length; i++) {
            n = b[i];
            if (n < 0) {
                n += 256;
            }
            buf.append(HEX_DIGITS[n >> 4]).append(HEX_DIGITS[n % 16]);
        }
        return buf.toString();
    }

    /**
     * 根据给定的key和seed,生成唯一的有效文件路径。 有效文件路径需要满足: 1、每个文件夹下不超过10000个文件; 2、可以根据文件路径和seed还原key
     * 
     * @param key 用来生成文件路径的一个唯一数值,一般为文件的ID
     * @param deep 生成文件路径的有效深度,实际生成的路径深度为deep+1
     * @return
     */
    public static String dir(long key) {
        return dir(key, 2, SEED);
    }

    /**
     * 根据给定的key和seed,生成唯一的有效文件路径。 有效文件路径需要满足: 1、每个文件夹下不超过10000个文件; 2、可以根据文件路径和seed还原key
     * 
     * @param key 用来生成文件路径的一个唯一数值,一般为文件的ID
     * @param deep 生成文件路径的有效深度,实际生成的路径深度为deep+1
     * @return
     */
    public static String dir(long key, int deep) {
        return dir(key, deep, SEED);
    }

    /**
     * 根据给定的key和seed,生成唯一的有效文件路径。 有效文件路径需要满足: 1、每个文件夹下不超过10000个文件; 2、可以根据文件路径和seed还原key
     * 
     * @param key 用来生成文件路径的一个唯一数值,一般为文件的ID
     * @param deep 生成文件路径的有效深度,实际生成的路径深度为deep+1
     * @param seed 文件夹最大数量,有效取值为小于10000的质数
     * @return
     */
    public static String dir(long key, int deep, long seed) {
        StringBuilder buf = new StringBuilder(32);
        for (int i = 0; (i < deep) && (key > 0); i++) {
            base62(buf, key % seed);
            buf.append(File.separatorChar);
            key = key / seed;
        }
        return buf.append(key).toString();
    }

    /**
     * 生成随机的字符串,字符串有效字符为数字和字母
     * 
     * @param length 字符串长度
     * @return
     */
    public static String salt(int length) {
        StringBuilder buf = new StringBuilder(length);

        for (int i = 0; i < length; i++) {
            buf.append(CHARS_DIGITS[(int) (Math.random() * CHARS_DIGITS.length)]);
        }

        return buf.toString();
    }

    public static boolean isBlank(CharSequence cs) {
        int strLen;
        if ((cs == null) || ((strLen = cs.length()) == 0)) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (Character.isWhitespace(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    public static boolean isBlank(CharSequence cs, int end) {
        int strLen;
        if ((cs == null) || ((strLen = cs.length()) == 0)) {
            return true;
        }
        if ((end > 0) && (end < strLen)) {
            strLen = end;
        }
        for (int i = 0; i < strLen; i++) {
            if (Character.isWhitespace(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    public static boolean isNotBlank(CharSequence cs) {
        return !isBlank(cs);
    }

    public static boolean isEmpty(CharSequence cs) {
        return (cs == null) || (cs.length() == 0);
    }

    public static boolean startsWith(String s, char c) {
        if (s.length() == 0) {
            return false;
        }
        return s.charAt(0) == c;
    }

    public static boolean endsWith(String s, char c) {
        if (s.length() == 0) {
            return false;
        }
        return s.charAt(s.length() - 1) == c;
    }

    public static String trim(String cs) {
        return cs == null ? null : cs.trim();
    }

    public static String trimToNull(String cs) {
        String s = trim(cs);
        return isEmpty(s) ? null : s;
    }

    public static String trimToEmpty(String cs) {
        String s = trim(cs);
        return s == null ? "" : s;
    }

    public static String trimToFlag(String src, String flag) {
        int index = src.indexOf(flag);
        if (index == -1) {
            return src;
        }
        return src.substring(0, index);
    }

    public static Long toLong(Object o) {
        if (o instanceof Long) {
            return (Long) o;
        }
        if (o instanceof String) {
            return Misc.parseLong((String) o, 0L);
        }

        if (o instanceof Integer) {
            return ((Integer) o).longValue();
        }

        return null;
    }

    public static Integer toInteger(Object o) {
        if (o instanceof Long) {
            return ((Long) o).intValue();
        }

        if (o instanceof String) {
            return Misc.parseInt((String) o, 0);
        }

        if (o instanceof Integer) {
            return (Integer) o;
        }

        return null;
    }

    @SuppressWarnings("unchecked")
    public static List<Long> toLongList(Object o) {
        if (!(o instanceof List)) {
            return Collections.EMPTY_LIST;
        }
        List<Object> list = (List<Object>) o;
        List<Long> result = new ArrayList<Long>(list.size());
        for (Object e : list) {
            result.add(toLong(e));
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    public static List<Integer> toIntegerList(Object o) {
        if (!(o instanceof List)) {
            return Collections.EMPTY_LIST;
        }
        List<Object> list = (List<Object>) o;
        List<Integer> result = new ArrayList<Integer>(list.size());
        for (Object e : list) {
            result.add(toInteger(e));
        }
        return result;
    }

    public static String like(String src) {
        return like(src, LikeType.ALL);
    }

    public static String like(String src, LikeType type) {
        src = trim(src);
        if (src == null) {
            return src;
        }

        if (type == LikeType.LEFT) {
            return "%" + src;
        }
        if (type == LikeType.RIGHT) {
            return src + "%";
        }
        return "%" + src + "%";
    }

    private static void base62(StringBuilder buf, long val) {
        while (val > 0) {
            buf.append(CHARS_DIGITS[(int) (val % 62)]);
            val /= 62;
        }
    }

    private static byte[] digest(String algorithm, String src, String salt) {
        return digest(algorithm, src.getBytes(), salt);
    }

    private static byte[] digest(String algorithm, byte[] bytes, String salt) {

        try {
            if ((salt != null) && (salt.length() > 0)) {
                bytes = merge(bytes, salt.getBytes());
            }
            bytes = MessageDigest.getInstance(algorithm).digest(bytes);
        } catch (Exception e) {
            // 忽略异常
        }

        return bytes;
    }

    public static byte[] toBytes(Object obj) {
        try {
            Output output = new Output(new byte[2048], -1);
            new Kryo().writeObject(output, obj);
            output.flush();
            return output.toBytes();
        } catch (Exception e) {
            throw new AppException("Serialize Object failed", e);
        }
    }

    public static <T> T readBytes(byte[] bytes, Class<T> clazz) {
        if (bytes == null) {
            return null;
        }
        try {
            return new Kryo().readObject(new Input(bytes, 0, bytes.length), clazz);
        } catch (Exception e) {
            return null;
        }
    }

    public static byte[] int2Bytes(int num) {
        byte[] byteNum = new byte[4];
        for (int ix = 0; ix < 4; ++ix) {
            int offset = 32 - (ix + 1) * 8;
            byteNum[ix] = (byte) ((num >> offset) & 0xff);
        }
        return byteNum;
    }

    public static int bytes2Int(byte[] byteNum) {
        int num = 0;
        for (int ix = 0; ix < 4; ++ix) {
            num <<= 8;
            num |= (byteNum[ix] & 0xff);
        }
        return num;
    }

    public static byte[] long2Bytes(long num) {
        byte[] byteNum = new byte[8];
        for (int ix = 0; ix < 8; ++ix) {
            int offset = 64 - (ix + 1) * 8;
            byteNum[ix] = (byte) ((num >> offset) & 0xff);
        }
        return byteNum;
    }

    public static long bytes2Long(byte[] byteNum) {
        long num = 0;
        for (int ix = 0; ix < 8; ++ix) {
            num <<= 8;
            num |= (byteNum[ix] & 0xff);
        }
        return num;
    }

    public static enum LikeType {
        LEFT, RIGHT, ALL
    }
}

 

public final class Misc {
    private Misc() {
    }

    /**
     * 判断集合是否为null或空
     * 
     * @param c
     * @return
     */
    public static boolean isEmpty(Collection<?> c) {
        return ((c == null) || (c.size() == 0));
    }

    /**
     * 将字符串按','分割后存入Set
     * 
     * @param s
     * @return
     */
    public static Set<String> asSet(String s) {
        if ((s == null) || (s.trim().length() == 0)) {
            return null;
        }

        String[] parts = TextUtil.split(s, ',');
        Set<String> set = new HashSet<String>(parts.length);
        for (String part : parts) {
            String trimmed = part.trim();
            if (trimmed.length() > 0) {
                set.add(trimmed);
            }
        }
        return set;
    }

    /**
     * 生成上传文件的临时保存路径,并使用UUID对上传文件重命名。
     *
     * @param dir 临时文件保存的目录。使用系统绝对路径,一般使用"
     * @param suffix 文件名后缀
     * @param max 最大重试次数
     * @return 创建成功时返回文件的绝对路径,创建失败时返回null
     */
    public static File randomFile(String dir, String suffix, int max, boolean create) throws IOException {
        File p = new File(dir, TextUtil.formatTime(new Date(), "yyyyMMdd"));
        if (p.exists() || p.mkdirs()) {
            for (int i = 0; i < max; ++i) {
                File f = new File(p, TextUtil.salt(8) + suffix);
                if (!f.exists()) {
                    if (create && f.createNewFile()) {
                        return f;
                    }
                }
            }
        }

        return null;
    }

    /**
     * 安全地将字符串解析成整型
     * 
     * @param s 字符串
     * @param defaultValue 字符串为空(包括null)或解析失败时返回默认值
     * @return
     */
    public static int parseInt(String s, int defaultValue) {
        if ((s != null) && (s.length() > 0)) {
            try {
                return Integer.parseInt(s);
            } catch (NumberFormatException e) {
                // 忽略异常
            }
        }

        return defaultValue;
    }

    /**
     * 安全地将字符串解析成长整型
     * 
     * @param s 字符串
     * @param defaultValue 字符串为空(包括null)或解析失败时返回默认值
     * @return
     */
    public static long parseLong(String s, long defaultValue) {
        if ((s != null) && (s.length() > 0)) {
            try {
                return Long.parseLong(s);
            } catch (NumberFormatException e) {
                // 忽略异常
            }
        }

        return defaultValue;
    }

    /**
     * 获取远程客户端的IP地址
     * 
     * @param request
     * @return 没有找到ip时返回null
     */
    public static String parseIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if (TextUtil.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }

        ip = request.getHeader("X-Forwarded-For");
        if (!TextUtil.isBlank(ip)) {
            for (String value : TextUtil.split(ip, ',', TextUtil.TRIM_TO_NULL)) {
                if (value != null) {
                    if ("unknown".equalsIgnoreCase(ip)) {
                        break;
                    }
                    return value;
                }
            }
        }

        return request.getRemoteAddr();
    }

}

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [id] 推荐:

产生Id

- - 研发管理 - ITeye博客
// worker编号最大值,决定支持的部署节点数量. // 毫秒内自增位数,每毫秒最大序号支持65535. // worker编号偏移量. // 毫秒基线:2015-01-01 00:00:00. * 从环境变量中获取worker编号,每个部署环境编号不能重复. * 每个部署环境编号不能重复. * @param workerId Worker编号.

id Software发布《狂怒(Rage)》

- ArmadilloCommander - Solidot
id Software发布了容量为21GB的第一人称射击游戏《狂怒(Rage)》. 游戏基于id Tech 5引擎,背景是世界末日后的未来. 目前对它的评价好坏参半,媒体综合评分80左右,玩家评分相似或更低. 在游戏中,玩家将扮演一位小行星Apophis撞击地球后的幸存者. 在灾难发生前,全世界展开合作将包括科学家在内的精英冰冻在地下,以在灾难后重建地球.

小米手机ID简介

- miyizs - Billwang 工业设计
      小米手机是小米公司(全称北京小米科技有限责任公司)专为发烧友级手机控打造的 一款高品质智能手机. 下面我们将对其做一个简单的介绍.       小米手机的外观设计走的是简约内敛路线,直板加圆润的边角让其显得简单清爽. 小米手机配置了,1.5GHz双核处理器、1G RAM、4英寸夏普屏、800万像素摄像头以及大容量电池.

标签?ID?还是CLASS?

- - 前端观察
想谈一下几个基本的HTML问题,都是围绕着应该怎样使用HTML. 多用有语义的标签,少用div和span,避免使用没有class的div和span. 设想一下HTML的世界最初只有div和span这两个标签,其实网页依然可以写得出来. 更多标签的出现,其实是为了替代利用率高但不好书写的 
 和  来的.

设备id那些事

- - 算法之道
随着用户隐私关注度越来越高,搜广推以imei收集用户数据的方式开始被要求整改,首先是客户端不会上传imei,那么追踪用户的唯一标识没有了,那后续该怎么做. 其实除了这个唯一标识符问题,还有一些应用设置项:出现了允许关闭推荐的选项,致命一击. 先了解一下现在移动终端可以收集哪些 id. 不可逆、唯一性、不可篡改、一致性.

Linuxer:制作自己的Linux ID Card吧

- rex - Wow! Ubuntu
Super Boot Manager的作者Alessandro Lanave,又为Linuxer带来了一个web程序,制作Linux ID Card ,Card效果如图. 可以把ID Card做为论坛签名,博客签名,任何你需要的地方. 当然,如果觉得没有自己喜欢的发行版的模板,可以向Alessandro Lanave提交哦,.

两个 Apple ID 是很有必要的

- 闷闲居士 - Page to Page
刚买iPad的时候注册的是中国区的Apple ID,绑定了自己的信用卡. 可当搜索下载Kindle for iPad的时候才觉得,注册一个美国区的Apple ID还是很有必要的,否则很多中国区没有的App无法下载. 大致学习了2篇注册美国区ID的帖子,赶紧动手,可是还是因为疏忽产生了问题,提示我说“我的邮箱地址已经注册了”.

获取id 的一种策略

- - 企业架构 - ITeye博客
从数据库中批量(step个)拿出Id,然后使用,待消耗完后再批量拿出Id. mapLock:判断是否存在锁. mapGenId:保存每次的currNo值. mapMaxId:保存每次currNo+step后的值. 三个ConcurrentHashMap的key都为 key1. 一、由genKey、subKey构建一个锁,获取Id值时先判断是否存在锁,当不存在锁时,先初始化.

全局唯一ID生成方案

- - 标点符
在实现大型分布式程序时,通常会有全局唯一ID生成的需求,用来对每一个对象标识一个代号. 另外,业务层对于全局唯一ID生成也有要求:. 全局唯一性:不能出现重复的ID号. 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能.

用户体系搭建之ID-Mapping

- - 标点符
在推进用户画像和风险控制时,遇到的最大的问题是用户身份信息的混乱:. 相同用户,不同渠道下账号不相同,如微信小程序和APP. 同个用户,在不同的设备商登录. ID-Mapping是大数据分析中非常基本但又关键的环节,ID-Mapping通俗的说就是把几份不同来源的数据,通过各种技术手段识别为同一个对象或主题,例如同一台设备(直接),同一个用户(间接),同一家企业(间接)等等,可以形象地理解为用户画像的“拼图”过程.