复现已有算法
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
#include "Client.h"
|
||||
|
||||
using namespace drogon;
|
||||
|
||||
void Client::get(const HttpRequestPtr &,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
std::string key)
|
||||
{
|
||||
nosql::RedisClientPtr redisClient = app().getRedisClient();
|
||||
|
||||
redisClient->execCommandAsync(
|
||||
[callback](const nosql::RedisResult &r) {
|
||||
if (r.type() == nosql::RedisResultType::kNil)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k404NotFound);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string redisResponse = r.asString();
|
||||
Json::Value response;
|
||||
|
||||
Json::CharReaderBuilder builder;
|
||||
Json::CharReader *reader = builder.newCharReader();
|
||||
|
||||
Json::Value json;
|
||||
std::string errors;
|
||||
|
||||
bool parsingSuccessful =
|
||||
reader->parse(redisResponse.c_str(),
|
||||
redisResponse.c_str() + redisResponse.size(),
|
||||
&json,
|
||||
&errors);
|
||||
|
||||
delete reader;
|
||||
|
||||
response["response"] = redisResponse;
|
||||
if (parsingSuccessful)
|
||||
{
|
||||
response["response"] = json;
|
||||
}
|
||||
|
||||
auto resp = HttpResponse::newHttpJsonResponse(response);
|
||||
|
||||
callback(resp);
|
||||
},
|
||||
[](const std::exception &err) {
|
||||
LOG_ERROR << "something failed!!! " << err.what();
|
||||
},
|
||||
"get %s",
|
||||
key.c_str());
|
||||
}
|
||||
|
||||
void Client::post(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
std::string key)
|
||||
{
|
||||
nosql::RedisClientPtr redisClient = app().getRedisClient();
|
||||
auto json = req->getJsonObject();
|
||||
|
||||
if (!json)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setBody("missing 'value' in body");
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string value = (*json)["value"].asString();
|
||||
|
||||
redisClient->execCommandAsync(
|
||||
[callback](const nosql::RedisResult &) {
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
|
||||
resp->setStatusCode(k201Created);
|
||||
|
||||
callback(resp);
|
||||
},
|
||||
[](const std::exception &err) {
|
||||
LOG_ERROR << "something failed!!! " << err.what();
|
||||
},
|
||||
"set %s %s",
|
||||
key.c_str(),
|
||||
value.c_str());
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <drogon/HttpController.h>
|
||||
using namespace drogon;
|
||||
|
||||
class Client : public drogon::HttpController<Client>
|
||||
{
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
METHOD_ADD(Client::get, "{1}", Get);
|
||||
METHOD_ADD(Client::post, "{1}", Post);
|
||||
METHOD_LIST_END
|
||||
|
||||
void get(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
std::string key);
|
||||
void post(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
std::string key);
|
||||
};
|
||||
@@ -0,0 +1,142 @@
|
||||
#include "WsClient.h"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
struct ClientContext
|
||||
{
|
||||
std::unordered_set<std::string> channels_;
|
||||
std::shared_ptr<nosql::RedisSubscriber> subscriber_;
|
||||
};
|
||||
|
||||
void WsClient::handleNewMessage(const WebSocketConnectionPtr &wsConnPtr,
|
||||
std::string &&message,
|
||||
const WebSocketMessageType &type)
|
||||
{
|
||||
if (type == WebSocketMessageType::Ping ||
|
||||
type == WebSocketMessageType::Pong ||
|
||||
type == WebSocketMessageType::Close)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != WebSocketMessageType::Text)
|
||||
{
|
||||
LOG_ERROR << "Unsupported message type " << (int)type;
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG << "WsClient new message from "
|
||||
<< wsConnPtr->peerAddr().toIpPort();
|
||||
|
||||
auto context = wsConnPtr->getContext<ClientContext>();
|
||||
if (!context)
|
||||
{
|
||||
auto pos = message.find(' ');
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
wsConnPtr->send("Invalid publish message.");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string channel = message.substr(0, pos);
|
||||
std::string msg = message.substr(pos + 1);
|
||||
LOG_INFO << "PUBLISH " << channel << " " << msg;
|
||||
|
||||
// Publisher
|
||||
drogon::app().getRedisClient()->execCommandAsync(
|
||||
[wsConnPtr](const nosql::RedisResult &result) {
|
||||
std::string nSubs = std::to_string(result.asInteger());
|
||||
LOG_INFO << "PUBLISH success to " << nSubs << " subscribers.";
|
||||
wsConnPtr->send("PUBLISH success to " + nSubs +
|
||||
" subscribers.");
|
||||
},
|
||||
[wsConnPtr](const nosql::RedisException &ex) {
|
||||
LOG_INFO << "PUBLISH failed, " << ex.what();
|
||||
wsConnPtr->send(std::string("PUBLISH failed: ") + ex.what());
|
||||
},
|
||||
"PUBLISH %s %s",
|
||||
channel.c_str(),
|
||||
msg.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::string channel = std::move(message);
|
||||
if (channel.empty())
|
||||
{
|
||||
wsConnPtr->send("Channel not provided");
|
||||
return;
|
||||
}
|
||||
|
||||
bool subscribe = true;
|
||||
if (channel.compare(0, 6, "unsub ") == 0)
|
||||
{
|
||||
channel = channel.substr(6);
|
||||
subscribe = false;
|
||||
}
|
||||
|
||||
if (subscribe)
|
||||
{
|
||||
if (context->channels_.find(channel) != context->channels_.end())
|
||||
{
|
||||
wsConnPtr->send("Already subscribed to channel " + channel);
|
||||
return;
|
||||
}
|
||||
|
||||
context->subscriber_->subscribe(
|
||||
channel,
|
||||
[channel, wsConnPtr](const std::string &subChannel,
|
||||
const std::string &subMessage) {
|
||||
assert(subChannel == channel);
|
||||
LOG_INFO << "Receive channel message " << subMessage;
|
||||
std::string resp = "{\"channel\":\"" + subChannel +
|
||||
"\",\"message\":\"" + subMessage + "\"}";
|
||||
wsConnPtr->send(resp);
|
||||
});
|
||||
|
||||
context->channels_.insert(channel);
|
||||
wsConnPtr->send("Subscribe to channel: " + channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context->channels_.find(channel) == context->channels_.end())
|
||||
{
|
||||
wsConnPtr->send("Channel not subscribed.");
|
||||
return;
|
||||
}
|
||||
context->channels_.erase(channel);
|
||||
context->subscriber_->unsubscribe(channel);
|
||||
wsConnPtr->send("Unsubscribe from channel: " + channel);
|
||||
}
|
||||
}
|
||||
|
||||
void WsClient::handleNewConnection(const HttpRequestPtr &req,
|
||||
const WebSocketConnectionPtr &wsConnPtr)
|
||||
{
|
||||
if (req->getPath() == "/sub")
|
||||
{
|
||||
LOG_DEBUG << "WsClient new subscriber connection from "
|
||||
<< wsConnPtr->peerAddr().toIpPort();
|
||||
std::shared_ptr<ClientContext> context =
|
||||
std::make_shared<ClientContext>();
|
||||
context->subscriber_ = drogon::app().getRedisClient()->newSubscriber();
|
||||
wsConnPtr->setContext(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG << "WsClient new publisher connection from "
|
||||
<< wsConnPtr->peerAddr().toIpPort();
|
||||
}
|
||||
}
|
||||
|
||||
void WsClient::handleConnectionClosed(const WebSocketConnectionPtr &wsConnPtr)
|
||||
{
|
||||
LOG_DEBUG << "WsClient close connection from "
|
||||
<< wsConnPtr->peerAddr().toIpPort();
|
||||
// Channels will be auto unsubscribed when subscriber destructed.
|
||||
// auto context = wsConnPtr->getContext<ClientContext>();
|
||||
// for (auto& channel : context->channels_)
|
||||
// {
|
||||
// context->subscriber_->unsubscribe(channel);
|
||||
// }
|
||||
wsConnPtr->clearContext();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/WebSocketController.h>
|
||||
|
||||
using namespace drogon;
|
||||
|
||||
class WsClient : public drogon::WebSocketController<WsClient>
|
||||
{
|
||||
public:
|
||||
void handleNewMessage(const WebSocketConnectionPtr &,
|
||||
std::string &&,
|
||||
const WebSocketMessageType &) override;
|
||||
void handleNewConnection(const HttpRequestPtr &,
|
||||
const WebSocketConnectionPtr &) override;
|
||||
void handleConnectionClosed(const WebSocketConnectionPtr &) override;
|
||||
WS_PATH_LIST_BEGIN
|
||||
WS_PATH_ADD("/sub");
|
||||
WS_PATH_ADD("/pub");
|
||||
WS_PATH_LIST_END
|
||||
};
|
||||
Reference in New Issue
Block a user