本章节我们将学习如何使用MQ
库.
MQ库简介
MQ
库实现了各类消息代理中间件(Message Broker)的连接协议, 目前支持:redis
、mqtt
、stomp
协议.
MQ
库基于上述协议实现了: 生产者 -> 消费者
与订阅 -> 发布
模型, 可以在不依赖其它服务的情况下独立完成任务.
API介绍
cf框架提供了多种MQ
的封装, 当我们需要使用的时候需要根据实际的协议进行选择:
-- local MQ = require "MQ.mqtt"
-- local MQ = require "MQ.redis"
-- local MQ = require "MQ.stomp"
MQ:new(opt)
此方法将会创建一个的MQ对象实例.
opt
是一个table
类型的参数, 可以传递如下值:
-
host - 字符串类型, 消息队列的域名或者IP地址.
-
port - int类型, 消息队列监听的端口.
-
auth/db - 字符串类型, 仅在redis协议下用作登录认证或者db选择(没有可以不填写).
-
username/password - 字符串类型, 仅在stomp/mqtt协议下用作登录认证(没有可以不填写).
-
vhost - 字符串类型, 仅在使用某些特定消息队列server的时候填写(例如:rabbit).
-
keepalive - int类型, 仅在使用mqtt的时候用来出发客户端主动发出心跳包的时间.
以redis broker为示例:
local MQ = require "MQ.redis"
local mq = MQ:new {
host = "localhost",
port = 6379,
-- db = 0,
-- auth = "123456789",
}
MQ:on(pattern, function)
此方法用来订阅一个指定pattern
. 当broker
将消息传递到cf后, function
将会被调用.
MQ
库会为function
注入一个table
类型的参数msg
, 此参数将在断开连接的时候为nil
.
msg
根据采用的协议的不同msg
的内容也将有所不同. 具体内容以logging
库的打印为准.
标准使用示例:
local Log = require("logging"):new()
mq:on("/notice", function(msg)
if not msg then
return Log:ERROR("['/notice'] SUBSCRIBE ERROR: 连接已断开.")
end
Log:DEBUG(msg)
end)
开发者可以同时订阅多个parttern
.
MQ:emit(pattern, msg)
此方法用来向指定pattern
发送消息. msg为字符串类型的消息.
使用示例:
mq:emit('/notice', '{"code":200,"data":[1,2,3,4,5,6,7,8,9,10]}')
单个MQ
可以一直复用emit, 内部会创建一个写入队列去完成消息的顺序发送. (在多个实例中无法保证消息先后)
MQ:start()
此方法在作为独立运行服务端时候调用.
使用示例:
mq:start()
MQ:clsoe()
此方法可以关闭不再使用的MQ; 在任何情况下MQ使用完毕后都需要调用此方法来释放资源.
使用示例:
mq:close()
开始实践
为了演示更加直观, 这里仅使用redis作为broker中专消息.
1. 模拟生产者与消费者
我们模拟100个生产者向redis的/queue
投递消息, 同时定义了一个消费者订阅/queue
持续进行消费
代码如下:
local cf = require "cf"
local json = require "json"
local Log = require("logging"):new()
local MQ = require "MQ.redis"
cf.fork(function ()
local consumer = MQ:new {
host = "localhost",
port = 6379
}
local count = 0
consumer:on("/queue", function (msg)
if not msg then
Log:ERROR("[/queue]连接失败", "已经消费了"..count.."个消息")
return
end
count = count + 1
Log:DEBUG("开始消费:", msg, "已经消费了"..count.."个消息")
end)
consumer:start() -- Websoket内部无需使用这个方法
end)
for i = 1, 100 do
cf.fork(function()
local producer = MQ:new {
host = "localhost",
port = 6379
}
producer:emit("/queue", json.encode({
code = 200,
data = {
id = math.random(1, 1 << 32)
},
}))
producer:close()
end)
end
输出如下:
[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin
[2019-06-25 16:05:36,240] [@script/main.lua:19] [DEBUG] : 开始消费:, {
["pattern"]="/queue", ["payload"]="{"code":200,"data":{"id":3912595079}}", ["source"]="/queue", ["type"]="pmessage"}, 已经消费了1个消息
[2019-06-25 16:05:36,240] [@script/main.lua:19] [DEBUG] : 开始消费:, {
["pattern"]="/queue", ["payload"]="{"code":200,"data":{"id":2938696189}}", ["source"]="/queue", ["type"]="pmessage"}, 已经消费了2个消息
[2019-06-25 16:05:36,240] [@script/main.lua:19] [DEBUG] : 开始消费:, {
["pattern"]="/queue", ["payload"]="{"code":200,"data":{"id":3499397173}}", ["source"]="/queue", ["type"]="pmessage"}, 已经消费了3个消息
[2019-06-25 16:05:36,240] [@script/main.lua:19] [DEBUG] : 开始消费:, {
["pattern"]="/queue", ["payload"]="{"code":200,"data":{"id":1711272453}}", ["source"]="/queue", ["type"]="pmessage"}, 已经消费了4个消息
[2019-06-25 16:05:36,240] [@script/main.lua:19] [DEBUG] : 开始消费:, {
["pattern"]="/queue", ["payload"]="{"code":200,"data":{"id":3968420025}}", ["source"]="/queue", ["type"]="pmessage"}, 已经消费了5个消息
[2019-06-25 16:05:36,240] [@script/main.lua:19] [DEBUG] : 开始消费:, {
["pattern"]="/queue", ["payload"]="{"code":200,"data":{"id":1887895479}}", ["source"]="/queue", ["type"]="pmessage"}, 已经消费了6个消息
[2019-06-25 16:05:36,240] [@script/main.lua:19] [DEBUG] : 开始消费:, {
[