雪花算法介绍
SnowFlake
算法,是 Twitter 开源的分布式 id 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id
-
1位
,不用。二进制中最高位为1的都是负数,但是生成的id都是正数,所以这个最高位固定是0 -
41位
,用来记录时间戳(毫秒)。-
41位可以表示2<sup>41</sup>-1个数字,
-
如果只用来表示正整数(计算机中正数包含0),可以表示的数值范围是:0 至 2<sup>41</sup> - 1,减1是因为可表示的数值范围是从0开始算的,而不是1。
-
也就是说41位可以表示2<sup>41</sup> - 1个毫秒的值,转化成单位年则是2<sup>41</sup> - 1 / (1000 * 60 * 60 * 24 * 365) = 69年
-
-
10位
,用来记录工作机器id。-
可以部署在2<sup>10</sup> = 1024个节点,包括
5位datacenterId
和5位workerId
-
5位(bit)
可以表示的最大正整数是2<sup>5</sup> - 1 = 31,即可以用0、1、2、3、....31这32个数字,来表示不同的datecenterId或workerId
-
-
12位
,序列号,用来记录同毫秒内产生的不同id。12位(bit)
可以表示的最大正整数是2<sup>12</sup> - 1 = 4095,即可以用0、1、2、3、....4095这4096个数字,来表示同一机器同一时间截(毫秒)内产生的4096个ID序号,减1是因为可表示的数值范围是从0开始算的,而不是1。
同一毫秒:0-4095 4096,因为这一毫秒生产的ID数已经满了,因此需要让CPU空转,使得毫秒数增加1,那么此时这一毫秒的ID生成应该从0重新开始
算法实现
java
package com.qf.electronic.util;
/**
* 雪花算法
*/
public class SnowFlake {
/**
* 这里需要记录最后一次生成ID的时间,因为同一毫秒内,可能会生成多个ID,记录后可以用来做比对
*/
private long lastTime = -1L;
/**
* 初始时间,因为时间占41位,所以可以从这个时间开始,差不多使用69年
*/
private long startTime = System.currentTimeMillis();
/**
* 机房ID,占5位,因此最多能够对32个机房有效
*/
private long dataCenterId;
/**
* 机器ID,占5位,因此最多能够对32个机器有效
*/
private long workId;
/**
* 序列号,占12位,因此在一毫秒内最多生成2^12 = 4096个序列号
*/
private long sequence;
public SnowFlake(long dataCenterId, long workId, long sequence){
//机房ID最小值为0,最大值为31
if(dataCenterId > 31 || dataCenterId < 0){
throw new IllegalArgumentException("机房ID必须>=0且<=31,当前使用机房ID:" + dataCenterId);
}
if(workId > 31 || workId < 0){
throw new IllegalArgumentException("机器ID必须>=0且<=31,当前使用机器ID:" + workId);
}
this.dataCenterId = dataCenterId;
this.workId = workId;
this.sequence = sequence;
}
public long generateId(){
long time = System.currentTimeMillis(); //获取系统当前时间
if(time < lastTime){//防止时间回拨,必须手动将时间拨回
throw new RuntimeException("时间回拨");
}
if(time == lastTime){//同一时间段内,生成多个ID
//按位与操作符所得结果最大值不会超过参与运算的最小操作数,这里不能超过4096
sequence = (sequence + 1) & 4095;
if(sequence == 0){//如果计数重新归0,表示该毫秒内,ID生成的数量已满
//CPU空转1毫秒
while ((time = System.currentTimeMillis()) == lastTime);
}
} else {
sequence = 0;
}
//时间本身占41位,但是需要向左移动22位才能在指定的位置上
//机房ID占5位,但是需要向左移动17位才能在指定的位置上
//机器ID占5位,但是需要向左移动12位才能在指定的位置上
long id = (time - startTime) << 22 | dataCenterId << 17 | workId << 12 | sequence;
lastTime = time;
return id;
}
}
Nginx
1. Nginx是什么
Nginx是一个高性能的基于HTTP的反向代理的服务器、也是一个基于SMTP和POP3的邮件服务器其主要功能就类似于Tomcat,对外提供资源共享
反向代理(Reverse Proxy)是指以代理服务器来接收客户端请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给客户端
反向代理的作用:
-
保证内网的安全,阻止web攻击,因为客户端是无法感知真正做事的服务器的存在
-
负载均衡:代理服务器将接收的请求均分给被代理的服务器
2. Nginx能干什么
Nginx既然是服务器,那么其能做的事就是数据共享,具体体现如下:
-
虚拟主机
虚拟主机就是虚拟服务器,就跟Java虚拟机差不多,Java虚拟机虽然是虚拟的,但也具备计算机的功能,也能运行。虚拟主机也一样,虽然不存在,但Nginx可以虚拟出来,对外提供服务
-
反向代理
前面已经介绍过
-
负载均衡
负载就是服务器承载的负荷,所谓均衡指的是多个服务器之间的负荷对比,单个服务器没有均衡的说法。负载均衡就是指多个服务器之间处理的请求数量要大致相同
-
动静分离
动指的是资源会变化,比如从服务器获取的数据。
静指的就是资源不会变化,比如从服务器获取的html、css、js、图片等。
动静分离指的是动态资源放在服务器上处理,静态资源放在nginx上处理。
3. Nginx的安装及目录说明
演示
4. Nginx配置
nginx的学习也就是配置文件的学习
4.1 虚拟主机配置
java
server {
listen 8888;
server_name localhost;
location /{
root d:/demo;
index index.html;
}
}
4.2 反向代理配置
java
# 以/electronic/开始的请求会被代理到另外的服务器访问
location /electronic/ {
proxy_pass http://localhost:9000/;
}
4.3 负载均衡配置
java
upstream electronicServer {
server localhost:9000;
server localhost:9001;
}
# 以/electronic/开始的请求会被代理到另外的服务器访问
location /electronic/ {
proxy_pass http://electronicServer/;
}
-
负载均衡---轮询
javaupstream electronicServer { server localhost:9000; server localhost:9001; }
-
负载均衡---权重
javaupstream electronicServer { server localhost:9000 weight=2; #权重越大,被分配处理的请求越多 server localhost:9001 weight=1; }
-
负载均衡---ip_hash
ip_hash就是通过请求的ip地址,使用hash算法,算出来应该访问那一台服务器,如果是ip地址没变,那么这个请求的服务器 就永远都不会变
javaupstream electronicServer { ip_hash; # 负载均衡规则 server localhost:9000; server localhost:9001; }
4.4 动静分离
5. 附录: 匹配规则
-
=
= 开头表示精确匹配
-
^~
^~ 开头 表示uri以某个常规字符串开头,理解为匹配 url路径即可。nginx不对url做编码,因此请求为/static/20%/aa,可以被规则^~ /static/ /aa匹配到(注意是空格)。
-
~
~ 开头表示区分大小写的正则匹配
-
~*
~* 开头表示不区分大小写的正则匹配
-
!~
!~开头表示区分大小写不匹配
-
!~*
!~*开头表示不区分大小写不匹配
-
/
/ 通用匹配,任何请求都会匹配到。