复现已有算法

This commit is contained in:
cloud
2026-07-14 15:43:18 +08:00
parent abebd2a683
commit 50b8111fd9
860 changed files with 182250 additions and 18 deletions
+68
View File
@@ -0,0 +1,68 @@
cmake_minimum_required(VERSION 3.5)
project(redis CXX)
include(CheckIncludeFileCXX)
check_include_file_cxx(any HAS_ANY)
check_include_file_cxx(string_view HAS_STRING_VIEW)
check_include_file_cxx(coroutine HAS_COROUTINE)
if (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)
set(CMAKE_CXX_STANDARD 20)
elseif (HAS_ANY AND HAS_STRING_VIEW)
set(CMAKE_CXX_STANDARD 17)
else ()
set(CMAKE_CXX_STANDARD 14)
endif ()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(${PROJECT_NAME} main.cc)
# ##############################################################################
# If you include the drogon source code locally in your project, use this method
# to add drogon
# add_subdirectory(drogon)
# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
#
# and comment out the following lines
find_package(Drogon CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
# ##############################################################################
if (CMAKE_CXX_STANDARD LESS 17)
# With C++14, use boost to support any and std::string_view
message(STATUS "use c++14")
find_package(Boost 1.61.0 REQUIRED)
target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
elseif (CMAKE_CXX_STANDARD LESS 20)
message(STATUS "use c++17")
else ()
message(STATUS "use c++20")
endif ()
aux_source_directory(controllers CTL_SRC)
aux_source_directory(filters FILTER_SRC)
aux_source_directory(plugins PLUGIN_SRC)
aux_source_directory(models MODEL_SRC)
drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
${CMAKE_CURRENT_BINARY_DIR})
# use the following line to create views with namespaces.
# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
# ${CMAKE_CURRENT_BINARY_DIR} TRUE)
target_include_directories(${PROJECT_NAME}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/models)
target_sources(${PROJECT_NAME}
PRIVATE
${SRC_DIR}
${CTL_SRC}
${FILTER_SRC}
${PLUGIN_SRC}
${MODEL_SRC})
# ##############################################################################
# uncomment the following line for dynamically loading views
# set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)
+41
View File
@@ -0,0 +1,41 @@
# Redis example
A simple redis example
## Usage
First of all you need redis running on the port 6379
### Post
```
curl --location --request POST 'localhost:8080/client/foo' \
--header 'Content-Type: application/json' \
--data-raw '{
"value": "bar"
}'
```
### Get
```
curl --location --request GET 'localhost:8080/client/foo'
```
## Subscribe and Publish
Go to a websocket test website, such as https://wstool.js.org
### Subscribe
Connect to ws://localhost:8080/sub
To subscribe to a channel, send channel name: `mychannel`
To unsubscribe from a channel, send 'unsub ' + channel name: `unsub mychannel`
### Publish
Connect to ws://localhost:8080/pub
To publish message, send channel name and message: `mychannel anything follows will be the message.`
@@ -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
};
+11
View File
@@ -0,0 +1,11 @@
#include <drogon/drogon.h>
int main()
{
// Set HTTP listener address and port
drogon::app().addListener("0.0.0.0", 8080);
// Run HTTP framework,the method will block in the internal event loop
drogon::app().createRedisClient("127.0.0.1", 6379);
drogon::app().run();
return 0;
}