1 问题
在项目中我们用到了多级缓存(caffeine + redis),为了提高localcache的命中率,我们希望某些参数相同的请求能打到相同的机器。
1.1 问题分析
- 我们是内部服务,无法使用 ipHash
- 因为请求不一定都带有userId/buildingId/guestCode等参数,需要根据不同url来配置不同hash策略
- 端上/网关没有做统一公参的处理
典型的需求case如下表所示:
URI | 入参 | hash方式 |
---|---|---|
/room/room_info | buildingId, roomCode, needOwner | buildingId + roomCode |
/room/guest_list | buildingId, roomCode | buildingId + roomCode |
/guest/last_lesson | guestCode | guestCode |
/owner/virtual_room | buildingId,ownerCode | buildingId + ownerCode |
/lessons_date | userId,date | userId |
/owner_info | userId,userIds | 不hash |
/room/virtual_total | vRoomCode | 不hash |
2 方案探讨
个人想到两种方案:
2.1 应用分发
思路
- 单起一个应用A,专门来做请求分发。
- 假设后端应用B有N台服务器,应用B的每台服务器都单独订阅一个消息队列的topic。
- 应用A收到请求后,根据需求解析参数,一致性hash计算出应该需要应用B的哪台机器来处理,然后将请求发送到对应的topic。
- 应用B的服务器从对应的topic订阅请求消息并解析处理。
特点
适用场景:快速应答(应用A) + 异步处理复杂逻辑(消息队列 + 应用B)
优点:研发侧方便管控;
缺点:需要单独部署服务;异步处理可能需要在业务侧增加额外状态来表示处理状态
2.2 nginx分发
思路
nginx支持lua扩展,同时也支持一致性hash。可以用lua来解析请求并根据需求判断是一致性hash分发还是轮询分发。默认轮询分发;如果需要一致性hash分发,根据实际需求计算出hashKey,按hashKey分发即可。
特点
优点:适用于大部分场景,无需部署单独服务,对应用无侵入
缺点:lua语法需要适应,nginx性能会略有下降,需要运维侧配合实施
因为每个请求在nginx层都要增加一层逻辑,nginx性能会略有下降;如果不是对nginx吞吐量有特别高的要求,一般问题不大。
需要特别注意nginx版本/nginx各个模块/luajit的兼容性问题。
我们的应用采用nginx分发的方案。
3 环境安装
安装lua
参考:http://www.lua.org/download.html
curl -R -O http://www.lua.org/ftp/lua-5.4.0.tar.gz
tar zxf lua-5.4.0.tar.gz
cd lua-5.4.0
make all test
make install
下载nginx及各种插件
为了避免nginx的兼容性等问题,我们使用openresty版本, openresty自带了lua扩展。
下载openresty: https://openresty.org/en/download.html 。我是用的是 openresty-1.11.2.5 。
下载ngix一致性hash插件: https://github.com/replay/ngx_http_consistent_hash
安装依赖
brew update
brew install pcre openssl curl
安装nginx及插件
./configure --with-cc-opt="-I/usr/local/opt/openssl/include/ -I/usr/local/opt/pcre/include/" --with-ld-opt="-L/usr/local/opt/openssl/lib/ -L/usr/local/opt/pcre/lib/" -j2 --add-module=…/pkgs/ngx_http_consistent_hash-master //(-j2 2为核数)
sudo make -j2
sudo make install
检测是否安装成功:
/usr/local/openresty/nginx/sbin/nginx -V
默认conf文件位置: /usr/local/openresty/nginx/conf/nginx.conf
我们可以执行下面命令来简化nginx执行:
sudo ln -s /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx
4 nginx常用命令
nginx // 直接输入nginx命令启动
nginx -t //检测是否可以正常启动
nginx -s reload //重启
nginx -s stop //停止nginx进程
nginx -t -c /usr/local/nginx/conf/nginx.conf // 检测指定配置文件语法是否正确
5 代码开发
修改 nginx.conf:
server {
listen 80;
server_name localhost;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location / {
set $hashkey "";
set $backendupstream "rrbackend";
// lua脚本设置backendupstream和hashkey
rewrite_by_lua_file '../set_upstream.lua';
proxy_pass http://$backendupstream;
}
}
// 轮询
upstream rrbackend {
// 模拟4台后端服务器
server 127.0.0.1