我有一个Node.js应用程序,我正在尝试使用Redis缓存来记录所发出的请求数量。这只是一个概念证明,以了解Redis是否适合我,但是我对结果感到有些失望,我想知道Redis是否真的表现不佳,或者我的代码是否存在缺陷。 / p>
我有以下代码:
const express = require('express')
const Redis = require('ioredis')
const client = new Redis(6379, 'redis');
client.on('connect', function() {
console.log('Redis client connected');
});
client.on('error', function (err) {
console.log('Something went wrong ' + err);
});
const getRedisCount = async (question_id) => {
const count = await client.get('votes_' + question_id);
if(count) {
return count;
}
await setRedisCount(question_id, 0)
return 0;
}
const setRedisCount = async (question_id, count) => {
await client.set('votes_' + question_id, count)
}
const app = express();
app.use(express.json());
// post a vote
app.post('/vote', async (req, res) => {
let count = await getRedisCount(req.body.question_id)
count++
await setRedisCount(req.body.question_id, count)
res.status(200).send({ status: 200, data: count });
})
app.listen(5000, () => {
console.log('Vote service listening on port 5000')
})
现在,当我仅在Postman中执行单个发帖请求时,所有这些工作都可以按预期进行。计数器将按预期更新。
但是,当我在JMeter中运行一次同时测试150个请求的测试时,在最后一个请求之后,我得到的计数约为20-30,而不是预期的150。
所以我的问题是-这是预期的行为,还是我的代码有问题?在两种情况下-如何解决?
答案 0 :(得分:2)
这可能与您的Redis服务器配置有关。
您可以从主机上的redis cli运行MONITOR。
通常,您通过cli进行连接,例如:
https://redis.io/topics/rediscli
redis-cli -h <your server ip> -p <your server port> -a <if you have a server password enabled>
$ redis-cli -h host -p port -a password
默认端口为6379
这将显示与redis服务器的所有交互,但是请不要运行它,因为始终运行它会降低性能,但是非常有助于故障排除。
https://redis.io/commands/monitor
$ redis> monitor
1339518083.107412 [0 127.0.0.1:60866] "keys" "*"
1339518087.877697 [0 127.0.0.1:60866] "dbsize"
1339518090.420270 [0 127.0.0.1:60866] "set" "x" "6"
1339518096.506257 [0 127.0.0.1:60866]
这应该给您指示该问题。
您还可以在redis cli中运行INFO命令,以查看服务器统计信息和信息,例如内存使用情况。
https://redis.io/commands/info
redis> INFO
# Server
redis_version 999.999.999
redis_git_sha1:3c968ff0
redis_git_dirty:0
redis_build_id:51089de051945df4
redis_mode:standalone
os:Linux 4.8.0-1-amd64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:6.3.0
process_id:8394
</snip>
还要在代码中使用来自Redis客户端的INCRBY命令,否则在设置与异步模式不一致的计数值之前,需要先增加计数值。
https://redis.io/commands/incrby
我认为您的问题是因为这是异步的,对吗?
因此,计数在发布时是不同步的,因为某些计数可能在其他计数之前等待。
您可能会尝试以任何一种方式尝试将它们同步发布以查看是否有所不同,然后您知道这是问题所在,为此最好使用redis INCR命令。
答案 1 :(得分:0)
听起来像原子性问题。
// post a vote
app.post('/vote', async (req, res) => {
let count = await getRedisCount(req.body.question_id)
count++
await setRedisCount(req.body.question_id, count)
res.status(200).send({ status: 200, data: count });
})
假设计数为20,那么您的150个请求中有许多将读取20作为其值。
let count = await getRedisCount(req.body.question_id)
下一条语句将递增,并将count设置为21,下一行将尝试将其保存为redis。
这是标准比赛条件,https://en.wikipedia.org/wiki/Race_condition#Example
有更好的解释您将必须在代码中实现lock
或使用redis提供的解决方案。
本质上,锁将使每个请求排队等待一个接一个的递增,以避免覆盖值。
答案 2 :(得分:0)
不确定这是否可行,但可以改用 GETSET
吗?我正在尝试这个,因为我有同样的问题。