简单介绍下Hash算法,⽐如说在安全加密领域MD5、SHA等加密算法,在数据存储和查找⽅⾯有Hash表等, 以上
都应⽤到了Hash算法。
查找效率(时间&空间,时间优先) :
顺序查找法(for遍历) < ⼆分查找(折半查找) < 直接寻址法(数组) < 开放寻址法(简单hash) < 拉链法(hash+链表)。
如果Hash算法设计的⽐较好的话,那么查询效率会更接近于O(1),如果Hash算法设计的⽐较low,那么
查询效率就会很低了 (hash冲突造成链表过长)
Hash算法在很多分布式集群产品中都有应⽤,⽐如分布式集群架构Redis、Hadoop、ElasticSearch,
Mysql分库分表,Nginx负载均衡等
主要的应⽤场景归纳起来两个:
请求的负载均衡(⽐如nginx的ip_hash策略)
分布式存储
例子 :集群中有redis1,redis2,redis3 三台Redis服务器,查找时候使用hash(key1)%3=index,用余数定位服务器。(mysql分表同理)
以ip_hash为例,如果一个服务宕机了,需要重新求模重新计算。服务器多了,缩容和扩容都会出现问题,比如⽤户在原来服务器中的会话都会丢失。
思路如下:
1->2的32次⽅减1,把它想成一个闭环(hash环)我们把服务器的ip或者主机名求
hash值然后对应到hash环上,那么针对客户端⽤户,也根据它的ip进⾏hash求值,对应到环上某个位
置,然后如何确定⼀个客户端路由到哪个服务器处理呢?按照顺时针⽅向找最近的服务器节点。(简单理解为双方同时hash,找最近的节点)
假如 3下线了,原来打到3的请求重新路由到4,对其他客户端没有影响。避免大量请求迁移,如下图:
假如 增加了服务器5,原来到3 的路由到5,也是迁移了一小部分,避免了大量请求迁移。如下图:
综上,,每⼀台服务器负责⼀段,⼀致性哈希算法对于节点的增减都只需重定位环空间中的⼀⼩
部分数据,具有较好的容错性和可扩展性。
但是节点太少的情况下,会产生数据倾斜问题。例如只有2台服务器,根据一致性hash,大量请求会打到节点1,节点2 负责的非常少。这就是数据倾斜问题。
此时,虚拟节点解决方案就来了:将每一个节点计算多个hash,每个hash计算结果都设置成当前节点,成为虚拟节点。
具体做法:在服务器ip后面增加编号来实现,比如,可以为每台服务器计算三个虚拟节点, 如下图:
github地址-> https://github.com/znbsmd/handwriting_framework/tree/master/ConsistentHash
ngx_http_upstream_consistent_hash 模块是⼀个负载均衡器,使⽤⼀个内部⼀致性hash算法来选择
合适的后端节点。
该模块可以根据配置参数采取不同的⽅式将请求均匀映射到后端机器,
ngx_http_upstream_consistent_hash模块是⼀个第三⽅模块,需要下载安装后使⽤->https://github.com/replay/ngx_http_consistent_hash 使用步骤如下:
upstream zjnServer {
consistent_hash $request_uri;// 根据客户端请求的uri映射
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}
下单分发到不同的服务器,由于创建时间都是取系统时间,由于时间不同,导致数据错乱。
分布式集群中各个服务器节点都可以连接互联⽹
#使⽤ ntpdate ⽹络时间同步命令
ntpdate -u ntp.api.bz #从⼀个时间服务器同步时间
最后使用crontab 定时任务去同步
分布式集群中某⼀个服务器节点可以访问互联⽹或者所有节点都不能够访问互联⽹
选node1 作为主时间服务器,(如果这台服务器能够访问互联⽹,可以让这台服务器和⽹络时间保持同步,如果不能就⼿动设置⼀个时间)
# 1、如果有 restrict default ignore,注释掉它
# 2、添加如下⼏⾏内容
restrict 172.17.0.0 mask 255.255.255.0 nomodify notrap #放开局域⽹同步功能,172.17.0.0是你的局域⽹⽹段
server 127.127.1.0 # local clock
fudge 127.127.1.0 stratum 10
# 3、重启⽣效并配置ntpd服务开机⾃启动
service ntpd restart
chkconfig ntpd on
ntpdate 172.17.0.17
分表后,ID不能重复
public class MyTest {
public static void main(String[] args) {
System.out.println(java.util.UUID.randomUUID().toString());
}
}
⼀些互联⽹公司也基于上述的⽅案封装了⼀些分布式ID⽣成器,⽐如滴滴的tinyid(基于数据库实现)、百度的uidgenerator(基于SnowFlake)和美团的leaf(基于数据库和SnowFlake)等
redis.incr (推荐)
redis.clients
jedis
2.9.0
Jedis jedis = new Jedis("127.0.0.1",6379);
try {
long id = jedis.incr("id");
System.out.println("从redis中获取的分布式id为:" + id);
}
finally {
if (null != jedis) {
jedis.close();
}
}
本质不同,定时任务是时间驱动,⽽MQ是事件驱动;定时任务作业更倾向于批处理,MQ倾向于逐条处理;
任务调度框架Quartz
Elastic-Job是当当⽹开源的⼀个分布式调度解决⽅案,基于Quartz⼆次开发的,由两个相互独⽴的⼦项
⽬Elastic-Job-Lite和Elastic-Job-Cloud组成。地址-> :https://github.com/elasticjob
主要功能:
jar包(API) + 安装zk软件
Elastic-Job依赖于Zookeeper进⾏分布式协调,所以需要安装Zookeeper软件(3.4.6版本以上)
安装配置zk
引入jar
com.dangdang
elastic-job-lite-core
2.1.5
定时任务实例…
Leader节点选举机制
每个Elastic-Job的任务执⾏实例App作为Zookeeper的客户端来操作ZooKeeper的znode
ElasticJob可以把作业分为多个的task(每⼀个task就是⼀个任务分⽚),每
⼀个task交给具体的⼀个机器实例去处理(⼀个机器实例是可以处理多个task的),但是具体每个task
执⾏什么逻辑由我们⾃⼰来指定。
Strategy策略定义这些分⽚项怎么去分配到各个机器上去,默认是平均分配,可以定制,⽐如某⼀个机
器负载 ⽐较⾼或者预配置⽐较⾼,那么就可以写策略。分⽚和作业本身是通过⼀个注册中⼼协调的,因
为在分布式环境下,状态数据肯定集中到⼀点,才可以在分布式中沟通。
新增加⼀个运⾏实例app3,它会⾃动注册到注册中⼼,注册中⼼发现新的服务上线,注册中⼼会通知
ElasticJob 进⾏重新分⽚,那么总得分⽚项有多少,那么就可以搞多少个实例机器,⽐如完全可以分1000⽚
最多就可以有多少app实例,完全可以分1000⽚,那么就可以搞1000台机器⼀起执⾏作业
注意:
因为Http协议是⽆状态,Http为什么要设计为⽆状态协议?早期都是静态⻚⾯⽆所谓有⽆状态,后来有动态的内容更丰富,就需要有状态,出现了两种⽤于保持Http状态的技术,那就是Cookie和Session。
使用nginx默认轮询策略,会出现Session打到不同的服务器问题。
Nginx的 IP_Hash 策略(可以使⽤)
Session复制(不推荐)
redis,Session共享,Session集中存储(推荐)
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.session
spring-session-data-redis
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
@EnableRedisHttpSession
《lagouedu》分布式解决方案总结