bloom 是支持多proxy 配置的(基于shared)但是对于多proxy 的处理有一个bug (处理map 一直获取的是第一个)参考https://github.com/valeriansaliou/bloom/issues/16,以前有写过简单的介绍
以下是一个多api 接口cache 的测试以及对于api cache 控制的试用
环境准备
- bug 说明
上边有说明关于多个proxy 的bug,修改方法
https://github.com/valeriansaliou/bloom/blob/master/src/proxy/tunnel.rs
修改原有:
fn map_shards() -> [Option<Uri>; MAX_SHARDS as usize] {
// Notice: this array cannot be initialized using the short format, as hyper::Uri doesnt \
// implement the Copy trait, hence the ugly hardcoded initialization vector w/ Nones.
let mut shards = [
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None,
];
for shard in &APP_CONF.proxy.shard {
// Shard number overflows?
if shard.shard >= MAX_SHARDS {
panic!("shard number overflows maximum of {} shards", MAX_SHARDS);
}
// Store this shard
shards[shard.shard as usize] = Some(
format!(
"http://{}:{}",
// 此处有bug
APP_CONF.proxy.shard[0].host, APP_CONF.proxy.shard[0].port
)
.parse()
.expect("could not build shard uri"),
);
}
shards
}
为:
fn map_shards() -> [Option<Uri>; MAX_SHARDS as usize] {
// Notice: this array cannot be initialized using the short format, as hyper::Uri doesnt \
// implement the Copy trait, hence the ugly hardcoded initialization vector w/ Nones.
let mut shards = [
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None,
];
for shard in &APP_CONF.proxy.shard {
// Shard number overflows?
if shard.shard >= MAX_SHARDS {
panic!("shard number overflows maximum of {} shards", MAX_SHARDS);
}
// Store this shard
shards[shard.shard as usize] = Some(
format!(
"http://{}:{}",
shard.host, shard.port
)
.parse()
.expect("could not build shard uri"),
);
}
shards
}
相关修复的docker 镜像 dalongrong/bloom:v1.28.1
- docker-compose 文件
version: "3"
services:
lb:
image: openresty/openresty:alpine
volumes:
- "./nginx-lb.conf:/usr/local/openresty/nginx/conf/nginx.conf"
- "./access_test.log:/usr/local/openresty/nginx/logs/access_test.log"
- "./access_test2.log:/usr/local/openresty/nginx/logs/access_test2.log"
ports:
- "80:80"
- "8080:8080"
- "8081:8081"
bloom:
image: dalongrong/bloom:v1.28.1
volumes:
- "./bloom.cfg:/etc/bloom.cfg"
ports:
- "8811:8811"
- "9001:8080"
redis:
image: redis
ports:
- "6379:6379"
- bloom 配置
[server]
log_level = "debug"
inet = "0.0.0.0:8080"
[control]
inet = "0.0.0.0:8811"
tcp_timeout = 900
[proxy]
[[proxy.shard]]
shard = 1
host = "lb"
port = 8080
[[proxy.shard]]
shard = 2
host = "lb"
port = 8081
[cache]
ttl_default = 3600
executor_pool = 64
disable_read = false
disable_write = false
compress_body = false
[redis]
host = "redis"
port = 6379
database = 0
pool_size = 80
max_lifetime_seconds = 60
idle_timeout_seconds = 600
connection_timeout_seconds = 1
max_key_size = 256000
max_key_expiration = 2592000
- openresty 配置以及基于openresty 的api
为了方便测试同时基于openresty 的content_by_lua 以及header_filter_by_lua 处理api 以及添加api 控制需要的
Bloom-Response-Buckets
http header
worker_processes 1;
user root;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
lua_need_request_body on;
#gzip on; // should not use gzip
resolver 127.0.0.11 ipv6=off;
log_format graylog2_json escape=json '{ "timestamp": "$time_iso8601", '
'"remote_addr": "$remote_addr", '
'"body_bytes_sent": $body_bytes_sent, '
'"request_time": $request_time, '
'"response_status": $status, '
'"request": "$request", '
'"request_method": "$request_method", '
'"host": "$host",'
'"request_body":"$request_body",'
'"upstream_cache_status": "$upstream_cache_status",'
'"upstream_addr": "$upstream_addr",'
'"http_x_forwarded_for": "$http_x_forwarded_for",'
'"http_referrer": "$http_referer", '
'"http_user_agent": "$http_user_agent" }';
log_format graylog2_json2 escape=json '{ "timestamp": "$time_iso8601", '
'"remote_addr": "$remote_addr", '
'"body_bytes_sent": $body_bytes_sent, '
'"request_time": $request_time, '
'"response_status": $status, '
'"request": "$request", '
'"request_method": "$request_method", '
'"host": "$host",'
'"request_body":"$request_body",'
'"upstream_cache_status": "$upstream_cache_status",'
'"upstream_addr": "$upstream_addr",'
'"http_x_forwarded_for": "$http_x_forwarded_for",'
'"http_referrer": "$http_referer", '
'"http_user_agent": "$http_user_agent" }';
real_ip_header X-Forwarded-For;
real_ip_recursive on;
server {
listen 80;
charset utf-8;
default_type text/html;
location /userdemo {
proxy_pass http://bloom:8080;
proxy_set_header Bloom-Request-Shard 1;
proxy_set_header Host $host;
proxy_read_timeout 10000;
proxy_send_timeout 10000;
proxy_buffer_size 1M;
proxy_buffers 8 1M;
proxy_busy_buffers_size 1M;
proxy_temp_file_write_size 1M;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
}
location ^~ /appdemo {
proxy_pass http://bloom:8080;
proxy_set_header Bloom-Request-Shard 2;
proxy_set_header Host $host;
proxy_read_timeout 10000;
proxy_send_timeout 10000;
proxy_buffer_size 1M;
proxy_buffers 8 1M;
proxy_busy_buffers_size 1M;
proxy_temp_file_write_size 1M;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 8080;
charset utf-8;
default_type text/html;
location /userdemo {
access_log /usr/local/openresty/nginx/logs/access_test.log graylog2_json;
content_by_lua_block {
ngx.say("this is a demo")
}
header_filter_by_lua_block {
ngx.header["Bloom-Response-Buckets"] = "user_id:10012"
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 8081;
charset utf-8;
default_type text/html;
location /appdemo {
access_log /usr/local/openresty/nginx/logs/access_test2.log graylog2_json2;
content_by_lua_block {
ngx.say("appdemo")
}
header_filter_by_lua_block {
ngx.header["Bloom-Response-Buckets"] = "login:10012"
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
- api 接口控制
基于nodejs
package.json
{
"name": "api",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"bloom-control": "^1.2.1"
},
"scripts":{
"start":"node app.js"
}
}
app.js
var BloomControl = require("bloom-control").BloomControl;
var bloomControl = new BloomControl({
host : "127.0.0.1", // Or '127.0.0.1' if you are still using IPv4
port : 8811, // Default port is '8811'
shard : 1 // Specify the Bloom shard to use, as \
// a Bloom instance can host multiple cache shards, eg. for different API workers
}).connect({
connected : function() {
// Connected handler
console.info("Bloom Control succeeded to connect to host.");
},
disconnected : function() {
// Disconnected handler
console.error("Bloom Control is now disconnected.");
},
timeout : function() {
// Timeout handler
console.error("Bloom Control connection timed out.");
},
retrying : function() {
// Retry handler
console.error("Trying to reconnect to Bloom Control...");
},
error : function(error) {
// Failure handler
console.error("Bloom Control failed to connect to host.", error);
}
});
bloomControl.purgeBucket(`user_id:10012`, function(error) {
// Handle purge errors here
if (error) {
console.log("some wrong")
}
else{
console.log("ok")
}
});
测试
- 启动服务
docker-compose up -d
- cache 测试
清理cache
cd api && yarn && yarn start
效果
说明
基于bloom高效,方便的cache 能力,我们可以方便的开发基于cache 的应用,提高应用的稳定性以及可控制性,以上是一个试用
大家可以自己扩展下,实现更加灵活的cache应用
参考代码
https://github.com/valeriansaliou/bloom
https://github.com/rongfengliang/bloom-api-test