概述
我的项目地址:https://github.com/snower/slock
何为状态与原子操作数据库?区别于 redis 次要用于保留数据,可在多节点多零碎间高效统同步数据,slock 则是设计为只用于保留同步状态,简直不能携带数据,高性能的异步二进制协定也保障了在状态达成时高效的被动触发期待零碎。区别于 redis 被动查看的过期工夫,slock 的期待超时工夫及锁定过期工夫都是准确被动触发的。多核反对、更简略的系统结构也保障了其领有远超 redis 的性能及延时,这也更合乎状态同步需要中更高性能更低延时的需要。
秒杀为何难做?其问题就是咱们须要在很短时间内实现大量的有效申请中夹杂仅很少的无效申请解决,进一步简化就是须要实现超高并发下海量申请间的状态同步的过程,slock 高 QPS 能够疾速解决过滤大量有效申请的问题,高性能的原子操作又能够很好的解决抢库存的逻辑。
随着 nodejs 的应用,异步 IO 相干框架也越来越成熟,应用也越来越不便,多线程同步 IO 模式下,某些场景很多时候咱们须要转化为队列解决而后再推送后果,但异步 IO 就齐全不须要这么简单,间接加分布式锁期待可用就行,整个过程齐全回到了单机多线程编程的逻辑,更简略也更容易了解和保护了,比方下单申请须要操作很多,在高并发下可能须要发送到队列中解决实现再推送后果,但用异步 IO 的加分布式锁话,认真看异步 IO 加锁其实又组成了一个更大的分布式队列,大大简化了实现步骤。
个性
- 超高性能,在 Intel i5-4590 上超过 200 万 QPS
- 高性能二进制异步协定简略稳固牢靠,也可用 redis 同步文本协定
- 多核多线程反对
-
4 级 AOF 长久化
- 不长久化间接返回
- 超过过期工夫百分比工夫后长久化后返回
- 超过 AOF 工夫长久化后返回
- 立即异步长久化后返回
- 需整个集群沉闷节点都胜利并长久化后返回
- 高可用集群模式,主动迁徙、主动代理
- 准确到毫秒、秒、分钟超时、过期工夫,可独自订阅超时、过期工夫
- 屡次锁定反对,重入锁定反对
- 遗嘱命令
场景示例
分布式锁
整个协定只有中间指令,Lock 和 Unlock,分布式锁也即是最罕用场景,和 redis 实现分布式锁区别除了性能更好延时也更低外,期待超时及锁定超时过期工夫时准确被动触发的,所以有 wait 机制,redis 实现的分布式锁个别则须要 client 被动延时重试来查看。
package main;
import io.github.snower.jaslock.Client;
import io.github.snower.jaslock.Event;
import io.github.snower.jaslock.Lock;
import io.github.snower.jaslock.ReplsetClient;
import io.github.snower.jaslock.exceptions.SlockException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class App {public static void main(String[] args) {ReplsetClient replsetClient = new ReplsetClient(new String[]{"172.27.214.150:5658"});
try {replsetClient.open();
Lock lock = replsetClient.newLock("test".getBytes(StandardCharsets.UTF_8), 5, 5);
lock.acquire();
lock.release();} catch (SlockException e) {e.printStackTrace();
} finally {replsetClient.close();
}
}
}
nginx & openresty 限流
openresty 应用此服务实现限流能够很不便的实现跨节点,同时因为应用高性能异步二进制协定,每个 work 只须要一个和 server 的连贯,高并发下不会产生外部连贯耗尽的问题,server 主节点变更的时候 work 可主动应用新可用主节点,实现高可用。
最大并发数限流
每个 key 能够设置最大锁定次数,应用该逻辑能够十分不便的实现最大并发限流。
lua_package_path "lib/resty/slock.lua;";
init_worker_by_lua_block {local slock = require("slock")
slock:connect("lock1", "127.0.0.1", 5658)
}
server {
listen 80;
location /flow/maxconcurrent {
access_by_lua_block {local slock = require("slock")
local client = slock:get("lock1")
local flow_key = "flow:maxconcurrent"
local args = ngx.req.get_uri_args()
for key, val in pairs(args) do
if key == "flow_key" then
flow_key = val
end
end
local lock = client:newMaxConcurrentFlow(flow_key, 10, 5, 60)
local ok, err = lock:acquire()
if not ok then
ngx.say("acquire error:" .. err)
ngx.exit(ngx.HTTP_OK)
else
ngx.ctx.lock1 = lock
end
}
echo "hello world";
log_by_lua_block {
local lock = ngx.ctx.lock1
if lock ~= nil then
local ok, err = lock:release()
if not ok then
ngx.log(ngx.ERR, "slock release error:" .. err)
end
end
}
}
}
令牌桶限流
每个 key 能够设置最大锁定次数,并设置为在令牌到期时过期,即可实现令牌桶限流,应用毫秒级过期工夫的时候也能够从此形式来实现削峰均衡流量。
lua_package_path "lib/resty/?.lua;";
init_worker_by_lua_block {local slock = require("slock")
slock:connect("lock1", "127.0.0.1", 5658)
}
server {
listen 80;
location /flow/tokenbucket {
access_by_lua_block {local slock = require("slock")
local client = slock:get("lock1")
local flow_key = "flow:tokenbucket"
local args = ngx.req.get_uri_args()
for key, val in pairs(args) do
if key == "flow_key" then
flow_key = val
end
end
local lock = client:newTokenBucketFlow(flow_key, 10, 5, 60)
local ok, err = lock:acquire()
if not ok then
ngx.say("acquire error:" .. err)
ngx.exit(ngx.HTTP_OK)
end
}
echo "hello world";
}
}
其它可用场景
- 分布式 Event,一个罕用场景如扫码登录,二维码这边需期待扫码状态。
- 分布式 Semaphore,这个即是更通用的限流,此外也能够用于异步工作后果告诉。
- 分布式读写锁。
- 秒杀场景,秒杀场景是典型的申请数很高但无效申请数非常少的场景,原子操作的个性能够很好反对抢库存的逻辑,超高的并发反对也能够很好解决天量有效申请的问题。
- 异步后果告诉,网页间接实现的性能又须要后盾定时工作执行,此时齐全能够网络也调用异步工作,而后通过分布式 Event 期待执行实现即可。
以上这些应用场景都能够在 openresty 实现对外接口,再有外部零碎实现触发即可,openresty 的高性能高并发齐全能够很容易的解决很多之前须要用队列须要长连贯推送的需要。