redis setnx lua脚本(抢购限流)|Redis 服务器会单线程原子性执行 lua 脚本,保证 lua 脚本在处理的过程中不会被任意其它请求打断 用户商品id数据作为key
乐观锁 version sql set a= x where x=a+1
zk wather
先更新数据库,再删除缓存 可能删除失败就导致缓存旧值
单删可能中间来了新查询,旧数据
延时双删 sleep一段时间,已更新到数据库了 要估计好读数据+写缓存的时间
redis 时单线程 多路复用
watch指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改
watch key1 key2 ... : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )
multi : 标记一个事务块的开始( queued )
set a b
exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )
!!!类似于java编译性错误执行EXEC命令时,所有命令都不会执行 类似于java的1/0的运行时异常,则执行EXEC命令时,其他正确命令会被执行,错误命令抛出异常
discard : 取消事务,放弃事务块中的所有命令
unwatch : 取消watch对所有key的监控
zookeeper=文件系统+监听通知机制
znode(目录节点) PERSISTENT-持久化目录节点 EPHEMERAL-临时目录节点 可存储数据
监听通知机制 当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端
实现Watcher 实现process方法
https://www.cnblogs.com/mengchunchen/p/9647756.html 分布式锁
redis分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能 可以支撑高并发的获取、释放锁操作。!!
zk分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小
另外一点就是,如果是redis获取锁的那个客户端bug了或者挂了,那么只能等待超时时间之后才能释放锁;而zk的话,因为创建的是临时znode,只要客户端挂了,znode就没了,此时就自动释放锁!!
zk分布式锁,就是某个节点尝试创建临时znode,此时创建成功了就获取了这个锁;这个时候别的客户端来创建锁会失败,只能注册个监听器监听这个锁。
释放锁就是删除这个znode,一旦释放掉就会通知客户端,然后有一个等待着的客户端就可以再次重新加锁。
分布式事务seata 分库分表/读写分离(强制走主库) 集群docker k8s netty
设计模式 公共组件 泛型方法 入参检验 异常抛出!!!
rpc更快,基于tcp,udp,保持对象状态 http上层协议!!
多表调用join 三表以上造成笛卡尔积过大,性能差不走全表的话
jwt 第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).
RS256 (采用SHA-256 的 RSA 签名) 是一种非对称算法
HS256 (带有 SHA-256 的 HMAC 是一种对称算法, 双方之间仅共享一个 密钥
保护安全,防止被盗用 使用https 或者ip和token绑定!!!
jwt不过期,可以加如黑名单等待过期后删除
登录流程 sso->role->权限验证、模块权限、jedis分布式锁
ZuulFilter 接口权限放行/验证登录/服务转发到相应服务
WebMvcConfigurer 替代xml配置 接口权限放行和拦截器注册并验证逻辑(登录/权限)
Filter函数回调 interceptor则是基于Java反射的(AOP思想)
Filter Servlet 容器支持 interceptor Spring容器
Filter先执行 interceptor后执行
注册多个服务实例
idea 点击Edit Configurations 其次勾选允许启动,新版本的是勾选Allow parallel run xshell服务器启动一个注册到本地
负载均衡 ribbon+RestTemplate 多个地方地用多个地方配置(但是可以调用集群外的服务 指定ip) FeignClient 动态代理注册到ioc 封装http请求 调用方法invoke
客户端从Eureka中拿到对应的服务列表,默认进行轮询RoundRobinRule 随机
https://www.cnblogs.com/jing99/p/11696192.html
zuul网关(统一入口,安全),进行路径的路由,定位到具体的服务节点上
底层使用ribbon来实现请求的路由,并内置Hystrix zuul.routes service-ids: /service-ids/**
ZuulFilter filterType前置|后置 run做权限验证!!!是否登录和接口权限
分布式锁 redis zk
登录需要限定次数 多个服务的多个syn 同步锁也就失效了。
redis分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能
zk分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小
如果是redis获取锁的那个客户端bug了或者挂了,那么只能等待超时时间之后才能释放锁;
而zk的话,因为创建的是临时znode,只要客户端挂了,znode就没了,此时就自动释放锁
Jedis jedis = new Jedis("192.168.31.251",6379);
jedis.auth("nssa_redis_1qazZAQ!");
String result = "NO";
try{
while (true) {
//获取分布式锁成功退出循环 但是不能无限等待 可以用次数或者时间限制
result = jedis.set("testkey", "testvalue", "NX", "PX", 100000);
if ("OK".equals(result)) {
//操作公共的参数 业务
break;
}
}
}finally {
//业务结果等操作
if("OK".equals(result)){
//释放锁 应判断是否获取到锁 成功返回1
Long RELEASE_SUCCESS = 1L;
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object resultTest = jedis.eval(script, Collections.singletonList("testkey"), Collections.singletonList("testvalue"));
}
}
登录成功 需要清0
if ("OK".equals(result)) {
//操作公共的参数 业务
String numkey ="login-userLoginNum"+user.getUsername();
jedis.set(numkey,"0");
break;
}
Jedis jedis = new Jedis("192.168.31.251",6379);
jedis.auth("nssa_redis_1qazZAQ!");
while (true){
//获取分布式锁成功退出循环 但是不能无限等待
String result = jedis.set("testkey", "testvalue", "NX", "PX", 100000);
if ("OK".equals(result)) {
//操作公共的参数 业务
String numkey ="login-userLoginNum"+user.getUsername();
String num = jedis.get(numkey);
if(num == null || num.equals("0")){
jedis.set(numkey,"1");
}else{
int nowNum = Integer.parseInt(num)+1;
jedis.set(numkey,String.valueOf(nowNum));
if(nowNum > 5){
System.out.println("账号或者密码错误,登录5次失败,用户锁定5分钟");
resultObject.setMsg("账号或者密码错误,登录5次失败,用户锁定5分钟");
redisUtil.set("login-"+user.getUsername(),nowNum,5*60); 需要锁住 和记录次数key不一样
}
}
break;
}
}finally 释放锁
集群连接
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(50);
config.setMaxWaitMillis(3000);
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
//集群池子
JedisShardInfo jedisShardInfo1 = new JedisShardInfo("120.26.1.1", 6379);
jedisShardInfo1.setPassword("123456888888");
List<JedisShardInfo> list = new LinkedList<JedisShardInfo>();
list.add(jedisShardInfo1);
ShardedJedisPool pool = new ShardedJedisPool(config, list);
//获取连接
ShardedJedis jedis = pool.getResource();
jedis.del()直接删除锁可能出现误删除
分布式锁解决 一个集群中多台机器操作一个变量 成员变量 A 存在 JVM1、JVM2、JVM3 三个 JVM 内存中!!!
分布式锁具备的条件:高可用高性能获取锁与释放锁/锁失效机制,防止死锁/非阻塞锁特性,即没有获取到锁将直接返回获取锁失败
Memcached:add
jedis:Redis:setnx,原子性操作,只有在 key 不存在的情况下,才能 set 成功 但是容易产生死锁,并且 setnx和expire(设置超时时间)不是原子操作,redis宕机,就无法释放锁
2.6.12版本:set(String key, String value, String nxxx, String expx, long time); ex/px 秒/毫秒 NX/XX 键不存在/键存在在操作
Zookeeper:顺序临时节点
Chubby:Google 公司实现的粗粒度分布式锁服务,底层利用了 Paxos 一致性算法
数据库:乐观锁(通过版本号)或者悲观锁(通过for update)
redission:为了不断提高的吞吐量,异步非阻塞线程模型大行其道 RLock lock = redisson.getLock("myLock") lock.lock();
分布式事务:操作位于不同的节点上,需要保证事务的 AICD 特性。
两阶段提交(2PC) Oracle 非常明显的性能问题
补偿事务(TCC)
MQ 事务消息
LCN seata
https://blog.csdn.net/burpee/article/details/52812837
zookeeper=文件系统!!!!+监听通知机制,分布式应用配置管理、统一命名服务、状态同步服务、集群管理等功能
znode(目录节点): 可以存储数据 peristent ephemeral 持久化和临时,客户端与zookeeper断开连接后,该节点被删除 seqential节点顺序,自动增加
将数据保存在内存中,这也就保证了 高吞吐量和低延迟!!!大小限制为1M
Session:一个客户端连接是指客户端和服务器之间的一个 TCP 长连接。无论是哪台服务器为客户端分配的 sessionID,都务必保证全局唯一
只要在 sessionTimeout 规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。
一个Watch事件是一个一次性的触发器!!!!!
监听通知机制:当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。
在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候
if (KeeperState.SyncConnected == event.getState()) { //zk连接成功通知事件
if (EventType.None == event.getType() && null == event.getPath()) {
connectedSemaphore.countDown();
} else if (event.getType() == EventType.NodeDataChanged) { //zk目录节点数据变化通知事件
try {
System.out.println("配置已修改,新值为:" + new String(zk.getData(event.getPath(), true, stat)));
} catch (Exception e) {
}
}
}
ACL:权限,create read write delete admin create/delete针对字节点
zkServer.sh start conf/zoo-1.cfg 集群 zkCli.sh 启动服务端和客户端
ls create set get delete
tickTime: 服务器之间或客户端与服务器之间维持心跳的时间间隔 initLimit syncLimit
myid中添加服务号(id唯一标识) zxid事务的操作顺序 dataDir 数据路径
sercer.a=b:c:d a是几号服务器,那个配置文件 b服务器ip c交换端口 d服务器挂了,从哪个端口重新选举 2888:3888
选举初leader 3.4.0只有fast算法
leader提供读服务,Follower 和 Observer(观察状态) 唯一的区别在于 Observer 机器不参与 Leader 的选举过程 LOOKING:竞选状态
半数机制:ZooKeeper集群中,半数以上节点存活,集群可用,所以ZooKeeper适合安装奇数服务器
FastLeaderElection(最新默认) 启动给自己投票,5台一般是第三台是leader
监听过程 main->connnet->watcher注册->事件变化->线程的process方法处理事件变化!!!!
写过程:向server1发送请求,不是就找到真正的leader,然后广播给其他server,超过半数写成功就说明写成功了!!!,然后通知server1写成功了
崩溃恢复和消息广播
ZAB(ZooKeeper Atomic Broadcast 原子广播)协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。
为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid
ZooKeeper每个对节点的改变都将产生一个唯一的Zxid,并且这个时间戳全局有序。。如果Zxid1的值小于Zxid2的值,那么Zxid1所对应的事件发生在Zxid2所对应的事件之前!!!
Zxid实现中Zxid是一个64为的数字,它高32位是epoch用来标识Leader关系是否改变,每次一个Leader被选出来,它都会有一个新的epoch。低32位是个递增计数。
public class ParkInfoEntity implements Serializable 序列化和反序列化
public static void main(String[] args) throws IOException, ClassNotFoundException { byte[] bytes = output(); input(bytes); } public static byte[] output() throws IOException { ParkInfoEntity parkInfoEntity = new ParkInfoEntity(); parkInfoEntity.setAreaName("中国"); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(outputStream); oos.writeObject(parkInfoEntity); oos.flush(); return outputStream.toByteArray(); } public static void input(byte[] bytes) throws IOException, ClassNotFoundException { ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(inputStream); ParkInfoEntity parkInfoEntity = (ParkInfoEntity) ois.readObject(); System.out.println(parkInfoEntity.getAreaName()); }