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