├── src └── main │ ├── resources │ ├── server.properties │ └── logback.xml │ └── java │ └── com │ └── wolfbe │ └── distributedid │ ├── core │ ├── Server.java │ ├── BaseServer.java │ └── SnowFlake.java │ ├── sdks │ ├── SdkProto.java │ ├── SdkServerEncoder.java │ ├── SdkServerDecoder.java │ ├── SdkServer.java │ └── SdkServerHandler.java │ ├── exception │ ├── RemotingTooMuchRequestException.java │ ├── RemotingException.java │ ├── RemotingConnectException.java │ ├── RemotingSendRequestException.java │ └── RemotingTimeoutException.java │ ├── util │ ├── NettyUtil.java │ ├── BlankUtil.java │ ├── GlobalConfig.java │ └── DateTimeUtil.java │ ├── ServerStartup.java │ └── http │ ├── HttpServer.java │ └── HttpServerHandler.java ├── README.md ├── pom.xml ├── .idea └── uiDesigner.xml └── LICENSE /src/main/resources/server.properties: -------------------------------------------------------------------------------- 1 | #must set datacenterId and machineId be unique 2 | datacenterId=1 3 | machineId=1 -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/core/Server.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.core; 2 | 3 | /** 4 | * @author Andy 5 | */ 6 | public interface Server { 7 | 8 | void start(); 9 | 10 | void shutdown(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/sdks/SdkProto.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.sdks; 2 | 3 | /** 4 | * @author Andy 5 | */ 6 | public class SdkProto { 7 | 8 | private int rqid; //请求的ID 9 | private long did; //全局的ID 10 | 11 | public SdkProto(int rqid, long did) { 12 | this.rqid = rqid; 13 | this.did = did; 14 | } 15 | 16 | public int getRqid() { 17 | return rqid; 18 | } 19 | 20 | public void setRqid(int rqid) { 21 | this.rqid = rqid; 22 | } 23 | 24 | public long getDid() { 25 | return did; 26 | } 27 | 28 | public void setDid(long did) { 29 | this.did = did; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "SdkProto{" + 35 | "rqid=" + rqid + 36 | ", did=" + did + 37 | '}'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/sdks/SdkServerEncoder.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.sdks; 2 | 3 | import com.wolfbe.distributedid.util.NettyUtil; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.Channel; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.handler.codec.MessageToByteEncoder; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * @author Andy 13 | */ 14 | public class SdkServerEncoder extends MessageToByteEncoder { 15 | private static final Logger logger = LoggerFactory.getLogger(SdkServerEncoder.class); 16 | 17 | 18 | @Override 19 | protected void encode(ChannelHandlerContext channelHandlerContext, SdkProto sdkProto, ByteBuf out) throws Exception { 20 | out.writeInt(sdkProto.getRqid()); 21 | out.writeLong(sdkProto.getDid()); 22 | } 23 | 24 | @Override 25 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 26 | Channel channel = ctx.channel(); 27 | logger.error("SdkServerEncoder channel [{}] error and will be closed", NettyUtil.parseRemoteAddr(channel), cause); 28 | NettyUtil.closeChannel(channel); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/exception/RemotingTooMuchRequestException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.wolfbe.distributedid.exception; 18 | 19 | /** 20 | * @author Andy 21 | */ 22 | public class RemotingTooMuchRequestException extends RemotingException { 23 | private static final long serialVersionUID = 4326919581254519654L; 24 | 25 | 26 | public RemotingTooMuchRequestException(String message) { 27 | super(message); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/exception/RemotingException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.wolfbe.distributedid.exception; 18 | 19 | /** 20 | * @author Andy 21 | */ 22 | public class RemotingException extends Exception { 23 | private static final long serialVersionUID = -5690687334570505110L; 24 | 25 | 26 | public RemotingException(String message) { 27 | super(message); 28 | } 29 | 30 | 31 | public RemotingException(String message, Throwable cause) { 32 | super(message, cause); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/exception/RemotingConnectException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.wolfbe.distributedid.exception; 18 | 19 | /** 20 | * @author Andy 21 | */ 22 | public class RemotingConnectException extends RemotingException { 23 | private static final long serialVersionUID = -5565366231695911316L; 24 | 25 | 26 | public RemotingConnectException(String addr) { 27 | this(addr, null); 28 | } 29 | 30 | 31 | public RemotingConnectException(String addr, Throwable cause) { 32 | super("connect to <" + addr + "> failed", cause); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/exception/RemotingSendRequestException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.wolfbe.distributedid.exception; 18 | 19 | /** 20 | * @author Andy 21 | */ 22 | public class RemotingSendRequestException extends RemotingException { 23 | private static final long serialVersionUID = 5391285827332471674L; 24 | 25 | 26 | public RemotingSendRequestException(String addr) { 27 | this(addr, null); 28 | } 29 | 30 | 31 | public RemotingSendRequestException(String addr, Throwable cause) { 32 | super("send request to <" + addr + "> failed", cause); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/exception/RemotingTimeoutException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.wolfbe.distributedid.exception; 18 | 19 | /** 20 | * @author Andy 21 | */ 22 | public class RemotingTimeoutException extends RemotingException { 23 | 24 | private static final long serialVersionUID = 4106899185095245979L; 25 | 26 | 27 | public RemotingTimeoutException(String message) { 28 | super(message); 29 | } 30 | 31 | 32 | public RemotingTimeoutException(String addr, long timeoutMillis) { 33 | this(addr, timeoutMillis, null); 34 | } 35 | 36 | 37 | public RemotingTimeoutException(String addr, long timeoutMillis, Throwable cause) { 38 | super("wait response on the channel <" + addr + "> timeout, " + timeoutMillis + "(ms)", cause); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/util/NettyUtil.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.util; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelFutureListener; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.net.SocketAddress; 10 | 11 | /** 12 | * @author Andy 13 | */ 14 | public class NettyUtil { 15 | 16 | private static final Logger logger = LoggerFactory.getLogger(NettyUtil.class); 17 | 18 | /** 19 | * 获取Channel的远程IP地址 20 | * @param channel 21 | * @return 22 | */ 23 | public static String parseRemoteAddr(final Channel channel) { 24 | if (null == channel) { 25 | return ""; 26 | } 27 | SocketAddress remote = channel.remoteAddress(); 28 | final String addr = remote != null ? remote.toString() : ""; 29 | 30 | if (addr.length() > 0) { 31 | int index = addr.lastIndexOf("/"); 32 | if (index >= 0) { 33 | return addr.substring(index + 1); 34 | } 35 | 36 | return addr; 37 | } 38 | 39 | return ""; 40 | } 41 | 42 | public static void closeChannel(Channel channel) { 43 | final String addrRemote = parseRemoteAddr(channel); 44 | channel.close().addListener(new ChannelFutureListener() { 45 | @Override 46 | public void operationComplete(ChannelFuture future) throws Exception { 47 | logger.info("closeChannel: close the connection to remote address[{}] result: {}", addrRemote, 48 | future.isSuccess()); 49 | } 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 雪花算法分布式ID生成器 2 | 这个项目的目的是提供一个轻量级、高并发、高可用的生成唯一ID的服务,生成的ID是一个64位的 3 | 长整型,全局唯一,保持递增,相对有序。基于twitter的雪花算法来生成ID,用于取代UUID那种无序、128位的字符串形式的ID,提供 4 | 一种更加高效、人性化的全局唯一ID的生成方式,目前单机CPU4核、内存8G压测的并发数可以达到 5 | **250万/每秒**,即每秒最多可以生成250万个唯一的ID,当然如果部署集群的话,这个数据可以 6 | 更高。 7 |

8 | 具体的教程可以参考我写的一篇文章:[基于twitter雪花算法的分布式ID —— 服务器篇](http://www.wolfbe.com/detail/201701/386.html) 9 |

10 | **温馨提示:** 各位同学在fork之前,希望能够给个star,尊重一下作者的努力。 11 |


12 | ## 特点 13 | * 基于twitter的雪花算法生成ID; 14 | * 基于netty框架提供通信层接入; 15 | * 提供HTTP和SDK两种方式接入; 16 | * 轻量级、高并发、易伸缩; 17 | * 部署简单,支持分布式部署; 18 |
19 | 20 | ## 接入 21 | 服务器支持两种方式接入:**HTTP**和**SDK**,无论哪一种方式接入,对于同一台服务器来说,调用的是同 22 | 一个ID生成器,所以得到的ID都是递增、有序的。 23 |

24 | 25 | ### HTTP接入 26 | HTTP的接入方式很简单,直接访问IP+端口即可,或者域名+端口,端口号固定为**16830**。如果你不喜欢这种带有端口号的方式,可以考虑配置Nginx来做代理转发,配置Nginx对于部署分布式ID集群也有好处,可以通过Nginx来做负载均衡。 27 |

28 | 29 | ### SDK接入 30 | SDK接入前需要在自己的项目中加入SDK的jar包,SDK可以参照我的另外一个项目[DistributedID-SDK](https://github.com/beyondfengyu/DistributedID-SDK),或者自己写一个SDK来接入,语言不限。**DistributedID-SDK**提供了同步和异步两种请求方式,如果有高并发的要求,建议使用异步请求的方式,相同的环境下异步请求的性能会比同步请求的性能更高。 31 |

32 | 33 | ## 部署 34 | 部署之前需要把项目源码打包成jar包,或者使用项目打包好的jar包,把jar包上传到服务器,执行如下命令: 35 |
36 | ·java -jar distributedid.jar 1 2· 37 | 执行上面命令指定了两个参数1和2,前面的1代表数据中心标识,后面的2代表的是机器或进程标识. 38 |

39 | 40 | 如果不指定这两个参数,那么会使用默认的值1。如果只考虑部署单机服务器,那么可以不考虑这两个参数,**如果需要分布式集群来生成ID时,需要指定数据中心标识ID和机器进程标识ID,并且每一个服务器的数据中心标识ID和机器进程标识ID作为联合键全局唯一,这样才能保证集群生成的ID都是唯一的。** 41 |
42 |
43 | 44 | ## 交流 45 | 如果有兴趣交流Netty相关知识,可以加入**Netty联盟:379119816** 46 |
47 |
48 | ## 赞助 49 | 如果觉得项目还不错,想要表达些什么的话,可以上[爱淘汇:http://itao.wolfbe.com](http://itao.wolfbe.com) 领淘宝天猫的优惠券,领取优惠券再下单可以省不少钱喔。你们使用这些优惠券购买东西时,我也可以得到一些佣金的,多谢支持!!! 50 |
51 |
52 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/sdks/SdkServerDecoder.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.sdks; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONArray; 5 | import com.alibaba.fastjson.JSONObject; 6 | import com.wolfbe.distributedid.core.SnowFlake; 7 | import com.wolfbe.distributedid.util.GlobalConfig; 8 | import com.wolfbe.distributedid.util.NettyUtil; 9 | import io.netty.buffer.ByteBuf; 10 | import io.netty.channel.*; 11 | import io.netty.handler.codec.ByteToMessageDecoder; 12 | import io.netty.handler.codec.FixedLengthFrameDecoder; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import java.nio.charset.Charset; 17 | import java.util.List; 18 | import java.util.concurrent.Semaphore; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | /** 22 | * 23 | * @author Andy 24 | */ 25 | public class SdkServerDecoder extends FixedLengthFrameDecoder { 26 | private static final Logger logger = LoggerFactory.getLogger(SdkServerDecoder.class); 27 | 28 | public SdkServerDecoder(int frameLength) { 29 | super(frameLength); 30 | } 31 | 32 | @Override 33 | protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { 34 | ByteBuf buf = null; 35 | try { 36 | buf = (ByteBuf) super.decode(ctx, in); 37 | if (buf == null) { 38 | return null; 39 | } 40 | return new SdkProto(buf.readInt(), buf.readLong()); 41 | } catch (Exception e) { 42 | logger.error("decode exception, " + NettyUtil.parseRemoteAddr(ctx.channel()), e); 43 | NettyUtil.closeChannel(ctx.channel()); 44 | }finally { 45 | if (buf != null) { 46 | buf.release(); 47 | } 48 | } 49 | return null; 50 | } 51 | 52 | @Override 53 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 54 | Channel channel = ctx.channel(); 55 | logger.error("SdkServerDecoder channel [{}] error and will be closed", NettyUtil.parseRemoteAddr(channel),cause); 56 | NettyUtil.closeChannel(channel); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/util/BlankUtil.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.util; 2 | 3 | import java.io.Serializable; 4 | import java.util.Collection; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | /** 9 | * @author Andy 2015-11-12 10 | * @description 判断字符串、集合、哈希、数组对象是否为空 11 | */ 12 | public class BlankUtil { 13 | 14 | /** 15 | * 判断字符串是否为空 16 | */ 17 | public static boolean isBlank(final String str) { 18 | return (str == null) || (str.trim().length() <= 0); 19 | } 20 | 21 | /** 22 | * 判断字符是否为空 23 | * 24 | * @param cha 25 | * @return 26 | */ 27 | public static boolean isBlank(final Character cha) { 28 | return (cha == null) || cha.equals(' '); 29 | } 30 | 31 | /** 32 | * 判断对象是否为空 33 | */ 34 | public static boolean isBlank(final Object obj) { 35 | return (obj == null); 36 | } 37 | 38 | /** 39 | * 判断数组是否为空 40 | * 41 | * @param objs 42 | * @return 43 | */ 44 | public static boolean isBlank(final Object[] objs) { 45 | return (objs == null) || (objs.length <= 0); 46 | } 47 | 48 | /** 49 | * 判断Collectionj是否为空 50 | * 51 | * @param obj 52 | * @return 53 | */ 54 | public static boolean isBlank(final Collection obj) { 55 | return (obj == null) || (obj.size() <= 0); 56 | } 57 | 58 | /** 59 | * 判断Set是否为空 60 | * 61 | * @param obj 62 | * @return 63 | */ 64 | public static boolean isBlank(final Set obj) { 65 | return (obj == null) || (obj.size() <= 0); 66 | } 67 | 68 | public static boolean isBlank(Integer i) { 69 | return i == null || i < 1; 70 | } 71 | 72 | /** 73 | * 判断Serializable是否为空 74 | * 75 | * @param obj 76 | * @return 77 | */ 78 | public static boolean isBlank(final Serializable obj) { 79 | return obj == null; 80 | } 81 | 82 | /** 83 | * 判断Map是否为空 84 | * 85 | * @param obj 86 | * @return 87 | */ 88 | public static boolean isBlank(final Map obj) { 89 | return (obj == null) || (obj.size() <= 0); 90 | } 91 | } -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/sdks/SdkServer.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.sdks; 2 | 3 | import com.wolfbe.distributedid.core.BaseServer; 4 | import com.wolfbe.distributedid.core.SnowFlake; 5 | import com.wolfbe.distributedid.util.GlobalConfig; 6 | import io.netty.channel.ChannelInitializer; 7 | import io.netty.channel.ChannelOption; 8 | import io.netty.channel.socket.SocketChannel; 9 | import io.netty.channel.socket.nio.NioServerSocketChannel; 10 | 11 | import java.net.InetSocketAddress; 12 | 13 | /** 14 | * @author Andy 15 | */ 16 | public class SdkServer extends BaseServer { 17 | private SnowFlake snowFlake; 18 | 19 | public SdkServer(SnowFlake snowFlake) { 20 | this.snowFlake = snowFlake; 21 | this.port = GlobalConfig.SDKS_PORT; 22 | } 23 | 24 | @Override 25 | public void init() { 26 | super.init(); 27 | b.group(bossGroup, workGroup) 28 | .channel(NioServerSocketChannel.class) 29 | .option(ChannelOption.SO_KEEPALIVE, true) 30 | .option(ChannelOption.TCP_NODELAY, true) 31 | .option(ChannelOption.SO_BACKLOG, 1024) 32 | .localAddress(new InetSocketAddress(port)) 33 | .childHandler(new ChannelInitializer() { 34 | 35 | @Override 36 | protected void initChannel(SocketChannel ch) throws Exception { 37 | ch.pipeline().addLast(defLoopGroup, 38 | new SdkServerDecoder(12), // 自定义解码器 39 | new SdkServerEncoder(), // 自定义编码器 40 | new SdkServerHandler(snowFlake) // 自定义处理器 41 | ); 42 | } 43 | }); 44 | } 45 | 46 | @Override 47 | public void start() { 48 | try { 49 | cf = b.bind().sync(); 50 | InetSocketAddress addr = (InetSocketAddress) cf.channel().localAddress(); 51 | logger.info("SdkServer start success, port is:{}", addr.getPort()); 52 | } catch (InterruptedException e) { 53 | logger.error("SdkServer start fail,", e); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/ServerStartup.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid; 2 | 3 | import com.wolfbe.distributedid.core.SnowFlake; 4 | import com.wolfbe.distributedid.http.HttpServer; 5 | import com.wolfbe.distributedid.sdks.SdkServer; 6 | import com.wolfbe.distributedid.util.GlobalConfig; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.Date; 11 | 12 | /** 13 | * 两个服务器进程最好用同一个SnowFlake实例, 14 | * 部署在分布式环境时,SnowFlake的datacenterId和machineId作为联合键必须全局唯一, 15 | * 否则多个节点的服务可能产生相同的ID 16 | * @author Andy 17 | */ 18 | public class ServerStartup { 19 | private static final Logger logger = LoggerFactory.getLogger(ServerStartup.class); 20 | 21 | public static void main(String[] args) { 22 | long datacenterId = GlobalConfig.DATACENTER_ID; 23 | long machineId = GlobalConfig.MACHINES_SIGN; 24 | 25 | if (args != null && args.length == 2) { 26 | datacenterId = Long.valueOf(args[0]); 27 | machineId = Long.valueOf(args[1]); 28 | }else{ 29 | System.out.println(">>>>>You don't appoint the datacenterId and machineId argement,will use default value"); 30 | } 31 | 32 | final SnowFlake snowFlake = new SnowFlake(datacenterId, machineId); 33 | 34 | // 启动Http服务器 35 | final HttpServer httpServer = new HttpServer(snowFlake); 36 | httpServer.init(); 37 | httpServer.start(); 38 | 39 | // 启动Sdk服务器 40 | final SdkServer sdkServer = new SdkServer(snowFlake); 41 | sdkServer.init(); 42 | sdkServer.start(); 43 | 44 | System.out.println(String.format(new Date().getTime()+">>>>>Server start success, SnowFlake datacenterId is %d, machineId is %d", 45 | datacenterId, 46 | machineId 47 | )); 48 | 49 | // TODO 雪花算法数据中心标识、机器标识、服务IP、服务端口上报到配置中心 50 | 51 | // 注册进程钩子,在JVM进程关闭前释放资源 52 | Runtime.getRuntime().addShutdownHook(new Thread() { 53 | @Override 54 | public void run() { 55 | httpServer.shutdown(); 56 | logger.warn(">>>>>>>>>> httpServer shutdown"); 57 | sdkServer.shutdown(); 58 | logger.warn(">>>>>>>>>> sdkServer shutdown"); 59 | System.exit(0); 60 | } 61 | }); 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/core/BaseServer.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.core; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.DefaultEventLoopGroup; 6 | import io.netty.channel.nio.NioEventLoopGroup; 7 | import io.netty.channel.socket.nio.NioServerSocketChannel; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.concurrent.ThreadFactory; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | 14 | /** 15 | * @author Andy 16 | */ 17 | public abstract class BaseServer implements Server { 18 | 19 | protected Logger logger = LoggerFactory.getLogger(getClass()); 20 | 21 | protected DefaultEventLoopGroup defLoopGroup; 22 | protected NioEventLoopGroup bossGroup; 23 | protected NioEventLoopGroup workGroup; 24 | protected NioServerSocketChannel ssch; 25 | protected ChannelFuture cf; 26 | protected ServerBootstrap b; 27 | protected int port; 28 | 29 | public void init(){ 30 | defLoopGroup = new DefaultEventLoopGroup(8, new ThreadFactory() { 31 | private AtomicInteger index = new AtomicInteger(0); 32 | 33 | @Override 34 | public Thread newThread(Runnable r) { 35 | return new Thread(r, "DEFAULTEVENTLOOPGROUP_" + index.incrementAndGet()); 36 | } 37 | }); 38 | bossGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(), new ThreadFactory() { 39 | private AtomicInteger index = new AtomicInteger(0); 40 | 41 | @Override 42 | public Thread newThread(Runnable r) { 43 | return new Thread(r, "BOSS_" + index.incrementAndGet()); 44 | } 45 | }); 46 | workGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 10, new ThreadFactory() { 47 | private AtomicInteger index = new AtomicInteger(0); 48 | 49 | @Override 50 | public Thread newThread(Runnable r) { 51 | return new Thread(r, "WORK_" + index.incrementAndGet()); 52 | } 53 | }); 54 | 55 | b = new ServerBootstrap(); 56 | } 57 | 58 | @Override 59 | public void shutdown() { 60 | if (defLoopGroup != null) { 61 | defLoopGroup.shutdownGracefully(); 62 | } 63 | bossGroup.shutdownGracefully(); 64 | workGroup.shutdownGracefully(); 65 | logger.info("Server EventLoopGroup shutdown finish"); 66 | } 67 | 68 | public int getPort() { 69 | return port; 70 | } 71 | 72 | public void setPort(int port) { 73 | this.port = port; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/http/HttpServer.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.http; 2 | 3 | import com.wolfbe.distributedid.core.SnowFlake; 4 | import com.wolfbe.distributedid.core.BaseServer; 5 | import com.wolfbe.distributedid.util.GlobalConfig; 6 | import io.netty.channel.ChannelInitializer; 7 | import io.netty.channel.ChannelOption; 8 | import io.netty.channel.socket.SocketChannel; 9 | import io.netty.channel.socket.nio.NioServerSocketChannel; 10 | import io.netty.handler.codec.http.HttpObjectAggregator; 11 | import io.netty.handler.codec.http.HttpRequestDecoder; 12 | import io.netty.handler.codec.http.HttpResponseEncoder; 13 | 14 | import java.net.InetSocketAddress; 15 | 16 | /** 17 | * Http服务器,使用Netty中的Http协议栈, 18 | * 实现中支持多条请求路径,对于不存在的请求路径返回404状态码 19 | * 如:http://localhost:8099/getTime 20 | * @author Andy 21 | */ 22 | public class HttpServer extends BaseServer { 23 | 24 | private SnowFlake snowFlake; 25 | 26 | 27 | public HttpServer(SnowFlake snowFlake) { 28 | this.snowFlake = snowFlake; 29 | this.port = GlobalConfig.HTTP_PORT; 30 | } 31 | 32 | @Override 33 | public void init() { 34 | super.init(); 35 | b.group(bossGroup, workGroup) 36 | .channel(NioServerSocketChannel.class) 37 | .option(ChannelOption.SO_KEEPALIVE, false) 38 | .option(ChannelOption.TCP_NODELAY, true) 39 | .option(ChannelOption.SO_BACKLOG, 1024) 40 | .localAddress(new InetSocketAddress(port)) 41 | .childHandler(new ChannelInitializer() { 42 | 43 | @Override 44 | protected void initChannel(SocketChannel ch) throws Exception { 45 | ch.pipeline().addLast(defLoopGroup, 46 | new HttpRequestDecoder(), //请求解码器 47 | new HttpObjectAggregator(65536),//将多个消息转换成单一的消息对象 48 | new HttpResponseEncoder(), // 响应编码器 49 | new HttpServerHandler(snowFlake)//自定义处理器 50 | ); 51 | } 52 | }); 53 | 54 | } 55 | 56 | @Override 57 | public void start() { 58 | try { 59 | cf = b.bind().sync(); 60 | InetSocketAddress addr = (InetSocketAddress) cf.channel().localAddress(); 61 | logger.info("HttpServer start success, port is:{}", addr.getPort()); 62 | } catch (InterruptedException e) { 63 | logger.error("HttpServer start fail,", e); 64 | } 65 | } 66 | 67 | @Override 68 | public void shutdown() { 69 | if (defLoopGroup != null) { 70 | defLoopGroup.shutdownGracefully(); 71 | } 72 | bossGroup.shutdownGracefully(); 73 | workGroup.shutdownGracefully(); 74 | } 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | UTF-8 7 | /data/javalog/distributedid.wolfbe.com.log 8 | 9 | /data/javalog/distributedid.wolfbe.com.log.%d{yyyy-MM-dd} 10 | 30 11 | 12 | 13 | ${layout_pattern} 14 | 15 | 16 | INFO 17 | 18 | 19 | 21 | true 22 | UTF-8 23 | /data/javalog/distributedid.wolfbe.com_error.log 24 | 25 | /data/javalog/distributedid.wolfbe.com_error.log.%d{yyyy-MM-dd} 26 | 30 27 | 28 | 29 | ${layout_pattern} 30 | 31 | 32 | ERROR 33 | 34 | 35 | 36 | true 37 | UTF-8 38 | /data/javalog/distributedid.wolfbe.com_warn.log 39 | 40 | /data/javalog/distributedid.wolfbe.com_warn.log.%d{yyyy-MM-dd} 41 | 30 42 | 43 | 44 | ${layout_pattern} 45 | 46 | 47 | WARN 48 | 49 | 50 | 51 | UTF-8 52 | 53 | ${layout_pattern} 54 | 55 | 56 | DEBUG 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/util/GlobalConfig.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.util; 2 | 3 | /** 4 | * @author Andy 5 | */ 6 | public class GlobalConfig { 7 | 8 | public static String DEFAULT_HOST = "localhost"; 9 | public static int DEFAULT_PORT = 16830; 10 | 11 | /** 12 | * HTTP协议和SDK协议服务器的端口 13 | */ 14 | public static int HTTP_PORT = 16830; 15 | public static int SDKS_PORT = 16831; 16 | 17 | /** 18 | * HTTP协议和SDK协议的请求路径 19 | */ 20 | public static String HTTP_REQUEST = "getid"; 21 | public static String SDKS_REQUEST = "getid"; 22 | 23 | /** 24 | * 数据中心的标识ID,取值范围:0~31 25 | * 机器或进程的标识ID,取值范围:0~31 26 | * 两个标识ID组合在分布式环境中必须唯一 27 | */ 28 | public static long DATACENTER_ID = 1; 29 | public static long MACHINES_SIGN = 1; 30 | 31 | /** 32 | * 流量控制,表示每秒处理的并发数 33 | */ 34 | public static int HANDLE_HTTP_TPS = 10000; 35 | public static int HANDLE_SDKS_TPS = 50000; 36 | public static int ACQUIRE_TIMEOUTMILLIS = 5000; 37 | 38 | public static String getDefaultHost() { 39 | return DEFAULT_HOST; 40 | } 41 | 42 | public static void setDefaultHost(String defaultHost) { 43 | DEFAULT_HOST = defaultHost; 44 | } 45 | 46 | public static int getDefaultPort() { 47 | return DEFAULT_PORT; 48 | } 49 | 50 | public static void setDefaultPort(int defaultPort) { 51 | DEFAULT_PORT = defaultPort; 52 | } 53 | 54 | public static int getHttpPort() { 55 | return HTTP_PORT; 56 | } 57 | 58 | public static void setHttpPort(int httpPort) { 59 | HTTP_PORT = httpPort; 60 | } 61 | 62 | public static int getSdksPort() { 63 | return SDKS_PORT; 64 | } 65 | 66 | public static void setSdksPort(int sdksPort) { 67 | SDKS_PORT = sdksPort; 68 | } 69 | 70 | public static String getHttpRequest() { 71 | return HTTP_REQUEST; 72 | } 73 | 74 | public static void setHttpRequest(String httpRequest) { 75 | HTTP_REQUEST = httpRequest; 76 | } 77 | 78 | public static String getSdksRequest() { 79 | return SDKS_REQUEST; 80 | } 81 | 82 | public static void setSdksRequest(String sdksRequest) { 83 | SDKS_REQUEST = sdksRequest; 84 | } 85 | 86 | public static long getDatacenterId() { 87 | return DATACENTER_ID; 88 | } 89 | 90 | public static void setDatacenterId(long datacenterId) { 91 | DATACENTER_ID = datacenterId; 92 | } 93 | 94 | public static long getMachinesSign() { 95 | return MACHINES_SIGN; 96 | } 97 | 98 | public static void setMachinesSign(long machinesSign) { 99 | MACHINES_SIGN = machinesSign; 100 | } 101 | 102 | 103 | public static int getAcquireTimeoutmillis() { 104 | return ACQUIRE_TIMEOUTMILLIS; 105 | } 106 | 107 | public static void setAcquireTimeoutmillis(int acquireTimeoutmillis) { 108 | ACQUIRE_TIMEOUTMILLIS = acquireTimeoutmillis; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/sdks/SdkServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.sdks; 2 | 3 | import com.wolfbe.distributedid.core.SnowFlake; 4 | import com.wolfbe.distributedid.exception.RemotingTooMuchRequestException; 5 | import com.wolfbe.distributedid.util.GlobalConfig; 6 | import com.wolfbe.distributedid.util.NettyUtil; 7 | import io.netty.channel.*; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.concurrent.Semaphore; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | /** 15 | * 通过雪花算法生成唯一ID,写入Channel返回 16 | * 17 | * @author Andy 18 | */ 19 | public class SdkServerHandler extends SimpleChannelInboundHandler { 20 | private static final Logger logger = LoggerFactory.getLogger(SdkServerHandler.class); 21 | /** 22 | * 通过信号量来控制流量 23 | */ 24 | private Semaphore semaphore = new Semaphore(GlobalConfig.HANDLE_SDKS_TPS); 25 | private SnowFlake snowFlake; 26 | 27 | public SdkServerHandler(SnowFlake snowFlake) { 28 | this.snowFlake = snowFlake; 29 | } 30 | 31 | @Override 32 | protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { 33 | if (msg != null && msg instanceof SdkProto) { 34 | SdkProto sdkProto = (SdkProto) msg; 35 | // logger.info("SdkServerHandler msg is: {}", sdkProto.toString()); 36 | if (semaphore.tryAcquire(GlobalConfig.ACQUIRE_TIMEOUTMILLIS, TimeUnit.MILLISECONDS)) { 37 | try { 38 | sdkProto.setDid(snowFlake.nextId()); 39 | ctx.channel().writeAndFlush(sdkProto).addListener(new ChannelFutureListener() { 40 | @Override 41 | public void operationComplete(ChannelFuture channelFuture) throws Exception { 42 | semaphore.release(); 43 | } 44 | }); 45 | } catch (Exception e) { 46 | semaphore.release(); 47 | logger.error("SdkServerhandler error", e); 48 | } 49 | } else { 50 | sdkProto.setDid(-1); 51 | ctx.channel().writeAndFlush(sdkProto); 52 | String info = String.format("SdkServerHandler tryAcquire semaphore timeout, %dms, waiting thread " + 53 | "nums: %d availablePermit: %d", // 54 | GlobalConfig.ACQUIRE_TIMEOUTMILLIS, // 55 | this.semaphore.getQueueLength(), // 56 | this.semaphore.availablePermits() // 57 | ); 58 | logger.warn(info); 59 | throw new RemotingTooMuchRequestException(info); 60 | } 61 | } 62 | } 63 | 64 | @Override 65 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 66 | Channel channel = ctx.channel(); 67 | logger.error("SdkServerHandler channel [{}] error and will be closed", NettyUtil.parseRemoteAddr(channel), cause); 68 | NettyUtil.closeChannel(channel); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/http/HttpServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.http; 2 | 3 | import com.wolfbe.distributedid.core.SnowFlake; 4 | import com.wolfbe.distributedid.exception.RemotingTooMuchRequestException; 5 | import com.wolfbe.distributedid.util.GlobalConfig; 6 | import com.wolfbe.distributedid.util.NettyUtil; 7 | import io.netty.channel.*; 8 | import io.netty.handler.codec.http.*; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.util.concurrent.Semaphore; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | /** 16 | * 自定义的处理器,目前支持三种请求: 17 | * getTime: 获取服务器当前时间; 18 | * clientInfo: 获取请求客户端的User-Agent信息 19 | * 其它: 返回404状态,并且提示404信息 20 | * @author Andy 21 | */ 22 | public class HttpServerHandler extends SimpleChannelInboundHandler { 23 | 24 | private final Logger logger = LoggerFactory.getLogger(getClass()); 25 | /** 26 | * 通过信号量来控制流量 27 | */ 28 | private Semaphore semaphore = new Semaphore(GlobalConfig.HANDLE_HTTP_TPS); 29 | private SnowFlake snowFlake; 30 | 31 | public HttpServerHandler(SnowFlake snowFlake) { 32 | this.snowFlake = snowFlake; 33 | } 34 | 35 | @Override 36 | protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) 37 | throws Exception { 38 | String uri = getUriNoSprit(request); 39 | logger.info(">>>>>> request uri is: {}", uri); 40 | FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); 41 | if (GlobalConfig.HTTP_REQUEST.equals(uri)) { 42 | if (semaphore.tryAcquire(GlobalConfig.ACQUIRE_TIMEOUTMILLIS, TimeUnit.MILLISECONDS)) { 43 | try { 44 | long id = snowFlake.nextId(); 45 | logger.info("HttpServerHandler id is: {}", id); 46 | response.content().writeBytes((""+id).getBytes()); 47 | ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); 48 | }catch (Exception e){ 49 | semaphore.release(); 50 | logger.error("HttpServerHandler error", e); 51 | } 52 | }else{ 53 | String info = String.format("HttpServerHandler tryAcquire semaphore timeout, %dms, waiting thread " + 54 | "nums: %d availablePermit: %d", // 55 | GlobalConfig.ACQUIRE_TIMEOUTMILLIS, // 56 | this.semaphore.getQueueLength(), // 57 | this.semaphore.availablePermits() // 58 | ); 59 | logger.warn(info); 60 | throw new RemotingTooMuchRequestException(info); 61 | } 62 | } 63 | } 64 | 65 | @Override 66 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 67 | Channel channel = ctx.channel(); 68 | logger.error("HttpServerHandler channel [{}] error and will be closed", NettyUtil.parseRemoteAddr(channel), cause); 69 | NettyUtil.closeChannel(channel); 70 | } 71 | 72 | 73 | private String getUriNoSprit(FullHttpRequest request) { 74 | String uri = request.uri(); 75 | if (uri.startsWith("/")) { 76 | uri = uri.substring(1); 77 | } 78 | return uri; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/core/SnowFlake.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.core; 2 | 3 | /** 4 | * twitter的snowflake算法 -- java实现 5 | * 协议格式: 0 - 41位时间戳 - 5位数据中心标识 - 5位机器标识 - 12位序列号 6 | * 7 | * @author Andy 8 | */ 9 | public class SnowFlake { 10 | 11 | /** 12 | * 起始的时间戳,可以修改为服务第一次启动的时间 13 | * 一旦服务已经开始使用,起始时间戳就不应该改变 14 | */ 15 | private final static long START_STMP = 1484754361114L; 16 | 17 | /** 18 | * 每一部分占用的位数 19 | */ 20 | private final static long SEQUENCE_BIT = 12; //序列号占用的位数 21 | private final static long MACHINE_BIT = 5; //机器标识占用的位数 22 | private final static long DATACENTER_BIT = 5;//数据中心占用的位数 23 | 24 | /** 25 | * 每一部分的最大值 26 | */ 27 | private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT); 28 | private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); 29 | private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); 30 | 31 | /** 32 | * 每一部分向左的位移 33 | */ 34 | private final static long MACHINE_LEFT = SEQUENCE_BIT; 35 | private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; 36 | private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; 37 | 38 | private long datacenterId; //数据中心 39 | private long machineId; //机器标识 40 | private long sequence = 0L; //序列号 41 | private long lastStmp = -1L;//上一次时间戳 42 | 43 | 44 | /** 45 | * 通过单例模式来获取实例 46 | * 分布式部署服务时,数据节点标识和机器标识作为联合键必须唯一 47 | * @param datacenterId 数据节点标识ID 48 | * @param machineId 机器标识ID 49 | */ 50 | public SnowFlake(long datacenterId, long machineId) { 51 | if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { 52 | throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); 53 | } 54 | if (machineId > MAX_MACHINE_NUM || machineId < 0) { 55 | throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); 56 | } 57 | this.datacenterId = datacenterId; 58 | this.machineId = machineId; 59 | } 60 | 61 | /** 62 | * 产生下一个ID 63 | * 64 | * @return 65 | */ 66 | public synchronized long nextId() { 67 | long currStmp = getNewstmp(); 68 | if (currStmp < lastStmp) { 69 | throw new RuntimeException("Clock moved backwards. Refusing to generate id"); 70 | } 71 | 72 | if (currStmp == lastStmp) { 73 | //相同毫秒内,序列号自增 74 | sequence = (sequence + 1) & MAX_SEQUENCE; 75 | //同一毫秒的序列数已经达到最大 76 | if (sequence == 0L) { 77 | currStmp = getNextMill(); 78 | } 79 | } else { 80 | //不同毫秒内,序列号置为0 81 | sequence = 0L; 82 | } 83 | 84 | lastStmp = currStmp; 85 | 86 | return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分 87 | | datacenterId << DATACENTER_LEFT //数据中心部分 88 | | machineId << MACHINE_LEFT //机器标识部分 89 | | sequence; //序列号部分 90 | } 91 | 92 | private long getNextMill() { 93 | long mill = getNewstmp(); 94 | while (mill <= lastStmp) { 95 | mill = getNewstmp(); 96 | } 97 | return mill; 98 | } 99 | 100 | private long getNewstmp() { 101 | return System.currentTimeMillis(); 102 | } 103 | 104 | public static void main(String[] args) { 105 | SnowFlake snowFlake = new SnowFlake(2, 3); 106 | long start = System.currentTimeMillis(); 107 | for (int i = 0; i < (1 << 18); i++) { 108 | System.out.println(i+": "+snowFlake.nextId()); 109 | } 110 | long end = System.currentTimeMillis(); 111 | System.out.println(end - start); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/wolfbe/distributedid/util/DateTimeUtil.java: -------------------------------------------------------------------------------- 1 | package com.wolfbe.distributedid.util; 2 | 3 | import java.sql.Timestamp; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | 8 | import org.slf4j.Logger; import org.slf4j.LoggerFactory; 9 | 10 | 11 | /** 12 | * @author Andy 2015-7-31 13 | * @description 时间日期的工具类 14 | */ 15 | public class DateTimeUtil { 16 | 17 | private static final Logger logger = LoggerFactory.getLogger(DateTimeUtil.class); 18 | private static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss"; 19 | private static final String DEFAULT_TIME_PATTERN = "HH:mm:ss"; 20 | 21 | /** 22 | * 获取当天的字符串 23 | * @return 24 | */ 25 | public static String getTodayStr(){ 26 | SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd"); 27 | return sdf.format(new Date()); 28 | } 29 | 30 | public static String getTodayStr2(){ 31 | SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd"); 32 | return sdf.format(new Date()); 33 | } 34 | 35 | public static String getCurrentTime(){ 36 | return getCurrentTime(DEFAULT_TIME_PATTERN); 37 | } 38 | 39 | 40 | /** 41 | * 获取当前时间的字符串 42 | * @return 43 | */ 44 | public static String getCurrentDateTime(){ 45 | return getCurrentTime(DEFAULT_DATE_PATTERN); 46 | } 47 | 48 | /** 49 | * 获取当前时间的字符串 50 | * @param format 字符串格式,如:yy-MM-dd HH:mm:ss 51 | * @return 52 | */ 53 | public static String getCurrentTime(String format){ 54 | SimpleDateFormat sdf = new SimpleDateFormat(format); 55 | Timestamp timestamp = new Timestamp(System.currentTimeMillis()); 56 | return sdf.format(timestamp); 57 | } 58 | 59 | /** 60 | * 获取当前的月份 61 | * @return 62 | */ 63 | public static String getCurrentMonth(){ 64 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM"); 65 | return sdf.format(new Date()); 66 | } 67 | 68 | /** 69 | * 比较两个时间,如果返回大于0,time1大于time2,如果返回-1,time1小于time2,返回0则相等 70 | * @param time1 71 | * @param time2 72 | * @return 73 | * @throws ParseException 74 | */ 75 | public static int compareTime(String time1,String time2) throws ParseException{ 76 | SimpleDateFormat sdf = new SimpleDateFormat(DEFAULT_DATE_PATTERN); 77 | Date date1 = sdf.parse(time1); 78 | Date date2 = sdf.parse(time2); 79 | long result = date1.getTime() - date2.getTime(); 80 | if(result > 0){ 81 | return 1; 82 | }else if(result==0){ 83 | return 0; 84 | }else{ 85 | return -1; 86 | } 87 | } 88 | 89 | /** 90 | * 转换字符串成日期对象 91 | * @param dateStr 日期字符串 92 | * @param format 格式,如:yy-MM-dd HH:mm:ss 93 | * @return 94 | */ 95 | public static Date convertStrToDate(String dateStr,String format){ 96 | if(!BlankUtil.isBlank(dateStr)&&!BlankUtil.isBlank(format)){ 97 | try{ 98 | SimpleDateFormat sdf = new SimpleDateFormat(format); 99 | return sdf.parse(dateStr); 100 | }catch (Exception e) { 101 | logger.warn("convertDate fail, date is "+ dateStr, e); 102 | } 103 | } 104 | return null; 105 | } 106 | 107 | /** 108 | * 把字符串日期转换成另一种格式 109 | * @param dateStr 字符串日期 110 | * @param format 转换日期格式 111 | * @param otherFormat 转换日期格式 112 | * @return 113 | */ 114 | public static String convertDate(String dateStr,String format,String otherFormat){ 115 | try{ 116 | Date date = convertStrToDate(dateStr, format); 117 | SimpleDateFormat sdf = new SimpleDateFormat(otherFormat); 118 | return sdf.format(date); 119 | }catch (Exception e) { 120 | logger.warn("convertDate fail, date is "+ dateStr, e); 121 | } 122 | return null; 123 | } 124 | 125 | /** 126 | * 把字符串日期转换成另一种格式 127 | * @param dateStr 字符串日期 128 | * @param format 转换格式 129 | * @return 130 | */ 131 | public static String convertDate(String dateStr,String format){ 132 | return convertDate(dateStr, DEFAULT_DATE_PATTERN,format); 133 | } 134 | 135 | 136 | public static void main(String[] args) { 137 | System.out.println(convertDate("2015-11-10 20:33", "yy年MM月dd日 hh时")); 138 | System.out.println(getCurrentMonth()); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 4.0.0 20 | 21 | com.wolfbe.distributedid 22 | distributedid 23 | jar 24 | 1.0.0 25 | 26 | distributedid ${project.version} 27 | http://www.wolfbe.com 28 | 29 | 30 | 31 | 3162057882 32 | Andy 33 | 34 | project leader 35 | architect 36 | 37 | 3162057882@qq.com 38 | https://github.com/beyondfengyu 39 | 8 40 | 41 | 42 | 43 | 44 | 45 | Apache License, Version 2.0 46 | http://www.apache.org/licenses/LICENSE-2.0 47 | 48 | 49 | 50 | 51 | 52 | com.alibaba 53 | fastjson 54 | 1.2.23 55 | 56 | 57 | io.netty 58 | netty-all 59 | 4.1.6.Final 60 | 61 | 62 | ch.qos.logback 63 | logback-classic 64 | 1.1.7 65 | 66 | 67 | 68 | 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-compiler-plugin 73 | 3.5.1 74 | 75 | 1.7 76 | 1.7 77 | 78 | 79 | 80 | maven-assembly-plugin 81 | 2.2 82 | 83 | 84 | 85 | com.wolfbe.distributedid.ServerStartup 86 | 87 | 88 | 89 | 90 | jar-with-dependencies 91 | 92 | 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-jar-plugin 98 | 2.4 99 | 100 | 101 | 102 | true 103 | lib/ 104 | com.wolfbe.chat.HappyChatMain 105 | 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-source-plugin 112 | 2.2.1 113 | 114 | 115 | attach-sources 116 | 117 | jar-no-fork 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Alibaba Group 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. --------------------------------------------------------------------------------