From 14723d62f21bf9d2aad03f1d6c2e7cd4f79e0dfa Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 10 Dec 2018 19:46:53 +0800 Subject: [PATCH 001/176] add tag 0.5 --- README.md | 8 ++++++-- README_cn.md | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 905ba4b2..8ed3275e 100644 --- a/README.md +++ b/README.md @@ -117,11 +117,15 @@ A simple testing can be start with a NebulaInterface only, but you need to devel ## Todo list - - ipv6 support - - coroutine support. + - NebulaBeacon adds node status information query, registration center master-slave arbitration. + - NebulaMydis Data Agency Service. + - Developing an IM with the Nebula. ## Change log +#### v0.5 + - add node info to worker the worker process terminated unexpectedly and restarted by the Manager. + - ipv6 support. #### v0.4 - distributed log service test passing. - add https support. diff --git a/README_cn.md b/README_cn.md index 7e35f952..2a398ea1 100644 --- a/README_cn.md +++ b/README_cn.md @@ -104,11 +104,15 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) ## 开发任务 - - 增加ipv6支持 - - 增加协程支持 + - NebulaBeacon增加节点状态信息查询,注册中心主从仲裁 + - NebulaMydis数据代理服务 + - 应用Nebula开发IM项目 ## 版本历史 +#### v0.5 + - 增加worker进程意外终止并被Manager重新拉起时的节点信息下发 + - 增加ipv6支持 #### v0.4 - 分布式日志服务测试通过 - 增加https支持 From 871a15282b561adc24194c79afcd82034f7499d7 Mon Sep 17 00:00:00 2001 From: Bwar Date: Thu, 20 Dec 2018 20:05:45 +0800 Subject: [PATCH 002/176] add related project --- README.md | 1 + README_cn.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 8ed3275e..e5c5e7ac 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ A simple testing can be start with a NebulaInterface only, but you need to devel * [NebulaLogger](https://github.com/Bwar/NebulaLogger) * [NebulaAccess](https://github.com/Bwar/NebulaAccess) * [NebulaDynamic](https://github.com/Bwar/NebulaDynamic) + * [Nebcli](https://github.com/Bwar/Nebcli) ## Todo list diff --git a/README_cn.md b/README_cn.md index 2a398ea1..404350ba 100644 --- a/README_cn.md +++ b/README_cn.md @@ -101,6 +101,7 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) * [NebulaLogger](https://github.com/Bwar/NebulaLogger) Nebula集群分布式日志服务 * [NebulaAccess](https://github.com/Bwar/NebulaAccess) Nebula集群私有应用协议接入服务 * [NebulaDynamic](https://github.com/Bwar/NebulaDynamic) Nebula集群动态加载插件 + * [Nebcli](https://github.com/Bwar/Nebcli) Nebula集群命令行管理工具 ## 开发任务 From db8a457bf48ee0c1ac9559bcd6826bc3af33752f Mon Sep 17 00:00:00 2001 From: Bwar Date: Thu, 10 Jan 2019 09:00:54 +0800 Subject: [PATCH 003/176] add beacon election --- src/actor/cmd/CW.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/actor/cmd/CW.hpp b/src/actor/cmd/CW.hpp index 49f3497e..6ae4d007 100644 --- a/src/actor/cmd/CW.hpp +++ b/src/actor/cmd/CW.hpp @@ -45,7 +45,7 @@ enum E_CMD CMD_RSP_REFRESH_NODE_ID = 18, ///< 更新节点ID应答(一般无须应答) CMD_REQ_DISCONNECT = 19, ///< 连接断开(由框架层触发通知并以Cmd的形式通知到系统Cmd,断开原因有error、timeout之类) CMD_RSP_DISCONNECT = 20, ///< 连接断开应答(无效CMD) - CMD_REQ_SERVER_CONFIG = 21, ///< Center通知配置到节点服务器 + CMD_REQ_SERVER_CONFIG = 21, ///< BEACON通知配置到节点服务器 CMD_RSP_SERVER_CONFIG = 22, ///< 节点服务器得到通知配置应答Center CMD_REQ_SERVER_DATA_STATUS_REPORT = 25, ///< 服务器数据状态上报请求 CMD_RSP_SERVER_DATA_STATUS_REPORT = 26, ///< 服务器数据状态上报应答 @@ -55,8 +55,10 @@ enum E_CMD CMD_RSP_SET_LOG_LEVEL = 30, ///< 设置日志级别响应(无须响应) CMD_REQ_RELOAD_SO = 31, ///< 重新加载so请求(manager to worker) CMD_RSP_RELOAD_SO = 32, ///< 重新加载so响应(无须响应) - CMD_REQ_LOG4_TRACE = 33, ///< 分布式网络日志请求 - CMD_RSP_LOG4_TRACE = 34, ///< 分布式网络日志响应(无须响应) + CMD_REQ_LOG4_TRACE = 33, ///< 分布式网络日志请求 + CMD_RSP_LOG4_TRACE = 34, ///< 分布式网络日志响应(无须响应) + CMD_REQ_LEADER_ELECTION = 35, ///< 分布式leader选举请求 + CMD_RSP_LEADER_ELECTION = 36, ///< 分布式leader选举响应(通过心跳结合优先权选举,无须响应) // 接入层转发命令字,如客户端数据转发给Logic,Logic数据转发给客户端等 CMD_REQ_FROM_CLIENT = 501, ///< 客户端发送过来需由接入层转发的数据,传输的MsgHead里的Cmd不会被改变(无业务逻辑直接转发的场景,如登录等接入层有业务逻辑的场景不适用) From 249446977ba8cc126c35a48ea47b42290a14ecf4 Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 28 Jan 2019 18:28:53 +0800 Subject: [PATCH 004/176] NebulaBeacon adds node status information query, registration center leader-fllower arbitration. --- README.md | 10 ++++++++-- README_cn.md | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8ed3275e..57a71bec 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,13 @@ MIT License The server should have started successfully now, startup.sh will print the server that had been started, If not, check logs for reason. Notice that the default configuration file of Nebula limits the number of connections per IP in a period. If you have a large amount of testing, you should check the configuration limit. If the server has been successfully started, testing with postman or curl. ``` +# testing start with NebulaInterface only. curl -H "Content-Type:application/json" -X POST -d '{"name": "Nebula", "address":"https://github.com/Bwar/Nebula"}' http://${your_ip}:16003/hello + +# testing start with NebulaInterface,NebulaLogic and NebulaBeacon. +curl -H "Content-Type:application/json" -X POST -d '{"name": "Nebula", "address":"https://github.com/Bwar/Nebula"}' http://${your_ip}:16003/hello_nebula ``` -A simple testing can be start with a NebulaInterface only, but you need to develop your own plugins. NebulaBootstrap provided the a cluster HelloWorld, the testing will launch NebulaBeacon, NebulaInterface and NebulaLogic. This is a diagram of the cluster architecture: +A simple testing can be start with a NebulaInterface only, and also can be start with NebulaBootstrap. NebulaBootstrap provided the a cluster HelloWorld, the testing will launch NebulaBeacon, NebulaInterface and NebulaLogic. This is a diagram of the cluster architecture: ![nebula_cluster](https://github.com/Bwar/NebulaBootstrap/blob/master/image/nebula_cluster.png?raw=true) @@ -117,12 +121,14 @@ A simple testing can be start with a NebulaInterface only, but you need to devel ## Todo list - - NebulaBeacon adds node status information query, registration center master-slave arbitration. - NebulaMydis Data Agency Service. - Developing an IM with the Nebula. ## Change log +#### v0.6 + - NebulaBeacon adds node status information query, registration center leader-fllower arbitration. + - NebulaInterface adds hello demo. #### v0.5 - add node info to worker the worker process terminated unexpectedly and restarted by the Manager. - ipv6 support. diff --git a/README_cn.md b/README_cn.md index 2a398ea1..1241df72 100644 --- a/README_cn.md +++ b/README_cn.md @@ -71,9 +71,13 @@ Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建 ```   server应该已经启动成功了,startup.sh会打印已启动的server。如果没有启动成功,可以到log目录查看原因。执行grep "ERROR" log/*和grep "FATAL" log/* 先看看是否有错误,再到具体日志文件查看错误详情。注意,Nebula的默认配置文件对IP单位时间连接次数做了限制,如果在测试量较大发生莫名奇妙的问题,可以修改配置限制,通过查看日志中的WARNING信息通常有助于定位这种不是错误的“错误”。如果server已启动成功,那么可以用postman、curl等做测试,看看结果。 ``` +# 只启动NebulaInterface即可完成http的hello测试 curl -H "Content-Type:application/json" -X POST -d '{"name": "Nebula", "address":"https://github.com/Bwar/Nebula"}' http://${your_ip}:16003/hello + +# 启动NebulaInterface、NebulaLogic和NebulaBeacon完成分布式服务http的hello测试。 +curl -H "Content-Type:application/json" -X POST -d '{"name": "Nebula", "address":"https://github.com/Bwar/Nebula"}' http://${your_ip}:16003/hello_nebula ``` -  这个简单的测试可以只启动一个NebulaInterface即可完成,不过这需要自己开发插件。NebulaBootstrap提供的HelloWorld是基于集群的,启动了NebulaBeacon、NebulaInterface、NebulaLogic三个server。下面是一张集群架构图: +  这个简单的测试可以只启动一个NebulaInterface即可完成,也可以启动分布式服务完成。NebulaBootstrap提供基于集群和单个Server的HelloWorld,基于集群的HelloWorld启动了NebulaBeacon、NebulaInterface、NebulaLogic三个server。下面是一张集群架构图: ![nebula_cluster](https://github.com/Bwar/NebulaBootstrap/blob/master/image/nebula_cluster.png?raw=true) @@ -104,12 +108,14 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) ## 开发任务 - - NebulaBeacon增加节点状态信息查询,注册中心主从仲裁 - NebulaMydis数据代理服务 - 应用Nebula开发IM项目 ## 版本历史 +#### v0.6 + - NebulaBeacon增加节点状态信息查询,注册中心主从仲裁 + - NebulaInterface提供HelloWorld示例。 #### v0.5 - 增加worker进程意外终止并被Manager重新拉起时的节点信息下发 - 增加ipv6支持 From ea20a851fa8c9768e229d0d34959b6d23001ebff Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 29 Jan 2019 15:23:11 +0800 Subject: [PATCH 005/176] add request context to PbStep --- src/actor/step/PbStep.cpp | 12 +++++++++--- src/actor/step/PbStep.hpp | 18 ++++++++++-------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/actor/step/PbStep.cpp b/src/actor/step/PbStep.cpp index f250beb3..98457f77 100644 --- a/src/actor/step/PbStep.cpp +++ b/src/actor/step/PbStep.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Project: Nebula - * @file Step.cpp + * @file PbStep.cpp * @brief * @author Bwar * @date: 2016年8月12日 @@ -17,9 +17,15 @@ PbStep::PbStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) { } -PbStep::PbStep(std::shared_ptr pChannel, const MsgHead& oReqMsgHead, const MsgBody& oReqMsgBody, std::shared_ptr pNextStep, ev_tstamp dTimeout) +PbStep::PbStep(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, std::shared_ptr pNextStep, ev_tstamp dTimeout) : Step(Actor::ACT_PB_STEP, pNextStep, dTimeout), - m_pUpstreamChannel(pChannel), m_oReqMsgHead(oReqMsgHead), m_oReqMsgBody(oReqMsgBody) + m_pChannel(pChannel), m_iReqCmd(iCmd), m_uiReqSeq(uiSeq) +{ +} + +PbStep::PbStep(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oReqMsgBody, std::shared_ptr pNextStep, ev_tstamp dTimeout) + : Step(Actor::ACT_PB_STEP, pNextStep, dTimeout), + m_pChannel(pChannel), m_iReqCmd(iCmd), m_uiReqSeq(uiSeq), m_oReqMsgBody(oReqMsgBody) { } diff --git a/src/actor/step/PbStep.hpp b/src/actor/step/PbStep.hpp index 821402ee..957c0db2 100644 --- a/src/actor/step/PbStep.hpp +++ b/src/actor/step/PbStep.hpp @@ -1,6 +1,6 @@ /******************************************************************************* * Project: Nebula - * @file Step.hpp + * @file PbStep.hpp * @brief * @author Bwar * @date: 2016年8月12日 @@ -21,7 +21,9 @@ class PbStep: public Step { public: PbStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); - PbStep(std::shared_ptr pUpstreamChannel, const MsgHead& oReqMsgHead, const MsgBody& oReqMsgBody, + PbStep(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, + std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + PbStep(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oReqMsgBody, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); PbStep(const PbStep&) = delete; PbStep& operator=(const PbStep&) = delete; @@ -32,21 +34,21 @@ class PbStep: public Step * @note 满足某个条件,比如监控某个文件描述符fd的EPOLLIN事件和EPOLLERR事件,当这个fd的 * 这两类事件中的任意一类到达时则会调用Callback()。具体使用到哪几个参数与业务逻辑有关,前三个 * 参数的使用概率高。 - * @param stCtx 消息来源上下文,回调可通过消息外壳原路回复消息,若消息不是来源于网络IO,则 - * 消息外壳为空 + * @param pChannel 消息来源通信通道 * @param oMsgHead 消息头 * @param oMsgBody 消息体 - * @param data 数据指针,基本网络IO时为空,有专用数据时使用,比如redis的reply。 + * @param data 数据指针 */ virtual E_CMD_STATUS Callback( - std::shared_ptr pUpstreamChannel, + std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody, void* data = NULL) = 0; protected: // 请求端的上下文信息,通过Step构造函数初始化,若调用的是不带参数的构造函数Step(),则这几个成员不会被初始化 - std::shared_ptr m_pUpstreamChannel; - MsgHead m_oReqMsgHead; + std::shared_ptr m_pChannel; + int32 m_iReqCmd; + uint32 m_uiReqSeq; MsgBody m_oReqMsgBody; private: From ecb9c4365eda9e59da17d5cea17bec85b0a79911 Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 29 Jan 2019 18:01:32 +0800 Subject: [PATCH 006/176] upgrade CJsonObject --- README.md | 2 +- README_cn.md | 2 +- src/actor/Actor.cpp | 4 +- src/actor/Actor.hpp | 2 +- src/actor/cmd/Cmd.hpp | 2 +- src/actor/cmd/Module.hpp | 2 +- src/actor/cmd/sys_cmd/CmdBeat.hpp | 2 +- src/actor/step/HttpStep.cpp | 4 +- src/actor/step/HttpStep.hpp | 6 +- src/actor/step/PbStep.cpp | 12 - src/actor/step/PbStep.hpp | 10 - src/actor/step/sys_step/StepIoTimeout.cpp | 6 +- src/actor/step/sys_step/StepIoTimeout.hpp | 2 +- src/actor/step/sys_step/StepTellWorker.cpp | 4 +- src/actor/step/sys_step/StepTellWorker.hpp | 2 +- src/labor/Manager.cpp | 4 +- src/labor/Manager.hpp | 2 +- src/labor/Worker.cpp | 4 +- src/labor/Worker.hpp | 2 +- src/labor/WorkerImpl.cpp | 4 +- src/labor/WorkerImpl.hpp | 2 +- src/util/json/CJsonObject.cpp | 60 +- src/util/json/CJsonObject.hpp | 10 + src/util/json/cJSON.c | 2180 ++++++++++---------- 24 files changed, 1191 insertions(+), 1139 deletions(-) diff --git a/README.md b/README.md index 57a71bec..4cb5fe69 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log #### v0.6 - - NebulaBeacon adds node status information query, registration center leader-fllower arbitration. + - NebulaBeacon adds node status information query, registration center leader-fllower election. - NebulaInterface adds hello demo. #### v0.5 - add node info to worker the worker process terminated unexpectedly and restarted by the Manager. diff --git a/README_cn.md b/README_cn.md index 1241df72..a10896f2 100644 --- a/README_cn.md +++ b/README_cn.md @@ -114,7 +114,7 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) ## 版本历史 #### v0.6 - - NebulaBeacon增加节点状态信息查询,注册中心主从仲裁 + - NebulaBeacon增加节点状态信息查询,注册中心主从高可用选举 - NebulaInterface提供HelloWorld示例。 #### v0.5 - 增加worker进程意外终止并被Manager重新拉起时的节点信息下发 diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 145d7b45..9de3bf18 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -130,9 +130,9 @@ bool Actor::SendTo(const std::string& strHost, int iPort) return(m_pWorker->SendTo(strHost, iPort, this)); } -bool Actor::SendPolling(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - return(m_pWorker->SendPolling(strNodeType, iCmd, uiSeq, oMsgBody, this)); + return(m_pWorker->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, this)); } bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index cd9a2068..3fa84b01 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -175,7 +175,7 @@ class Actor: public std::enable_shared_from_this * @param oMsgBody 数据包体 * @return 是否发送成功 */ - bool SendPolling(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); /** * @brief 以取模方式选择发送到同一类型节点 diff --git a/src/actor/cmd/Cmd.hpp b/src/actor/cmd/Cmd.hpp index 2bd070f5..bf95dfe2 100644 --- a/src/actor/cmd/Cmd.hpp +++ b/src/actor/cmd/Cmd.hpp @@ -53,7 +53,7 @@ class Cmd: public CmdModel * @return 命令是否处理成功 */ virtual bool AnyMessage( - std::shared_ptr pUpstreamChannel, + std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) = 0; diff --git a/src/actor/cmd/Module.hpp b/src/actor/cmd/Module.hpp index 4808fec8..343fce56 100644 --- a/src/actor/cmd/Module.hpp +++ b/src/actor/cmd/Module.hpp @@ -48,7 +48,7 @@ class Module: public ModuleModel * @return 是否处理成功 */ virtual bool AnyMessage( - std::shared_ptr pUpstreamChannel, + std::shared_ptr pChannel, const HttpMsg& oHttpMsg) = 0; protected: diff --git a/src/actor/cmd/sys_cmd/CmdBeat.hpp b/src/actor/cmd/sys_cmd/CmdBeat.hpp index b2592a52..80234615 100644 --- a/src/actor/cmd/sys_cmd/CmdBeat.hpp +++ b/src/actor/cmd/sys_cmd/CmdBeat.hpp @@ -22,7 +22,7 @@ class CmdBeat: public Cmd, public DynamicCreator, public WorkerF CmdBeat(int32 iCmd); virtual ~CmdBeat(); virtual bool AnyMessage( - std::shared_ptr pUpstreamChannel, + std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody); }; diff --git a/src/actor/step/HttpStep.cpp b/src/actor/step/HttpStep.cpp index 2c636427..d2d64483 100644 --- a/src/actor/step/HttpStep.cpp +++ b/src/actor/step/HttpStep.cpp @@ -18,9 +18,9 @@ HttpStep::HttpStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) { } -HttpStep::HttpStep(std::shared_ptr pUpstreamChannel, std::shared_ptr pNextStep, ev_tstamp dTimeout) +HttpStep::HttpStep(std::shared_ptr pChannel, std::shared_ptr pNextStep, ev_tstamp dTimeout) : Step(ACT_HTTP_STEP, pNextStep, dTimeout), - m_pUpstreamChannel(pUpstreamChannel) + m_pChannel(pChannel) { } diff --git a/src/actor/step/HttpStep.hpp b/src/actor/step/HttpStep.hpp index a892d12b..0cce3f02 100644 --- a/src/actor/step/HttpStep.hpp +++ b/src/actor/step/HttpStep.hpp @@ -20,13 +20,13 @@ class HttpStep: public Step { public: HttpStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); - HttpStep(std::shared_ptr pUpstreamChannel, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + HttpStep(std::shared_ptr pChannel, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); HttpStep(const HttpStep&) = delete; HttpStep& operator=(const HttpStep&) = delete; virtual ~HttpStep(); virtual E_CMD_STATUS Callback( - std::shared_ptr pUpstreamChannel, + std::shared_ptr pChannel, const HttpMsg& oHttpMsg, void* data = NULL) = 0; @@ -36,7 +36,7 @@ class HttpStep: public Step protected: bool HttpRequest(const HttpMsg& oHttpMsg); - std::shared_ptr m_pUpstreamChannel; + std::shared_ptr m_pChannel; }; } /* namespace neb */ diff --git a/src/actor/step/PbStep.cpp b/src/actor/step/PbStep.cpp index 98457f77..0bf8f296 100644 --- a/src/actor/step/PbStep.cpp +++ b/src/actor/step/PbStep.cpp @@ -17,18 +17,6 @@ PbStep::PbStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) { } -PbStep::PbStep(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, std::shared_ptr pNextStep, ev_tstamp dTimeout) - : Step(Actor::ACT_PB_STEP, pNextStep, dTimeout), - m_pChannel(pChannel), m_iReqCmd(iCmd), m_uiReqSeq(uiSeq) -{ -} - -PbStep::PbStep(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oReqMsgBody, std::shared_ptr pNextStep, ev_tstamp dTimeout) - : Step(Actor::ACT_PB_STEP, pNextStep, dTimeout), - m_pChannel(pChannel), m_iReqCmd(iCmd), m_uiReqSeq(uiSeq), m_oReqMsgBody(oReqMsgBody) -{ -} - PbStep::~PbStep() { } diff --git a/src/actor/step/PbStep.hpp b/src/actor/step/PbStep.hpp index 957c0db2..399bb52e 100644 --- a/src/actor/step/PbStep.hpp +++ b/src/actor/step/PbStep.hpp @@ -21,10 +21,6 @@ class PbStep: public Step { public: PbStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); - PbStep(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, - std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); - PbStep(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oReqMsgBody, - std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); PbStep(const PbStep&) = delete; PbStep& operator=(const PbStep&) = delete; virtual ~PbStep(); @@ -45,12 +41,6 @@ class PbStep: public Step const MsgBody& oMsgBody, void* data = NULL) = 0; -protected: // 请求端的上下文信息,通过Step构造函数初始化,若调用的是不带参数的构造函数Step(),则这几个成员不会被初始化 - std::shared_ptr m_pChannel; - int32 m_iReqCmd; - uint32 m_uiReqSeq; - MsgBody m_oReqMsgBody; - private: friend class WorkerImpl; }; diff --git a/src/actor/step/sys_step/StepIoTimeout.cpp b/src/actor/step/sys_step/StepIoTimeout.cpp index 0f8f5a26..9d88cad2 100644 --- a/src/actor/step/sys_step/StepIoTimeout.cpp +++ b/src/actor/step/sys_step/StepIoTimeout.cpp @@ -14,7 +14,7 @@ namespace neb { StepIoTimeout::StepIoTimeout(std::shared_ptr pChannel) - : m_pUpstreamChannel(pChannel) + : m_pChannel(pChannel) { } @@ -26,7 +26,7 @@ E_CMD_STATUS StepIoTimeout::Emit(int iErrno, const std::string& strErrMsg, void* data) { MsgBody oOutMsgBody; - if (SendTo(m_pUpstreamChannel, CMD_REQ_BEAT, GetSequence(), oOutMsgBody)) + if (SendTo(m_pChannel, CMD_REQ_BEAT, GetSequence(), oOutMsgBody)) { return(CMD_STATUS_RUNNING); } @@ -45,7 +45,7 @@ E_CMD_STATUS StepIoTimeout::Callback(std::shared_ptr pChannel, E_CMD_STATUS StepIoTimeout::Timeout() { - GetWorkerImpl(this)->Disconnect(m_pUpstreamChannel); + GetWorkerImpl(this)->Disconnect(m_pChannel); return(CMD_STATUS_FAULT); } diff --git a/src/actor/step/sys_step/StepIoTimeout.hpp b/src/actor/step/sys_step/StepIoTimeout.hpp index b6ef1d68..6ea5b463 100644 --- a/src/actor/step/sys_step/StepIoTimeout.hpp +++ b/src/actor/step/sys_step/StepIoTimeout.hpp @@ -43,7 +43,7 @@ class StepIoTimeout: public PbStep, public DynamicCreator m_pUpstreamChannel; + std::shared_ptr m_pChannel; }; } /* namespace neb */ diff --git a/src/actor/step/sys_step/StepTellWorker.cpp b/src/actor/step/sys_step/StepTellWorker.cpp index 9c70d48b..947825bc 100644 --- a/src/actor/step/sys_step/StepTellWorker.cpp +++ b/src/actor/step/sys_step/StepTellWorker.cpp @@ -13,7 +13,7 @@ namespace neb { StepTellWorker::StepTellWorker(std::shared_ptr pChannel) - : m_pUpstreamChannel(pChannel) + : m_pChannel(pChannel) { } @@ -33,7 +33,7 @@ E_CMD_STATUS StepTellWorker::Emit( oOutMsgBody.mutable_rsp_result()->set_code(0); oOutMsgBody.mutable_rsp_result()->set_msg("OK"); oOutMsgBody.set_data(oTargetWorker.SerializeAsString()); - Step::SendTo(m_pUpstreamChannel, CMD_REQ_TELL_WORKER, GetSequence(), oOutMsgBody); + Step::SendTo(m_pChannel, CMD_REQ_TELL_WORKER, GetSequence(), oOutMsgBody); return(CMD_STATUS_RUNNING); } diff --git a/src/actor/step/sys_step/StepTellWorker.hpp b/src/actor/step/sys_step/StepTellWorker.hpp index 2ff5d85f..c8db7d44 100644 --- a/src/actor/step/sys_step/StepTellWorker.hpp +++ b/src/actor/step/sys_step/StepTellWorker.hpp @@ -37,7 +37,7 @@ class StepTellWorker: public PbStep, public DynamicCreator m_pUpstreamChannel; + std::shared_ptr m_pChannel; }; } /* namespace neb */ diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 4b8e56b1..9c30ecd5 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -561,7 +561,7 @@ bool Manager::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, c } } -bool Manager::SendPolling(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Manager::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; @@ -620,7 +620,7 @@ bool Manager::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 ui } else { - return(SendPolling(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); + return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); } } else diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index 1652bd97..507d3f0b 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -214,7 +214,7 @@ class Manager: public Labor virtual bool SendTo(std::shared_ptr pChannel); virtual bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendPolling(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); + virtual bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 2720e17f..94e355d7 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -124,9 +124,9 @@ bool Worker::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, co return(m_pImpl->SendTo(strIdentify, iCmd, uiSeq, oMsgBody, pSender)); } -bool Worker::SendPolling(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Worker::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) { - return(m_pImpl->SendPolling(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); + return(m_pImpl->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); } bool Worker::SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index b6d56ed3..d8ea7fe7 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -67,7 +67,7 @@ class Worker: public Labor virtual bool SendTo(std::shared_ptr pChannel); virtual bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendPolling(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); + virtual bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 37535ce7..677a2f9a 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -1099,7 +1099,7 @@ bool WorkerImpl::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq } } -bool WorkerImpl::SendPolling(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool WorkerImpl::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; @@ -1165,7 +1165,7 @@ bool WorkerImpl::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 } else { - return(SendPolling(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); + return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); } } else diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 99f9f62b..f7033e83 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -208,7 +208,7 @@ class WorkerImpl virtual bool SendTo(std::shared_ptr pChannel); virtual bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendPolling(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); + virtual bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); diff --git a/src/util/json/CJsonObject.cpp b/src/util/json/CJsonObject.cpp index 2f1ce72c..9ce94af4 100644 --- a/src/util/json/CJsonObject.cpp +++ b/src/util/json/CJsonObject.cpp @@ -3,7 +3,7 @@ * @file CJsonObject.cpp * @brief * @author bwarliao - * @date: 2014-7-16- + * @date: 2014-7-16 * @note * Modify history: ******************************************************************************/ @@ -90,6 +90,7 @@ bool CJsonObject::AddEmptySubObject(const std::string& strKey) return(false); } cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + m_listKeys.clear(); return(true); } @@ -127,9 +128,55 @@ bool CJsonObject::AddEmptySubArray(const std::string& strKey) return(false); } cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + m_listKeys.clear(); return(true); } +bool CJsonObject::GetKey(std::string& strKey) +{ + if (IsArray()) + { + return(false); + } + if (m_listKeys.size() == 0) + { + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + return(false); + } + + cJSON *c = pFocusData->child; + while (c) + { + m_listKeys.push_back(c->string); + c = c->next; + } + m_itKey = m_listKeys.begin(); + } + + if (m_itKey == m_listKeys.end()) + { + strKey = ""; + m_itKey = m_listKeys.begin(); + return(false); + } + else + { + strKey = *m_itKey; + ++m_itKey; + return(true); + } +} + CJsonObject& CJsonObject::operator[](const std::string& strKey) { std::map::iterator iter; @@ -400,6 +447,7 @@ void CJsonObject::Clear() } } m_mapJsonObjectRef.clear(); + m_listKeys.clear(); } bool CJsonObject::IsEmpty() const @@ -797,6 +845,7 @@ bool CJsonObject::Add(const std::string& strKey, const CJsonObject& oJsonObject) } m_mapJsonObjectRef.erase(iter); } + m_listKeys.clear(); return(true); } @@ -837,6 +886,7 @@ bool CJsonObject::Add(const std::string& strKey, const std::string& strValue) { return(false); } + m_listKeys.clear(); return(true); } @@ -877,6 +927,7 @@ bool CJsonObject::Add(const std::string& strKey, int32 iValue) { return(false); } + m_listKeys.clear(); return(true); } @@ -917,6 +968,7 @@ bool CJsonObject::Add(const std::string& strKey, uint32 uiValue) { return(false); } + m_listKeys.clear(); return(true); } @@ -957,6 +1009,7 @@ bool CJsonObject::Add(const std::string& strKey, int64 llValue) { return(false); } + m_listKeys.clear(); return(true); } @@ -997,6 +1050,7 @@ bool CJsonObject::Add(const std::string& strKey, uint64 ullValue) { return(false); } + m_listKeys.clear(); return(true); } @@ -1037,6 +1091,7 @@ bool CJsonObject::Add(const std::string& strKey, bool bValue, bool bValueAgain) { return(false); } + m_listKeys.clear(); return(true); } @@ -1077,6 +1132,7 @@ bool CJsonObject::Add(const std::string& strKey, float fValue) { return(false); } + m_listKeys.clear(); return(true); } @@ -1117,6 +1173,7 @@ bool CJsonObject::Add(const std::string& strKey, double dValue) { return(false); } + m_listKeys.clear(); return(true); } @@ -1152,6 +1209,7 @@ bool CJsonObject::Delete(const std::string& strKey) } m_mapJsonObjectRef.erase(iter); } + m_listKeys.clear(); return(true); } diff --git a/src/util/json/CJsonObject.hpp b/src/util/json/CJsonObject.hpp index 509bafde..6fe4e4e6 100644 --- a/src/util/json/CJsonObject.hpp +++ b/src/util/json/CJsonObject.hpp @@ -21,7 +21,14 @@ #include #include #include +#include +#ifdef __cplusplus +extern "C" { +#endif #include "cJSON.h" +#ifdef __cplusplus +} +#endif namespace neb @@ -52,6 +59,7 @@ class CJsonObject public: // method of ordinary json object bool AddEmptySubObject(const std::string& strKey); bool AddEmptySubArray(const std::string& strKey); + bool GetKey(std::string& strKey); CJsonObject& operator[](const std::string& strKey); std::string operator()(const std::string& strKey) const; bool Get(const std::string& strKey, CJsonObject& oJsonObject) const; @@ -134,6 +142,8 @@ class CJsonObject std::string m_strErrMsg; std::map m_mapJsonArrayRef; std::map m_mapJsonObjectRef; + std::list m_listKeys; + std::list::const_iterator m_itKey; }; } diff --git a/src/util/json/cJSON.c b/src/util/json/cJSON.c index 769cd093..5449d0c3 100644 --- a/src/util/json/cJSON.c +++ b/src/util/json/cJSON.c @@ -1,1087 +1,1093 @@ -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - */ - -/* cJSON */ -/* JSON parser in C. */ - -#include -#include -#include -#include -#include -#include -#include -#include "cJSON.h" - -static const char *ep; - -const char *cJSON_GetErrorPtr() -{ - return ep; -} - -static int cJSON_strcasecmp(const char *s1, const char *s2) -{ - if (!s1) - return (s1 == s2) ? 0 : 1; - if (!s2) - return 1; - for (; tolower(*s1) == tolower(*s2); ++s1, ++s2) - if (*s1 == 0) - return 0; - return tolower(*(const unsigned char *)s1) - - tolower(*(const unsigned char *)s2); -} - -static void *(*cJSON_malloc)(size_t sz) = malloc; -static void (*cJSON_free)(void *ptr) = free; - -static char* cJSON_strdup(const char* str) -{ - size_t len; - char* copy; - - len = strlen(str) + 1; - if (!(copy = (char*) cJSON_malloc(len))) - return 0; - memcpy(copy, str, len); - return copy; -} - -void cJSON_InitHooks(cJSON_Hooks* hooks) -{ - if (!hooks) - { /* Reset hooks */ - cJSON_malloc = malloc; - cJSON_free = free; - return; - } - - cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc; - cJSON_free = (hooks->free_fn) ? hooks->free_fn : free; -} - -/* Internal constructor. */ -static cJSON *cJSON_New_Item() -{ - cJSON* node = (cJSON*) cJSON_malloc(sizeof(cJSON)); - if (node) - memset(node, 0, sizeof(cJSON)); - return node; -} - -/* Delete a cJSON structure. */ -void cJSON_Delete(cJSON *c) -{ - cJSON *next; - while (c) - { - next = c->next; - if (!(c->type & cJSON_IsReference) && c->child) - cJSON_Delete(c->child); - if (!(c->type & cJSON_IsReference) && c->valuestring) - cJSON_free(c->valuestring); - if (c->string) - cJSON_free(c->string); - cJSON_free(c); - c = next; - } -} - -/* Parse the input text to generate a number, and populate the result into item. */ -static const char *parse_number(cJSON *item, const char *num) -{ - long double n = 0, scale = 0; - int subscale = 0, signsubscale = 1; - item->sign = 1; - - /* Could use sscanf for this? */ - if (*num == '-') - item->sign = -1, num++; /* Has sign? */ - if (*num == '0') - num++; /* is zero */ - if (*num >= '1' && *num <= '9') - do - n = (n * 10.0) + (*num++ - '0'); - while (*num >= '0' && *num <= '9'); /* Number? */ - if (*num == '.' && num[1] >= '0' && num[1] <= '9') - { - num++; - do - n = (n * 10.0) + (*num++ - '0'), scale--; - while (*num >= '0' && *num <= '9'); - } /* Fractional part? */ - if (*num == 'e' || *num == 'E') /* Exponent? */ - { - num++; - if (*num == '+') - num++; - else if (*num == '-') - signsubscale = -1, num++; /* With sign? */ - while (*num >= '0' && *num <= '9') - subscale = (subscale * 10) + (*num++ - '0'); /* Number? */ - } - - if (scale == 0 && subscale == 0) - { - item->valuedouble = (double)(item->sign * (uint64)n); - item->valueint = (uint64)(item->sign * (uint64)n); - item->type = cJSON_Int; - } - else - { - n = item->sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ - item->valuedouble = (double)n; - item->valueint = (uint64)n; - item->type = cJSON_Double; - } - return num; -} - -/* Render the number nicely from the given item into a string. */ -static char *print_double(cJSON *item) -{ - char *str; - double d = item->valuedouble; - str = (char*) cJSON_malloc(64); /* This is a nice tradeoff. */ - if (str) - { - if (fabs(floor(d) - d) <= DBL_EPSILON) - sprintf(str, "%.0f", d); - else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) - sprintf(str, "%lf", d); - else - sprintf(str, "%f", d); - } - return str; -} - -static char *print_int(cJSON *item) -{ - char *str; - str = (char*) cJSON_malloc(22); /* 2^64+1 can be represented in 21 chars. */ - if (str) - { - if (item->sign == -1) - { - if (item->valueint <= (int64)INT_MAX && item->valueint >= (int64)INT_MIN) - { - sprintf(str, "%d", (int32)item->valueint); - } - else - { - sprintf(str, "%lld", (int64)item->valueint); - } - } - else - { - if (item->valueint <= (uint64)UINT_MAX) - { - sprintf(str, "%u", (uint32)item->valueint); - } - else - { - sprintf(str, "%llu", item->valueint); - } - } - } - return str; -} - -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, - 0xF8, 0xFC }; -static const char *parse_string(cJSON *item, const char *str) -{ - const char *ptr = str + 1; - char *ptr2; - char *out; - int len = 0; - unsigned uc, uc2; - if (*str != '\"') - { - ep = str; - return 0; - } /* not a string! */ - - while (*ptr != '\"' && *ptr && ++len) - if (*ptr++ == '\\') - ptr++; /* Skip escaped quotes. */ - - out = (char*) cJSON_malloc(len + 1); /* This is how long we need for the string, roughly. */ - if (!out) - return 0; - - ptr = str + 1; - ptr2 = out; - while (*ptr != '\"' && *ptr) - { - if (*ptr != '\\') - *ptr2++ = *ptr++; - else - { - ptr++; - switch (*ptr) - { - case 'b': - *ptr2++ = '\b'; - break; - case 'f': - *ptr2++ = '\f'; - break; - case 'n': - *ptr2++ = '\n'; - break; - case 'r': - *ptr2++ = '\r'; - break; - case 't': - *ptr2++ = '\t'; - break; - case 'u': /* transcode utf16 to utf8. */ - sscanf(ptr + 1, "%4x", &uc); - ptr += 4; /* get the unicode char. */ - - if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) - break; // check for invalid. - - if (uc >= 0xD800 && uc <= 0xDBFF) // UTF16 surrogate pairs. - { - if (ptr[1] != '\\' || ptr[2] != 'u') - break; // missing second-half of surrogate. - sscanf(ptr + 3, "%4x", &uc2); - ptr += 6; - if (uc2 < 0xDC00 || uc2 > 0xDFFF) - break; // invalid second-half of surrogate. - uc = 0x10000 | ((uc & 0x3FF) << 10) | (uc2 & 0x3FF); - } - - len = 4; - if (uc < 0x80) - len = 1; - else if (uc < 0x800) - len = 2; - else if (uc < 0x10000) - len = 3; - ptr2 += len; - - switch (len) - { - case 4: - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 3: - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 2: - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 1: - *--ptr2 = (uc | firstByteMark[len]); - } - ptr2 += len; - break; - default: - *ptr2++ = *ptr; - break; - } - ptr++; - } - } - *ptr2 = 0; - if (*ptr == '\"') - ptr++; - item->valuestring = out; - item->type = cJSON_String; - return ptr; -} - -/* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const char *str) -{ - const char *ptr; - char *ptr2, *out; - int len = 0; - unsigned char token; - - if (!str) - return cJSON_strdup(""); - ptr = str; - while ((token = *ptr) && ++len) - { - if (strchr("\"\\\b\f\n\r\t", token)) - len++; - else if (token < 32) - len += 5; - ptr++; - } - - out = (char*) cJSON_malloc(len + 3); - if (!out) - return 0; - - ptr2 = out; - ptr = str; - *ptr2++ = '\"'; - while (*ptr) - { - if ((unsigned char) *ptr > 31 && *ptr != '\"' && *ptr != '\\') - *ptr2++ = *ptr++; - else - { - *ptr2++ = '\\'; - switch (token = *ptr++) - { - case '\\': - *ptr2++ = '\\'; - break; - case '\"': - *ptr2++ = '\"'; - break; - case '\b': - *ptr2++ = 'b'; - break; - case '\f': - *ptr2++ = 'f'; - break; - case '\n': - *ptr2++ = 'n'; - break; - case '\r': - *ptr2++ = 'r'; - break; - case '\t': - *ptr2++ = 't'; - break; - default: - sprintf(ptr2, "u%04x", token); - ptr2 += 5; - break; /* escape and print */ - } - } - } - *ptr2++ = '\"'; - *ptr2++ = 0; - return out; -} -/* Invote print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item) -{ - return print_string_ptr(item->valuestring); -} - -/* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item, const char *value); -static char *print_value(cJSON *item, int depth, int fmt); -static const char *parse_array(cJSON *item, const char *value); -static char *print_array(cJSON *item, int depth, int fmt); -static const char *parse_object(cJSON *item, const char *value); -static char *print_object(cJSON *item, int depth, int fmt); - -/* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) -{ - while (in && *in && (unsigned char) *in <= 32) - in++; - return in; -} - -/* Parse an object - create a new root, and populate. */ -cJSON *cJSON_Parse(const char *value) -{ - cJSON *c = cJSON_New_Item(); - ep = 0; - if (!c) - return 0; /* memory fail */ - - if (!parse_value(c, skip(value))) - { - cJSON_Delete(c); - return 0; - } - return c; -} - -/* Render a cJSON item/entity/structure to text. */ -char *cJSON_Print(cJSON *item) -{ - return print_value(item, 0, 1); -} -char *cJSON_PrintUnformatted(cJSON *item) -{ - return print_value(item, 0, 0); -} - -/* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item, const char *value) -{ - if (!value) - return 0; /* Fail on null. */ - if (!strncmp(value, "null", 4)) - { - item->type = cJSON_NULL; - return value + 4; - } - if (!strncmp(value, "false", 5)) - { - item->type = cJSON_False; - return value + 5; - } - if (!strncmp(value, "true", 4)) - { - item->type = cJSON_True; - item->valueint = 1; - return value + 4; - } - if (*value == '\"') - { - return parse_string(item, value); - } - if (*value == '-' || (*value >= '0' && *value <= '9')) - { - return parse_number(item, value); - } - if (*value == '[') - { - return parse_array(item, value); - } - if (*value == '{') - { - return parse_object(item, value); - } - - ep = value; - return 0; /* failure. */ -} - -/* Render a value to text. */ -static char *print_value(cJSON *item, int depth, int fmt) -{ - char *out = 0; - if (!item) - return 0; - switch ((item->type) & 255) - { - case cJSON_NULL: - out = cJSON_strdup("null"); - break; - case cJSON_False: - out = cJSON_strdup("false"); - break; - case cJSON_True: - out = cJSON_strdup("true"); - break; - case cJSON_Int: - out = print_int(item); - break; - case cJSON_Double: - out = print_double(item); - break; - case cJSON_String: - out = print_string(item); - break; - case cJSON_Array: - out = print_array(item, depth, fmt); - break; - case cJSON_Object: - out = print_object(item, depth, fmt); - break; - } - return out; -} - -/* Build an array from input text. */ -static const char *parse_array(cJSON *item, const char *value) -{ - cJSON *child; - if (*value != '[') - { - ep = value; - return 0; - } /* not an array! */ - - item->type = cJSON_Array; - value = skip(value + 1); - if (*value == ']') - return value + 1; /* empty array. */ - - item->child = child = cJSON_New_Item(); - if (!item->child) - return 0; /* memory fail */ - value = skip(parse_value(child, skip(value))); /* skip any spacing, get the value. */ - if (!value) - return 0; - - while (*value == ',') - { - cJSON *new_item; - if (!(new_item = cJSON_New_Item())) - return 0; /* memory fail */ - child->next = new_item; - new_item->prev = child; - child = new_item; - value = skip(parse_value(child, skip(value + 1))); - if (!value) - return 0; /* memory fail */ - } - - if (*value == ']') - return value + 1; /* end of array */ - ep = value; - return 0; /* malformed. */ -} - -/* Render an array to text */ -static char *print_array(cJSON *item, int depth, int fmt) -{ - char **entries; - char *out = 0, *ptr, *ret; - int len = 5; - cJSON *child = item->child; - int numentries = 0, i = 0, fail = 0; - - /* How many entries in the array? */ - while (child) - numentries++, child = child->next; - /* Allocate an array to hold the values for each */ - entries = (char**) cJSON_malloc(numentries * sizeof(char*)); - if (!entries) - return 0; - memset(entries, 0, numentries * sizeof(char*)); - /* Retrieve all the results: */ - child = item->child; - while (child && !fail) - { - ret = print_value(child, depth + 1, fmt); - entries[i++] = ret; - if (ret) - len += strlen(ret) + 2 + (fmt ? 1 : 0); - else - fail = 1; - child = child->next; - } - - /* If we didn't fail, try to malloc the output string */ - if (!fail) - out = (char*) cJSON_malloc(len); - /* If that fails, we fail. */ - if (!out) - fail = 1; - - /* Handle failure. */ - if (fail) - { - for (i = 0; i < numentries; i++) - if (entries[i]) - cJSON_free(entries[i]); - cJSON_free(entries); - return 0; - } - - /* Compose the output array. */ - *out = '['; - ptr = out + 1; - *ptr = 0; - for (i = 0; i < numentries; i++) - { - strcpy(ptr, entries[i]); - ptr += strlen(entries[i]); - if (i != numentries - 1) - { - *ptr++ = ','; - if (fmt) - *ptr++ = ' '; - *ptr = 0; - } - cJSON_free(entries[i]); - } - cJSON_free(entries); - *ptr++ = ']'; - *ptr++ = 0; - return out; -} - -/* Build an object from the text. */ -static const char *parse_object(cJSON *item, const char *value) -{ - cJSON *child; - if (*value != '{') - { - ep = value; - return 0; - } /* not an object! */ - - item->type = cJSON_Object; - value = skip(value + 1); - if (*value == '}') - return value + 1; /* empty array. */ - - item->child = child = cJSON_New_Item(); - if (!item->child) - return 0; - value = skip(parse_string(child, skip(value))); - if (!value) - return 0; - child->string = child->valuestring; - child->valuestring = 0; - if (*value != ':') - { - ep = value; - return 0; - } /* fail! */ - value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ - if (!value) - return 0; - - while (*value == ',') - { - cJSON *new_item; - if (!(new_item = cJSON_New_Item())) - return 0; /* memory fail */ - child->next = new_item; - new_item->prev = child; - child = new_item; - value = skip(parse_string(child, skip(value + 1))); - if (!value) - return 0; - child->string = child->valuestring; - child->valuestring = 0; - if (*value != ':') - { - ep = value; - return 0; - } /* fail! */ - value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ - if (!value) - return 0; - } - - if (*value == '}') - return value + 1; /* end of array */ - ep = value; - return 0; /* malformed. */ -} - -/* Render an object to text. */ -static char *print_object(cJSON *item, int depth, int fmt) -{ - char **entries = 0, **names = 0; - char *out = 0, *ptr, *ret, *str; - int len = 7, i = 0, j; - cJSON *child = item->child; - int numentries = 0, fail = 0; - /* Count the number of entries. */ - while (child) - numentries++, child = child->next; - /* Allocate space for the names and the objects */ - entries = (char**) cJSON_malloc(numentries * sizeof(char*)); - if (!entries) - return 0; - names = (char**) cJSON_malloc(numentries * sizeof(char*)); - if (!names) - { - cJSON_free(entries); - return 0; - } - memset(entries, 0, sizeof(char*) * numentries); - memset(names, 0, sizeof(char*) * numentries); - - /* Collect all the results into our arrays: */ - child = item->child; - depth++; - if (fmt) - len += depth; - while (child) - { - names[i] = str = print_string_ptr(child->string); - entries[i++] = ret = print_value(child, depth, fmt); - if (str && ret) - len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0); - else - fail = 1; - child = child->next; - } - - /* Try to allocate the output string */ - if (!fail) - out = (char*) cJSON_malloc(len); - if (!out) - fail = 1; - - /* Handle failure */ - if (fail) - { - for (i = 0; i < numentries; i++) - { - if (names[i]) - cJSON_free(names[i]); - if (entries[i]) - cJSON_free(entries[i]); - } - cJSON_free(names); - cJSON_free(entries); - return 0; - } - - /* Compose the output: */ - *out = '{'; - ptr = out + 1; - if (fmt) - *ptr++ = '\n'; - *ptr = 0; - for (i = 0; i < numentries; i++) - { - if (fmt) - for (j = 0; j < depth; j++) - *ptr++ = '\t'; - strcpy(ptr, names[i]); - ptr += strlen(names[i]); - *ptr++ = ':'; - if (fmt) - *ptr++ = '\t'; - strcpy(ptr, entries[i]); - ptr += strlen(entries[i]); - if (i != numentries - 1) - *ptr++ = ','; - if (fmt) - *ptr++ = '\n'; - *ptr = 0; - cJSON_free(names[i]); - cJSON_free(entries[i]); - } - - cJSON_free(names); - cJSON_free(entries); - if (fmt) - for (i = 0; i < depth - 1; i++) - *ptr++ = '\t'; - *ptr++ = '}'; - *ptr++ = 0; - return out; -} - -/* Get Array size/item / object item. */ -int cJSON_GetArraySize(cJSON *array) -{ - cJSON *c = array->child; - int i = 0; - while (c) - i++, c = c->next; - return i; -} -cJSON *cJSON_GetArrayItem(cJSON *array, int item) -{ - cJSON *c = array->child; - while (c && item > 0) - item--, c = c->next; - return c; -} -cJSON *cJSON_GetObjectItem(cJSON *object, const char *string) -{ - cJSON *c = object->child; - while (c && cJSON_strcasecmp(c->string, string)) - c = c->next; - return c; -} - -/* Utility for array list handling. */ -static void suffix_object(cJSON *prev, cJSON *item) -{ - prev->next = item; - item->prev = prev; -} -/* Utility for handling references. */ -static cJSON *create_reference(cJSON *item) -{ - cJSON *ref = cJSON_New_Item(); - if (!ref) - return 0; - memcpy(ref, item, sizeof(cJSON)); - ref->string = 0; - ref->type |= cJSON_IsReference; - ref->next = ref->prev = 0; - return ref; -} - -/* Add item to array/object. */ -void cJSON_AddItemToArray(cJSON *array, cJSON *item) -{ - cJSON *c = array->child; - if (!item) - return; - if (!c) - { - array->child = item; - } - else - { - while (c && c->next) - c = c->next; - suffix_object(c, item); - } -} - -void cJSON_AddItemToArrayHead(cJSON *array, cJSON *item) -{ - cJSON *c = array->child; - if (!item) - return; - if (!c) - { - array->child = item; - } - else - { - item->prev = c->prev; - item->next = c; - c->prev = item; - array->child = item; - } -} - -void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) -{ - if (!item) - return; - if (item->string) - cJSON_free(item->string); - item->string = cJSON_strdup(string); - cJSON_AddItemToArray(object, item); -} -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) -{ - cJSON_AddItemToArray(array, create_reference(item)); -} -void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, - cJSON *item) -{ - cJSON_AddItemToObject(object, string, create_reference(item)); -} - -cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) -{ - cJSON *c = array->child; - while (c && which > 0) - c = c->next, which--; - if (!c) - return 0; - if (c->prev) - c->prev->next = c->next; - if (c->next) - c->next->prev = c->prev; - if (c == array->child) - array->child = c->next; - c->prev = c->next = 0; - return c; -} -void cJSON_DeleteItemFromArray(cJSON *array, int which) -{ - cJSON_Delete(cJSON_DetachItemFromArray(array, which)); -} -cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string) -{ - int i = 0; - cJSON *c = object->child; - while (c && cJSON_strcasecmp(c->string, string)) - i++, c = c->next; - if (c) - return cJSON_DetachItemFromArray(object, i); - return 0; -} -void cJSON_DeleteItemFromObject(cJSON *object, const char *string) -{ - cJSON_Delete(cJSON_DetachItemFromObject(object, string)); -} - -/* Replace array/object items with new ones. */ -void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) -{ - cJSON *c = array->child; - while (c && which > 0) - c = c->next, which--; - if (!c) - return; - newitem->next = c->next; - newitem->prev = c->prev; - if (newitem->next) - newitem->next->prev = newitem; - if (c == array->child) - array->child = newitem; - else - newitem->prev->next = newitem; - c->next = c->prev = 0; - cJSON_Delete(c); -} -void cJSON_ReplaceItemInObject(cJSON *object, const char *string, - cJSON *newitem) -{ - int i = 0; - cJSON *c = object->child; - while (c && cJSON_strcasecmp(c->string, string)) - i++, c = c->next; - if (c) - { - newitem->string = cJSON_strdup(string); - cJSON_ReplaceItemInArray(object, i, newitem); - } -} - -/* Create basic types: */ -cJSON *cJSON_CreateNull() -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_NULL; - return item; -} -cJSON *cJSON_CreateTrue() -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_True; - return item; -} -cJSON *cJSON_CreateFalse() -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_False; - return item; -} -cJSON *cJSON_CreateBool(int b) -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = b ? cJSON_True : cJSON_False; - return item; -} -cJSON *cJSON_CreateDouble(double num, int sign) -{ - cJSON *item = cJSON_New_Item(); - if (item) - { - item->type = cJSON_Double; - item->valuedouble = num; - item->valueint = (uint64)num; - item->sign = sign; - } - return item; -} -cJSON *cJSON_CreateInt(uint64 num, int sign) -{ - cJSON *item = cJSON_New_Item(); - if (item) - { - item->type = cJSON_Int; - item->valuedouble = (double)num; - item->valueint = (uint64)num; - item->sign = sign; - } - return item; -} -cJSON *cJSON_CreateString(const char *string) -{ - cJSON *item = cJSON_New_Item(); - if (item) - { - item->type = cJSON_String; - item->valuestring = cJSON_strdup(string); - } - return item; -} -cJSON *cJSON_CreateArray() -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_Array; - return item; -} -cJSON *cJSON_CreateObject() -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_Object; - return item; -} - -/* Create Arrays: */ -cJSON *cJSON_CreateIntArray(int *numbers, int sign, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) - { - n = cJSON_CreateDouble((long double)((unsigned int)numbers[i]), sign); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; -} -cJSON *cJSON_CreateFloatArray(float *numbers, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) - { - n = cJSON_CreateDouble((long double)numbers[i], -1); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; -} -cJSON *cJSON_CreateDoubleArray(double *numbers, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) - { - n = cJSON_CreateDouble((long double)numbers[i], -1); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; -} -cJSON *cJSON_CreateStringArray(const char **strings, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) - { - n = cJSON_CreateString(strings[i]); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; -} - +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +#ifndef INT_MAX +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX 4294967295U +#endif + +static const char *ep; + +const char *cJSON_GetErrorPtr() +{ + return ep; +} + +static int cJSON_strcasecmp(const char *s1, const char *s2) +{ + if (!s1) + return (s1 == s2) ? 0 : 1; + if (!s2) + return 1; + for (; tolower(*s1) == tolower(*s2); ++s1, ++s2) + if (*s1 == 0) + return 0; + return tolower(*(const unsigned char *)s1) + - tolower(*(const unsigned char *)s2); +} + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*) cJSON_malloc(len))) + return 0; + memcpy(copy, str, len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) + { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc; + cJSON_free = (hooks->free_fn) ? hooks->free_fn : free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item() +{ + cJSON* node = (cJSON*) cJSON_malloc(sizeof(cJSON)); + if (node) + memset(node, 0, sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next = c->next; + if (!(c->type & cJSON_IsReference) && c->child) + cJSON_Delete(c->child); + if (!(c->type & cJSON_IsReference) && c->valuestring) + cJSON_free(c->valuestring); + if (c->string) + cJSON_free(c->string); + cJSON_free(c); + c = next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item, const char *num) +{ + long double n = 0, scale = 0; + int subscale = 0, signsubscale = 1; + item->sign = 1; + + /* Could use sscanf for this? */ + if (*num == '-') + item->sign = -1, num++; /* Has sign? */ + if (*num == '0') + num++; /* is zero */ + if (*num >= '1' && *num <= '9') + do + n = (n * 10.0) + (*num++ - '0'); + while (*num >= '0' && *num <= '9'); /* Number? */ + if (*num == '.' && num[1] >= '0' && num[1] <= '9') + { + num++; + do + n = (n * 10.0) + (*num++ - '0'), scale--; + while (*num >= '0' && *num <= '9'); + } /* Fractional part? */ + if (*num == 'e' || *num == 'E') /* Exponent? */ + { + num++; + if (*num == '+') + num++; + else if (*num == '-') + signsubscale = -1, num++; /* With sign? */ + while (*num >= '0' && *num <= '9') + subscale = (subscale * 10) + (*num++ - '0'); /* Number? */ + } + + if (scale == 0 && subscale == 0) + { + item->valuedouble = (double)(item->sign * (uint64)n); + item->valueint = (uint64)(item->sign * (uint64)n); + item->type = cJSON_Int; + } + else + { + n = item->sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + item->valuedouble = (double)n; + item->valueint = (uint64)n; + item->type = cJSON_Double; + } + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_double(cJSON *item) +{ + char *str; + double d = item->valuedouble; + str = (char*) cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(floor(d) - d) <= DBL_EPSILON) + sprintf(str, "%.0f", d); + else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) + sprintf(str, "%lf", d); + else + sprintf(str, "%f", d); + } + return str; +} + +static char *print_int(cJSON *item) +{ + char *str; + str = (char*) cJSON_malloc(22); /* 2^64+1 can be represented in 21 chars. */ + if (str) + { + if (item->sign == -1) + { + if ((int64)item->valueint <= (int64)INT_MAX && (int64)item->valueint >= (int64)INT_MIN) + { + sprintf(str, "%d", (int32)item->valueint); + } + else + { + sprintf(str, "%lld", (int64)item->valueint); + } + } + else + { + if (item->valueint <= (uint64)UINT_MAX) + { + sprintf(str, "%u", (uint32)item->valueint); + } + else + { + sprintf(str, "%llu", item->valueint); + } + } + } + return str; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, + 0xF8, 0xFC }; +static const char *parse_string(cJSON *item, const char *str) +{ + const char *ptr = str + 1; + char *ptr2; + char *out; + int len = 0; + unsigned uc, uc2; + if (*str != '\"') + { + ep = str; + return 0; + } /* not a string! */ + + while (*ptr != '\"' && *ptr && ++len) + if (*ptr++ == '\\') + ptr++; /* Skip escaped quotes. */ + + out = (char*) cJSON_malloc(len + 1); /* This is how long we need for the string, roughly. */ + if (!out) + return 0; + + ptr = str + 1; + ptr2 = out; + while (*ptr != '\"' && *ptr) + { + if (*ptr != '\\') + *ptr2++ = *ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': + *ptr2++ = '\b'; + break; + case 'f': + *ptr2++ = '\f'; + break; + case 'n': + *ptr2++ = '\n'; + break; + case 'r': + *ptr2++ = '\r'; + break; + case 't': + *ptr2++ = '\t'; + break; + case 'u': /* transcode utf16 to utf8. */ + sscanf(ptr + 1, "%4x", &uc); + ptr += 4; /* get the unicode char. */ + + if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) + break; // check for invalid. + + if (uc >= 0xD800 && uc <= 0xDBFF) // UTF16 surrogate pairs. + { + if (ptr[1] != '\\' || ptr[2] != 'u') + break; // missing second-half of surrogate. + sscanf(ptr + 3, "%4x", &uc2); + ptr += 6; + if (uc2 < 0xDC00 || uc2 > 0xDFFF) + break; // invalid second-half of surrogate. + uc = 0x10000 | ((uc & 0x3FF) << 10) | (uc2 & 0x3FF); + } + + len = 4; + if (uc < 0x80) + len = 1; + else if (uc < 0x800) + len = 2; + else if (uc < 0x10000) + len = 3; + ptr2 += len; + + switch (len) + { + case 4: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 3: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 2: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 1: + *--ptr2 = (uc | firstByteMark[len]); + } + ptr2 += len; + break; + default: + *ptr2++ = *ptr; + break; + } + ptr++; + } + } + *ptr2 = 0; + if (*ptr == '\"') + ptr++; + item->valuestring = out; + item->type = cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str) +{ + const char *ptr; + char *ptr2, *out; + int len = 0; + unsigned char token; + + if (!str) + return cJSON_strdup(""); + ptr = str; + while ((token = *ptr) && ++len) + { + if (strchr("\"\\\b\f\n\r\t", token)) + len++; + else if (token < 32) + len += 5; + ptr++; + } + + out = (char*) cJSON_malloc(len + 3); + if (!out) + return 0; + + ptr2 = out; + ptr = str; + *ptr2++ = '\"'; + while (*ptr) + { + if ((unsigned char) *ptr > 31 && *ptr != '\"' && *ptr != '\\') + *ptr2++ = *ptr++; + else + { + *ptr2++ = '\\'; + switch (token = *ptr++) + { + case '\\': + *ptr2++ = '\\'; + break; + case '\"': + *ptr2++ = '\"'; + break; + case '\b': + *ptr2++ = 'b'; + break; + case '\f': + *ptr2++ = 'f'; + break; + case '\n': + *ptr2++ = 'n'; + break; + case '\r': + *ptr2++ = 'r'; + break; + case '\t': + *ptr2++ = 't'; + break; + default: + sprintf(ptr2, "u%04x", token); + ptr2 += 5; + break; /* escape and print */ + } + } + } + *ptr2++ = '\"'; + *ptr2++ = 0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item) +{ + return print_string_ptr(item->valuestring); +} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item, const char *value); +static char *print_value(cJSON *item, int depth, int fmt); +static const char *parse_array(cJSON *item, const char *value); +static char *print_array(cJSON *item, int depth, int fmt); +static const char *parse_object(cJSON *item, const char *value); +static char *print_object(cJSON *item, int depth, int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) +{ + while (in && *in && (unsigned char) *in <= 32) + in++; + return in; +} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_Parse(const char *value) +{ + cJSON *c = cJSON_New_Item(); + ep = 0; + if (!c) + return 0; /* memory fail */ + + if (!parse_value(c, skip(value))) + { + cJSON_Delete(c); + return 0; + } + return c; +} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) +{ + return print_value(item, 0, 1); +} +char *cJSON_PrintUnformatted(cJSON *item) +{ + return print_value(item, 0, 0); +} + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item, const char *value) +{ + if (!value) + return 0; /* Fail on null. */ + if (!strncmp(value, "null", 4)) + { + item->type = cJSON_NULL; + return value + 4; + } + if (!strncmp(value, "false", 5)) + { + item->type = cJSON_False; + return value + 5; + } + if (!strncmp(value, "true", 4)) + { + item->type = cJSON_True; + item->valueint = 1; + return value + 4; + } + if (*value == '\"') + { + return parse_string(item, value); + } + if (*value == '-' || (*value >= '0' && *value <= '9')) + { + return parse_number(item, value); + } + if (*value == '[') + { + return parse_array(item, value); + } + if (*value == '{') + { + return parse_object(item, value); + } + + ep = value; + return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item, int depth, int fmt) +{ + char *out = 0; + if (!item) + return 0; + switch ((item->type) & 255) + { + case cJSON_NULL: + out = cJSON_strdup("null"); + break; + case cJSON_False: + out = cJSON_strdup("false"); + break; + case cJSON_True: + out = cJSON_strdup("true"); + break; + case cJSON_Int: + out = print_int(item); + break; + case cJSON_Double: + out = print_double(item); + break; + case cJSON_String: + out = print_string(item); + break; + case cJSON_Array: + out = print_array(item, depth, fmt); + break; + case cJSON_Object: + out = print_object(item, depth, fmt); + break; + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item, const char *value) +{ + cJSON *child; + if (*value != '[') + { + ep = value; + return 0; + } /* not an array! */ + + item->type = cJSON_Array; + value = skip(value + 1); + if (*value == ']') + return value + 1; /* empty array. */ + + item->child = child = cJSON_New_Item(); + if (!item->child) + return 0; /* memory fail */ + value = skip(parse_value(child, skip(value))); /* skip any spacing, get the value. */ + if (!value) + return 0; + + while (*value == ',') + { + cJSON *new_item; + if (!(new_item = cJSON_New_Item())) + return 0; /* memory fail */ + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip(parse_value(child, skip(value + 1))); + if (!value) + return 0; /* memory fail */ + } + + if (*value == ']') + return value + 1; /* end of array */ + ep = value; + return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item, int depth, int fmt) +{ + char **entries; + char *out = 0, *ptr, *ret; + int len = 5; + cJSON *child = item->child; + int numentries = 0, i = 0, fail = 0; + + /* How many entries in the array? */ + while (child) + numentries++, child = child->next; + /* Allocate an array to hold the values for each */ + entries = (char**) cJSON_malloc(numentries * sizeof(char*)); + if (!entries) + return 0; + memset(entries, 0, numentries * sizeof(char*)); + /* Retrieve all the results: */ + child = item->child; + while (child && !fail) + { + ret = print_value(child, depth + 1, fmt); + entries[i++] = ret; + if (ret) + len += strlen(ret) + 2 + (fmt ? 1 : 0); + else + fail = 1; + child = child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) + out = (char*) cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) + fail = 1; + + /* Handle failure. */ + if (fail) + { + for (i = 0; i < numentries; i++) + if (entries[i]) + cJSON_free(entries[i]); + cJSON_free(entries); + return 0; + } + + /* Compose the output array. */ + *out = '['; + ptr = out + 1; + *ptr = 0; + for (i = 0; i < numentries; i++) + { + strcpy(ptr, entries[i]); + ptr += strlen(entries[i]); + if (i != numentries - 1) + { + *ptr++ = ','; + if (fmt) + *ptr++ = ' '; + *ptr = 0; + } + cJSON_free(entries[i]); + } + cJSON_free(entries); + *ptr++ = ']'; + *ptr++ = 0; + return out; +} + +/* Build an object from the text. */ +static const char *parse_object(cJSON *item, const char *value) +{ + cJSON *child; + if (*value != '{') + { + ep = value; + return 0; + } /* not an object! */ + + item->type = cJSON_Object; + value = skip(value + 1); + if (*value == '}') + return value + 1; /* empty array. */ + + item->child = child = cJSON_New_Item(); + if (!item->child) + return 0; + value = skip(parse_string(child, skip(value))); + if (!value) + return 0; + child->string = child->valuestring; + child->valuestring = 0; + if (*value != ':') + { + ep = value; + return 0; + } /* fail! */ + value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) + return 0; + + while (*value == ',') + { + cJSON *new_item; + if (!(new_item = cJSON_New_Item())) + return 0; /* memory fail */ + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip(parse_string(child, skip(value + 1))); + if (!value) + return 0; + child->string = child->valuestring; + child->valuestring = 0; + if (*value != ':') + { + ep = value; + return 0; + } /* fail! */ + value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) + return 0; + } + + if (*value == '}') + return value + 1; /* end of array */ + ep = value; + return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item, int depth, int fmt) +{ + char **entries = 0, **names = 0; + char *out = 0, *ptr, *ret, *str; + int len = 7, i = 0, j; + cJSON *child = item->child; + int numentries = 0, fail = 0; + /* Count the number of entries. */ + while (child) + numentries++, child = child->next; + /* Allocate space for the names and the objects */ + entries = (char**) cJSON_malloc(numentries * sizeof(char*)); + if (!entries) + return 0; + names = (char**) cJSON_malloc(numentries * sizeof(char*)); + if (!names) + { + cJSON_free(entries); + return 0; + } + memset(entries, 0, sizeof(char*) * numentries); + memset(names, 0, sizeof(char*) * numentries); + + /* Collect all the results into our arrays: */ + child = item->child; + depth++; + if (fmt) + len += depth; + while (child) + { + names[i] = str = print_string_ptr(child->string); + entries[i++] = ret = print_value(child, depth, fmt); + if (str && ret) + len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0); + else + fail = 1; + child = child->next; + } + + /* Try to allocate the output string */ + if (!fail) + out = (char*) cJSON_malloc(len); + if (!out) + fail = 1; + + /* Handle failure */ + if (fail) + { + for (i = 0; i < numentries; i++) + { + if (names[i]) + cJSON_free(names[i]); + if (entries[i]) + cJSON_free(entries[i]); + } + cJSON_free(names); + cJSON_free(entries); + return 0; + } + + /* Compose the output: */ + *out = '{'; + ptr = out + 1; + if (fmt) + *ptr++ = '\n'; + *ptr = 0; + for (i = 0; i < numentries; i++) + { + if (fmt) + for (j = 0; j < depth; j++) + *ptr++ = '\t'; + strcpy(ptr, names[i]); + ptr += strlen(names[i]); + *ptr++ = ':'; + if (fmt) + *ptr++ = '\t'; + strcpy(ptr, entries[i]); + ptr += strlen(entries[i]); + if (i != numentries - 1) + *ptr++ = ','; + if (fmt) + *ptr++ = '\n'; + *ptr = 0; + cJSON_free(names[i]); + cJSON_free(entries[i]); + } + + cJSON_free(names); + cJSON_free(entries); + if (fmt) + for (i = 0; i < depth - 1; i++) + *ptr++ = '\t'; + *ptr++ = '}'; + *ptr++ = 0; + return out; +} + +/* Get Array size/item / object item. */ +int cJSON_GetArraySize(cJSON *array) +{ + cJSON *c = array->child; + int i = 0; + while (c) + i++, c = c->next; + return i; +} +cJSON *cJSON_GetArrayItem(cJSON *array, int item) +{ + cJSON *c = array->child; + while (c && item > 0) + item--, c = c->next; + return c; +} +cJSON *cJSON_GetObjectItem(cJSON *object, const char *string) +{ + cJSON *c = object->child; + while (c && cJSON_strcasecmp(c->string, string)) + c = c->next; + return c; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) +{ + cJSON *ref = cJSON_New_Item(); + if (!ref) + return 0; + memcpy(ref, item, sizeof(cJSON)); + ref->string = 0; + ref->type |= cJSON_IsReference; + ref->next = ref->prev = 0; + return ref; +} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + cJSON *c = array->child; + if (!item) + return; + if (!c) + { + array->child = item; + } + else + { + while (c && c->next) + c = c->next; + suffix_object(c, item); + } +} + +void cJSON_AddItemToArrayHead(cJSON *array, cJSON *item) +{ + cJSON *c = array->child; + if (!item) + return; + if (!c) + { + array->child = item; + } + else + { + item->prev = c->prev; + item->next = c; + c->prev = item; + array->child = item; + } +} + +void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + if (!item) + return; + if (item->string) + cJSON_free(item->string); + item->string = cJSON_strdup(string); + cJSON_AddItemToArray(object, item); +} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + cJSON_AddItemToArray(array, create_reference(item)); +} +void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, + cJSON *item) +{ + cJSON_AddItemToObject(object, string, create_reference(item)); +} + +cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) +{ + cJSON *c = array->child; + while (c && which > 0) + c = c->next, which--; + if (!c) + return 0; + if (c->prev) + c->prev->next = c->next; + if (c->next) + c->next->prev = c->prev; + if (c == array->child) + array->child = c->next; + c->prev = c->next = 0; + return c; +} +void cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} +cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + int i = 0; + cJSON *c = object->child; + while (c && cJSON_strcasecmp(c->string, string)) + i++, c = c->next; + if (c) + return cJSON_DetachItemFromArray(object, i); + return 0; +} +void cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +/* Replace array/object items with new ones. */ +void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *c = array->child; + while (c && which > 0) + c = c->next, which--; + if (!c) + return; + newitem->next = c->next; + newitem->prev = c->prev; + if (newitem->next) + newitem->next->prev = newitem; + if (c == array->child) + array->child = newitem; + else + newitem->prev->next = newitem; + c->next = c->prev = 0; + cJSON_Delete(c); +} +void cJSON_ReplaceItemInObject(cJSON *object, const char *string, + cJSON *newitem) +{ + int i = 0; + cJSON *c = object->child; + while (c && cJSON_strcasecmp(c->string, string)) + i++, c = c->next; + if (c) + { + newitem->string = cJSON_strdup(string); + cJSON_ReplaceItemInArray(object, i, newitem); + } +} + +/* Create basic types: */ +cJSON *cJSON_CreateNull() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_NULL; + return item; +} +cJSON *cJSON_CreateTrue() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_True; + return item; +} +cJSON *cJSON_CreateFalse() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_False; + return item; +} +cJSON *cJSON_CreateBool(int b) +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = b ? cJSON_True : cJSON_False; + return item; +} +cJSON *cJSON_CreateDouble(double num, int sign) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_Double; + item->valuedouble = num; + item->valueint = (uint64)num; + item->sign = sign; + } + return item; +} +cJSON *cJSON_CreateInt(uint64 num, int sign) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_Int; + item->valuedouble = (double)num; + item->valueint = (uint64)num; + item->sign = sign; + } + return item; +} +cJSON *cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_String; + item->valuestring = cJSON_strdup(string); + } + return item; +} +cJSON *cJSON_CreateArray() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_Array; + return item; +} +cJSON *cJSON_CreateObject() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_Object; + return item; +} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(int *numbers, int sign, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateDouble((long double)((unsigned int)numbers[i]), sign); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} +cJSON *cJSON_CreateFloatArray(float *numbers, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateDouble((long double)numbers[i], -1); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} +cJSON *cJSON_CreateDoubleArray(double *numbers, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateDouble((long double)numbers[i], -1); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} +cJSON *cJSON_CreateStringArray(const char **strings, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateString(strings[i]); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} + From 394a2910807139649b51e09af1706c8782da95a2 Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 22 Feb 2019 16:58:01 +0800 Subject: [PATCH 007/176] add request context --- src/actor/Actor.cpp | 15 +++++++++++++-- src/actor/Actor.hpp | 4 ++++ src/actor/session/Timer.hpp | 2 +- src/channel/RedisChannel.cpp | 5 ++--- src/channel/RedisChannel.hpp | 2 +- src/labor/WorkerImpl.cpp | 6 +++--- src/labor/WorkerImpl.hpp | 11 +++++++++-- src/labor/WorkerImpl.inl | 13 +++++++++++++ 8 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 9de3bf18..5e3ef882 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -19,7 +19,7 @@ namespace neb Actor::Actor(ACTOR_TYPE eActorType, ev_tstamp dTimeout) : m_eActorType(eActorType), m_ulSequence(0), m_dActiveTime(0.0), m_dTimeout(dTimeout), - m_pWorker(nullptr), m_pTimerWatcher(NULL) + m_pWorker(nullptr), m_pTimerWatcher(NULL), m_pContext(nullptr) { } @@ -31,7 +31,8 @@ Actor::~Actor() uint32 Actor::GetSequence() { - if (0 == m_ulSequence) + if ((ACT_CMD == m_eActorType || ACT_MODULE == m_eActorType) // Cmd和Module总是获取最新Seq + || 0 == m_ulSequence) { if (nullptr != m_pWorker) { @@ -90,6 +91,16 @@ std::shared_ptr Actor::GetSession(const std::string& strSessionId) return(m_pWorker->GetSession(strSessionId)); } +std::shared_ptr Actor::GetContext() +{ + return(m_pContext); +} + +void Actor::SetContext(std::shared_ptr pContext) +{ + m_pContext = pContext; +} + bool Actor::SendTo(std::shared_ptr pChannel) { return(m_pWorker->SendTo(pChannel)); diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 3fa84b01..ffe7e861 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -43,6 +43,7 @@ class Cmd; class Module; class Session; class Timer; +class Context; class Step; class Actor: public std::enable_shared_from_this @@ -84,6 +85,8 @@ class Actor: public std::enable_shared_from_this std::shared_ptr GetSession(uint32 uiSessionId); std::shared_ptr GetSession(const std::string& strSessionId); + std::shared_ptr GetContext(); + void SetContext(std::shared_ptr pContext); protected: /** @@ -225,6 +228,7 @@ class Actor: public std::enable_shared_from_this Worker* m_pWorker; ev_timer* m_pTimerWatcher; std::string m_strTraceId; // for log trace + std::shared_ptr m_pContext; friend class WorkerImpl; friend class WorkerFriend; diff --git a/src/actor/session/Timer.hpp b/src/actor/session/Timer.hpp index 081cb3e7..9555d340 100644 --- a/src/actor/session/Timer.hpp +++ b/src/actor/session/Timer.hpp @@ -1,6 +1,6 @@ /******************************************************************************* * Project: Nebula - * @file Session.hpp + * @file Timer.hpp * @brief * @author Bwar * @date: 2018年8月5日 diff --git a/src/channel/RedisChannel.cpp b/src/channel/RedisChannel.cpp index dcbb8bda..b4fb156f 100644 --- a/src/channel/RedisChannel.cpp +++ b/src/channel/RedisChannel.cpp @@ -12,10 +12,9 @@ namespace neb { -RedisChannel::RedisChannel() - : bIsReady(false), m_pRedisCtx(NULL) +RedisChannel::RedisChannel(redisAsyncContext *c) + : bIsReady(false), m_pRedisCtx(c) { - } RedisChannel::~RedisChannel() diff --git a/src/channel/RedisChannel.hpp b/src/channel/RedisChannel.hpp index 4f78c2ff..836f0373 100644 --- a/src/channel/RedisChannel.hpp +++ b/src/channel/RedisChannel.hpp @@ -26,7 +26,7 @@ class RedisStep; class RedisChannel: public Channel { public: - RedisChannel(); + RedisChannel(redisAsyncContext *c); virtual ~RedisChannel(); const std::string& GetIdentify() diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 677a2f9a..10579f02 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -1563,15 +1563,15 @@ bool WorkerImpl::AutoRedisCmd(const std::string& strHost, int iPort, std::shared redisAsyncContext *c = redisAsyncConnect(strHost.c_str(), iPort); if (c->err) { - /* Let *c leak for now... */ LOG4_ERROR("error: %s", c->errstr); + redisAsyncFree(c); return(false); } - c->data = this; + c->data = m_pWorker; std::shared_ptr pRedisChannel = nullptr; try { - pRedisChannel = std::make_shared(); + pRedisChannel = std::make_shared(c); } catch(std::bad_alloc& e) { diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index f7033e83..e54e47ee 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -205,6 +205,8 @@ class WorkerImpl public: // about channel virtual bool AddNetLogMsg(const MsgBody& oMsgBody); + + // SendTo() for nebula socket virtual bool SendTo(std::shared_ptr pChannel); virtual bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); @@ -212,14 +214,19 @@ class WorkerImpl virtual bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); virtual bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); + virtual bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + + // SendTo() for http virtual bool SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); virtual bool SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); + virtual bool AutoSend(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); + + // SendTo() for redis virtual bool SendTo(std::shared_ptr pRedisChannel, Actor* pSender); virtual bool SendTo(const std::string& strIdentify, Actor* pSender); virtual bool SendTo(const std::string& strHost, int iPort, Actor* pSender); - virtual bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - virtual bool AutoSend(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); virtual bool AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep); + virtual bool Disconnect(std::shared_ptr pChannel, bool bChannelNotice = true); virtual bool Disconnect(const std::string& strIdentify, bool bChannelNotice = true); virtual bool DiscardNamedChannel(const std::string& strIdentify); diff --git a/src/labor/WorkerImpl.inl b/src/labor/WorkerImpl.inl index e88f33b6..6ec6469e 100644 --- a/src/labor/WorkerImpl.inl +++ b/src/labor/WorkerImpl.inl @@ -42,6 +42,7 @@ std::shared_ptr WorkerImpl::MakeSharedStep(Actor* pCreator, const std::str pStepAlias->SetActiveTime(ev_now(m_loop)); if (nullptr != pCreator) { + pStepAlias->SetContext(pCreator->GetContext()); switch(pCreator->m_eActorType) { case Actor::ACT_PB_STEP: @@ -115,6 +116,10 @@ std::shared_ptr WorkerImpl::MakeSharedSession(Actor* pCreator, const st pSessionAlias->SetWorker(m_pWorker); pSessionAlias->SetActiveTime(ev_now(m_loop)); + if (nullptr != pCreator) + { + pSessionAlias->SetContext(pCreator->GetContext()); + } ev_timer* timer_watcher = pSessionAlias->MutableTimerWatcher(); if (NULL == timer_watcher) { @@ -159,6 +164,10 @@ std::shared_ptr WorkerImpl::MakeSharedCmd(Actor* pCreator, const std::strin pCmdAlias->SetWorker(m_pWorker); pCmdAlias->SetActiveTime(ev_now(m_loop)); + if (nullptr != pCreator) + { + pCmdAlias->SetContext(pCreator->GetContext()); + } std::shared_ptr pSharedCmd; pSharedCmd.reset(pCmd); @@ -188,6 +197,10 @@ std::shared_ptr WorkerImpl::MakeSharedModule(Actor* pCreator, const std: pModuleAlias->SetWorker(m_pWorker); pModuleAlias->SetActiveTime(ev_now(m_loop)); + if (nullptr != pCreator) + { + pModuleAlias->SetContext(pCreator->GetContext()); + } std::shared_ptr pSharedModule; pSharedModule.reset(pModule); From 546d1935c1ed5ac80ccf45cfb13e869ecc194dbb Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 26 Feb 2019 11:59:46 +0800 Subject: [PATCH 008/176] add next step exector --- README_cn.md | 2 ++ src/actor/Actor.cpp | 5 +++++ src/actor/Actor.hpp | 1 + src/actor/step/StepModel.cpp | 13 +++++++++++++ src/actor/step/StepModel.hpp | 8 +++++++- src/labor/Worker.cpp | 5 +++++ src/labor/Worker.hpp | 1 + src/labor/WorkerImpl.cpp | 14 ++++++++++++++ src/labor/WorkerImpl.hpp | 6 +++++- 9 files changed, 53 insertions(+), 2 deletions(-) diff --git a/README_cn.md b/README_cn.md index a10896f2..1fa7187f 100644 --- a/README_cn.md +++ b/README_cn.md @@ -18,6 +18,8 @@ Nebula是一个C\+\+语言开发的事件驱动型的TCP协议网络框架,支 Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建集群才能真正体现其价值。为了能快速搭建分布式服务集群,开发了包括各种类型服务的NebulaBootstrap集群解决方案。关于NebulaBootstrap的详细说明请参考[NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap)。 +Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(也是Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++14)是Starship(C++03)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的第一个应用Nebio(埋点数据采集和实时分析项目)在2018年7月底上线并稳定运行,Bwar还准备开发基于Nebula的IM应用Nebim。 + ## 许可证 > Copyright(c)2018 Bwar diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 5e3ef882..ebac6907 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -91,6 +91,11 @@ std::shared_ptr Actor::GetSession(const std::string& strSessionId) return(m_pWorker->GetSession(strSessionId)); } +bool Actor::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg, void* data) +{ + return(m_pWorker->ExecStep(uiStepSeq, iErrno, strErrMsg, data)); +} + std::shared_ptr Actor::GetContext() { return(m_pContext); diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index ffe7e861..304e78d2 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -85,6 +85,7 @@ class Actor: public std::enable_shared_from_this std::shared_ptr GetSession(uint32 uiSessionId); std::shared_ptr GetSession(const std::string& strSessionId); + bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); std::shared_ptr GetContext(); void SetContext(std::shared_ptr pContext); diff --git a/src/actor/step/StepModel.cpp b/src/actor/step/StepModel.cpp index 231cf073..d9ffcaed 100644 --- a/src/actor/step/StepModel.cpp +++ b/src/actor/step/StepModel.cpp @@ -29,4 +29,17 @@ StepModel::~StepModel() m_setPreStepSeq.clear(); } +void StepModel::NextStep(int iErrno, const std::string& strErrMsg, void* data) +{ + if (iErrno != ERR_OK) + { + return; + } + + for (auto it = m_setNextStepSeq.begin(); it != m_setNextStepSeq.end(); ++it) + { + ExecStep(*it); + } +} + } /* namespace neb */ diff --git a/src/actor/step/StepModel.hpp b/src/actor/step/StepModel.hpp index 641eae71..df28ab75 100644 --- a/src/actor/step/StepModel.hpp +++ b/src/actor/step/StepModel.hpp @@ -30,13 +30,19 @@ class StepModel: public Actor * @brief 提交,发出 * @note 注册了一个回调步骤之后执行Emit()就开始等待回调。 */ - virtual E_CMD_STATUS Emit(int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = nullptr) = 0; + virtual E_CMD_STATUS Emit(int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL) = 0; /** * @brief 步骤超时回调 */ virtual E_CMD_STATUS Timeout() = 0; +protected: + /** + * @brief 执行当前步骤接下来的步骤 + */ + void NextStep(int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); + private: std::unordered_set m_setNextStepSeq; std::unordered_set m_setPreStepSeq; diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 94e355d7..31acb6b2 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -49,6 +49,11 @@ std::shared_ptr Worker::GetSession(const std::string& strSessionId) return(m_pImpl->GetSession(strSessionId)); } +bool Worker::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg, void* data) +{ + return(m_pImpl->ExecStep(uiStepSeq, iErrno, strErrMsg, data)); +} + uint32 Worker::GetNodeId() const { return(m_pImpl->GetWorkerInfo().uiNodeId); diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index d8ea7fe7..708d9832 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -47,6 +47,7 @@ class Worker: public Labor virtual uint32 GetSequence() const; virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); + virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); // 获取worker信息相关方法 virtual uint32 GetNodeId() const; diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 10579f02..d134b018 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -1903,6 +1903,20 @@ std::shared_ptr WorkerImpl::GetSession(const std::string& strSessionId) } } +bool WorkerImpl::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg, void* data) +{ + auto iter = m_mapCallbackStep.find(uiStepSeq); + if (iter == m_mapCallbackStep.end()) + { + return(false); + } + else + { + iter->second->Emit(iErrno, strErrMsg, data); + return(true); + } +} + std::shared_ptr WorkerImpl::CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient, bool bWithSsl) { LOG4_DEBUG("iFd %d, codec_type %d, with_ssl = %d", iFd, eCodecType, bWithSsl); diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index e54e47ee..91549063 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -232,10 +232,14 @@ class WorkerImpl virtual bool DiscardNamedChannel(const std::string& strIdentify); virtual bool SwitchCodec(std::shared_ptr pChannel, E_CODEC_TYPE eCodecType); -public: // about session +public: + // about session virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); + // about step + virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); + public: // Worker相关设置(由专用Cmd类调用这些方法完成Worker自身的初始化和更新) virtual bool SetProcessName(const CJsonObject& oJsonConf); virtual bool AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel); From 848490d029bb2eaa20203284764210a479600945 Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 1 Mar 2019 12:04:44 +0800 Subject: [PATCH 009/176] remove channel param from HttpStep construction --- README.md | 2 +- README_cn.md | 4 +--- src/actor/step/HttpStep.cpp | 6 ------ src/actor/step/HttpStep.hpp | 3 --- 4 files changed, 2 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 4cb5fe69..54211a85 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ English | [中文](/README_cn.md)         Nebula is an event-driven TCP protocol network framework developed in C++ language. It supports multiple application layer communication protocols including proto3, http, https, and websocket. The purpose of developing the Nebula framework is to provide a fast and high-performance distributed service cluster based on C++. -Nebula can be used as a single high-performance TCP server, but building a cluster based on Nebula will be truly reflect its value. In order to build distributed service clusters quickly, Nebula Bootstrap cluster solutions including various types of services have been developed. For details on NebulaBootstrap, please refer to [NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap). +Nebula can be used as a single high-performance TCP server, but building a cluster based on Nebula will be truly reflect its value. In order to build distributed service clusters quickly, Nebula Bootstrap cluster solutions including various types of services have been developed. ## License diff --git a/README_cn.md b/README_cn.md index 1fa7187f..2c29e178 100644 --- a/README_cn.md +++ b/README_cn.md @@ -16,7 +16,7 @@ Nebula是一个C\+\+语言开发的事件驱动型的TCP协议网络框架,支持包括proto3、http、https、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建一个高性能的分布式服务集群。Nebula自身核心代码只有万行左右(不计算proto文件生成的代码)。 -Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建集群才能真正体现其价值。为了能快速搭建分布式服务集群,开发了包括各种类型服务的NebulaBootstrap集群解决方案。关于NebulaBootstrap的详细说明请参考[NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap)。 +Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建集群才能真正体现其价值。为了能快速搭建分布式服务集群,开发了包括各种类型服务的NebulaBootstrap集群解决方案。 Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(也是Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++14)是Starship(C++03)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的第一个应用Nebio(埋点数据采集和实时分析项目)在2018年7月底上线并稳定运行,Bwar还准备开发基于Nebula的IM应用Nebim。 @@ -113,8 +113,6 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) - NebulaMydis数据代理服务 - 应用Nebula开发IM项目 - -## 版本历史 #### v0.6 - NebulaBeacon增加节点状态信息查询,注册中心主从高可用选举 - NebulaInterface提供HelloWorld示例。 diff --git a/src/actor/step/HttpStep.cpp b/src/actor/step/HttpStep.cpp index d2d64483..a25bbff8 100644 --- a/src/actor/step/HttpStep.cpp +++ b/src/actor/step/HttpStep.cpp @@ -18,12 +18,6 @@ HttpStep::HttpStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) { } -HttpStep::HttpStep(std::shared_ptr pChannel, std::shared_ptr pNextStep, ev_tstamp dTimeout) - : Step(ACT_HTTP_STEP, pNextStep, dTimeout), - m_pChannel(pChannel) -{ -} - HttpStep::~HttpStep() { } diff --git a/src/actor/step/HttpStep.hpp b/src/actor/step/HttpStep.hpp index 0cce3f02..d368915e 100644 --- a/src/actor/step/HttpStep.hpp +++ b/src/actor/step/HttpStep.hpp @@ -20,7 +20,6 @@ class HttpStep: public Step { public: HttpStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); - HttpStep(std::shared_ptr pChannel, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); HttpStep(const HttpStep&) = delete; HttpStep& operator=(const HttpStep&) = delete; virtual ~HttpStep(); @@ -35,8 +34,6 @@ class HttpStep: public Step protected: bool HttpRequest(const HttpMsg& oHttpMsg); - - std::shared_ptr m_pChannel; }; } /* namespace neb */ From aa0abfee293255f16d06f7f1c50b8511ffcb0883 Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 1 Mar 2019 14:37:50 +0800 Subject: [PATCH 010/176] add context --- src/actor/session/Context.cpp | 67 ++++++++++++++++++++++ src/actor/session/Context.hpp | 103 ++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 src/actor/session/Context.cpp create mode 100644 src/actor/session/Context.hpp diff --git a/src/actor/session/Context.cpp b/src/actor/session/Context.cpp new file mode 100644 index 00000000..bd7301e2 --- /dev/null +++ b/src/actor/session/Context.cpp @@ -0,0 +1,67 @@ +/******************************************************************************* + * Project: Nebula + * @file Context.cpp + * @brief + * @author Bwar + * @date: 2019年2月16日 + * @note + * Modify history: + ******************************************************************************/ + +#include "Context.hpp" + +namespace neb +{ + +Context::Context(const std::string& strSessionId, ev_tstamp dSessionTimeout) + : Session(strSessionId, dSessionTimeout), + m_pChannel(nullptr), m_iReqCmd(0), m_uiReqSeq(0) +{ +} + +Context::Context(const std::string& strSessionId, + std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq, + ev_tstamp dSessionTimeout) + : Session(strSessionId, dSessionTimeout), + m_pChannel(pChannel), m_iReqCmd(iCmd), m_uiReqSeq(uiSeq) +{ +} + +Context::Context(const std::string& strSessionId, + std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq, + const MsgBody& oReqMsgBody, + ev_tstamp dSessionTimeout) + : Session(strSessionId, dSessionTimeout), + m_pChannel(pChannel), m_iReqCmd(iCmd), m_uiReqSeq(uiSeq), + m_oReqMsgBody(oReqMsgBody) +{ +} + +Context::~Context() +{ +} + +bool Context::Response(int iErrno, const std::string& strData) +{ + MsgBody oMsgBody; + oMsgBody.mutable_rsp_result()->set_code(iErrno); + if (ERR_OK == iErrno) + { + oMsgBody.mutable_rsp_result()->set_msg("success"); + oMsgBody.set_data(strData); + } + else + { + oMsgBody.mutable_rsp_result()->set_msg(strData); + } + + if (SendTo(m_pChannel, m_iReqCmd + 1, m_uiReqSeq, oMsgBody)) + { + return(true); + } + return(false); +} + +} /* namespace neb */ diff --git a/src/actor/session/Context.hpp b/src/actor/session/Context.hpp new file mode 100644 index 00000000..f72d56ed --- /dev/null +++ b/src/actor/session/Context.hpp @@ -0,0 +1,103 @@ +/******************************************************************************* + * Project: Nebula + * @file Context.hpp + * @brief + * @author Bwar + * @date: 2019年2月16日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_SESSION_CONTEXT_HPP_ +#define SRC_ACTOR_SESSION_CONTEXT_HPP_ + +#include "labor/Worker.hpp" +#include "actor/DynamicCreator.hpp" +#include "Session.hpp" + +namespace neb +{ + +/** + * @brief Actor上下文信息 + * @note Actor上下文信息用于保存Actor(主要是Step,也可用于Session)运行 + * 过程中的上下信息存储和数据共享。 + * 比如Cmd收到一个消息,后续需要若干个Step才完成全部操作,在最后一个 + * Step中给请求方发送响应,那么最后一个Step需要获取Cmd当时收到的请求上下 + * 文,而最后一个Step可能不是Cmd所创建的,一种实现方式是将上下文信息顺着 + * Step链条赋值传递(因为前面的Step执行完毕可能会被销毁,不能用引用传递) + * 下去,这样会导致多次内存拷贝。Context的存在是为了减少这种不必要的复制, + * Context由框架管理,Step只需传递Context的引用。 + * 从Context基类派生出业务自己的Context类可以用于Step间自定义数据的共 + * 享(当然,Session类也可以达到同样目的,区别在于GetSession()需要传入 + * SessionId才能获取到Session,GetContext()则不需要传参即可获取)。什么时 + * 候用Context?建议是同一个StepChain(共同完成一个操作的Step执行链)的上 + * 下文信息和数据共享用Context,其他情况用Session。 + */ +class Context: public Session +{ +public: + Context(const std::string& strSessionId, ev_tstamp dSessionTimeout = 60.0); + Context(const std::string& strSessionId, + std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq, + ev_tstamp dSessionTimeout = 60.0); + Context(const std::string& strSessionId, + std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq, + const MsgBody& oReqMsgBody, + ev_tstamp dSessionTimeout = 60.0); + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + virtual ~Context(); + + /** + * @brief 会话超时回调 + * @note 上下文由Worker进行管理,超时回调时一般默认返回CMD_STATUS_COMPLETED, + * 让框架执行回收操作即可(实际上因为上下文以shared_ptr存储,并未在框架执行 + * 回收操作时立即销毁,而是在所有引用该Context的Actor实例均已销毁时才会销毁)。 + */ + virtual E_CMD_STATUS Timeout() + { + return(CMD_STATUS_COMPLETED); + } + +public: + /** + * @brief 给请求方发响应 + * @param iErrno 错误码,如果正确则应传入ERR_OK + * @param strData 响应结果(错误码为ERR_OK时),或错误信息 + */ + bool Response(int iErrno, const std::string& strData); + + std::shared_ptr GetChannel() + { + return(m_pChannel); + } + + int32 GetCmd() const + { + return(m_iReqCmd); + } + + uint32 GetSeq() const + { + return(m_uiReqSeq); + } + + const MsgBody& GetMsgBody() const + { + return(m_oReqMsgBody); + } + +private: + std::shared_ptr m_pChannel; + int32 m_iReqCmd; + uint32 m_uiReqSeq; + MsgBody m_oReqMsgBody; + + friend class WorkerImpl; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_SESSION_CONTEXT_HPP_ */ From 11706721c04a4eb7031fdb23fd5b095d7c5e97a4 Mon Sep 17 00:00:00 2001 From: Bwar Date: Thu, 7 Mar 2019 18:43:43 +0800 Subject: [PATCH 011/176] modify readme --- README_cn.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README_cn.md b/README_cn.md index 2c29e178..a65e1d63 100644 --- a/README_cn.md +++ b/README_cn.md @@ -113,6 +113,8 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) - NebulaMydis数据代理服务 - 应用Nebula开发IM项目 + +## 版本历史 #### v0.6 - NebulaBeacon增加节点状态信息查询,注册中心主从高可用选举 - NebulaInterface提供HelloWorld示例。 From f13d77e6390466c6da1d1ba9f584d9fb0626c541 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 24 Mar 2019 20:12:38 +0800 Subject: [PATCH 012/176] add server config file update --- src/actor/cmd/CW.hpp | 104 ++++++++++++--------- src/actor/cmd/Cmd.hpp | 4 +- src/actor/cmd/sys_cmd/CmdSetServerConf.cpp | 37 ++++++++ src/actor/cmd/sys_cmd/CmdSetServerConf.hpp | 32 +++++++ src/labor/WorkerImpl.cpp | 12 +++ src/labor/WorkerImpl.hpp | 7 ++ 6 files changed, 149 insertions(+), 47 deletions(-) create mode 100644 src/actor/cmd/sys_cmd/CmdSetServerConf.cpp create mode 100644 src/actor/cmd/sys_cmd/CmdSetServerConf.hpp diff --git a/src/actor/cmd/CW.hpp b/src/actor/cmd/CW.hpp index 6ae4d007..96f56f40 100644 --- a/src/actor/cmd/CW.hpp +++ b/src/actor/cmd/CW.hpp @@ -24,55 +24,67 @@ const unsigned int gc_uiCmdBit = 0x0000FFFF; ///< 命令为无符号短 enum E_CMD { CMD_UNDEFINE = 0, ///< 未定义 - CMD_REQ_UPDATE_CONFIG_FILE = 1, ///< 更新配置文件请求(由管理中心向Server发送更新配置文件指令,更新指定配置文件) - CMD_RSP_UPDATE_CONFIG_FILE = 2, ///< 更新配置文件响应 - CMD_REQ_REFRESH_SERVER = 3, ///< 刷新Server配置请求(由Manager进程根据配置文件变化情况刷新Server配置,如修改日志级别,重新加载动态库等) - CMD_RSP_REFRESH_SERVER = 4, ///< 刷新Server配置应答 - CMD_REQ_UPDATE_WORKER_LOAD = 5, ///< 更新Worker进程负载信息请求 - CMD_RSP_UPDATE_WORKER_LOAD = 6, ///< 更新Worker进程负载信息应答(一般无须应答) - CMD_REQ_CONNECT_TO_WORKER = 7, ///< 连接到Worker进程请求(Manager进程在收到这个包之后才会将文件描述符转发给对应Worker进程) - CMD_RSP_CONNECT_TO_WORKER = 8, ///< 连接到Worker进程应答(无须响应) - CMD_REQ_TELL_WORKER = 9, ///< 将己方Worker信息告知对方,并请求对方作对等的回应 - CMD_RSP_TELL_WORKER = 10, ///< 收到对方Worker告知的信息,并将己方的对等信息回应之 - CMD_REQ_NODE_STATUS_REPORT = 11, ///< 节点Server状态上报请求(各节点向控制中心上报自身状态信息) - CMD_RSP_NODE_STATUS_REPORT = 12, ///< 节点Server状态上报应答 + CMD_REQ_CONNECT_TO_WORKER = 1, ///< 连接到Worker进程请求(Manager进程在收到这个包之后才会将文件描述符转发给对应Worker进程) + CMD_RSP_CONNECT_TO_WORKER = 2, ///< 连接到Worker进程应答(无须响应) + CMD_REQ_TELL_WORKER = 3, ///< 将己方Worker信息告知对方,并请求对方作对等的回应 + CMD_RSP_TELL_WORKER = 4, ///< 收到对方Worker告知的信息,并将己方的对等信息回应之 + CMD_REQ_DISCONNECT = 5, ///< 连接断开(由框架层触发通知并以Cmd的形式通知到系统Cmd,断开原因有error、timeout之类) + CMD_RSP_DISCONNECT = 6, ///< 连接断开应答(无效CMD) + CMD_REQ_REFRESH_SERVER = 7, ///< 刷新Server配置请求(由Manager进程根据配置文件变化情况刷新Server配置,如修改日志级别,重新加载动态库等) + CMD_RSP_REFRESH_SERVER = 8, ///< 刷新Server配置应答 + CMD_REQ_SET_LOG_LEVEL = 9, ///< 设置日志级别请求(manager to worker) + CMD_RSP_SET_LOG_LEVEL = 10, ///< 设置日志级别响应(无须响应) + CMD_REQ_RELOAD_SO = 11, ///< 重新加载so请求(manager to worker) + CMD_RSP_RELOAD_SO = 12, ///< 重新加载so响应(无须响应) + CMD_REQ_UPDATE_WORKER_LOAD = 13, ///< 更新Worker进程负载信息请求 + CMD_RSP_UPDATE_WORKER_LOAD = 14, ///< 更新Worker进程负载信息应答(一般无须应答) - CMD_REQ_NODE_REGISTER = 13, ///< 节点注册,各个节点启动时主动注册到Center模块 - CMD_RSP_NODE_REGISTER = 14, ///< 节点注册应答 - CMD_REQ_NODE_NOTICE = 15, ///< 节点通知,各个节点启动时主动注册到Center模块,发的通知 - CMD_RSP_NODE_NOTICE = 16, ///< 节点通知应答通知(Manager应答,Worker无须应答) - CMD_REQ_REFRESH_NODE_ID = 17, ///< 更新节点ID请求(Manager发往Worker) - CMD_RSP_REFRESH_NODE_ID = 18, ///< 更新节点ID应答(一般无须应答) - CMD_REQ_DISCONNECT = 19, ///< 连接断开(由框架层触发通知并以Cmd的形式通知到系统Cmd,断开原因有error、timeout之类) - CMD_RSP_DISCONNECT = 20, ///< 连接断开应答(无效CMD) - CMD_REQ_SERVER_CONFIG = 21, ///< BEACON通知配置到节点服务器 - CMD_RSP_SERVER_CONFIG = 22, ///< 节点服务器得到通知配置应答Center - CMD_REQ_SERVER_DATA_STATUS_REPORT = 25, ///< 服务器数据状态上报请求 - CMD_RSP_SERVER_DATA_STATUS_REPORT = 26, ///< 服务器数据状态上报应答 - CMD_REQ_GET_LOAD_MIN_SERVER = 27, ///< 获取低负载服务器请求 - CMD_RSP_GET_LOAD_MIN_SERVER = 28, ///< 获取低负载服务器应答 - CMD_REQ_SET_LOG_LEVEL = 29, ///< 设置日志级别请求(manager to worker) - CMD_RSP_SET_LOG_LEVEL = 30, ///< 设置日志级别响应(无须响应) - CMD_REQ_RELOAD_SO = 31, ///< 重新加载so请求(manager to worker) - CMD_RSP_RELOAD_SO = 32, ///< 重新加载so响应(无须响应) - CMD_REQ_LOG4_TRACE = 33, ///< 分布式网络日志请求 - CMD_RSP_LOG4_TRACE = 34, ///< 分布式网络日志响应(无须响应) - CMD_REQ_LEADER_ELECTION = 35, ///< 分布式leader选举请求 - CMD_RSP_LEADER_ELECTION = 36, ///< 分布式leader选举响应(通过心跳结合优先权选举,无须响应) + CMD_REQ_NODE_STATUS_REPORT = 101, ///< 节点Server状态上报请求(各节点向控制中心上报自身状态信息) + CMD_RSP_NODE_STATUS_REPORT = 102, ///< 节点Server状态上报应答 + CMD_REQ_NODE_REGISTER = 103, ///< 节点注册,各个节点启动时主动注册到Center模块 + CMD_RSP_NODE_REGISTER = 104, ///< 节点注册应答 + CMD_REQ_NODE_NOTICE = 105, ///< 节点通知,各个节点启动时主动注册到Center模块,发的通知 + CMD_RSP_NODE_NOTICE = 106, ///< 节点通知应答通知(Manager应答,Worker无须应答) + CMD_REQ_REFRESH_NODE_ID = 107, ///< 更新节点ID请求(Manager发往Worker) + CMD_RSP_REFRESH_NODE_ID = 108, ///< 更新节点ID应答(一般无须应答) + CMD_REQ_SERVER_DATA_STATUS_REPORT = 109, ///< 服务器数据状态上报请求 + CMD_RSP_SERVER_DATA_STATUS_REPORT = 110, ///< 服务器数据状态上报应答 + CMD_REQ_GET_LOAD_MIN_SERVER = 111, ///< 获取低负载服务器请求 + CMD_RSP_GET_LOAD_MIN_SERVER = 112, ///< 获取低负载服务器应答 + CMD_REQ_LEADER_ELECTION = 113, ///< 分布式leader选举请求 + CMD_RSP_LEADER_ELECTION = 114, ///< 分布式leader选举响应(通过心跳结合优先权选举,无须响应) + + CMD_REQ_SET_SERVER_CONFIG = 201, ///< 更新框架配置文件请求 + CMD_RSP_SET_SERVER_CONFIG = 202, ///< 更新框架配置文件应答 + CMD_REQ_GET_SERVER_CONFIG = 203, ///< 获取框架配置文件请求 + CMD_RSP_GET_SERVER_CONFIG = 204, ///< 获取框架配置文件应答 + CMD_REQ_SET_SERVER_CUSTOM_CONFIG = 205, ///< 更新框架配置文件中自定义部分配置请求 + CMD_RSP_SET_SERVER_CUSTOM_CONFIG = 206, ///< 更新框架配置文件中自定义部分配置应答 + CMD_REQ_GET_SERVER_CUSTOM_CONFIG = 207, ///< 获取框架配置文件中自定义部分配置请求 + CMD_RSP_GET_SERVER_CUSTOM_CONFIG = 208, ///< 获取框架配置文件中自定义部分配置应答 + CMD_REQ_SET_CUSTOM_CONFIG = 209, ///< 更新自定义配置文件请求 + CMD_RSP_SET_CUSTOM_CONFIG = 210, ///< 更新自定义配置文件响应 + CMD_REQ_GET_CUSTOM_CONFIG = 211, ///< 获取自定义配置文件请求 + CMD_RSP_GET_CUSTOM_CONFIG = 212, ///< 获取自定义配置文件响应 + CMD_REQ_RELOAD_CUSTOM_CONFIG = 213, ///< 重新加载自定义配置文件请求 + CMD_RSP_RELOAD_CUSTOM_CONFIG = 214, ///< 重新加载自定义配置文件响应 + + CMD_REQ_LOG4_TRACE = 401, ///< 分布式网络日志请求 + CMD_RSP_LOG4_TRACE = 402, ///< 分布式网络日志响应(无须响应) // 接入层转发命令字,如客户端数据转发给Logic,Logic数据转发给客户端等 - CMD_REQ_FROM_CLIENT = 501, ///< 客户端发送过来需由接入层转发的数据,传输的MsgHead里的Cmd不会被改变(无业务逻辑直接转发的场景,如登录等接入层有业务逻辑的场景不适用) - CMD_RSP_FROM_CLIENT = 502, ///< 无意义,不会被使用 - CMD_REQ_TO_CLIENT = 503, ///< 往客户端发送的数据,传输的MsgHead里的Cmd不会被改变(直接转发,不需要做任何处理) - CMD_RSP_TO_CLIENT = 504, ///< 无意义,不会被使用 - CMD_REQ_STORATE = 505, ///< 存储请求 - CMD_RSP_STORATE = 506, ///< 存储响应 - CMD_REQ_BEAT = 507, ///< 心跳请求 - CMD_RSP_BEAT = 508, ///< 心跳响应 - CMD_REQ_LOCATE_STORAGE = 511, ///< 定位数据存储位置请求 - CMD_RSP_LOCATE_STORAGE = 512, ///< 定位数据存储位置响应 - CMD_REQ_SYS_ERROR = 999, ///< 系统错误请求(无意义,不会被使用) - CMD_RSP_SYS_ERROR = 1000, ///< 系统错误响应 + CMD_REQ_FROM_CLIENT = 501, ///< 客户端发送过来需由接入层转发的数据,传输的MsgHead里的Cmd不会被改变(无业务逻辑直接转发的场景,如登录等接入层有业务逻辑的场景不适用) + CMD_RSP_FROM_CLIENT = 502, ///< 无意义,不会被使用 + CMD_REQ_TO_CLIENT = 503, ///< 往客户端发送的数据,传输的MsgHead里的Cmd不会被改变(直接转发,不需要做任何处理) + CMD_RSP_TO_CLIENT = 504, ///< 无意义,不会被使用 + CMD_REQ_STORATE = 505, ///< 存储请求 + CMD_RSP_STORATE = 506, ///< 存储响应 + CMD_REQ_BEAT = 507, ///< 心跳请求 + CMD_RSP_BEAT = 508, ///< 心跳响应 + CMD_REQ_LOCATE_STORAGE = 511, ///< 定位数据存储位置请求 + CMD_RSP_LOCATE_STORAGE = 512, ///< 定位数据存储位置响应 + CMD_REQ_SYS_ERROR = 999, ///< 系统错误请求(无意义,不会被使用) + CMD_RSP_SYS_ERROR = 1000, ///< 系统错误响应 }; diff --git a/src/actor/cmd/Cmd.hpp b/src/actor/cmd/Cmd.hpp index bf95dfe2..95a2d564 100644 --- a/src/actor/cmd/Cmd.hpp +++ b/src/actor/cmd/Cmd.hpp @@ -29,11 +29,13 @@ class Cmd: public CmdModel virtual ~Cmd(){}; /** - * @brief 初始化Cmd + * @brief 初始化Cmd;重新加载配置 * @note Cmd类实例初始化函数,大部分Cmd不需要初始化,需要初始化的Cmd可派生后实现此函数, * 在此函数里可以读取配置文件(配置文件必须为json格式)。配置文件由Cmd的设计者自行定义, * 存放于conf/目录,配置文件名最好与Cmd名字保持一致,加上.json后缀。配置文件的更新同步 * 会由框架自动完成。 + * Init()需设计成可重入方法,因各服务框架在收到Beacon的重新加载配置指令后会执行每个 + * Cmd的Init()方法,所以必须保证Init()方法执行后没有副作用。 * @return 是否初始化成功 */ virtual bool Init() diff --git a/src/actor/cmd/sys_cmd/CmdSetServerConf.cpp b/src/actor/cmd/sys_cmd/CmdSetServerConf.cpp new file mode 100644 index 00000000..2c371576 --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdSetServerConf.cpp @@ -0,0 +1,37 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdSetServerConf.cpp + * @brief + * @author Bwar + * @date: 2019年3月24日 + * @note + * Modify history: + ******************************************************************************/ +#include "actor/cmd/sys_cmd/CmdSetServerConf.hpp" + +namespace neb +{ + +CmdSetServerConf::CmdSetServerConf(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdSetServerConf::~CmdSetServerConf() +{ +} + +bool CmdSetServerConf::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + CJsonObject oConf; + if (oConf.Parse(oInMsgBody.data())) + { + return(GetWorkerImpl(this)->SetWorkerConf(oConf)); + } + return(false); +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/CmdSetServerConf.hpp b/src/actor/cmd/sys_cmd/CmdSetServerConf.hpp new file mode 100644 index 00000000..d2d1707b --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdSetServerConf.hpp @@ -0,0 +1,32 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdSetServerConf.hpp + * @brief + * @author Bwar + * @date: 2019年3月24日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_SYS_CMD_CMDSETSERVERCONF_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_CMDSETSERVERCONF_HPP_ + +#include "actor/cmd/Cmd.hpp" +#include "labor/WorkerFriend.hpp" + +namespace neb +{ + +class CmdSetServerConf: public Cmd, public DynamicCreator, public WorkerFriend +{ +public: + CmdSetServerConf(int32 iCmd); + virtual ~CmdSetServerConf(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody); +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_CMDSETSERVERCONF_HPP_ */ diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index d134b018..ea50c695 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -1873,6 +1873,18 @@ bool WorkerImpl::AddIoTimeout(std::shared_ptr pChannel, ev_tstamp } } +bool WorkerImpl::SetWorkerConf(const CJsonObject& oJsonConf) +{ + m_oWorkerConf = oJsonConf; + return(true); +} + +bool WorkerImpl::SetCustomConf(const CJsonObject& oJsonConf) +{ + m_oCustomConf = oJsonConf; + return(m_oWorkerConf.Replace("custom", oJsonConf)); +} + std::shared_ptr WorkerImpl::GetSession(uint32 uiSessionId) { std::ostringstream oss; diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 91549063..89f8b982 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -254,6 +254,13 @@ class WorkerImpl bool AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout = 1.0); + bool SetWorkerConf(const CJsonObject& oJsonConf); + const CJsonObject& GetWorkerConf() const + { + return(m_oWorkerConf); + } + bool SetCustomConf(const CJsonObject& oJsonConf); + protected: bool Init(CJsonObject& oJsonConf); bool InitLogger(const CJsonObject& oJsonConf); From fef2d0a9f12eb943921b28537f0cda0d5a3f6f6e Mon Sep 17 00:00:00 2001 From: Bwar Date: Thu, 28 Mar 2019 17:23:19 +0800 Subject: [PATCH 013/176] add configuration management --- proto/neb_sys.proto | 3 +- src/Error.hpp | 1 + src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp | 32 ++ src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp | 32 ++ src/actor/cmd/sys_cmd/CmdSetServerConf.cpp | 10 +- .../cmd/sys_cmd/CmdSetServerCustomConf.cpp | 41 ++ .../cmd/sys_cmd/CmdSetServerCustomConf.hpp | 32 ++ src/actor/step/sys_step/StepTellWorker.cpp | 2 +- src/labor/Manager.cpp | 349 ++++++++++++++---- src/labor/Manager.hpp | 11 +- src/labor/WorkerImpl.cpp | 16 + src/labor/WorkerImpl.hpp | 2 + src/pb/neb_sys.pb.cc | 124 ++++++- src/pb/neb_sys.pb.h | 56 +++ 14 files changed, 619 insertions(+), 92 deletions(-) create mode 100644 src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp create mode 100644 src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp create mode 100644 src/actor/cmd/sys_cmd/CmdSetServerCustomConf.cpp create mode 100644 src/actor/cmd/sys_cmd/CmdSetServerCustomConf.hpp diff --git a/proto/neb_sys.proto b/proto/neb_sys.proto index 771aa42f..850d8dc6 100644 --- a/proto/neb_sys.proto +++ b/proto/neb_sys.proto @@ -4,12 +4,13 @@ package neb; /** * @brief 更新配置文件 - * @note 要更新的配置文件信息,对应系统命令字CMD_REQ_UPDATE_CONFIG_FILE + * @note 要更新的配置文件信息,接收来自Beacon的配置下发,更新指定配置文件内容 */ message ConfigInfo { string file_name = 1; ///< 配置文件名 string file_content = 2; ///< 配置文件内容(所有配置文件内容均必须是标准JSON结构) + string file_path = 3; ///< 配置文件相对于${NEBULA_HOME}/conf的路径,默认为空,即配置文件存储于${NEBULA_HOME}/conf } /** diff --git a/src/Error.hpp b/src/Error.hpp index 305274e3..f2dbe86e 100644 --- a/src/Error.hpp +++ b/src/Error.hpp @@ -41,6 +41,7 @@ enum E_ERROR_NO ERR_SSL_NEW_CONNECTION = 10017, ///< 新建SSL连接错误 ERR_SSL_HANDSHAKE = 10018, ///< 建立SSL连接错误 ERR_SSL_SHUTDOWN = 10019, ///< 关闭SSL连接错误 + ERR_FILE_NOT_EXIST = 10020, ///< 文件不存在 /* 存储代理错误码段 11000~11999 */ ERR_INCOMPLET_DATAPROXY_DATA = 11001, ///< DataProxy请求数据包不完整 diff --git a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp new file mode 100644 index 00000000..93c7afae --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp @@ -0,0 +1,32 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdReloadCustomConf.cpp + * @brief + * @author Bwar + * @date: 2019年3月28日 + * @note + * Modify history: + ******************************************************************************/ +#include "actor/cmd/sys_cmd/CmdReloadCustomConf.hpp" + +namespace neb +{ + +CmdReloadCustomConf::CmdReloadCustomConf(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdReloadCustomConf::~CmdReloadCustomConf() +{ +} + +bool CmdReloadCustomConf::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + return(GetWorkerImpl(this)->ReloadCmdConf()); +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp new file mode 100644 index 00000000..b972dab7 --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp @@ -0,0 +1,32 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdReloadCustomConf.hpp + * @brief + * @author Bwar + * @date: 2019年3月28日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_SYS_CMD_CMDRELOADCUSTOMCONF_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_CMDRELOADCUSTOMCONF_HPP_ + +#include "actor/cmd/Cmd.hpp" +#include "labor/WorkerFriend.hpp" + +namespace neb +{ + +class CmdReloadCustomConf: public Cmd, public DynamicCreator, public WorkerFriend +{ +public: + CmdReloadCustomConf(int32 iCmd); + virtual ~CmdReloadCustomConf(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody); +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_CMDRELOADCUSTOMCONF_HPP_ */ diff --git a/src/actor/cmd/sys_cmd/CmdSetServerConf.cpp b/src/actor/cmd/sys_cmd/CmdSetServerConf.cpp index 2c371576..fe671da2 100644 --- a/src/actor/cmd/sys_cmd/CmdSetServerConf.cpp +++ b/src/actor/cmd/sys_cmd/CmdSetServerConf.cpp @@ -26,10 +26,14 @@ bool CmdSetServerConf::AnyMessage( const MsgHead& oInMsgHead, const MsgBody& oInMsgBody) { - CJsonObject oConf; - if (oConf.Parse(oInMsgBody.data())) + ConfigInfo oConfigInfo; + if (oConfigInfo.ParseFromString(oInMsgBody.data())) { - return(GetWorkerImpl(this)->SetWorkerConf(oConf)); + CJsonObject oConf; + if (oConf.Parse(oConfigInfo.file_content())) + { + return(GetWorkerImpl(this)->SetWorkerConf(oConf)); + } } return(false); } diff --git a/src/actor/cmd/sys_cmd/CmdSetServerCustomConf.cpp b/src/actor/cmd/sys_cmd/CmdSetServerCustomConf.cpp new file mode 100644 index 00000000..a46041d3 --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdSetServerCustomConf.cpp @@ -0,0 +1,41 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdSetServerCustomConf.cpp + * @brief + * @author Bwar + * @date: 2019年3月28日 + * @note + * Modify history: + ******************************************************************************/ +#include "actor/cmd/sys_cmd/CmdSetServerCustomConf.hpp" + +namespace neb +{ + +CmdSetServerCustomConf::CmdSetServerCustomConf(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdSetServerCustomConf::~CmdSetServerCustomConf() +{ +} + +bool CmdSetServerCustomConf::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + ConfigInfo oConfigInfo; + if (oConfigInfo.ParseFromString(oInMsgBody.data())) + { + CJsonObject oConf; + if (oConf.Parse(oConfigInfo.file_content())) + { + return(GetWorkerImpl(this)->SetCustomConf(oConf)); + } + } + return(false); +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/CmdSetServerCustomConf.hpp b/src/actor/cmd/sys_cmd/CmdSetServerCustomConf.hpp new file mode 100644 index 00000000..9b33e342 --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdSetServerCustomConf.hpp @@ -0,0 +1,32 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdSetServerCustomConf.hpp + * @brief + * @author Bwar + * @date: 2019年3月28日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_SYS_CMD_CMDSETSERVERCUSTOMCONF_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_CMDSETSERVERCUSTOMCONF_HPP_ + +#include "actor/cmd/Cmd.hpp" +#include "labor/WorkerFriend.hpp" + +namespace neb +{ + +class CmdSetServerCustomConf: public Cmd, public DynamicCreator, public WorkerFriend +{ +public: + CmdSetServerCustomConf(int32 iCmd); + virtual ~CmdSetServerCustomConf(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody); +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_CMDSETSERVERCUSTOMCONF_HPP_ */ diff --git a/src/actor/step/sys_step/StepTellWorker.cpp b/src/actor/step/sys_step/StepTellWorker.cpp index 947825bc..3a33c048 100644 --- a/src/actor/step/sys_step/StepTellWorker.cpp +++ b/src/actor/step/sys_step/StepTellWorker.cpp @@ -30,7 +30,7 @@ E_CMD_STATUS StepTellWorker::Emit( TargetWorker oTargetWorker; oTargetWorker.set_worker_identify(GetNodeIdentify()); oTargetWorker.set_node_type(GetNodeType()); - oOutMsgBody.mutable_rsp_result()->set_code(0); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("OK"); oOutMsgBody.set_data(oTargetWorker.SerializeAsString()); Step::SendTo(m_pChannel, CMD_REQ_TELL_WORKER, GetSequence(), oOutMsgBody); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 9c30ecd5..6938f432 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -1896,94 +1896,60 @@ bool Manager::OnBeaconData(std::shared_ptr pChannel, const MsgHea LOG4_DEBUG("cmd %u, seq %u", oInMsgHead.cmd(), oInMsgHead.seq()); if (gc_uiCmdReq & oInMsgHead.cmd()) // 新请求,直接转发给Worker,并回复beacon已收到请求 { - if (CMD_REQ_BEAT == oInMsgHead.cmd()) // beacon发过来的心跳包 - { - MsgBody oOutMsgBody = oInMsgBody; - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); - if (CODEC_STATUS_OK == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - } - else if (CODEC_STATUS_PAUSE == eCodecStatus) - { - AddIoWriteEvent(pChannel); - } - else - { - DiscardSocketChannel(pChannel); - return(false); - } - return(true); + MsgBody oOutMsgBody; + switch (oInMsgHead.cmd()) + { + case CMD_REQ_BEAT: + OnBeat(oInMsgBody, oOutMsgBody); + break; + case CMD_REQ_TELL_WORKER: + OnTellWorker(pChannel, oInMsgBody, oOutMsgBody); + break; + case CMD_REQ_NODE_NOTICE: + OnNodeNotify(oInMsgBody, oOutMsgBody); + break; + case CMD_REQ_SET_SERVER_CONFIG: + OnSetServerConf(oInMsgBody, oOutMsgBody); + break; + case CMD_REQ_GET_SERVER_CONFIG: + OnGetServerConf(oInMsgBody, oOutMsgBody); + break; + case CMD_REQ_SET_SERVER_CUSTOM_CONFIG: + OnSetServerCustomConf(oInMsgBody, oOutMsgBody); + break; + case CMD_REQ_GET_SERVER_CUSTOM_CONFIG: + OnGetServerCustomConf(oInMsgBody, oOutMsgBody); + break; + case CMD_REQ_SET_CUSTOM_CONFIG: + OnSetCustomConf(oInMsgBody, oOutMsgBody); + break; + case CMD_REQ_GET_CUSTOM_CONFIG: + OnGetCustomConf(oInMsgBody, oOutMsgBody); + break; + case CMD_REQ_RELOAD_CUSTOM_CONFIG: + OnReloadCustomConf(oInMsgBody, oOutMsgBody); + break; + default: + oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); + oOutMsgBody.mutable_rsp_result()->set_msg("unknow cmd!"); } - else if (CMD_REQ_TELL_WORKER == oInMsgHead.cmd()) - { - MsgBody oOutMsgBody; - TargetWorker oInTargetWorker; - TargetWorker oOutTargetWorker; - if (oInTargetWorker.ParseFromString(oInMsgBody.data())) - { - LOG4_DEBUG("AddNodeIdentify(%s, %s)!", oInTargetWorker.node_type().c_str(), - oInTargetWorker.worker_identify().c_str()); - AddNamedSocketChannel(oInTargetWorker.worker_identify(), pChannel); - AddNodeIdentify(oInTargetWorker.node_type(), oInTargetWorker.worker_identify()); - oOutTargetWorker.set_worker_identify(GetNodeIdentify()); - oOutTargetWorker.set_node_type(m_stManagerInfo.strNodeType); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("OK"); - } - else - { - oOutTargetWorker.set_worker_identify("unknow"); - oOutTargetWorker.set_node_type(m_stManagerInfo.strNodeType); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); - oOutMsgBody.mutable_rsp_result()->set_msg("WorkerLoad ParseFromString error!"); - LOG4_ERROR("error %d: WorkerLoad ParseFromString error!", ERR_PARASE_PROTOBUF); - } - oOutMsgBody.set_data(oOutTargetWorker.SerializeAsString()); - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(CMD_RSP_TELL_WORKER, oInMsgHead.seq(), oOutMsgBody); - if (CODEC_STATUS_OK == eCodecStatus) - { - eCodecStatus = pChannel->m_pImpl->Send(); - } - if (CODEC_STATUS_OK == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - } - else if (CODEC_STATUS_PAUSE == eCodecStatus) - { - AddIoWriteEvent(pChannel); - } - else - { - DiscardSocketChannel(pChannel); - return(false); - } - return(true); - } - SendToWorker(oInMsgHead.cmd(), oInMsgHead.seq(), oInMsgBody); - if (oInMsgHead.cmd() == CMD_REQ_NODE_NOTICE) - { - OnNodeNotify(oInMsgBody); - } - MsgBody oOutMsgBody; - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("OK"); E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); if (CODEC_STATUS_OK == eCodecStatus) { RemoveIoWriteEvent(pChannel); + return(true); } else if (CODEC_STATUS_PAUSE == eCodecStatus) { AddIoWriteEvent(pChannel); + return(true); } else { DiscardSocketChannel(pChannel); return(false); } - return(true); } else // 回调 { @@ -2051,14 +2017,49 @@ bool Manager::OnBeaconData(std::shared_ptr pChannel, const MsgHea return(true); } -bool Manager::OnNodeNotify(const MsgBody& oMsgBody) +bool Manager::OnBeat(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +{ + oOutMsgBody = oInMsgBody; + return(true); +} + +bool Manager::OnTellWorker(std::shared_ptr pChannel, const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +{ + TargetWorker oInTargetWorker; + TargetWorker oOutTargetWorker; + if (oInTargetWorker.ParseFromString(oInMsgBody.data())) + { + LOG4_DEBUG("AddNodeIdentify(%s, %s)!", oInTargetWorker.node_type().c_str(), + oInTargetWorker.worker_identify().c_str()); + AddNamedSocketChannel(oInTargetWorker.worker_identify(), pChannel); + AddNodeIdentify(oInTargetWorker.node_type(), oInTargetWorker.worker_identify()); + oOutTargetWorker.set_worker_identify(GetNodeIdentify()); + oOutTargetWorker.set_node_type(m_stManagerInfo.strNodeType); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("OK"); + oOutMsgBody.set_data(oOutTargetWorker.SerializeAsString()); + return(true); + } + else + { + LOG4_ERROR("error %d: WorkerLoad ParseFromString error!", ERR_PARASE_PROTOBUF); + oOutTargetWorker.set_worker_identify("unknow"); + oOutTargetWorker.set_node_type(m_stManagerInfo.strNodeType); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); + oOutMsgBody.mutable_rsp_result()->set_msg("WorkerLoad ParseFromString error!"); + return(false); + } +} + +bool Manager::OnNodeNotify(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) { CJsonObject oJson; - if (!oJson.Parse(oMsgBody.data())) + if (!oJson.Parse(oInMsgBody.data())) { LOG4_ERROR("failed to parse msgbody content!"); return(false); } + SendToWorker(CMD_REQ_NODE_NOTICE, GetSequence(), oInMsgBody); std::ostringstream oss; std::string strIdentify; std::string strNodeType; @@ -2130,4 +2131,204 @@ bool Manager::OnNodeNotify(const MsgBody& oMsgBody) return(true); } +bool Manager::OnSetServerConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +{ + ConfigInfo oConfigInfo; + if (oConfigInfo.ParseFromString(oInMsgBody.data())) + { + CJsonObject oJsonData; + if (oJsonData.Parse(oConfigInfo.file_content())) + { + // some data can not be set by beacon. + oJsonData.Replace("node_type", m_oCurrentConf("node_type")); + oJsonData.Replace("access_host", m_oCurrentConf("access_host")); + oJsonData.Replace("access_port", m_oCurrentConf("access_port")); + oJsonData.Replace("access_codec", m_oCurrentConf("access_codec")); + oJsonData.Replace("host", m_oCurrentConf("host")); + oJsonData.Replace("port", m_oCurrentConf("port")); + oJsonData.Replace("server_name", m_oCurrentConf("server_name")); + oJsonData.Replace("process_num", m_oCurrentConf("process_num")); + std::ofstream fout(m_stManagerInfo.strConfFile.c_str()); + if (fout.good()) + { + std::string strNewConfData = oJsonData.ToFormattedString(); + fout.write(strNewConfData.c_str(), strNewConfData.size()); + fout.close(); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + SendToWorker(CMD_REQ_SET_SERVER_CONFIG, GetSequence(), oInMsgBody); + return(true); + } + return(false); + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_BODY_JSON); + oOutMsgBody.mutable_rsp_result()->set_msg("the server config must be in json format!"); + return(false); + } + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); + oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); + return(false); + } +} + +bool Manager::OnGetServerConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +{ + ConfigInfo oConfigInfo; + oConfigInfo.set_file_name(m_stManagerInfo.strConfFile); + oConfigInfo.set_file_content(m_oCurrentConf.ToString()); + oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + return(true); +} + +bool Manager::OnSetServerCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +{ + ConfigInfo oConfigInfo; + if (oConfigInfo.ParseFromString(oInMsgBody.data())) + { + CJsonObject oJsonData; + if (oJsonData.Parse(oConfigInfo.file_content())) + { + m_oCurrentConf.Replace("custom", oJsonData); + std::ofstream fout(m_stManagerInfo.strConfFile.c_str()); + if (fout.good()) + { + std::string strNewConfData = m_oCurrentConf.ToFormattedString(); + fout.write(strNewConfData.c_str(), strNewConfData.size()); + fout.close(); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + SendToWorker(CMD_REQ_SET_SERVER_CUSTOM_CONFIG, GetSequence(), oInMsgBody); + return(true); + } + return(false); + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_BODY_JSON); + oOutMsgBody.mutable_rsp_result()->set_msg("the server custom config must be in json format!"); + return(false); + } + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); + oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); + return(false); + } +} + +bool Manager::OnGetServerCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +{ + ConfigInfo oConfigInfo; + oConfigInfo.set_file_name(m_stManagerInfo.strConfFile); + oConfigInfo.set_file_content(m_oCurrentConf["custom"].ToString()); + oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + return(true); +} + +bool Manager::OnSetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +{ + ConfigInfo oConfigInfo; + if (oConfigInfo.ParseFromString(oInMsgBody.data())) + { + std::stringstream ssConfFile; + if (oConfigInfo.file_path().size() > 0) + { + ssConfFile << m_stManagerInfo.strWorkPath + << "/conf/" << oConfigInfo.file_path() + << "/" << oConfigInfo.file_name(); + } + else + { + ssConfFile << m_stManagerInfo.strWorkPath + << "/conf/" << oConfigInfo.file_name(); + } + + std::ofstream fout(ssConfFile.str().c_str()); + if (fout.good()) + { + fout.write(oConfigInfo.file_content().c_str(), oConfigInfo.file_content().size()); + fout.close(); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + SendToWorker(CMD_REQ_SET_CUSTOM_CONFIG, GetSequence(), oInMsgBody); + return(true); + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_FILE_NOT_EXIST); + oOutMsgBody.mutable_rsp_result()->set_msg("file \"" + ssConfFile.str() + "\" not exist!"); + return(false); + } + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); + oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); + return(false); + } +} + +bool Manager::OnGetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +{ + ConfigInfo oConfigInfo; + if (oConfigInfo.ParseFromString(oInMsgBody.data())) + { + std::stringstream ssConfFile; + if (oConfigInfo.file_path().size() > 0) + { + ssConfFile << m_stManagerInfo.strWorkPath + << "/conf/" << oConfigInfo.file_path() + << "/" << oConfigInfo.file_name(); + } + else + { + ssConfFile << m_stManagerInfo.strWorkPath + << "/conf/" << oConfigInfo.file_name(); + } + + std::ifstream fin(ssConfFile.str().c_str()); + if (fin.good()) + { + std::stringstream ssContent; + ssContent << fin.rdbuf(); + oConfigInfo.set_file_content(ssContent.str()); + oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + fin.close(); + return(true); + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_FILE_NOT_EXIST); + oOutMsgBody.mutable_rsp_result()->set_msg("file \"" + ssConfFile.str() + "\" not exist!"); + return(false); + } + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); + oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); + return(false); + } +} + +bool Manager::OnReloadCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +{ + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + SendToWorker(CMD_REQ_RELOAD_CUSTOM_CONFIG, GetSequence(), oInMsgBody); + return(true); +} + } /* namespace neb */ diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index 507d3f0b..b4788066 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -287,7 +287,16 @@ class Manager: public Labor bool OnWorkerData(std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody); bool OnDataAndTransferFd(std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody); bool OnBeaconData(std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody); - bool OnNodeNotify(const MsgBody& oMsgBody); + bool OnBeat(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnTellWorker(std::shared_ptr pChannel, const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnNodeNotify(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnSetServerConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnGetServerConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnSetServerCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnGetServerCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnSetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnGetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnReloadCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); private: mutable uint32 m_uiSequence; diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index ea50c695..1ad78e64 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -864,6 +864,9 @@ void WorkerImpl::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdUpdateNodeId", (int)CMD_REQ_REFRESH_NODE_ID); MakeSharedCmd(nullptr, "neb::CmdNodeNotice", (int)CMD_REQ_NODE_NOTICE); MakeSharedCmd(nullptr, "neb::CmdBeat", (int)CMD_REQ_BEAT); + MakeSharedCmd(nullptr, "neb::CmdSetServerConf", (int)CMD_REQ_SET_SERVER_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdSetServerCustomConf", (int)CMD_REQ_SET_SERVER_CUSTOM_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdReloadCustomConf", (int)CMD_REQ_RELOAD_CUSTOM_CONFIG); #if __cplusplus >= 201401L m_pSessionNode = std::make_unique(); #else @@ -1885,6 +1888,19 @@ bool WorkerImpl::SetCustomConf(const CJsonObject& oJsonConf) return(m_oWorkerConf.Replace("custom", oJsonConf)); } +bool WorkerImpl::ReloadCmdConf() +{ + for (auto cmd_iter = m_mapCmd.begin(); cmd_iter != m_mapCmd.end(); ++cmd_iter) + { + cmd_iter->second->Init(); + } + for (auto module_iter = m_mapModule.begin(); module_iter != m_mapModule.end(); ++module_iter) + { + module_iter->second->Init(); + } + return(true); +} + std::shared_ptr WorkerImpl::GetSession(uint32 uiSessionId) { std::ostringstream oss; diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 89f8b982..b423ff3b 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -48,6 +48,7 @@ extern "C" { #include #include #include +#include #include "pb/msg.pb.h" #include "pb/http.pb.h" @@ -260,6 +261,7 @@ class WorkerImpl return(m_oWorkerConf); } bool SetCustomConf(const CJsonObject& oJsonConf); + bool ReloadCmdConf(); protected: bool Init(CJsonObject& oJsonConf); diff --git a/src/pb/neb_sys.pb.cc b/src/pb/neb_sys.pb.cc index 9c8ed842..0d433cf6 100644 --- a/src/pb/neb_sys.pb.cc +++ b/src/pb/neb_sys.pb.cc @@ -48,9 +48,10 @@ void protobuf_AssignDesc_neb_5fsys_2eproto() { "neb_sys.proto"); GOOGLE_CHECK(file != NULL); ConfigInfo_descriptor_ = file->message_type(0); - static const int ConfigInfo_offsets_[2] = { + static const int ConfigInfo_offsets_[3] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ConfigInfo, file_name_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ConfigInfo, file_content_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ConfigInfo, file_path_), }; ConfigInfo_reflection_ = ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( @@ -181,17 +182,18 @@ void protobuf_AddDesc_neb_5fsys_2eproto() { GOOGLE_PROTOBUF_VERIFY_VERSION; ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( - "\n\rneb_sys.proto\022\003neb\"5\n\nConfigInfo\022\021\n\tfi" - "le_name\030\001 \001(\t\022\024\n\014file_content\030\002 \001(\t\"\'\n\nW" - "orkerLoad\022\013\n\003pid\030\001 \001(\005\022\014\n\004load\030\002 \001(\005\":\n\014" - "TargetWorker\022\027\n\017worker_identify\030\001 \001(\t\022\021\n" - "\tnode_type\030\002 \001(\t\"4\n\010LogLevel\022\021\n\tlog_leve" - "l\030\001 \001(\005\022\025\n\rnet_log_level\030\002 \001(\005\"\265\001\n\010Trace" - "Log\022\020\n\010log_time\030\001 \001(\t\022\021\n\tnode_type\030\002 \001(\t" - "\022\025\n\rnode_identify\030\003 \001(\t\022\021\n\tlog_level\030\004 \001" - "(\t\022\026\n\016code_file_name\030\005 \001(\t\022\026\n\016code_file_" - "line\030\006 \001(\r\022\025\n\rcode_function\030\007 \001(\t\022\023\n\013log" - "_content\030\010 \001(\014b\006proto3", 422); + "\n\rneb_sys.proto\022\003neb\"H\n\nConfigInfo\022\021\n\tfi" + "le_name\030\001 \001(\t\022\024\n\014file_content\030\002 \001(\t\022\021\n\tf" + "ile_path\030\003 \001(\t\"\'\n\nWorkerLoad\022\013\n\003pid\030\001 \001(" + "\005\022\014\n\004load\030\002 \001(\005\":\n\014TargetWorker\022\027\n\017worke" + "r_identify\030\001 \001(\t\022\021\n\tnode_type\030\002 \001(\t\"4\n\010L" + "ogLevel\022\021\n\tlog_level\030\001 \001(\005\022\025\n\rnet_log_le" + "vel\030\002 \001(\005\"\265\001\n\010TraceLog\022\020\n\010log_time\030\001 \001(\t" + "\022\021\n\tnode_type\030\002 \001(\t\022\025\n\rnode_identify\030\003 \001" + "(\t\022\021\n\tlog_level\030\004 \001(\t\022\026\n\016code_file_name\030" + "\005 \001(\t\022\026\n\016code_file_line\030\006 \001(\r\022\025\n\rcode_fu" + "nction\030\007 \001(\t\022\023\n\013log_content\030\010 \001(\014b\006proto" + "3", 441); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "neb_sys.proto", &protobuf_RegisterTypes); ConfigInfo::default_instance_ = new ConfigInfo(); @@ -219,6 +221,7 @@ struct StaticDescriptorInitializer_neb_5fsys_2eproto { #if !defined(_MSC_VER) || _MSC_VER >= 1900 const int ConfigInfo::kFileNameFieldNumber; const int ConfigInfo::kFileContentFieldNumber; +const int ConfigInfo::kFilePathFieldNumber; #endif // !defined(_MSC_VER) || _MSC_VER >= 1900 ConfigInfo::ConfigInfo() @@ -245,6 +248,7 @@ void ConfigInfo::SharedCtor() { _cached_size_ = 0; file_name_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); file_content_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + file_path_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ConfigInfo::~ConfigInfo() { @@ -255,6 +259,7 @@ ConfigInfo::~ConfigInfo() { void ConfigInfo::SharedDtor() { file_name_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); file_content_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + file_path_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); if (this != default_instance_) { } } @@ -288,6 +293,7 @@ void ConfigInfo::Clear() { // @@protoc_insertion_point(message_clear_start:neb.ConfigInfo) file_name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); file_content_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + file_path_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } bool ConfigInfo::MergePartialFromCodedStream( @@ -329,6 +335,23 @@ bool ConfigInfo::MergePartialFromCodedStream( } else { goto handle_unusual; } + if (input->ExpectTag(26)) goto parse_file_path; + break; + } + + // optional string file_path = 3; + case 3: { + if (tag == 26) { + parse_file_path: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_file_path())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->file_path().data(), this->file_path().length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "neb.ConfigInfo.file_path")); + } else { + goto handle_unusual; + } if (input->ExpectAtEnd()) goto success; break; } @@ -377,6 +400,16 @@ void ConfigInfo::SerializeWithCachedSizes( 2, this->file_content(), output); } + // optional string file_path = 3; + if (this->file_path().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->file_path().data(), this->file_path().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "neb.ConfigInfo.file_path"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 3, this->file_path(), output); + } + // @@protoc_insertion_point(serialize_end:neb.ConfigInfo) } @@ -405,6 +438,17 @@ ::google::protobuf::uint8* ConfigInfo::InternalSerializeWithCachedSizesToArray( 2, this->file_content(), target); } + // optional string file_path = 3; + if (this->file_path().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->file_path().data(), this->file_path().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "neb.ConfigInfo.file_path"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 3, this->file_path(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:neb.ConfigInfo) return target; } @@ -427,6 +471,13 @@ int ConfigInfo::ByteSize() const { this->file_content()); } + // optional string file_path = 3; + if (this->file_path().size() > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->file_path()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = total_size; GOOGLE_SAFE_CONCURRENT_WRITES_END(); @@ -463,6 +514,10 @@ void ConfigInfo::MergeFrom(const ConfigInfo& from) { file_content_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.file_content_); } + if (from.file_path().size() > 0) { + + file_path_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.file_path_); + } } void ConfigInfo::CopyFrom(const ::google::protobuf::Message& from) { @@ -491,6 +546,7 @@ void ConfigInfo::Swap(ConfigInfo* other) { void ConfigInfo::InternalSwap(ConfigInfo* other) { file_name_.Swap(&other->file_name_); file_content_.Swap(&other->file_content_); + file_path_.Swap(&other->file_path_); _internal_metadata_.Swap(&other->_internal_metadata_); std::swap(_cached_size_, other->_cached_size_); } @@ -594,6 +650,50 @@ void ConfigInfo::clear_file_content() { // @@protoc_insertion_point(field_set_allocated:neb.ConfigInfo.file_content) } +// optional string file_path = 3; +void ConfigInfo::clear_file_path() { + file_path_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& ConfigInfo::file_path() const { + // @@protoc_insertion_point(field_get:neb.ConfigInfo.file_path) + return file_path_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void ConfigInfo::set_file_path(const ::std::string& value) { + + file_path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:neb.ConfigInfo.file_path) +} + void ConfigInfo::set_file_path(const char* value) { + + file_path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:neb.ConfigInfo.file_path) +} + void ConfigInfo::set_file_path(const char* value, size_t size) { + + file_path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:neb.ConfigInfo.file_path) +} + ::std::string* ConfigInfo::mutable_file_path() { + + // @@protoc_insertion_point(field_mutable:neb.ConfigInfo.file_path) + return file_path_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* ConfigInfo::release_file_path() { + // @@protoc_insertion_point(field_release:neb.ConfigInfo.file_path) + + return file_path_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void ConfigInfo::set_allocated_file_path(::std::string* file_path) { + if (file_path != NULL) { + + } else { + + } + file_path_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), file_path); + // @@protoc_insertion_point(field_set_allocated:neb.ConfigInfo.file_path) +} + #endif // PROTOBUF_INLINE_NOT_IN_HEADERS // =================================================================== diff --git a/src/pb/neb_sys.pb.h b/src/pb/neb_sys.pb.h index 2332f1bf..102daeb5 100644 --- a/src/pb/neb_sys.pb.h +++ b/src/pb/neb_sys.pb.h @@ -126,6 +126,17 @@ class ConfigInfo : public ::google::protobuf::Message /* @@protoc_insertion_poin ::std::string* release_file_content(); void set_allocated_file_content(::std::string* file_content); + // optional string file_path = 3; + void clear_file_path(); + static const int kFilePathFieldNumber = 3; + const ::std::string& file_path() const; + void set_file_path(const ::std::string& value); + void set_file_path(const char* value); + void set_file_path(const char* value, size_t size); + ::std::string* mutable_file_path(); + ::std::string* release_file_path(); + void set_allocated_file_path(::std::string* file_path); + // @@protoc_insertion_point(class_scope:neb.ConfigInfo) private: @@ -133,6 +144,7 @@ class ConfigInfo : public ::google::protobuf::Message /* @@protoc_insertion_poin bool _is_default_instance_; ::google::protobuf::internal::ArenaStringPtr file_name_; ::google::protobuf::internal::ArenaStringPtr file_content_; + ::google::protobuf::internal::ArenaStringPtr file_path_; mutable int _cached_size_; friend void protobuf_AddDesc_neb_5fsys_2eproto(); friend void protobuf_AssignDesc_neb_5fsys_2eproto(); @@ -680,6 +692,50 @@ inline void ConfigInfo::set_allocated_file_content(::std::string* file_content) // @@protoc_insertion_point(field_set_allocated:neb.ConfigInfo.file_content) } +// optional string file_path = 3; +inline void ConfigInfo::clear_file_path() { + file_path_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& ConfigInfo::file_path() const { + // @@protoc_insertion_point(field_get:neb.ConfigInfo.file_path) + return file_path_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void ConfigInfo::set_file_path(const ::std::string& value) { + + file_path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:neb.ConfigInfo.file_path) +} +inline void ConfigInfo::set_file_path(const char* value) { + + file_path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:neb.ConfigInfo.file_path) +} +inline void ConfigInfo::set_file_path(const char* value, size_t size) { + + file_path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:neb.ConfigInfo.file_path) +} +inline ::std::string* ConfigInfo::mutable_file_path() { + + // @@protoc_insertion_point(field_mutable:neb.ConfigInfo.file_path) + return file_path_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* ConfigInfo::release_file_path() { + // @@protoc_insertion_point(field_release:neb.ConfigInfo.file_path) + + return file_path_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void ConfigInfo::set_allocated_file_path(::std::string* file_path) { + if (file_path != NULL) { + + } else { + + } + file_path_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), file_path); + // @@protoc_insertion_point(field_set_allocated:neb.ConfigInfo.file_path) +} + // ------------------------------------------------------------------- // WorkerLoad From d9fe7f20ff5eefa3e7511fa5cbcb31298bf8d61a Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 2 Apr 2019 11:27:59 +0800 Subject: [PATCH 014/176] change name "ServerConf" to "NodeConf" --- .../{CmdSetServerConf.cpp => CmdSetNodeConf.cpp} | 10 +++++----- .../{CmdSetServerConf.hpp => CmdSetNodeConf.hpp} | 14 +++++++------- ...erCustomConf.cpp => CmdSetNodeCustomConf.cpp} | 10 +++++----- ...erCustomConf.hpp => CmdSetNodeCustomConf.hpp} | 14 +++++++------- src/labor/Manager.cpp | 16 ++++++++-------- src/labor/Manager.hpp | 8 ++++---- src/labor/WorkerImpl.cpp | 4 ++-- 7 files changed, 38 insertions(+), 38 deletions(-) rename src/actor/cmd/sys_cmd/{CmdSetServerConf.cpp => CmdSetNodeConf.cpp} (78%) rename src/actor/cmd/sys_cmd/{CmdSetServerConf.hpp => CmdSetNodeConf.hpp} (61%) rename src/actor/cmd/sys_cmd/{CmdSetServerCustomConf.cpp => CmdSetNodeCustomConf.cpp} (74%) rename src/actor/cmd/sys_cmd/{CmdSetServerCustomConf.hpp => CmdSetNodeCustomConf.hpp} (58%) diff --git a/src/actor/cmd/sys_cmd/CmdSetServerConf.cpp b/src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp similarity index 78% rename from src/actor/cmd/sys_cmd/CmdSetServerConf.cpp rename to src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp index fe671da2..b8a7b806 100644 --- a/src/actor/cmd/sys_cmd/CmdSetServerConf.cpp +++ b/src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp @@ -1,27 +1,27 @@ /******************************************************************************* * Project: Nebula - * @file CmdSetServerConf.cpp + * @file CmdSetNodeConf.cpp * @brief * @author Bwar * @date: 2019年3月24日 * @note * Modify history: ******************************************************************************/ -#include "actor/cmd/sys_cmd/CmdSetServerConf.hpp" +#include "actor/cmd/sys_cmd/CmdSetNodeConf.hpp" namespace neb { -CmdSetServerConf::CmdSetServerConf(int32 iCmd) +CmdSetNodeConf::CmdSetNodeConf(int32 iCmd) : Cmd(iCmd) { } -CmdSetServerConf::~CmdSetServerConf() +CmdSetNodeConf::~CmdSetNodeConf() { } -bool CmdSetServerConf::AnyMessage( +bool CmdSetNodeConf::AnyMessage( std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody) diff --git a/src/actor/cmd/sys_cmd/CmdSetServerConf.hpp b/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp similarity index 61% rename from src/actor/cmd/sys_cmd/CmdSetServerConf.hpp rename to src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp index d2d1707b..c063db7b 100644 --- a/src/actor/cmd/sys_cmd/CmdSetServerConf.hpp +++ b/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp @@ -1,14 +1,14 @@ /******************************************************************************* * Project: Nebula - * @file CmdSetServerConf.hpp + * @file CmdSetNodeConf.hpp * @brief * @author Bwar * @date: 2019年3月24日 * @note * Modify history: ******************************************************************************/ -#ifndef SRC_ACTOR_CMD_SYS_CMD_CMDSETSERVERCONF_HPP_ -#define SRC_ACTOR_CMD_SYS_CMD_CMDSETSERVERCONF_HPP_ +#ifndef SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECONF_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECONF_HPP_ #include "actor/cmd/Cmd.hpp" #include "labor/WorkerFriend.hpp" @@ -16,11 +16,11 @@ namespace neb { -class CmdSetServerConf: public Cmd, public DynamicCreator, public WorkerFriend +class CmdSetNodeConf: public Cmd, public DynamicCreator, public WorkerFriend { public: - CmdSetServerConf(int32 iCmd); - virtual ~CmdSetServerConf(); + CmdSetNodeConf(int32 iCmd); + virtual ~CmdSetNodeConf(); virtual bool AnyMessage( std::shared_ptr pChannel, const MsgHead& oInMsgHead, @@ -29,4 +29,4 @@ class CmdSetServerConf: public Cmd, public DynamicCreator pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody) diff --git a/src/actor/cmd/sys_cmd/CmdSetServerCustomConf.hpp b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp similarity index 58% rename from src/actor/cmd/sys_cmd/CmdSetServerCustomConf.hpp rename to src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp index 9b33e342..2fd15ca0 100644 --- a/src/actor/cmd/sys_cmd/CmdSetServerCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp @@ -1,14 +1,14 @@ /******************************************************************************* * Project: Nebula - * @file CmdSetServerCustomConf.hpp + * @file CmdSetNodeCustomConf.hpp * @brief * @author Bwar * @date: 2019年3月28日 * @note * Modify history: ******************************************************************************/ -#ifndef SRC_ACTOR_CMD_SYS_CMD_CMDSETSERVERCUSTOMCONF_HPP_ -#define SRC_ACTOR_CMD_SYS_CMD_CMDSETSERVERCUSTOMCONF_HPP_ +#ifndef SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECUSTOMCONF_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECUSTOMCONF_HPP_ #include "actor/cmd/Cmd.hpp" #include "labor/WorkerFriend.hpp" @@ -16,11 +16,11 @@ namespace neb { -class CmdSetServerCustomConf: public Cmd, public DynamicCreator, public WorkerFriend +class CmdSetNodeCustomConf: public Cmd, public DynamicCreator, public WorkerFriend { public: - CmdSetServerCustomConf(int32 iCmd); - virtual ~CmdSetServerCustomConf(); + CmdSetNodeCustomConf(int32 iCmd); + virtual ~CmdSetNodeCustomConf(); virtual bool AnyMessage( std::shared_ptr pChannel, const MsgHead& oInMsgHead, @@ -29,4 +29,4 @@ class CmdSetServerCustomConf: public Cmd, public DynamicCreator pChannel, const MsgHea OnNodeNotify(oInMsgBody, oOutMsgBody); break; case CMD_REQ_SET_SERVER_CONFIG: - OnSetServerConf(oInMsgBody, oOutMsgBody); + OnSetNodeConf(oInMsgBody, oOutMsgBody); break; case CMD_REQ_GET_SERVER_CONFIG: - OnGetServerConf(oInMsgBody, oOutMsgBody); + OnGetNodeConf(oInMsgBody, oOutMsgBody); break; case CMD_REQ_SET_SERVER_CUSTOM_CONFIG: - OnSetServerCustomConf(oInMsgBody, oOutMsgBody); + OnSetNodeCustomConf(oInMsgBody, oOutMsgBody); break; case CMD_REQ_GET_SERVER_CUSTOM_CONFIG: - OnGetServerCustomConf(oInMsgBody, oOutMsgBody); + OnGetNodeCustomConf(oInMsgBody, oOutMsgBody); break; case CMD_REQ_SET_CUSTOM_CONFIG: OnSetCustomConf(oInMsgBody, oOutMsgBody); @@ -2131,7 +2131,7 @@ bool Manager::OnNodeNotify(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) return(true); } -bool Manager::OnSetServerConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +bool Manager::OnSetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) { ConfigInfo oConfigInfo; if (oConfigInfo.ParseFromString(oInMsgBody.data())) @@ -2176,7 +2176,7 @@ bool Manager::OnSetServerConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) } } -bool Manager::OnGetServerConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +bool Manager::OnGetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) { ConfigInfo oConfigInfo; oConfigInfo.set_file_name(m_stManagerInfo.strConfFile); @@ -2187,7 +2187,7 @@ bool Manager::OnGetServerConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) return(true); } -bool Manager::OnSetServerCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +bool Manager::OnSetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) { ConfigInfo oConfigInfo; if (oConfigInfo.ParseFromString(oInMsgBody.data())) @@ -2224,7 +2224,7 @@ bool Manager::OnSetServerCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgB } } -bool Manager::OnGetServerCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +bool Manager::OnGetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) { ConfigInfo oConfigInfo; oConfigInfo.set_file_name(m_stManagerInfo.strConfFile); diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index b4788066..8e93ff15 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -290,10 +290,10 @@ class Manager: public Labor bool OnBeat(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); bool OnTellWorker(std::shared_ptr pChannel, const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); bool OnNodeNotify(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnSetServerConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnGetServerConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnSetServerCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnGetServerCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnSetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnGetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnSetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool OnGetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); bool OnSetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); bool OnGetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); bool OnReloadCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 1ad78e64..ae499019 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -864,8 +864,8 @@ void WorkerImpl::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdUpdateNodeId", (int)CMD_REQ_REFRESH_NODE_ID); MakeSharedCmd(nullptr, "neb::CmdNodeNotice", (int)CMD_REQ_NODE_NOTICE); MakeSharedCmd(nullptr, "neb::CmdBeat", (int)CMD_REQ_BEAT); - MakeSharedCmd(nullptr, "neb::CmdSetServerConf", (int)CMD_REQ_SET_SERVER_CONFIG); - MakeSharedCmd(nullptr, "neb::CmdSetServerCustomConf", (int)CMD_REQ_SET_SERVER_CUSTOM_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdSetNodeConf", (int)CMD_REQ_SET_SERVER_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdSetNodeCustomConf", (int)CMD_REQ_SET_SERVER_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdReloadCustomConf", (int)CMD_REQ_RELOAD_CUSTOM_CONFIG); #if __cplusplus >= 201401L m_pSessionNode = std::make_unique(); From 2829220789f6f1fe4feee56bfc46532ad00bcd33 Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 5 Apr 2019 17:48:44 +0800 Subject: [PATCH 015/176] add configuration management --- src/Error.hpp | 1 + src/actor/cmd/CW.hpp | 16 ++++++++-------- src/labor/Manager.cpp | 12 ++++++------ src/labor/WorkerImpl.cpp | 4 ++-- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/Error.hpp b/src/Error.hpp index f2dbe86e..6d3463b0 100644 --- a/src/Error.hpp +++ b/src/Error.hpp @@ -42,6 +42,7 @@ enum E_ERROR_NO ERR_SSL_HANDSHAKE = 10018, ///< 建立SSL连接错误 ERR_SSL_SHUTDOWN = 10019, ///< 关闭SSL连接错误 ERR_FILE_NOT_EXIST = 10020, ///< 文件不存在 + ERR_CONNECTION = 10021, ///< 连接错误 /* 存储代理错误码段 11000~11999 */ ERR_INCOMPLET_DATAPROXY_DATA = 11001, ///< DataProxy请求数据包不完整 diff --git a/src/actor/cmd/CW.hpp b/src/actor/cmd/CW.hpp index 96f56f40..1063898e 100644 --- a/src/actor/cmd/CW.hpp +++ b/src/actor/cmd/CW.hpp @@ -54,14 +54,14 @@ enum E_CMD CMD_REQ_LEADER_ELECTION = 113, ///< 分布式leader选举请求 CMD_RSP_LEADER_ELECTION = 114, ///< 分布式leader选举响应(通过心跳结合优先权选举,无须响应) - CMD_REQ_SET_SERVER_CONFIG = 201, ///< 更新框架配置文件请求 - CMD_RSP_SET_SERVER_CONFIG = 202, ///< 更新框架配置文件应答 - CMD_REQ_GET_SERVER_CONFIG = 203, ///< 获取框架配置文件请求 - CMD_RSP_GET_SERVER_CONFIG = 204, ///< 获取框架配置文件应答 - CMD_REQ_SET_SERVER_CUSTOM_CONFIG = 205, ///< 更新框架配置文件中自定义部分配置请求 - CMD_RSP_SET_SERVER_CUSTOM_CONFIG = 206, ///< 更新框架配置文件中自定义部分配置应答 - CMD_REQ_GET_SERVER_CUSTOM_CONFIG = 207, ///< 获取框架配置文件中自定义部分配置请求 - CMD_RSP_GET_SERVER_CUSTOM_CONFIG = 208, ///< 获取框架配置文件中自定义部分配置应答 + CMD_REQ_SET_NODE_CONFIG = 201, ///< 更新框架配置文件请求 + CMD_RSP_SET_NODE_CONFIG = 202, ///< 更新框架配置文件应答 + CMD_REQ_GET_NODE_CONFIG = 203, ///< 获取框架配置文件请求 + CMD_RSP_GET_NODE_CONFIG = 204, ///< 获取框架配置文件应答 + CMD_REQ_SET_NODE_CUSTOM_CONFIG = 205, ///< 更新框架配置文件中自定义部分配置请求 + CMD_RSP_SET_NODE_CUSTOM_CONFIG = 206, ///< 更新框架配置文件中自定义部分配置应答 + CMD_REQ_GET_NODE_CUSTOM_CONFIG = 207, ///< 获取框架配置文件中自定义部分配置请求 + CMD_RSP_GET_NODE_CUSTOM_CONFIG = 208, ///< 获取框架配置文件中自定义部分配置应答 CMD_REQ_SET_CUSTOM_CONFIG = 209, ///< 更新自定义配置文件请求 CMD_RSP_SET_CUSTOM_CONFIG = 210, ///< 更新自定义配置文件响应 CMD_REQ_GET_CUSTOM_CONFIG = 211, ///< 获取自定义配置文件请求 diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index f8a97bb6..8c0415e0 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -1908,16 +1908,16 @@ bool Manager::OnBeaconData(std::shared_ptr pChannel, const MsgHea case CMD_REQ_NODE_NOTICE: OnNodeNotify(oInMsgBody, oOutMsgBody); break; - case CMD_REQ_SET_SERVER_CONFIG: + case CMD_REQ_SET_NODE_CONFIG: OnSetNodeConf(oInMsgBody, oOutMsgBody); break; - case CMD_REQ_GET_SERVER_CONFIG: + case CMD_REQ_GET_NODE_CONFIG: OnGetNodeConf(oInMsgBody, oOutMsgBody); break; - case CMD_REQ_SET_SERVER_CUSTOM_CONFIG: + case CMD_REQ_SET_NODE_CUSTOM_CONFIG: OnSetNodeCustomConf(oInMsgBody, oOutMsgBody); break; - case CMD_REQ_GET_SERVER_CUSTOM_CONFIG: + case CMD_REQ_GET_NODE_CUSTOM_CONFIG: OnGetNodeCustomConf(oInMsgBody, oOutMsgBody); break; case CMD_REQ_SET_CUSTOM_CONFIG: @@ -2156,7 +2156,7 @@ bool Manager::OnSetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) fout.close(); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); - SendToWorker(CMD_REQ_SET_SERVER_CONFIG, GetSequence(), oInMsgBody); + SendToWorker(CMD_REQ_SET_NODE_CONFIG, GetSequence(), oInMsgBody); return(true); } return(false); @@ -2204,7 +2204,7 @@ bool Manager::OnSetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBod fout.close(); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); - SendToWorker(CMD_REQ_SET_SERVER_CUSTOM_CONFIG, GetSequence(), oInMsgBody); + SendToWorker(CMD_REQ_SET_NODE_CUSTOM_CONFIG, GetSequence(), oInMsgBody); return(true); } return(false); diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index ae499019..5b08d253 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -864,8 +864,8 @@ void WorkerImpl::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdUpdateNodeId", (int)CMD_REQ_REFRESH_NODE_ID); MakeSharedCmd(nullptr, "neb::CmdNodeNotice", (int)CMD_REQ_NODE_NOTICE); MakeSharedCmd(nullptr, "neb::CmdBeat", (int)CMD_REQ_BEAT); - MakeSharedCmd(nullptr, "neb::CmdSetNodeConf", (int)CMD_REQ_SET_SERVER_CONFIG); - MakeSharedCmd(nullptr, "neb::CmdSetNodeCustomConf", (int)CMD_REQ_SET_SERVER_CUSTOM_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdSetNodeConf", (int)CMD_REQ_SET_NODE_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdSetNodeCustomConf", (int)CMD_REQ_SET_NODE_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdReloadCustomConf", (int)CMD_REQ_RELOAD_CUSTOM_CONFIG); #if __cplusplus >= 201401L m_pSessionNode = std::make_unique(); From cfedf9c01e17505fb80b52b6612119535f750b9d Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 9 Apr 2019 17:13:23 +0800 Subject: [PATCH 016/176] configuration management testing --- src/labor/Manager.cpp | 16 +++------------- src/labor/Manager.hpp | 1 - 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 8c0415e0..ecd78bc4 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -1926,9 +1926,6 @@ bool Manager::OnBeaconData(std::shared_ptr pChannel, const MsgHea case CMD_REQ_GET_CUSTOM_CONFIG: OnGetCustomConf(oInMsgBody, oOutMsgBody); break; - case CMD_REQ_RELOAD_CUSTOM_CONFIG: - OnReloadCustomConf(oInMsgBody, oOutMsgBody); - break; default: oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); oOutMsgBody.mutable_rsp_result()->set_msg("unknow cmd!"); @@ -2180,7 +2177,7 @@ bool Manager::OnGetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) { ConfigInfo oConfigInfo; oConfigInfo.set_file_name(m_stManagerInfo.strConfFile); - oConfigInfo.set_file_content(m_oCurrentConf.ToString()); + oConfigInfo.set_file_content(m_oCurrentConf.ToFormattedString()); oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); @@ -2228,7 +2225,7 @@ bool Manager::OnGetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBod { ConfigInfo oConfigInfo; oConfigInfo.set_file_name(m_stManagerInfo.strConfFile); - oConfigInfo.set_file_content(m_oCurrentConf["custom"].ToString()); + oConfigInfo.set_file_content(m_oCurrentConf["custom"].ToFormattedString()); oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); @@ -2261,6 +2258,7 @@ bool Manager::OnSetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); SendToWorker(CMD_REQ_SET_CUSTOM_CONFIG, GetSequence(), oInMsgBody); + SendToWorker(CMD_REQ_RELOAD_CUSTOM_CONFIG, GetSequence(), oInMsgBody); return(true); } else @@ -2323,12 +2321,4 @@ bool Manager::OnGetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) } } -bool Manager::OnReloadCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) -{ - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("success"); - SendToWorker(CMD_REQ_RELOAD_CUSTOM_CONFIG, GetSequence(), oInMsgBody); - return(true); -} - } /* namespace neb */ diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index 8e93ff15..29ccb317 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -296,7 +296,6 @@ class Manager: public Labor bool OnGetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); bool OnSetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); bool OnGetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnReloadCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); private: mutable uint32 m_uiSequence; From 303d0087d30cb2577227563b36016f6022e6620d Mon Sep 17 00:00:00 2001 From: Bwar Date: Thu, 11 Apr 2019 22:39:26 +0800 Subject: [PATCH 017/176] configuration management test passing --- src/util/encrypt/hconv.c | 2 +- src/util/encrypt/hconv.h | 2 +- src/util/encrypt/rc5.c | 2 +- src/util/encrypt/rc5.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/encrypt/hconv.c b/src/util/encrypt/hconv.c index 082a8b51..c2e34321 100644 --- a/src/util/encrypt/hconv.c +++ b/src/util/encrypt/hconv.c @@ -1,5 +1,5 @@ /******************************************************************************* -* Project: loss +* Project: nebula * @file hconv.c * @brief * @author lbh diff --git a/src/util/encrypt/hconv.h b/src/util/encrypt/hconv.h index ab07929b..58dc8f77 100644 --- a/src/util/encrypt/hconv.h +++ b/src/util/encrypt/hconv.h @@ -1,5 +1,5 @@ /******************************************************************************* -* Project: loss +* Project: nebula * @file hconv.h * @brief * @author lbh diff --git a/src/util/encrypt/rc5.c b/src/util/encrypt/rc5.c index 853300e2..4ebc91cd 100644 --- a/src/util/encrypt/rc5.c +++ b/src/util/encrypt/rc5.c @@ -1,5 +1,5 @@ /******************************************************************************* - * Project: loss + * Project: nebula * @file rc5.cpp * @brief * @author lbh diff --git a/src/util/encrypt/rc5.h b/src/util/encrypt/rc5.h index ce1de368..28e70a4c 100644 --- a/src/util/encrypt/rc5.h +++ b/src/util/encrypt/rc5.h @@ -1,5 +1,5 @@ /******************************************************************************* -* Project: loss +* Project: nebula * @file rc5.h * @brief rc5 Operations, Terminology and Notation * @author lbh From c61ff9052cc519ac887f128389868d80af54651e Mon Sep 17 00:00:00 2001 From: Bwar Date: Thu, 11 Apr 2019 22:48:18 +0800 Subject: [PATCH 018/176] add version decription to readme. --- README.md | 2 ++ README_cn.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 775eee39..288f4bdd 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,8 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v0.7 + - add configuration management(check [Nebcli](https://github.com/Bwar/Nebcli) for detail). #### v0.6 - NebulaBeacon adds node status information query, registration center leader-fllower election. - NebulaInterface adds hello demo. diff --git a/README_cn.md b/README_cn.md index a2ac3a71..97c60600 100644 --- a/README_cn.md +++ b/README_cn.md @@ -116,6 +116,8 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) ## 版本历史 +#### v0.7 + - 添加配置管理,NebulaBeacon为配置中心,使用说明见命令行管理工具[Nebcli](https://github.com/Bwar/Nebcli)的get和set命令。 #### v0.6 - NebulaBeacon增加节点状态信息查询,注册中心主从高可用选举 - NebulaInterface提供HelloWorld示例。 From 300dd71243ea4db87de08d790d628675d18f4b36 Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 8 May 2019 16:58:42 +0800 Subject: [PATCH 019/176] add shared resource session assembly line --- src/actor/Actor.cpp | 5 ++++ src/actor/Actor.hpp | 5 ++-- src/actor/session/Session.cpp | 48 +++++++++++++++++++++++++++++++++-- src/actor/session/Session.hpp | 27 ++++++++++++++++++++ src/actor/step/PbStep.hpp | 4 +-- src/labor/Worker.cpp | 5 ++++ src/labor/Worker.hpp | 1 + src/labor/WorkerImpl.cpp | 31 ++++++++++++++++++++++ src/labor/WorkerImpl.hpp | 8 ++++-- 9 files changed, 126 insertions(+), 8 deletions(-) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index ebac6907..288bc1f3 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -106,6 +106,11 @@ void Actor::SetContext(std::shared_ptr pContext) m_pContext = pContext; } +void Actor::AddAssemblyLine(std::shared_ptr pSession) +{ + m_pWorker->AddAssemblyLine(pSession); +} + bool Actor::SendTo(std::shared_ptr pChannel) { return(m_pWorker->SendTo(pChannel)); diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 304e78d2..4daf04ef 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -88,6 +88,7 @@ class Actor: public std::enable_shared_from_this bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); std::shared_ptr GetContext(); void SetContext(std::shared_ptr pContext); + void AddAssemblyLine(std::shared_ptr pSession); protected: /** @@ -123,8 +124,8 @@ class Actor: public std::enable_shared_from_this /** * @brief 发送数据 - * @note 指定连接标识符将数据发送。此函数先查找与strIdentify匹配的stMsgShell,如果找到就调用 - * SendTo(const tagMsgShell& stMsgShell, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) + * @note 指定连接标识符将数据发送。此函数先查找与strIdentify匹配的Channel,如果找到就调用 + * SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) * 发送,如果未找到则调用SendToWithAutoConnect(const std::string& strIdentify, * uint32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody)连接后再发送。 * @param strIdentify 连接标识符(IP:port.worker_index, e.g 192.168.11.12:3001.1) diff --git a/src/actor/session/Session.cpp b/src/actor/session/Session.cpp index 5bed5c28..6c810aa5 100644 --- a/src/actor/session/Session.cpp +++ b/src/actor/session/Session.cpp @@ -9,17 +9,20 @@ ******************************************************************************/ #include "Session.hpp" +#include "actor/step/Step.hpp" namespace neb { Session::Session(uint32 ulSessionId, ev_tstamp dSessionTimeout) - : SessionModel(ulSessionId, dSessionTimeout) + : SessionModel(ulSessionId, dSessionTimeout), + m_bDataReady(false), m_bDataLoading(false) { } Session::Session(const std::string& strSessionId, ev_tstamp dSessionTimeout) - : SessionModel(strSessionId, dSessionTimeout) + : SessionModel(strSessionId, dSessionTimeout), + m_bDataReady(false), m_bDataLoading(false) { } @@ -27,4 +30,45 @@ Session::~Session() { } +bool Session::IsReady(Step* pStep) +{ + if (!m_bDataReady) + { + m_vecWaitingStep.push(pStep->GetSequence()); + } + return(m_bDataReady); +} + +void Session::SetReady() +{ + m_bDataReady = true; + m_bDataLoading = false; + if (m_vecWaitingStep.size() > 0) + { + AddAssemblyLine(std::dynamic_pointer_cast(shared_from_this())); + } +} + +bool Session::IsLoading() +{ + return(m_bDataLoading); +} + +void Session::SetLoading() +{ + m_bDataLoading = true; + m_bDataReady = false; +} + +uint32 Session::PopWaitingStep() +{ + uint32 uiStepSeq = 0; + if (m_vecWaitingStep.size() > 0) + { + uiStepSeq = m_vecWaitingStep.front(); + m_vecWaitingStep.pop(); + } + return(uiStepSeq); +} + } /* namespace neb */ diff --git a/src/actor/session/Session.hpp b/src/actor/session/Session.hpp index 08e5265f..53e3169f 100644 --- a/src/actor/session/Session.hpp +++ b/src/actor/session/Session.hpp @@ -10,6 +10,7 @@ #ifndef SRC_ACTOR_SESSION_SESSION_HPP_ #define SRC_ACTOR_SESSION_SESSION_HPP_ +#include #include "labor/Worker.hpp" #include "actor/DynamicCreator.hpp" #include "SessionModel.hpp" @@ -31,6 +32,26 @@ class Session: public SessionModel */ virtual E_CMD_STATUS Timeout() = 0; + /** + * @brief 检查Session内数据是否已加载 + * @note 为满足数据共享并确保同一数据在同一个Worker内只需从 + * 外部存储中加载一次,提供了IsReady(),SetReady(),IsLoading(), + * SetLoading()四个方法。如果一个或若干个Step获取到一个已创建好 + * 的Session,则需先调用IsReady()判断数据是否就绪,若就绪则直接 + * 从Session中读取,若未就绪则调用IsLoading()判断数据是否正在加 + * 载,若正在加载则直接return(CMD_STATUS_RUNNING)(框架会在数据 + * 加载完毕后调用该Step的Callback),否则加载数据并且SetLoading(), + * 数据加载完毕后SetReady()。 + * @param pStep 调用IsReady()方法的调用者Step指针,用于记录 + * 哪些Step依赖于Session的数据,在数据就绪时由框架主动调用 + * 依赖这些数据的Step回调而不需要等到超时才回调。 + */ + bool IsReady(Step* pStep); + + void SetReady(); + bool IsLoading(); + void SetLoading(); + protected: template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); @@ -38,8 +59,14 @@ class Session: public SessionModel template std::shared_ptr MakeSharedCmd(const std::string& strCmdName, Targs... args); template std::shared_ptr MakeSharedModule(const std::string& strModuleName, Targs... args); +private: + uint32 PopWaitingStep(); + private: friend class WorkerImpl; + bool m_bDataReady; + bool m_bDataLoading; + std::queue m_vecWaitingStep; }; template diff --git a/src/actor/step/PbStep.hpp b/src/actor/step/PbStep.hpp index 399bb52e..87f81a0e 100644 --- a/src/actor/step/PbStep.hpp +++ b/src/actor/step/PbStep.hpp @@ -28,8 +28,8 @@ class PbStep: public Step /** * @brief 步骤回调函数 * @note 满足某个条件,比如监控某个文件描述符fd的EPOLLIN事件和EPOLLERR事件,当这个fd的 - * 这两类事件中的任意一类到达时则会调用Callback()。具体使用到哪几个参数与业务逻辑有关,前三个 - * 参数的使用概率高。 + * 这两类事件中的任意一类到达时则会调用Callback()。具体使用到哪几个参数与业务逻辑有关,第四个 + * 参数一般不需要用到。 * @param pChannel 消息来源通信通道 * @param oMsgHead 消息头 * @param oMsgBody 消息体 diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 31acb6b2..57739607 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -54,6 +54,11 @@ bool Worker::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg return(m_pImpl->ExecStep(uiStepSeq, iErrno, strErrMsg, data)); } +void Worker::AddAssemblyLine(std::shared_ptr pSession) +{ + m_pImpl->AddAssemblyLine(pSession); +} + uint32 Worker::GetNodeId() const { return(m_pImpl->GetWorkerInfo().uiNodeId); diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 708d9832..8d7d66c9 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -48,6 +48,7 @@ class Worker: public Labor virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); + virtual void AddAssemblyLine(std::shared_ptr pSession); // 获取worker信息相关方法 virtual uint32 GetNodeId() const; diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 5b08d253..d52a1a6d 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -1876,6 +1876,11 @@ bool WorkerImpl::AddIoTimeout(std::shared_ptr pChannel, ev_tstamp } } +void WorkerImpl::AddAssemblyLine(std::shared_ptr pSession) +{ + m_setAssemblyLine.insert(pSession); +} + bool WorkerImpl::SetWorkerConf(const CJsonObject& oJsonConf) { m_oWorkerConf = oJsonConf; @@ -2236,6 +2241,7 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const MsgHead& Remove(step_iter->second); } } + ExecAssemblyLine(pChannel, oMsgHead, oMsgBody); } else { @@ -2309,4 +2315,29 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const HttpMsg& return(true); } +void WorkerImpl::ExecAssemblyLine(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) +{ + for (auto session_iter = m_setAssemblyLine.begin(); session_iter != m_setAssemblyLine.end(); ++session_iter) + { + uint32 uiStepSeq = 0; + do + { + uiStepSeq = (*session_iter)->PopWaitingStep(); + auto step_iter = m_mapCallbackStep.find(uiStepSeq); + if (step_iter != m_mapCallbackStep.end()) + { + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(ev_now(m_loop)); + eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); + if (CMD_STATUS_RUNNING != eResult) + { + Remove(step_iter->second); + } + } + } + while(uiStepSeq > 0); + } + m_setAssemblyLine.clear(); +} + } /* namespace neb */ diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index b423ff3b..142ed846 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -254,6 +254,7 @@ class WorkerImpl virtual void SetClientData(std::shared_ptr pChannel, const std::string& strClientData); bool AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout = 1.0); + void AddAssemblyLine(std::shared_ptr pSession); bool SetWorkerConf(const CJsonObject& oJsonConf); const CJsonObject& GetWorkerConf() const @@ -292,7 +293,7 @@ class WorkerImpl * false则应退出解析数据包循环体。处理过程或处理完成后,如需回复对端,则直接使用pSendBuff * 回复数据即可。 * - * @param[in] stMsgShell 数据包来源消息通道 + * @param[in] pChannel 数据包来源消息通道 * @param[in] oMsgHead 接收的数据包头 * @param[in] oMsgBody 接收的数据包体 * @return 是否正常处理 @@ -301,12 +302,14 @@ class WorkerImpl /** * @brief 收到完整的hhtp包后处理 - * @param stMsgShell 数据包来源消息通道 + * @param pChannel 数据包来源消息通道 * @param oHttpMsg 接收的HTTP包 * @return 是否正常处理 */ bool Handle(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + void ExecAssemblyLine(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); + void BootLoadCmd(CJsonObject& oCmdConf); void DynamicLoadCmd(CJsonObject& oCmdConf); tagSo* LoadSo(const std::string& strSoPath, int iVersion); @@ -335,6 +338,7 @@ class WorkerImpl // Step and Session std::unordered_map > m_mapCallbackStep; std::unordered_map > m_mapCallbackSession; + std::unordered_set > m_setAssemblyLine; ///< 资源就绪后执行队列 // Channel std::unordered_map > m_mapSocketChannel; From be14446f03c727349681cde15d1e60de0ae9fcc9 Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 15 May 2019 21:54:23 +0800 Subject: [PATCH 020/176] modify proto comments --- proto/msg.proto | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proto/msg.proto b/proto/msg.proto index c2764135..69b60765 100644 --- a/proto/msg.proto +++ b/proto/msg.proto @@ -17,7 +17,7 @@ message MsgHead /** * @brief 消息体 - * @note 消息体主体是content,所有业务逻辑内容均放在content里。req_target是请求目标,用于 + * @note 消息体主体是data,所有业务逻辑内容均放在data里。req_target是请求目标,用于 * 服务端接入路由,请求包必须填充。rsp_result是响应结果,响应包必须填充。 */ message MsgBody @@ -30,7 +30,7 @@ message MsgBody bytes data = 3; ///< 消息体主体 bytes add_on = 4; ///< 服务端接入层附加在请求包的数据(客户端无须理会) string trace_id = 5; ///< for log trace - // google.protobuf.Any content = 3; + // google.protobuf.Any data = 3; // google.protobuf.Any add_on = 4; message Request @@ -42,6 +42,7 @@ message MsgBody message Response { int32 code = 1; ///< 错误码 - bytes msg = 2; ///< 错误信息 + bytes msg = 2; ///< 错误信息 } } + From c5efee29e5b0ecb92f85d2c0ea1cf038077d62d8 Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 27 May 2019 23:19:52 +0800 Subject: [PATCH 021/176] =?UTF-8?q?gcc-4.8.5=20compile=20passing:=20cannot?= =?UTF-8?q?=20pass=20objects=20of=20non-trivially-copyable=20type=20throug?= =?UTF-8?q?h=20'=E2=80=A6'=20fixed,=20'put=5Ftime'=20is=20not=20a=20member?= =?UTF-8?q?=20of=20'std'=20(bug=20of=20gcc-5=20below)=20fixed.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- README_cn.md | 4 ++-- src/labor/WorkerImpl.cpp | 5 ++--- src/logger/FileLogger.cpp | 30 ++++++++++++++++++++++++++---- src/logger/FileLogger.hpp | 3 +++ 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 288f4bdd..b40b000b 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,9 @@ MIT License ## Getting Start Nebula was developed with C++11/C++14 standard, and the compiler must fully support C++11(some C++14 features are replaced by C++11 standard when encountering a lower version of the compiler). - Nebula was builded passing with gcc6.4 on centos6.5(upgrade binutils to 2.22 or later) and centos7.4. + Nebula was builded passing with gcc6 on centos6.5(upgrade binutils to 2.22 or later) and centos7.4. We provides [NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap), which allows developers to build and deploy Nebula quickly. Nebula will be a framework that be widely used. A distributed solution based on NebulaBootstrap will make it easy to develop micro-service applications in C++. - You must ensure that your system is installed with a fully C++11 compiler before you build, and all dependencies will be automatically resolved in the following build steps. + You must ensure that your system is installed with a fully C++11 compiler(gcc4.8.2 or above) before you build, and all dependencies will be automatically resolved in the following build steps. build step: 1. wget https://github.com/Bwar/NebulaBootstrap/archive/master.zip @@ -103,7 +103,7 @@ A simple testing can be start with a NebulaInterface only, and also can be start * [protobuf](https://github.com/google/protobuf) * [libev](http://software.schmorp.de/pkg/libev.html) or [libev](https://github.com/kindy/libev) * [hiredis](https://github.com/redis/hiredis) - * [crypto++](http://www.cryptopp.com) + * [crypto++](https://github.com/weidai11/cryptopp) * [http_parse](https://github.com/nodejs/http-parser) integrate into Nebula/src/util/http * [CJsonObject](https://github.com/Bwar/CJsonObject) integrate into Nebula/src/util/json diff --git a/README_cn.md b/README_cn.md index 97c60600..4212d09c 100644 --- a/README_cn.md +++ b/README_cn.md @@ -32,7 +32,7 @@ Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架S ## 开始 -  Nebula以C++11/C++14标准开发,编译器必须完全支持C++11(部分C++14的特性在遇到较低版本的编译器时有预编译开关控制使用C++11标准替代),建议使用5以上gcc版本,推荐使用gcc6。Nebula目前只有Linux版本,暂无支持Linux之外的其他类UNIX系统的时间表。 +  Nebula以C++11/C++14标准开发,编译器必须完全支持C++11(部分C++14的特性在遇到较低版本的编译器时有预编译开关控制使用C++11标准替代),最低要求gcc4.8.2,建议使用5以上gcc版本,推荐使用gcc6。Nebula目前只有Linux版本,暂无支持Linux之外的其他类UNIX系统的时间表。   Nebula在centos6.5(需升级binutils到2.22之后版本)和centos7.4上用gcc6.4编译和测试通过。同时Nebula也在[Travis CI](https://travis-ci.org/Bwar/Nebula)持续集成构建项目,构建结果可以直接通过项目首页的[![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula)跳转过去查看。Travis CI的系统是Ubuntu,编译器是gcc6。 @@ -92,7 +92,7 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) * [protobuf](https://github.com/google/protobuf) * [libev](http://software.schmorp.de/pkg/libev.html) 或 [libev](https://github.com/kindy/libev) * [hiredis](https://github.com/redis/hiredis) - * [crypto++](http://www.cryptopp.com) + * [crypto++](https://github.com/weidai11/cryptopp) * [http_parse](https://github.com/nodejs/http-parser) 已集成到 Nebula/src/util/http * [CJsonObject](https://github.com/Bwar/CJsonObject) 已集成到 Nebula/src/util/json diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index d52a1a6d..942dfb76 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -545,7 +545,6 @@ bool WorkerImpl::OnStepTimeout(std::shared_ptr pStep) bool WorkerImpl::OnSessionTimeout(std::shared_ptr pSession) { - LOG4_TRACE("CHECK pSession = 0x%x", pSession); ev_timer* watcher = pSession->MutableTimerWatcher(); LOG4_TRACE("CHECK watchar = 0x%x", watcher); ev_tstamp after = pSession->GetActiveTime() - ev_now(m_loop) + pSession->GetTimeout(); @@ -2232,9 +2231,9 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const MsgHead& { E_CMD_STATUS eResult; step_iter->second->SetActiveTime(ev_now(m_loop)); - LOG4_TRACE("cmd %u, seq %u, step_seq %u, step addr 0x%x, active_time %lf", + LOG4_TRACE("cmd %u, seq %u, step_seq %u, active_time %lf", oMsgHead.cmd(), oMsgHead.seq(), step_iter->second->GetSequence(), - step_iter->second, step_iter->second->GetActiveTime()); + step_iter->second->GetActiveTime()); eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); if (CMD_STATUS_RUNNING != eResult) { diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp index aad054bf..2da728cc 100644 --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -30,6 +30,9 @@ FileLogger::FileLogger(const std::string& strLogFile, int iLogLev, : m_iLogLevel(iLogLev), m_uiMaxFileSize(uiMaxFileSize), m_uiMaxRollFileIndex(uiMaxRollFileIndex), m_strLogFileBase(strLogFile) { +#if __GNUC__ < 5 + m_szTime = (char*)malloc(20); +#endif m_fp = NULL; OpenLogFile(strLogFile); WriteLog(Logger::NOTICE, __FILE__, __LINE__, __FUNCTION__, "new log instance."); @@ -37,6 +40,9 @@ FileLogger::FileLogger(const std::string& strLogFile, int iLogLev, int FileLogger::OpenLogFile(const std::string strLogFile) { +#if __GNUC__ < 5 + free(m_szTime); +#endif m_fp = fopen(strLogFile.c_str(), "a+" ); if(NULL == m_fp) { @@ -164,9 +170,18 @@ int FileLogger::Vappend(int iLev, const char* szFileName, unsigned int uiFileLin auto duration_in_ms = std::chrono::duration_cast(time_now.time_since_epoch()); auto t = std::chrono::system_clock::to_time_t(time_now); std::ostringstream oss; +// There is a bug: The std::get_time and std::put_time manipulators are still missing in gcc 4.9. +// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54354 +#if __GNUC__ >= 5 oss << "[" << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S") << "," - << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" - << szFileName << ":" << uiFileLine << "][" << szFunction << "] "; + << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" + << szFileName << ":" << uiFileLine << "][" << szFunction << "] "; +#else + strftime(m_szTime, 20, "%Y-%m-%d %H:%M:%S", std::localtime(&t)); + oss << "[" << m_szTime << "," + << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" + << szFileName << ":" << uiFileLine << "][" << szFunction << "] "; +#endif fprintf(m_fp, oss.str().c_str()); //fprintf(m_fp, "[%s] [%s,%03d] ", std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S").c_str(), duration_in_ms.count() % 1000, Logger::LogLevMsg[iLev].c_str()); vfprintf(m_fp, szLogStr, ap); @@ -202,9 +217,16 @@ int FileLogger::Vappend(const std::string& strTraceId, int iLev, const char* szF auto duration_in_ms = std::chrono::duration_cast(time_now.time_since_epoch()); auto t = std::chrono::system_clock::to_time_t(time_now); std::ostringstream oss; +#if __GNUC__ >= 5 oss << "[" << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S") << "," - << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" - << szFileName << ":" << uiFileLine << "][" << szFunction << "][" << strTraceId << "] "; + << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" + << szFileName << ":" << uiFileLine << "][" << szFunction << "][" << strTraceId << "] "; +#else + strftime(m_szTime, 20, "%Y-%m-%d %H:%M:%S", std::localtime(&t)); + oss << "[" << m_szTime << "," + << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" + << szFileName << ":" << uiFileLine << "][" << szFunction << "][" << strTraceId << "] "; +#endif fprintf(m_fp, oss.str().c_str()); //fprintf(m_fp, "[%s] [%s,%03d] ", std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S").c_str(), duration_in_ms.count() % 1000, Logger::LogLevMsg[iLev].c_str()); vfprintf(m_fp, szLogStr, ap); diff --git a/src/logger/FileLogger.hpp b/src/logger/FileLogger.hpp index f2ebaa8a..ce275699 100644 --- a/src/logger/FileLogger.hpp +++ b/src/logger/FileLogger.hpp @@ -59,6 +59,9 @@ class FileLogger: public Logger static FileLogger* m_pInstance; +#if __GNUC__ < 5 + char* m_szTime; +#endif FILE* m_fp; int m_iLogLevel; unsigned int m_uiMaxFileSize; // 日志文件大小 From 52525cf327f67ad3b0774717778ffa7b79d95ea8 Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 28 May 2019 22:53:08 +0800 Subject: [PATCH 022/176] nebula compile with gcc4.8.5 and test passing. --- README.md | 5 ++--- README_cn.md | 4 +--- conf/nebula.json | 4 ++-- src/labor/Manager.cpp | 4 ++-- src/logger/FileLogger.cpp | 3 --- src/logger/FileLogger.hpp | 3 +++ 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b40b000b..2a3df51e 100644 --- a/README.md +++ b/README.md @@ -45,10 +45,9 @@ MIT License ## Getting Start - Nebula was developed with C++11/C++14 standard, and the compiler must fully support C++11(some C++14 features are replaced by C++11 standard when encountering a lower version of the compiler). - Nebula was builded passing with gcc6 on centos6.5(upgrade binutils to 2.22 or later) and centos7.4. + Nebula was developed with C++11/C++14 standard, requires a compiler capable of the C++11-standard and at least gcc4.8(some C++14 features are replaced by C++11 standard when encountering a lower version of the compiler). We provides [NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap), which allows developers to build and deploy Nebula quickly. Nebula will be a framework that be widely used. A distributed solution based on NebulaBootstrap will make it easy to develop micro-service applications in C++. - You must ensure that your system is installed with a fully C++11 compiler(gcc4.8.2 or above) before you build, and all dependencies will be automatically resolved in the following build steps. + All dependencies will be automatically resolved in the following build steps. build step: 1. wget https://github.com/Bwar/NebulaBootstrap/archive/master.zip diff --git a/README_cn.md b/README_cn.md index 4212d09c..739be286 100644 --- a/README_cn.md +++ b/README_cn.md @@ -32,9 +32,7 @@ Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架S ## 开始 -  Nebula以C++11/C++14标准开发,编译器必须完全支持C++11(部分C++14的特性在遇到较低版本的编译器时有预编译开关控制使用C++11标准替代),最低要求gcc4.8.2,建议使用5以上gcc版本,推荐使用gcc6。Nebula目前只有Linux版本,暂无支持Linux之外的其他类UNIX系统的时间表。 - -  Nebula在centos6.5(需升级binutils到2.22之后版本)和centos7.4上用gcc6.4编译和测试通过。同时Nebula也在[Travis CI](https://travis-ci.org/Bwar/Nebula)持续集成构建项目,构建结果可以直接通过项目首页的[![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula)跳转过去查看。Travis CI的系统是Ubuntu,编译器是gcc6。 +  Nebula以C++11/C++14标准开发,编译器必须完全支持C++11(部分C++14的特性在遇到较低版本的编译器时有预编译开关控制使用C++11标准替代),最低要求gcc4.8.2,建议使用5以上gcc版本。Nebula目前只有Linux版本,暂无支持Linux之外的其他类UNIX系统的时间表。   Nebula是个较大型项目(因为要构建一个生产用的分布式集群),有一些外部[依赖](#DependOn),鉴于外部依赖的存在和框架本身较难测试,提供了[NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap),让开发者可以快速部署和体验Nebula。相信部署和体验之后,你会对Nebula产生兴趣,这将会是一个可以广泛应用的框架,基于NebulaBootstrap提供的分布式解决方案可以很方便地用C++开发微服务应用。 diff --git a/conf/nebula.json b/conf/nebula.json index a3f8bdcd..7d7e4e54 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -15,8 +15,8 @@ "port": 9987, "//server_name": "异步事件驱动Server", "server_name": "AsyncServer", - "//process_num": "进程数量", - "process_num": 10, + "//worker_num": "进程数量", + "worker_num": 10, "//worker_capacity": "子进程最大工作负荷", "worker_capacity": 1000000, "//config_path": "配置文件路径(相对路径)", diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index ecd78bc4..04d32411 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -795,7 +795,7 @@ bool Manager::GetConf() m_oCurrentConf.Get("io_timeout", m_stManagerInfo.dIoTimeout); if (m_oLastConf.ToString().length() == 0) { - m_stManagerInfo.uiWorkerNum = strtoul(m_oCurrentConf("process_num").c_str(), NULL, 10); + m_stManagerInfo.uiWorkerNum = strtoul(m_oCurrentConf("worker_num").c_str(), NULL, 10); m_oCurrentConf.Get("node_type", m_stManagerInfo.strNodeType); m_oCurrentConf.Get("host", m_stManagerInfo.strHostForServer); m_oCurrentConf.Get("port", m_stManagerInfo.iPortForServer); @@ -2144,7 +2144,7 @@ bool Manager::OnSetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) oJsonData.Replace("host", m_oCurrentConf("host")); oJsonData.Replace("port", m_oCurrentConf("port")); oJsonData.Replace("server_name", m_oCurrentConf("server_name")); - oJsonData.Replace("process_num", m_oCurrentConf("process_num")); + oJsonData.Replace("worker_num", m_oCurrentConf("worker_num")); std::ofstream fout(m_stManagerInfo.strConfFile.c_str()); if (fout.good()) { diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp index 2da728cc..f27cacae 100644 --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -40,9 +40,6 @@ FileLogger::FileLogger(const std::string& strLogFile, int iLogLev, int FileLogger::OpenLogFile(const std::string strLogFile) { -#if __GNUC__ < 5 - free(m_szTime); -#endif m_fp = fopen(strLogFile.c_str(), "a+" ); if(NULL == m_fp) { diff --git a/src/logger/FileLogger.hpp b/src/logger/FileLogger.hpp index ce275699..8ec363be 100644 --- a/src/logger/FileLogger.hpp +++ b/src/logger/FileLogger.hpp @@ -27,6 +27,9 @@ class FileLogger: public Logger unsigned int uiMaxRollFileIndex = neb::gc_uiMaxRollLogFileIndex); virtual ~FileLogger() { +#if __GNUC__ < 5 + free(m_szTime); +#endif fclose(m_fp); } From fa16c18cfa84da3c4e4f12fdd47d3486fd985411 Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 29 May 2019 22:45:37 +0800 Subject: [PATCH 023/176] 1. add cpu affinity. 2. add actor name. --- conf/nebula.json | 10 ++++++++-- src/actor/Actor.cpp | 5 +++++ src/actor/Actor.hpp | 18 +++++++++++++----- src/channel/SocketChannel.cpp | 2 +- src/labor/WorkerImpl.cpp | 19 ++++++++++++++++++- src/labor/WorkerImpl.hpp | 8 +++++++- src/labor/WorkerImpl.inl | 4 ++++ 7 files changed, 56 insertions(+), 10 deletions(-) diff --git a/conf/nebula.json b/conf/nebula.json index 7d7e4e54..d5582a33 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -17,6 +17,8 @@ "server_name": "AsyncServer", "//worker_num": "进程数量", "worker_num": 10, + "//cpu_affinity":"是否设置进程CPU亲和度(绑定CPU)", + "cpu_affinity":false, "//worker_capacity": "子进程最大工作负荷", "worker_capacity": 1000000, "//config_path": "配置文件路径(相对路径)", @@ -73,7 +75,9 @@ "module": [ { "path": "im/user/login", "class": "neb::ModuleLogin" }, { "path": "im/user/logout", "class": "neb::ModuleLogout" } - ] + ], + "session":["im::SessionUser", "im::SessionGroup"], + "step":["im::StepLogin", "im::StepLogout", "im::StepP2pChat"] }, { "so_path": "plugins/ChatMsg.so", @@ -82,7 +86,9 @@ "cmd": [ { "cmd": 2001, "class": "neb::CmdChat" } ], - "module": [] + "module": [], + "session":[], + "step":[] } ], "//custom": "自定义配置,用于通过框架层带给业务", diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 288bc1f3..8b1bf73b 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -185,4 +185,9 @@ ev_timer* Actor::MutableTimerWatcher() return(m_pTimerWatcher); } +void Actor::SetActorName(const std::string& strActorName) +{ + m_strActorName = strActorName; +} + } /* namespace neb */ diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 4daf04ef..0ec23390 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -67,6 +67,16 @@ class Actor: public std::enable_shared_from_this Actor& operator=(const Actor&) = delete; virtual ~Actor(); + ACTOR_TYPE GetActorType() const + { + return(m_eActorType); + } + + const std::string& GetActorName() const + { + return(m_strActorName); + } + protected: uint32 GetSequence(); uint32 GetNodeId() const; @@ -212,16 +222,13 @@ class Actor: public std::enable_shared_from_this return(m_dTimeout); } - ACTOR_TYPE GetActorType() const - { - return(m_eActorType); - } - private: void SetWorker(Worker* pWorker); ev_timer* MutableTimerWatcher(); + void SetActorName(const std::string& strActorName); + private: ACTOR_TYPE m_eActorType; uint32 m_ulSequence; @@ -229,6 +236,7 @@ class Actor: public std::enable_shared_from_this ev_tstamp m_dTimeout; Worker* m_pWorker; ev_timer* m_pTimerWatcher; + std::string m_strActorName; std::string m_strTraceId; // for log trace std::shared_ptr m_pContext; diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index 6e3db95a..ce14788a 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -153,7 +153,7 @@ int SocketChannel::RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, in } if (n == 0) { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "recvmsg() return zero, errno %d", errno); + pLogger->WriteLog(neb::Logger::WARNING, __FILE__, __LINE__, __FUNCTION__, "recvmsg() return zero, errno %d", errno); iError = (errno == 0) ? ERR_TRANSFER_FD : errno; return(ERR_CHANNEL_EOF); } diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 942dfb76..f6bffdd3 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -10,6 +10,7 @@ //#include #include +#include #ifdef __cplusplus extern "C" { #endif @@ -319,7 +320,7 @@ bool WorkerImpl::FdTransfer() { if (iErrno == ERR_CHANNEL_EOF) { - LOG4_ERROR("recv_fd from m_iManagerDataFd %d error %d", m_stWorkerInfo.iManagerDataFd, errno); + LOG4_WARNING("recv_fd from m_iManagerDataFd %d error %d", m_stWorkerInfo.iManagerDataFd, errno); Destroy(); exit(2); // manager与worker通信fd已关闭,worker进程退出 } @@ -764,6 +765,22 @@ bool WorkerImpl::Init(CJsonObject& oJsonConf) { return(false); } + + bool bCpuAffinity = false; + oJsonConf.Get("cpu_affinity", bCpuAffinity); + if (bCpuAffinity) + { + /* get logical cpu number */ + int iCpuNum = sysconf(_SC_NPROCESSORS_CONF);; ///< cpu数量 + cpu_set_t stCpuMask; ///< cpu set + CPU_ZERO(&stCpuMask); + CPU_SET(m_stWorkerInfo.iWorkerIndex % iCpuNum, &stCpuMask); + if (sched_setaffinity(0, sizeof(cpu_set_t), &stCpuMask) == -1) + { + LOG4_WARNING("sched_setaffinity failed."); + } + } + if (oJsonConf["with_ssl"]("config_path").length() > 0) { #ifdef WITH_OPENSSL diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 142ed846..d5999017 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -330,10 +330,16 @@ class WorkerImpl std::unique_ptr m_pSessionNode; std::shared_ptr m_pSessionLogger; + // dynamic load,use for load and unload. + std::unordered_map m_mapLoadedSo; + std::unordered_map m_mapLoadedCmd; + std::unordered_map m_mapLoadedModule; + std::unordered_map m_mapLoadedStep; + std::unordered_map m_mapLoadedSession; + // Cmd and Module std::unordered_map > m_mapCmd; std::unordered_map > m_mapModule; - std::unordered_map m_mapLoadedSo; // Step and Session std::unordered_map > m_mapCallbackStep; diff --git a/src/labor/WorkerImpl.inl b/src/labor/WorkerImpl.inl index 6ec6469e..db1bd1a5 100644 --- a/src/labor/WorkerImpl.inl +++ b/src/labor/WorkerImpl.inl @@ -40,6 +40,7 @@ std::shared_ptr WorkerImpl::MakeSharedStep(Actor* pCreator, const std::str pStepAlias->SetWorker(m_pWorker); pStepAlias->SetActiveTime(ev_now(m_loop)); + pStepAlias->SetActorName(strStepName); if (nullptr != pCreator) { pStepAlias->SetContext(pCreator->GetContext()); @@ -116,6 +117,7 @@ std::shared_ptr WorkerImpl::MakeSharedSession(Actor* pCreator, const st pSessionAlias->SetWorker(m_pWorker); pSessionAlias->SetActiveTime(ev_now(m_loop)); + pSessionAlias->SetActorName(strSessionName); if (nullptr != pCreator) { pSessionAlias->SetContext(pCreator->GetContext()); @@ -164,6 +166,7 @@ std::shared_ptr WorkerImpl::MakeSharedCmd(Actor* pCreator, const std::strin pCmdAlias->SetWorker(m_pWorker); pCmdAlias->SetActiveTime(ev_now(m_loop)); + pCmdAlias->SetActorName(strCmdName); if (nullptr != pCreator) { pCmdAlias->SetContext(pCreator->GetContext()); @@ -197,6 +200,7 @@ std::shared_ptr WorkerImpl::MakeSharedModule(Actor* pCreator, const std: pModuleAlias->SetWorker(m_pWorker); pModuleAlias->SetActiveTime(ev_now(m_loop)); + pModuleAlias->SetActorName(strModuleName); if (nullptr != pCreator) { pModuleAlias->SetContext(pCreator->GetContext()); From 597dedd194df713d9ec6cec94f84ed64d37a19b1 Mon Sep 17 00:00:00 2001 From: Bwar Date: Thu, 30 May 2019 23:02:11 +0800 Subject: [PATCH 024/176] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E5=BA=93=E5=8D=B8=E8=BD=BD=E5=92=8C=E5=8A=A8=E6=80=81=E5=BA=93?= =?UTF-8?q?=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/labor/WorkerImpl.cpp | 182 +++++++++++++++++++++++++++++++-------- src/labor/WorkerImpl.hpp | 12 +-- src/labor/WorkerImpl.inl | 20 +++++ 3 files changed, 175 insertions(+), 39 deletions(-) diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index f6bffdd3..d10499dc 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Project: Nebula - * @file Worker.cpp + * @file WorkerImpl.cpp * @brief * @author Bwar * @date: 2018年3月27日 @@ -172,7 +172,7 @@ void WorkerImpl::Run() } LoadSysCmd(); BootLoadCmd(m_oWorkerConf["boot_load"]); - DynamicLoadCmd(m_oWorkerConf["dynamic_loading"]); + DynamicLoad(m_oWorkerConf["dynamic_loading"]); //mtrace(); ev_run (m_loop, 0); @@ -1678,13 +1678,11 @@ void WorkerImpl::BootLoadCmd(CJsonObject& oCmdConf) } } -void WorkerImpl::DynamicLoadCmd(CJsonObject& oDynamicLoadingConf) +void WorkerImpl::DynamicLoad(CJsonObject& oDynamicLoadingConf) { LOG4_TRACE(" "); int iVersion = 0; bool bIsload = false; - int32 iCmd = 0; - std::string strUrlPath; std::string strSoPath; std::unordered_map::iterator so_iter; tagSo* pSo = nullptr; @@ -1698,59 +1696,63 @@ void WorkerImpl::DynamicLoadCmd(CJsonObject& oDynamicLoadingConf) so_iter = m_mapLoadedSo.find(strSoPath); if (so_iter == m_mapLoadedSo.end()) { - strSoPath = m_stWorkerInfo.strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); - pSo = LoadSo(strSoPath, iVersion); + std::string strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); + if (0 != access(strSoFile.c_str(), F_OK)) + { + LOG4_WARNING("%s not exist!", strSoFile.c_str()); + continue; + } + pSo = LoadSo(strSoFile, iVersion); if (pSo != nullptr) { LOG4_INFO("succeed in loading %s", strSoPath.c_str()); m_mapLoadedSo.insert(std::make_pair(strSoPath, pSo)); - for (int j = 0; j < oDynamicLoadingConf[i]["cmd"].GetArraySize(); ++j) - { - oDynamicLoadingConf[i]["cmd"][j].Get("cmd", iCmd); - MakeSharedCmd(nullptr, oDynamicLoadingConf[i]["cmd"][j]("class"), iCmd); - } - for (int k = 0; k < oDynamicLoadingConf[i]["module"].GetArraySize(); ++k) - { - oDynamicLoadingConf[i]["module"][k].Get("path", strUrlPath); - MakeSharedModule(nullptr, oDynamicLoadingConf[i]["module"][k]("class"), strUrlPath); - } + LoadDynamicSymbol(oDynamicLoadingConf[i]); } } - /* else { - if (iVersion != so_iter->second->iVersion) + if (iVersion != so_iter->second->iVersion) // 版本升级,先卸载再加载 { - strSoPath = m_stWorkerInfo.strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); - if (0 != access(strSoPath.c_str(), F_OK)) + std::string strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); + if (0 != access(strSoFile.c_str(), F_OK)) { - LOG4_WARNING("%s not exist!", strSoPath.c_str()); + LOG4_WARNING("%s not exist!", strSoFile.c_str()); continue; } - pSo = LoadSo(strSoPath, iVersion); - LOG4_TRACE("%s:%d after LoadSoAndGetCmd", __FILE__, __LINE__); + UnloadDynamicSymbol(oDynamicLoadingConf[i]); + dlclose(so_iter->second->pSoHandle); + delete so_iter->second; + pSo = LoadSo(strSoFile, iVersion); if (pSo != nullptr) { LOG4_INFO("succeed in loading %s", strSoPath.c_str()); - delete so_iter->second; so_iter->second = pSo; + LoadDynamicSymbol(oDynamicLoadingConf[i]); + } + else + { + m_mapLoadedSo.erase(so_iter); } } } - */ } } - /* else // 卸载动态库 { - if (oDynamicLoadingConf[i].Get("cmd", iCmd)) + if (oDynamicLoadingConf[i].Get("so_path", strSoPath)) { - strSoPath = m_stWorkerInfo.strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); - UnloadSoAndDeleteCmd(iCmd); - LOG4_INFO("unload %s", strSoPath.c_str()); + so_iter = m_mapLoadedSo.find(strSoPath); + if (so_iter != m_mapLoadedSo.end()) + { + UnloadDynamicSymbol(oDynamicLoadingConf[i]); + dlclose(so_iter->second->pSoHandle); + delete so_iter->second; + m_mapLoadedSo.erase(so_iter); + LOG4_INFO("unload %s", strSoPath.c_str()); + } } } - */ } } @@ -1780,6 +1782,111 @@ WorkerImpl::tagSo* WorkerImpl::LoadSo(const std::string& strSoPath, int iVersion return(pSo); } +void WorkerImpl::LoadDynamicSymbol(CJsonObject& oOneSoConf) +{ + int32 iCmd = 0; + std::string strUrlPath; + for (int i = 0; i < oOneSoConf["cmd"].GetArraySize(); ++i) + { + std::unordered_set setCmd; + m_mapLoadedCmd.insert(std::make_pair(oOneSoConf["cmd"][i]("class"), setCmd)); + oOneSoConf["cmd"][i].Get("cmd", iCmd); + MakeSharedCmd(nullptr, oOneSoConf["cmd"][i]("class"), iCmd); + } + for (int j = 0; j < oOneSoConf["module"].GetArraySize(); ++j) + { + std::unordered_set setModule; + m_mapLoadedModule.insert(std::make_pair(oOneSoConf["module"][j]("class"), setModule)); + oOneSoConf["module"][j].Get("path", strUrlPath); + MakeSharedModule(nullptr, oOneSoConf["module"][j]("class"), strUrlPath); + } + for (int k = 0; k < oOneSoConf["session"].GetArraySize(); ++k) + { + std::unordered_set setSession; + m_mapLoadedSession.insert(std::make_pair(oOneSoConf["session"](k), setSession)); + } + for (int l = 0; l < oOneSoConf["step"].GetArraySize(); ++l) + { + std::unordered_set setStep; + m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); + } +} + +void WorkerImpl::UnloadDynamicSymbol(CJsonObject& oOneSoConf) +{ + for (int i = 0; i < oOneSoConf["cmd"].GetArraySize(); ++i) + { + auto class_iter = m_mapLoadedCmd.find(oOneSoConf["cmd"][i]("class")); + if (class_iter != m_mapLoadedCmd.end()) + { + for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) + { + auto cmd_iter = m_mapCmd.find(*id_iter); + if (cmd_iter != m_mapCmd.end()) + { + m_mapCmd.erase(cmd_iter); + } + } + class_iter->second.clear(); + m_mapLoadedCmd.erase(class_iter); + } + } + for (int j = 0; j < oOneSoConf["module"].GetArraySize(); ++j) + { + auto class_iter = m_mapLoadedModule.find(oOneSoConf["module"][j]("class")); + if (class_iter != m_mapLoadedModule.end()) + { + for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) + { + auto module_iter = m_mapModule.find(*id_iter); + if (module_iter != m_mapModule.end()) + { + m_mapModule.erase(module_iter); + } + } + class_iter->second.clear(); + m_mapLoadedModule.erase(class_iter); + } + } + for (int k = 0; k < oOneSoConf["session"].GetArraySize(); ++k) + { + auto class_iter = m_mapLoadedSession.find(oOneSoConf["session"](k)); + if (class_iter != m_mapLoadedSession.end()) + { + for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) + { + auto session_iter = m_mapCallbackSession.find(*id_iter); + if (session_iter != m_mapCallbackSession.end()) + { + m_mapCallbackSession.erase(session_iter); + } + } + class_iter->second.clear(); + m_mapLoadedSession.erase(class_iter); + } + } + for (int l = 0; l < oOneSoConf["step"].GetArraySize(); ++l) + { + auto class_iter = m_mapLoadedStep.find(oOneSoConf["step"](l)); + if (class_iter != m_mapLoadedStep.end()) + { + for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) + { + auto step_iter = m_mapCallbackStep.find(*id_iter); + if (step_iter != m_mapCallbackStep.end()) + { + step_iter->second->Timeout(); + m_mapCallbackStep.erase(step_iter); + } + } + class_iter->second.clear(); + m_mapLoadedStep.erase(class_iter); + } + std::unordered_set setStep; + m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); + } +} + bool WorkerImpl::AddPeriodicTaskEvent() { LOG4_TRACE(" "); @@ -2158,8 +2265,15 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const MsgHead& } else if (CMD_REQ_RELOAD_SO == oMsgHead.cmd()) { - CJsonObject oSoConfJson; // TODO So config - DynamicLoadCmd(oSoConfJson); + CJsonObject oSoConfJson; + if (oSoConfJson.Parse(oMsgBody.data())) + { + DynamicLoad(oSoConfJson); + } + else + { + LOG4_ERROR("json parse string error: \"%s\""); + } } else { diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index d5999017..3a4ee5f5 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -311,8 +311,10 @@ class WorkerImpl void ExecAssemblyLine(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); void BootLoadCmd(CJsonObject& oCmdConf); - void DynamicLoadCmd(CJsonObject& oCmdConf); + void DynamicLoad(CJsonObject& oSoConf); tagSo* LoadSo(const std::string& strSoPath, int iVersion); + void LoadDynamicSymbol(CJsonObject& oOneSoConf); + void UnloadDynamicSymbol(CJsonObject& oOneSoConf); private: mutable uint32 m_ulSequence = 0; @@ -332,10 +334,10 @@ class WorkerImpl // dynamic load,use for load and unload. std::unordered_map m_mapLoadedSo; - std::unordered_map m_mapLoadedCmd; - std::unordered_map m_mapLoadedModule; - std::unordered_map m_mapLoadedStep; - std::unordered_map m_mapLoadedSession; + std::unordered_map > m_mapLoadedCmd; //key为CmdClassName,value为iCmd集合 + std::unordered_map > m_mapLoadedModule; //key为ModuleClassName,value为strModulePath集合 + std::unordered_map > m_mapLoadedStep; //key为StepClassName,value为uiSeq集合 + std::unordered_map > m_mapLoadedSession; //key为SessionClassName,value为strSessionId集合 // Cmd and Module std::unordered_map > m_mapCmd; diff --git a/src/labor/WorkerImpl.inl b/src/labor/WorkerImpl.inl index db1bd1a5..6b1665ac 100644 --- a/src/labor/WorkerImpl.inl +++ b/src/labor/WorkerImpl.inl @@ -94,6 +94,11 @@ std::shared_ptr WorkerImpl::MakeSharedStep(Actor* pCreator, const std::str } LOG4_TRACE("Step(seq %u, active_time %lf, lifetime %lf) register successful.", pStepAlias->GetSequence(), pStepAlias->GetActiveTime(), pStepAlias->GetTimeout()); + auto step_class_iter = m_mapLoadedStep.find(pStepAlias->GetActorName()); + if (step_class_iter != m_mapLoadedStep.end()) + { + step_class_iter->second.insert(pStepAlias->GetSequence()); + } return(pSharedStep); } else @@ -142,6 +147,11 @@ std::shared_ptr WorkerImpl::MakeSharedSession(Actor* pCreator, const st } LOG4_TRACE("Session(seq %u, active_time %lf, lifetime %lf) register successful.", pSessionAlias->GetSequence(), pSessionAlias->GetActiveTime(), pSessionAlias->GetTimeout()); + auto session_class_iter = m_mapLoadedSession.find(pSessionAlias->GetActorName()); + if (session_class_iter != m_mapLoadedSession.end()) + { + session_class_iter->second.insert(pSessionAlias->GetSessionId()); + } return(pSharedSession); } else @@ -179,6 +189,11 @@ std::shared_ptr WorkerImpl::MakeSharedCmd(Actor* pCreator, const std::strin { if (pCmdAlias->Init()) { + auto cmd_class_iter = m_mapLoadedCmd.find(pCmdAlias->GetActorName()); + if (cmd_class_iter != m_mapLoadedCmd.end()) + { + cmd_class_iter->second.insert(pCmdAlias->GetCmd()); + } return(pSharedCmd); } } @@ -213,6 +228,11 @@ std::shared_ptr WorkerImpl::MakeSharedModule(Actor* pCreator, const std: { if (pModuleAlias->Init()) { + auto module_class_iter = m_mapLoadedModule.find(pModuleAlias->GetActorName()); + if (module_class_iter != m_mapLoadedModule.end()) + { + module_class_iter->second.insert(pModuleAlias->GetModulePath()); + } return(pSharedModule); } } From 50c53538a31d6590c01984eff1e12bb0b7cedf8c Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 31 May 2019 08:56:54 +0800 Subject: [PATCH 025/176] patch nebim's commit, remove step id or session id while they callback or timeout. --- src/labor/WorkerImpl.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index d10499dc..e4dbf461 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -2181,6 +2181,15 @@ void WorkerImpl::Remove(std::shared_ptr pStep) return; } } + auto class_iter = m_mapLoadedStep.find(pStep->GetActorName()); + if (class_iter != m_mapLoadedStep.end()) + { + auto id_iter = class_iter->second.find(pStep->GetSequence()); + if (id_iter != class_iter->second.end()) + { + class_iter->second.erase(id_iter); + } + } if (pStep->MutableTimerWatcher() != NULL) { ev_timer_stop (m_loop, pStep->MutableTimerWatcher()); @@ -2199,6 +2208,15 @@ void WorkerImpl::Remove(std::shared_ptr pSession) { return; } + auto class_iter = m_mapLoadedSession.find(pSession->GetActorName()); + if (class_iter != m_mapLoadedSession.end()) + { + auto id_iter = class_iter->second.find(pSession->GetSessionId()); + if (id_iter != class_iter->second.end()) + { + class_iter->second.erase(id_iter); + } + } if (pSession->MutableTimerWatcher() != NULL) { ev_timer_stop (m_loop, pSession->MutableTimerWatcher()); From 1c0c4aa34b2398de64508de23c4b3b3cf46df73d Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 1 Jun 2019 16:09:46 +0800 Subject: [PATCH 026/176] Version V0.8 released --- README.md | 6 ++++++ README_cn.md | 16 ++++++++++++---- src/Makefile | 4 ++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2a3df51e..b77dee9b 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ English | [中文](/README_cn.md)         Nebula is an event-driven TCP protocol network framework developed in C++ language. It supports multiple application layer communication protocols including proto3, http, https, and websocket. The purpose of developing the Nebula framework is to provide a fast and high-performance distributed service cluster based on C++. +Nebula is a production level framework and distributed solution project for instant messaging, data collection, real-time computing, message push and other applications, as well as web api services. There were production applications for instant messaging, data acquisition and real-time analysis on line now, and a recommendation engine application for a large user base will be born soon. By the way, using Nebula for toy-level projects is also good for learning network communication. Bwar welcomes more developers to join the Nebula project. Nebula is a proactor development framework(a framework-implemented proactor, not an operating system support). The IO-intensive application on nebula with be good performance. + Nebula can be used as a single high-performance TCP server, but building a cluster based on Nebula will be truly reflect its value. In order to build distributed service clusters quickly, Nebula Bootstrap cluster solutions including various types of services have been developed. @@ -126,6 +128,10 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v0.8 + - compatible with gcc4.8 compiler. + - add cpu affinity inorder to support cpu binding. + - add dynamic library unload. #### v0.7 - add configuration management(check [Nebcli](https://github.com/Bwar/Nebcli) for detail). #### v0.6 diff --git a/README_cn.md b/README_cn.md index 739be286..36b070ba 100644 --- a/README_cn.md +++ b/README_cn.md @@ -14,11 +14,15 @@ ## 概述 -Nebula是一个C\+\+语言开发的事件驱动型的TCP协议网络框架,支持包括proto3、http、https、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建一个高性能的分布式服务集群。Nebula自身核心代码只有万行左右(不计算proto文件生成的代码)。 +  Nebula是一个C\+\+语言开发的事件驱动型的TCP协议网络框架,支持包括proto3、http、https、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建一个高性能的分布式服务集群。Nebula自身核心代码只有万行左右(不计算proto文件生成的代码)。 -Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建集群才能真正体现其价值。为了能快速搭建分布式服务集群,开发了包括各种类型服务的NebulaBootstrap集群解决方案。 +  Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建集群才能真正体现其价值。为了能快速搭建分布式服务集群,开发了包括各种类型服务的NebulaBootstrap集群解决方案。 -Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(也是Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++14)是Starship(C++03)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的第一个应用Nebio(埋点数据采集和实时分析项目)在2018年7月底上线并稳定运行,Bwar还准备开发基于Nebula的IM应用Nebim。 +  Nebula是一个产线级的框架和分布式解决方案项目,适用于即时通讯、数据采集、实时计算、消息推送等应用场景,也适用于web后台服务。Nebula已有即时通讯、埋点数据采集及实时分析的生产应用案例,很快将有一个面向庞大用户群的推荐引擎产线应用案例。 + +  把Nebula用于玩具级项目,用于学习交流也不错,Bwar欢迎更多有兴趣的开发者加入到Nebula这个项目中来,Bwar也乐意解答项目中遇到的问题。Nebula是个proactor模式开发框架,不错,是proactor不是reactor(框架层实现的proactor而不是操作系统支持),应用于IO密集型的项目可以达到非常好的性能。Nebula现在不支持同步调用不支持rpc,以后也应该不会支持rpc,做全异步的通信框架目标不变。对了解异步回调编程方式的开发者,Nebula是个非常简单的框架,比写常见的异步回调写法要简单多了。Nebula网络框架的技术分享和交流见[C++网络框架Nebula](https://zhuanlan.zhihu.com/c_216558336) + +  Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(也是Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++14)是Starship(C++03)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的第一个应用Nebio(埋点数据采集和实时分析项目)在2018年7月底上线并稳定运行,Bwar还准备开发基于Nebula的IM应用Nebim。 ## 许可证 @@ -114,8 +118,12 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) ## 版本历史 +#### v0.8 + - 兼容gcc4.8编译器(从这个版本起无须另行安装5以上gcc版本,可以无障碍无等待地在个人机器上部署和测试,也为应用于生产铺平道路。之前Bwar的埋> + - 增加CPU亲和度设置以支持将Worker进程绑定CPU功能。(有人测试过繁忙的多核服务器,绑定CPU比不绑定CPU有20%左右的性能提升,现在Nebua可以让开发 + - 增加动态库(业务插件)卸载功能。(支持不停服务升级的重要功能) #### v0.7 - - 添加配置管理,NebulaBeacon为配置中心,使用说明见命令行管理工具[Nebcli](https://github.com/Bwar/Nebcli)的get和set命令。 + - 增加配置管理,NebulaBeacon为配置中心,使用说明见命令行管理工具[Nebcli](https://github.com/Bwar/Nebcli)的get和set命令。 #### v0.6 - NebulaBeacon增加节点状态信息查询,注册中心主从高可用选举 - NebulaInterface提供HelloWorld示例。 diff --git a/src/Makefile b/src/Makefile index 103af0fa..c0c37a43 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,8 +1,8 @@ CC = gcc CXX = g++ -CFLAGS = -g -O3 -Wall -m64 -Wl,--export-dynamic -D__GUNC__ -fPIC +CFLAGS = -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic -D__GUNC__ -fPIC cplusplus_version=$(shell g++ -dumpversion | awk '{if ($$NF > 5.0) print "c++14"; else print "c++11";}') -CXXFLAG = -std=$(cplusplus_version) -g -O3 -Wall -m64 -Wl,--export-dynamic -D_GNU_SOURCE=1 -D_REENTRANT -D__GUNC__ -fPIC -DNODE_BEAT=10.0 +CXXFLAG = -std=$(cplusplus_version) -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic -D_GNU_SOURCE=1 -D_REENTRANT -D__GUNC__ -fPIC -DNODE_BEAT=10.0 ifeq ($(unit_test),y) CXXFLAG += -DUNIT_TEST From 241c928ced178eb524151daa58f836dd7a96b9f2 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 8 Jun 2019 18:00:04 +0800 Subject: [PATCH 027/176] change static variable name from m_xxxx to s_xxxx, and try to add chain. --- .gitignore | 12 ++++ .travis.yml | 6 -- README_cn.md | 12 ++-- conf/nebula.json | 7 +++ package.json | 10 ++-- src/actor/Actor.cpp | 15 +++-- src/actor/Actor.hpp | 18 +++++- src/actor/ActorFactory.hpp | 10 ++-- src/actor/DynamicCreator.hpp | 6 +- src/actor/chain/Chain.cpp | 26 +++++++++ src/actor/chain/Chain.hpp | 80 +++++++++++++++++++++++++++ src/actor/chain/ChainModel.cpp | 24 ++++++++ src/actor/chain/ChainModel.hpp | 37 +++++++++++++ src/actor/cmd/Cmd.hpp | 14 ----- src/actor/cmd/Module.hpp | 14 ----- src/actor/engine/Engine.hpp | 80 +++++++++++++++++++++++++++ src/actor/engine/EngineModel.hpp | 40 ++++++++++++++ src/actor/session/Session.hpp | 14 ----- src/actor/session/SessionModel.cpp | 1 - src/actor/step/Step.hpp | 15 ----- src/actor/step/StepModel.cpp | 1 - src/channel/SocketChannelSslImpl.cpp | 62 ++++++++++----------- src/channel/SocketChannelSslImpl.hpp | 4 +- src/labor/.WorkerImpl.hpp.swp | Bin 0 -> 28672 bytes src/labor/.WorkerImpl.inl.swp | Bin 0 -> 16384 bytes src/labor/WorkerImpl.hpp | 8 +++ src/logger/FileLogger.cpp | 2 +- src/logger/FileLogger.hpp | 8 +-- 28 files changed, 399 insertions(+), 127 deletions(-) create mode 100644 src/actor/chain/Chain.cpp create mode 100644 src/actor/chain/Chain.hpp create mode 100644 src/actor/chain/ChainModel.cpp create mode 100644 src/actor/chain/ChainModel.hpp create mode 100644 src/actor/engine/Engine.hpp create mode 100644 src/actor/engine/EngineModel.hpp create mode 100644 src/labor/.WorkerImpl.hpp.swp create mode 100644 src/labor/.WorkerImpl.inl.swp diff --git a/.gitignore b/.gitignore index 94a0eb9d..07acaa31 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,15 @@ /Debug/ docs/html *.o +*.so +config* +include/ +lib/ +*.cache +*.txt +.ycm_extra_conf.py +Makefile.am +aclocal.m4 +compile +install-sh +missing diff --git a/.travis.yml b/.travis.yml index c5276b1d..8f79e4d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,18 +7,12 @@ addons: sources: - ubuntu-toolchain-r-test packages: - - gcc-6 - - g++-6 - doxygen - doxygen-doc - doxygen-latex - doxygen-gui - graphviz before_install: - - sudo ln -s /usr/bin/gcc-6 /usr/local/bin/gcc - - sudo ln -s /usr/bin/g++-6 /usr/local/bin/g++ - - export CC=/usr/bin/gcc-6 - - export CXX=/usr/bin/g++-6 - gcc -v && g++ -v - doxygen docs/Doxyfile diff --git a/README_cn.md b/README_cn.md index 36b070ba..712fcd0e 100644 --- a/README_cn.md +++ b/README_cn.md @@ -10,13 +10,14 @@ 6. [相关项目](#RelatedProject) 7. [开发任务](#TODO) 8. [版本历史](#ChangeLog) +9. [交流与反馈](#Exchange) ## 概述 -  Nebula是一个C\+\+语言开发的事件驱动型的TCP协议网络框架,支持包括proto3、http、https、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建一个高性能的分布式服务集群。Nebula自身核心代码只有万行左右(不计算proto文件生成的代码)。 +  Nebula是一个C\+\+语言开发的事件驱动型的TCP协议分布式网络框架,支持包括proto3、http、https、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建高性能的分布式服务。Nebula自身核心代码只有2万行左右(不计算proto文件生成的代码)。 -  Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建集群才能真正体现其价值。为了能快速搭建分布式服务集群,开发了包括各种类型服务的NebulaBootstrap集群解决方案。 +  Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建分布式服务才能真正体现其价值。为了能快速搭建分布式服务,开发了包括各种类型服务的NebulaBootstrap解决方案。   Nebula是一个产线级的框架和分布式解决方案项目,适用于即时通讯、数据采集、实时计算、消息推送等应用场景,也适用于web后台服务。Nebula已有即时通讯、埋点数据采集及实时分析的生产应用案例,很快将有一个面向庞大用户群的推荐引擎产线应用案例。 @@ -119,8 +120,8 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) ## 版本历史 #### v0.8 - - 兼容gcc4.8编译器(从这个版本起无须另行安装5以上gcc版本,可以无障碍无等待地在个人机器上部署和测试,也为应用于生产铺平道路。之前Bwar的埋> - - 增加CPU亲和度设置以支持将Worker进程绑定CPU功能。(有人测试过繁忙的多核服务器,绑定CPU比不绑定CPU有20%左右的性能提升,现在Nebua可以让开发 + - 兼容gcc4.8编译器(从这个版本起无须另行安装5以上gcc版本,可以无障碍无等待地在个人机器上部署和测试,也为应用于生产铺平道路。之前Bwar的埋点数据采集和实时分析的生产项目Nebio是在服务器上安装了gcc6才部署的。) + - 增加CPU亲和度设置以支持将Worker进程绑定CPU功能。(有人测试过繁忙的多核服务器,绑定CPU比不绑定CPU有20%左右的性能提升,现在Nebua可以让开发者自行选择是否绑定CPU) - 增加动态库(业务插件)卸载功能。(支持不停服务升级的重要功能) #### v0.7 - 增加配置管理,NebulaBeacon为配置中心,使用说明见命令行管理工具[Nebcli](https://github.com/Bwar/Nebcli)的get和set命令。 @@ -145,4 +146,7 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) - 第一个可运行并http测试通过的版本
+ +## 交流与反馈 +  Bug、修改建议、疑惑都欢迎提在issue中,或加入qq群[809075299](点击链接加入群聊【Nebula框架技术交流】:https://jq.qq.com/?_wv=1027&k=5qL2ZKt)交流。 diff --git a/conf/nebula.json b/conf/nebula.json index d5582a33..2f08b9f3 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -91,6 +91,13 @@ "step":[] } ], + "runtime":{ + "engines":[], + "chains":[ + "chain_1":["step1", "engine1", ["step2A", "step2B", "step2C"], "step3", "engine2"], + "chain_2":[] + ] + } "//custom": "自定义配置,用于通过框架层带给业务", "custom": {} } diff --git a/package.json b/package.json index dbc09555..ac3e4e03 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name":"Nebula", - "version":"0.3.0", + "version":"0.8.0", "description":"Nebula is a BSD licensed, proto3-protocol event driven asynchronous c++ framework, it support multiple protocol like protobuf, http, websocket. A high performance of distributed service clusters can be easily constructed using Nebula.", "keywords":["Nebula", "proto3", "libev", "asynchronous", "actor", "websocket", "distributed", "starship"], "homepage":"", "bugs":{"url":"", "email":"cqc@vip.qq.com"}, - "license":"BSD", + "license":"MIT", "author":{"name":"Bwar", "email":"cqc@vip.qq.com", "url":""}, "files":"src/actor", "main":"", @@ -15,8 +15,8 @@ "google/protobuf":"^3.5.2", "kindy/libev":"^4.22", "redis/hiredis":"^0.13.3", - "weidai11/cryptopp":"^6.0.0", + "weidai11/cryptopp":"^8.0.0", "nodejs/http-parser":"^2.8.0", - "DaveGamble/cJSON":"^1.7.5" + "Bwar/CJsonObject":"^1.0.0" } -} \ No newline at end of file +} diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 8b1bf73b..e7d294f4 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -18,7 +18,7 @@ namespace neb Actor::Actor(ACTOR_TYPE eActorType, ev_tstamp dTimeout) : m_eActorType(eActorType), - m_ulSequence(0), m_dActiveTime(0.0), m_dTimeout(dTimeout), + m_uiSequence(0), m_dActiveTime(0.0), m_dTimeout(dTimeout), m_pWorker(nullptr), m_pTimerWatcher(NULL), m_pContext(nullptr) { } @@ -26,20 +26,20 @@ Actor::Actor(ACTOR_TYPE eActorType, ev_tstamp dTimeout) Actor::~Actor() { FREE(m_pTimerWatcher); - m_pWorker->Logger(m_strTraceId, Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "eActorType %d, seq %u", m_eActorType, m_ulSequence); + m_pWorker->Logger(m_strTraceId, Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "eActorType %d, seq %u", m_eActorType, m_uiSequence); } uint32 Actor::GetSequence() { if ((ACT_CMD == m_eActorType || ACT_MODULE == m_eActorType) // Cmd和Module总是获取最新Seq - || 0 == m_ulSequence) + || 0 == m_uiSequence) { if (nullptr != m_pWorker) { - m_ulSequence = m_pWorker->GetSequence(); + m_uiSequence = m_pWorker->GetSequence(); } } - return(m_ulSequence); + return(m_uiSequence); } uint32 Actor::GetNodeId() const @@ -190,4 +190,9 @@ void Actor::SetActorName(const std::string& strActorName) m_strActorName = strActorName; } +void Actor::SetChainId(uint32 uiChainId) +{ + m_uiChainId = uiChainId; +} + } /* namespace neb */ diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 0ec23390..f728f103 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -45,6 +45,8 @@ class Session; class Timer; class Context; class Step; +class Engine; +class Chain; class Actor: public std::enable_shared_from_this { @@ -59,6 +61,8 @@ class Actor: public std::enable_shared_from_this ACT_PB_STEP = 5, ///< Step步骤对象,处理pb请求或响应 ACT_HTTP_STEP = 6, ///< Step步骤对象,处理http请求或响应 ACT_REDIS_STEP = 7, ///< Step步骤对象,处理redis请求或响应 + ACT_ENGINE = 8, ///< Engine链块对象,Engine(IO无关)与Step(异步IO相关)共同构成功能链 + ACT_CHAIN = 9, ///< Chain链对象,用于将Engine和Step组合成功能链 }; public: @@ -222,6 +226,11 @@ class Actor: public std::enable_shared_from_this return(m_dTimeout); } + uint32 GetChainId() const + { + return(m_uiChainId); + } + private: void SetWorker(Worker* pWorker); @@ -229,9 +238,12 @@ class Actor: public std::enable_shared_from_this void SetActorName(const std::string& strActorName); + void SetChainId(uint32 uiChainId); + private: ACTOR_TYPE m_eActorType; - uint32 m_ulSequence; + uint32 m_uiSequence; + uint32 m_uiChainId; ev_tstamp m_dActiveTime; ev_tstamp m_dTimeout; Worker* m_pWorker; @@ -244,8 +256,10 @@ class Actor: public std::enable_shared_from_this friend class WorkerFriend; friend class Cmd; friend class Module; - friend class Step; friend class Session; + friend class Step; + friend class Engine; + friend class Chain; }; diff --git a/src/actor/ActorFactory.hpp b/src/actor/ActorFactory.hpp index 902369a0..5f9d6a2c 100644 --- a/src/actor/ActorFactory.hpp +++ b/src/actor/ActorFactory.hpp @@ -25,11 +25,11 @@ class ActorFactory public: static ActorFactory* Instance() { - if (nullptr == m_pActorFactory) + if (nullptr == s_pActorFactory) { - m_pActorFactory = new ActorFactory(); + s_pActorFactory = new ActorFactory(); } - return(m_pActorFactory); + return(s_pActorFactory); } virtual ~ActorFactory(){}; @@ -39,13 +39,13 @@ class ActorFactory private: ActorFactory(){}; - static ActorFactory* m_pActorFactory; + static ActorFactory* s_pActorFactory; std::unordered_map > m_mapCreateFunction; }; template -ActorFactory* ActorFactory::m_pActorFactory = nullptr; +ActorFactory* ActorFactory::s_pActorFactory = nullptr; template bool ActorFactory::Regist(const std::string& strTypeName, std::function pFunc) diff --git a/src/actor/DynamicCreator.hpp b/src/actor/DynamicCreator.hpp index dc6e3390..69b77b4b 100644 --- a/src/actor/DynamicCreator.hpp +++ b/src/actor/DynamicCreator.hpp @@ -45,7 +45,7 @@ class DynamicCreator DynamicCreator() { - m_oRegister.do_nothing(); + s_oRegister.do_nothing(); } virtual ~DynamicCreator(){}; @@ -65,11 +65,11 @@ class DynamicCreator } private: - static Register m_oRegister; + static Register s_oRegister; }; template -typename DynamicCreator::Register DynamicCreator::m_oRegister; +typename DynamicCreator::Register DynamicCreator::s_oRegister; } /* namespace neb */ diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp new file mode 100644 index 00000000..8131f231 --- /dev/null +++ b/src/actor/chain/Chain.cpp @@ -0,0 +1,26 @@ +/******************************************************************************* + * Project: Nebula + * @file Chain.cpp + * @brief + * @author Bwar + * @date: 2019年6月7日 + * @note + * Modify history: + ******************************************************************************/ + +#include "Chain.hpp" +#include "actor/step/Step.hpp" + +namespace neb +{ + +Chain::Chain(ev_tstamp dChainTimeout) + : ChainModel(dChainTimeout) +{ +} + +Chain::~Chain() +{ +} + +} /* namespace neb */ diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp new file mode 100644 index 00000000..aa942e42 --- /dev/null +++ b/src/actor/chain/Chain.hpp @@ -0,0 +1,80 @@ +/******************************************************************************* + * Project: Nebula + * @file Chain.hpp + * @brief + * @author Bwar + * @date: 2019年6月7日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CHAIN_CHAIN_HPP_ +#define SRC_ACTOR_CHAIN_CHAIN_HPP_ + +#include +#include "labor/Worker.hpp" +#include "actor/DynamicCreator.hpp" +#include "ChainModel.hpp" + +namespace neb +{ + +class Chain: public ChainModel +{ +public: + Chain(ev_tstamp dChainTimeout = 60.0); + Chain(const Chain&) = delete; + Chain& operator=(const Chain&) = delete; + virtual ~Chain(); + + /** + * @brief 会话超时回调 + */ + virtual E_CMD_STATUS Timeout() = 0; + + /** + * @brief 检查Chain内数据是否已加载 + * @note 为满足数据共享并确保同一数据在同一个Worker内只需从 + * 外部存储中加载一次,提供了IsReady(),SetReady(),IsLoading(), + * SetLoading()四个方法。如果一个或若干个Step获取到一个已创建好 + * 的Chain,则需先调用IsReady()判断数据是否就绪,若就绪则直接 + * 从Chain中读取,若未就绪则调用IsLoading()判断数据是否正在加 + * 载,若正在加载则直接return(CMD_STATUS_RUNNING)(框架会在数据 + * 加载完毕后调用该Step的Callback),否则加载数据并且SetLoading(), + * 数据加载完毕后SetReady()。 + * @param pStep 调用IsReady()方法的调用者Step指针,用于记录 + * 哪些Step依赖于Chain的数据,在数据就绪时由框架主动调用 + * 依赖这些数据的Step回调而不需要等到超时才回调。 + */ + bool IsReady(Step* pStep); + +protected: + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); + template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); + template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); + +private: + friend class WorkerImpl; + std::queue m_vecWaitingStep; +}; + +template +void Chain::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) +{ + m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); +} + +template +std::shared_ptr Chain::MakeSharedStep(const std::string& strStepName, Targs... args) +{ + return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); +} + +template +std::shared_ptr Chain::MakeSharedSession(const std::string& strSessionName, Targs... args) +{ + return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); +} + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CHAIN_CHAIN_HPP_ */ diff --git a/src/actor/chain/ChainModel.cpp b/src/actor/chain/ChainModel.cpp new file mode 100644 index 00000000..0bb9acff --- /dev/null +++ b/src/actor/chain/ChainModel.cpp @@ -0,0 +1,24 @@ +/******************************************************************************* + * Project: Nebula + * @file ChainModel.cpp + * @brief + * @author bwar + * @date: June 7, 2019 + * @note + * Modify history: + ******************************************************************************/ +#include "ChainModel.hpp" + +namespace neb +{ + +ChainModel::ChainModel(ev_tstamp dChainTimeout) + : Actor(Actor::ACT_CHAIN, dChainTimeout) +{ +} + +ChainModel::~ChainModel() +{ +} + +} /* namespace neb */ diff --git a/src/actor/chain/ChainModel.hpp b/src/actor/chain/ChainModel.hpp new file mode 100644 index 00000000..5a7aef56 --- /dev/null +++ b/src/actor/chain/ChainModel.hpp @@ -0,0 +1,37 @@ +/******************************************************************************* + * Project: Nebula + * @file ChainModel.hpp + * @brief + * @author bwar + * @date: June 6, 2019 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CHAIN_CHAINMODEL_HPP_ +#define SRC_ACTOR_CHAIN_CHAINMODEL_HPP_ + +#include "actor/Actor.hpp" + +namespace neb +{ + +class ChainModel: public Actor +{ +public: + ChainModel(ev_tstamp dChainTimeout = 60.0); + ChainModel(const ChainModel&) = delete; + ChainModel& operator=(const ChainModel&) = delete; + virtual ~ChainModel(); + + /** + * @brief 功能链超时回调 + */ + virtual E_CMD_STATUS Timeout() = 0; + +private: + friend class WorkerImpl; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CHAIN_CHAINMODEL_HPP_ */ diff --git a/src/actor/cmd/Cmd.hpp b/src/actor/cmd/Cmd.hpp index 95a2d564..33644861 100644 --- a/src/actor/cmd/Cmd.hpp +++ b/src/actor/cmd/Cmd.hpp @@ -63,8 +63,6 @@ class Cmd: public CmdModel template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); - template std::shared_ptr MakeSharedCmd(const std::string& strCmdName, Targs... args); - template std::shared_ptr MakeSharedModule(const std::string& strModuleName, Targs... args); }; template @@ -85,18 +83,6 @@ std::shared_ptr Cmd::MakeSharedSession(const std::string& strSessionNam return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); } -template -std::shared_ptr Cmd::MakeSharedCmd(const std::string& strCmdName, Targs... args) -{ - return(m_pWorker->MakeSharedStep(this, strCmdName, std::forward(args)...)); -} - -template -std::shared_ptr Cmd::MakeSharedModule(const std::string& strModuleName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strModuleName, std::forward(args)...)); -} - } /* namespace neb */ #endif /* SRC_ACTOR_CMD_CMD_HPP_ */ diff --git a/src/actor/cmd/Module.hpp b/src/actor/cmd/Module.hpp index 343fce56..829c8fb1 100644 --- a/src/actor/cmd/Module.hpp +++ b/src/actor/cmd/Module.hpp @@ -55,8 +55,6 @@ class Module: public ModuleModel template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); - template std::shared_ptr MakeSharedCmd(const std::string& strCmdName, Targs... args); - template std::shared_ptr MakeSharedModule(const std::string& strModuleName, Targs... args); }; template @@ -77,18 +75,6 @@ std::shared_ptr Module::MakeSharedSession(const std::string& strSession return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); } -template -std::shared_ptr Module::MakeSharedCmd(const std::string& strCmdName, Targs... args) -{ - return(m_pWorker->MakeSharedStep(this, strCmdName, std::forward(args)...)); -} - -template -std::shared_ptr Module::MakeSharedModule(const std::string& strModuleName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strModuleName, std::forward(args)...)); -} - } /* namespace neb */ #endif /* SRC_ACTOR_CMD_MODULE_HPP_ */ diff --git a/src/actor/engine/Engine.hpp b/src/actor/engine/Engine.hpp new file mode 100644 index 00000000..6db8fdbc --- /dev/null +++ b/src/actor/engine/Engine.hpp @@ -0,0 +1,80 @@ +/******************************************************************************* + * Project: Nebula + * @file Engine.hpp + * @brief + * @author Bwar + * @date: 2019年6月7日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_ENGINE_HPP_ +#define SRC_ACTOR_ENGINE_HPP_ + +#include "labor/Worker.hpp" +#include "EngineModel.hpp" +#include "actor/DynamicCreator.hpp" + +namespace neb +{ + +class Engine: public EngineModel +{ +public: + Engine() + : EngineModel(Actor::ACT_ENGINE, gc_dNoTimeout) + { + } + Engine(const Engine&) = delete; + Engine& operator=(const Engine&) = delete; + virtual ~Engine(){} + + /** + * @brief 提交 + */ + virtual E_CMD_STATUS Launch() = 0; + +protected: + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); + template std::shared_ptr MakeSharedStep(const std::string& strEngineName, Targs... args); + template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); + template std::shared_ptr MakeSharedCmd(const std::string& strCmdName, Targs... args); + template std::shared_ptr MakeSharedModule(const std::string& strModuleName, Targs... args); + +private: + friend class WorkerImpl; +}; + +template +void Engine::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) +{ + m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); +} + +template +std::shared_ptr Engine::MakeSharedStep(const std::string& strEngineName, Targs... args) +{ + return(m_pWorker->MakeSharedStep(this, strEngineName, std::forward(args)...)); +} + +template +std::shared_ptr Engine::MakeSharedSession(const std::string& strSessionName, Targs... args) +{ + return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); +} + +template +std::shared_ptr Engine::MakeSharedCmd(const std::string& strCmdName, Targs... args) +{ + return(m_pWorker->MakeSharedCmd(this, strCmdName, std::forward(args)...)); +} + +template +std::shared_ptr Engine::MakeSharedModule(const std::string& strModuleName, Targs... args) +{ + return(m_pWorker->MakeSharedSession(this, strModuleName, std::forward(args)...)); +} + + +} /* namespace neb */ + +#endif /* SRC_ACTOR_ENGINE_HPP_ */ diff --git a/src/actor/engine/EngineModel.hpp b/src/actor/engine/EngineModel.hpp new file mode 100644 index 00000000..55063d50 --- /dev/null +++ b/src/actor/engine/EngineModel.hpp @@ -0,0 +1,40 @@ +/******************************************************************************* + * Project: Nebula + * @file EngineModel.hpp + * @brief + * @author bwar + * @date: June 6, 2019 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_ENGINEMODEL_HPP_ +#define SRC_ACTOR_ENGINEMODEL_HPP_ + +#include "actor/Actor.hpp" + +namespace neb +{ + +class EngineModel: public Actor +{ +public: + EngineModel(Actor::ACTOR_TYPE eActorType, ev_tstamp dTimeout = gc_dDefaultTimeout) + : Actor(eActorType, dTimeout) + { + } + EngineModel(const EngineModel&) = delete; + EngineModel& operator=(const EngineModel&) = delete; + virtual ~EngineModel(); + + /** + * @brief 提交 + */ + virtual E_CMD_STATUS Launch() = 0; + +private: + friend class WorkerImpl; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_ENGINEMODEL_HPP_ */ diff --git a/src/actor/session/Session.hpp b/src/actor/session/Session.hpp index 53e3169f..5124d057 100644 --- a/src/actor/session/Session.hpp +++ b/src/actor/session/Session.hpp @@ -56,8 +56,6 @@ class Session: public SessionModel template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); - template std::shared_ptr MakeSharedCmd(const std::string& strCmdName, Targs... args); - template std::shared_ptr MakeSharedModule(const std::string& strModuleName, Targs... args); private: uint32 PopWaitingStep(); @@ -87,18 +85,6 @@ std::shared_ptr Session::MakeSharedSession(const std::string& strSessio return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); } -template -std::shared_ptr Session::MakeSharedCmd(const std::string& strCmdName, Targs... args) -{ - return(m_pWorker->MakeSharedStep(this, strCmdName, std::forward(args)...)); -} - -template -std::shared_ptr Session::MakeSharedModule(const std::string& strModuleName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strModuleName, std::forward(args)...)); -} - } /* namespace neb */ #endif /* SRC_ACTOR_SESSION_SESSION_HPP_ */ diff --git a/src/actor/session/SessionModel.cpp b/src/actor/session/SessionModel.cpp index ee0b9199..40dc2d4c 100644 --- a/src/actor/session/SessionModel.cpp +++ b/src/actor/session/SessionModel.cpp @@ -29,7 +29,6 @@ SessionModel::SessionModel(const std::string& strSessionId, ev_tstamp dSessionTi SessionModel::~SessionModel() { - // TODO Auto-generated destructor stub } } /* namespace neb */ diff --git a/src/actor/step/Step.hpp b/src/actor/step/Step.hpp index 9ac13684..371bed10 100644 --- a/src/actor/step/Step.hpp +++ b/src/actor/step/Step.hpp @@ -43,8 +43,6 @@ class Step: public StepModel template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); - template std::shared_ptr MakeSharedCmd(const std::string& strCmdName, Targs... args); - template std::shared_ptr MakeSharedModule(const std::string& strModuleName, Targs... args); private: friend class WorkerImpl; @@ -68,19 +66,6 @@ std::shared_ptr Step::MakeSharedSession(const std::string& strSessionNa return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); } -template -std::shared_ptr Step::MakeSharedCmd(const std::string& strCmdName, Targs... args) -{ - return(m_pWorker->MakeSharedStep(this, strCmdName, std::forward(args)...)); -} - -template -std::shared_ptr Step::MakeSharedModule(const std::string& strModuleName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strModuleName, std::forward(args)...)); -} - - } /* namespace neb */ #endif /* SRC_ACTOR_STEP_STEP_HPP_ */ diff --git a/src/actor/step/StepModel.cpp b/src/actor/step/StepModel.cpp index d9ffcaed..6b9f76ed 100644 --- a/src/actor/step/StepModel.cpp +++ b/src/actor/step/StepModel.cpp @@ -24,7 +24,6 @@ StepModel::StepModel(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextSt StepModel::~StepModel() { - // TODO Auto-generated destructor stub m_setNextStepSeq.clear(); m_setPreStepSeq.clear(); } diff --git a/src/channel/SocketChannelSslImpl.cpp b/src/channel/SocketChannelSslImpl.cpp index fd70accd..a36d4a03 100644 --- a/src/channel/SocketChannelSslImpl.cpp +++ b/src/channel/SocketChannelSslImpl.cpp @@ -14,8 +14,8 @@ namespace neb { -SSL_CTX* SocketChannelSslImpl::m_pServerSslCtx = NULL; -SSL_CTX* SocketChannelSslImpl::m_pClientSslCtx = NULL; +SSL_CTX* SocketChannelSslImpl::s_pServerSslCtx = NULL; +SSL_CTX* SocketChannelSslImpl::s_pClientSslCtx = NULL; SocketChannelSslImpl::SocketChannelSslImpl( SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) @@ -63,51 +63,51 @@ int SocketChannelSslImpl::SslInit(std::shared_ptr pLogger) int SocketChannelSslImpl::SslServerCtxCreate(std::shared_ptr pLogger) { pLogger->WriteLog(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, " "); - m_pServerSslCtx = SSL_CTX_new(TLS_server_method()); + s_pServerSslCtx = SSL_CTX_new(TLS_server_method()); - if (m_pServerSslCtx == NULL) + if (s_pServerSslCtx == NULL) { pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "SSL_CTX_new() failed"); return(ERR_SSL_CTX); } - SSL_CTX_set_min_proto_version(m_pServerSslCtx, TLS1_1_VERSION); + SSL_CTX_set_min_proto_version(s_pServerSslCtx, TLS1_1_VERSION); #ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG - SSL_CTX_set_options(m_pServerSslCtx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG); + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG); #endif #ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER - SSL_CTX_set_options(m_pServerSslCtx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); #endif #ifdef SSL_OP_TLS_D5_BUG - SSL_CTX_set_options(m_pServerSslCtx, SSL_OP_TLS_D5_BUG); + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_TLS_D5_BUG); #endif #ifdef SSL_OP_TLS_BLOCK_PADDING_BUG - SSL_CTX_set_options(m_pServerSslCtx, SSL_OP_TLS_BLOCK_PADDING_BUG); + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_TLS_BLOCK_PADDING_BUG); #endif #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS - SSL_CTX_set_options(m_pServerSslCtx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); #endif - SSL_CTX_set_options(m_pServerSslCtx, SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_SINGLE_DH_USE); #ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(m_pServerSslCtx, SSL_OP_NO_COMPRESSION); + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_NO_COMPRESSION); #endif #ifdef SSL_MODE_RELEASE_BUFFERS - SSL_CTX_set_mode(m_pServerSslCtx, SSL_MODE_RELEASE_BUFFERS); + SSL_CTX_set_mode(s_pServerSslCtx, SSL_MODE_RELEASE_BUFFERS); #endif #ifdef SSL_MODE_NO_AUTO_CHAIN - SSL_CTX_set_mode(m_pServerSslCtx, SSL_MODE_NO_AUTO_CHAIN); + SSL_CTX_set_mode(s_pServerSslCtx, SSL_MODE_NO_AUTO_CHAIN); #endif - SSL_CTX_set_read_ahead(m_pServerSslCtx, 1); + SSL_CTX_set_read_ahead(s_pServerSslCtx, 1); return(ERR_OK); } @@ -118,7 +118,7 @@ int SocketChannelSslImpl::SslServerCertificate(std::shared_ptr pLogge pLogger->WriteLog(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, "SslServerCertificate(%s, %s)", strCertFile.c_str(), strKeyFile.c_str()); // 加载使用公钥证书 - if (!SSL_CTX_use_certificate_chain_file(m_pServerSslCtx, strCertFile.c_str())) + if (!SSL_CTX_use_certificate_chain_file(s_pServerSslCtx, strCertFile.c_str())) { pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "SSL_CTX_user_certificate_chain_file(\"%s\") failed!", strCertFile.c_str()); @@ -126,7 +126,7 @@ int SocketChannelSslImpl::SslServerCertificate(std::shared_ptr pLogge } // 加载使用私钥 - if (!SSL_CTX_use_PrivateKey_file(m_pServerSslCtx, strKeyFile.c_str(), SSL_FILETYPE_PEM)) + if (!SSL_CTX_use_PrivateKey_file(s_pServerSslCtx, strKeyFile.c_str(), SSL_FILETYPE_PEM)) { pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "SSL_CTX_use_PrivateKey_file(\"%s\") failed!", strKeyFile.c_str()); @@ -134,7 +134,7 @@ int SocketChannelSslImpl::SslServerCertificate(std::shared_ptr pLogge } // 检查私钥与证书是否匹配 - if (!SSL_CTX_check_private_key(m_pServerSslCtx)) + if (!SSL_CTX_check_private_key(s_pServerSslCtx)) { pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "SSL_CTX_check_private_key() failed: private key does not match the certificate public key!", strKeyFile.c_str()); @@ -145,24 +145,24 @@ int SocketChannelSslImpl::SslServerCertificate(std::shared_ptr pLogge void SocketChannelSslImpl::SslFree() { - if (m_pServerSslCtx) + if (s_pServerSslCtx) { - SSL_CTX_free(m_pServerSslCtx); - m_pServerSslCtx = NULL; + SSL_CTX_free(s_pServerSslCtx); + s_pServerSslCtx = NULL; } - if (m_pClientSslCtx) + if (s_pClientSslCtx) { - SSL_CTX_free(m_pClientSslCtx); - m_pClientSslCtx = NULL; + SSL_CTX_free(s_pClientSslCtx); + s_pClientSslCtx = NULL; } } int SocketChannelSslImpl::SslClientCtxCreate() { - if (m_pClientSslCtx == NULL) + if (s_pClientSslCtx == NULL) { - m_pClientSslCtx = SSL_CTX_new(TLS_client_method()); - if (m_pClientSslCtx == NULL) + s_pClientSslCtx = SSL_CTX_new(TLS_client_method()); + if (s_pClientSslCtx == NULL) { LOG4_ERROR("SSL_CTX_new() failed!"); return(ERR_SSL_CTX); @@ -175,11 +175,11 @@ int SocketChannelSslImpl::SslCreateConnection() { if (m_bIsClientConnection) { - m_pSslConnection = SSL_new(m_pClientSslCtx); + m_pSslConnection = SSL_new(s_pClientSslCtx); } else { - m_pSslConnection = SSL_new(m_pServerSslCtx); + m_pSslConnection = SSL_new(s_pServerSslCtx); } if (m_pSslConnection == NULL) @@ -376,11 +376,11 @@ int SocketChannelSslImpl::SslShutdown() if (m_bIsClientConnection) { - SSL_CTX_remove_session(m_pClientSslCtx, SSL_get0_session(m_pSslConnection)); + SSL_CTX_remove_session(s_pClientSslCtx, SSL_get0_session(m_pSslConnection)); } else { - SSL_CTX_remove_session(m_pServerSslCtx, SSL_get0_session(m_pSslConnection)); + SSL_CTX_remove_session(s_pServerSslCtx, SSL_get0_session(m_pSslConnection)); } SSL_free(m_pSslConnection); diff --git a/src/channel/SocketChannelSslImpl.hpp b/src/channel/SocketChannelSslImpl.hpp index d36ca075..f9890b0f 100644 --- a/src/channel/SocketChannelSslImpl.hpp +++ b/src/channel/SocketChannelSslImpl.hpp @@ -81,8 +81,8 @@ class SocketChannelSslImpl : public SocketChannelImpl bool m_bIsClientConnection; SSL* m_pSslConnection; - static SSL_CTX* m_pServerSslCtx; - static SSL_CTX* m_pClientSslCtx; + static SSL_CTX* s_pServerSslCtx; + static SSL_CTX* s_pClientSslCtx; }; } diff --git a/src/labor/.WorkerImpl.hpp.swp b/src/labor/.WorkerImpl.hpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..c0bed6b9cca180a1bfe09931b20f01f0b2d3a214 GIT binary patch literal 28672 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 L-2FN0S?T`(iT(7h literal 0 HcmV?d00001 diff --git a/src/labor/.WorkerImpl.inl.swp b/src/labor/.WorkerImpl.inl.swp new file mode 100644 index 0000000000000000000000000000000000000000..2af31ad388f51e479bfa381dd55c8b57ef9f3ba5 GIT binary patch literal 16384 zcmeHNU5p!76}}J%zoisjc%ru(vWy+a-lPZx@2ERK9mW30k{Ty4$y&T-_F=CfDNDtG=LKD%-b0I2~Yvv2fP>f)ms_+ zF>nj`Ja9korMEEl1n^1V6mS%{{btw$`~dhO@EEWK6oI3_i*G`mz~jI+a1MA7cmTM4 z6zRY!Py*fq906W>BV&I6UI1h{((Tti@;lUtsxV^{`M5~2m(@x-W$BJHtP)l9Re>6Z(9M5$}0p}8k3L$x2|^AlnH zN{*kTXUpCTOvn}ZQZZKX{=@`hz?E%X?YHc(n1@yms37MnNmWvFgjaNhZd-ksqm4bA zBayH2p*qw?p}}M=zCLk<<0-D!FPDefWucBV!%^Zbxgflb`1Dv*6sI#LvqEE`X z!17p9(Gjljq<)a=7w!5=>d13eNV5_zW*I()E9$c7iPEKQ{i~KXG<0=j8-g(0Or_{Z_Ta6)$X7Piz~Ol3V7!@RYBBgSfPB=rTWX z!ml;WKYV5ESc)|^O>|g{kjR429NYu3X!M{zgeRoAGBxte)>fmzS65Rbj}cqpAk8N; z!tcV5Q zyF91sXu7EPVW}KO_O4KD*Dbo919wOkN3|i3udKj-TrcQ?Mbk*GH1|9$#MIM1ScYJRE3SGJIQR)-gNcNjq_WLTC(=EOtpD_zCgQn zzTQzg-P-1SxktL)%{vg=JeLxRNOt!OtT#%$rNZaCdvzqD$sA>WU>-k1A;+G(^RM-i%u2mSKQLzVCV*#OXxXp*=bQ@*B^LFat)g;E>;*F28bNp<2*!nABxJH)`(31{K zZS|L{d`t|phEIab$och3@o45ba6py!5R;Th>;OZN=hI)nuxVb?bLJl(zYN+g(F|Z&uS0;Z?uM*31%t*%I zD&H|&`jWkjln-0BC+ObC;3(5KP7Yz1{{Ioq!B69yOy~db|N9qk#{Ug)9iVgn zS>R=y>;DY=5_lH)Dezt3%fJ)Bbzld03FrL300z(mE&zYW8UMGyuYnst8Tb(J0PsA{ z`nP~@0^b0-KnrLB3jhOd;Ozf3;5wiK9|!)0v;WV5rvVM<0879kz=40#*&m?T!0&+P zf#(2f>n$L=G6pgRG6pgRG6pgRUMmb7Lx>Is`BI*D+EsPV=8)%0bR-E*B{NTobm$zL z!ao&LI8DCdDG|Dhj+@aFZP$V#gCMWe4GxWzO;=KZQigtws%~non+VBH;;%8APF4`h zknfwCw=)$plV?*ZmM#i!Ocg(g$2M%OFZ!~uVk#a>+I~QFT0Mx%Cba#yY@9pa&M0CM zMqQ}b$V4(PsZ_@w2w_-Q%8?9cog1YOns7jTUgicd(G+`=7`+q<%}jJ)7^e?JmKil7 zea@DdlUTicor=~c8+$K9RkFgMktjH#9tUli84p*@5T75gN-Nx9$SSIN610*@GHf+7 zP!;LKLRB&`7)$a6zrsZ1LcWC~R*7fkf=t494-yUi(V7So$r_2?(BQd& zA}KCIMe=#lh;+Cp$iCZvQDo2|!IGIFBdJ}9ojv10Bbj@rVWYSuuOM(Vv!6!QVL$9` zdVXb-GZYVt=xr50#Sz>A?MiB>2!zsnY987w00>)D`&qY%}IIWR~ Mr)J+sBb<8w1J9cQ%m4rY literal 0 HcmV?d00001 diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 3a4ee5f5..e35ff916 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -192,6 +192,9 @@ class WorkerImpl template void Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); + template + std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args); + template std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args); @@ -343,6 +346,11 @@ class WorkerImpl std::unordered_map > m_mapCmd; std::unordered_map > m_mapModule; + // Chain and Engine + std::unordered_map > m_mapChainConf; //key为Chain的类名,value为由Engine类名和Step类名构成的ChainBlock链 + std::unordered_map > m_mapChain; //key为Chain的Sequence + std::unordered_map > m_mapEngine; //key为Engine的Sequence + // Step and Session std::unordered_map > m_mapCallbackStep; std::unordered_map > m_mapCallbackSession; diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp index f27cacae..9310c63e 100644 --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -23,7 +23,7 @@ namespace neb { -FileLogger* FileLogger::m_pInstance = nullptr; +FileLogger* FileLogger::s_pInstance = nullptr; FileLogger::FileLogger(const std::string& strLogFile, int iLogLev, unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex) diff --git a/src/logger/FileLogger.hpp b/src/logger/FileLogger.hpp index 8ec363be..60e6cf01 100644 --- a/src/logger/FileLogger.hpp +++ b/src/logger/FileLogger.hpp @@ -38,11 +38,11 @@ class FileLogger: public Logger unsigned int uiMaxFileSize = neb::gc_uiMaxLogFileSize, unsigned int uiMaxRollFileIndex = neb::gc_uiMaxRollLogFileIndex) { - if (m_pInstance == nullptr) + if (s_pInstance == nullptr) { - m_pInstance = new FileLogger(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex); + s_pInstance = new FileLogger(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex); } - return(m_pInstance); + return(s_pInstance); } void SetLogLevel(int iLev) @@ -60,7 +60,7 @@ class FileLogger: public Logger int Vappend(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, va_list ap); int Vappend(const std::string& strTraceId, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, va_list ap); - static FileLogger* m_pInstance; + static FileLogger* s_pInstance; #if __GNUC__ < 5 char* m_szTime; From dd7877477500e3e33ad72df3bb9772918c879548 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 8 Jun 2019 18:01:11 +0800 Subject: [PATCH 028/176] change static variable name from m_xxxx to s_xxxx, and try to add chain. --- src/labor/.WorkerImpl.hpp.swp | Bin 28672 -> 0 bytes src/labor/.WorkerImpl.inl.swp | Bin 16384 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/labor/.WorkerImpl.hpp.swp delete mode 100644 src/labor/.WorkerImpl.inl.swp diff --git a/src/labor/.WorkerImpl.hpp.swp b/src/labor/.WorkerImpl.hpp.swp deleted file mode 100644 index c0bed6b9cca180a1bfe09931b20f01f0b2d3a214..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28672 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 L-2FN0S?T`(iT(7h diff --git a/src/labor/.WorkerImpl.inl.swp b/src/labor/.WorkerImpl.inl.swp deleted file mode 100644 index 2af31ad388f51e479bfa381dd55c8b57ef9f3ba5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHNU5p!76}}J%zoisjc%ru(vWy+a-lPZx@2ERK9mW30k{Ty4$y&T-_F=CfDNDtG=LKD%-b0I2~Yvv2fP>f)ms_+ zF>nj`Ja9korMEEl1n^1V6mS%{{btw$`~dhO@EEWK6oI3_i*G`mz~jI+a1MA7cmTM4 z6zRY!Py*fq906W>BV&I6UI1h{((Tti@;lUtsxV^{`M5~2m(@x-W$BJHtP)l9Re>6Z(9M5$}0p}8k3L$x2|^AlnH zN{*kTXUpCTOvn}ZQZZKX{=@`hz?E%X?YHc(n1@yms37MnNmWvFgjaNhZd-ksqm4bA zBayH2p*qw?p}}M=zCLk<<0-D!FPDefWucBV!%^Zbxgflb`1Dv*6sI#LvqEE`X z!17p9(Gjljq<)a=7w!5=>d13eNV5_zW*I()E9$c7iPEKQ{i~KXG<0=j8-g(0Or_{Z_Ta6)$X7Piz~Ol3V7!@RYBBgSfPB=rTWX z!ml;WKYV5ESc)|^O>|g{kjR429NYu3X!M{zgeRoAGBxte)>fmzS65Rbj}cqpAk8N; z!tcV5Q zyF91sXu7EPVW}KO_O4KD*Dbo919wOkN3|i3udKj-TrcQ?Mbk*GH1|9$#MIM1ScYJRE3SGJIQR)-gNcNjq_WLTC(=EOtpD_zCgQn zzTQzg-P-1SxktL)%{vg=JeLxRNOt!OtT#%$rNZaCdvzqD$sA>WU>-k1A;+G(^RM-i%u2mSKQLzVCV*#OXxXp*=bQ@*B^LFat)g;E>;*F28bNp<2*!nABxJH)`(31{K zZS|L{d`t|phEIab$och3@o45ba6py!5R;Th>;OZN=hI)nuxVb?bLJl(zYN+g(F|Z&uS0;Z?uM*31%t*%I zD&H|&`jWkjln-0BC+ObC;3(5KP7Yz1{{Ioq!B69yOy~db|N9qk#{Ug)9iVgn zS>R=y>;DY=5_lH)Dezt3%fJ)Bbzld03FrL300z(mE&zYW8UMGyuYnst8Tb(J0PsA{ z`nP~@0^b0-KnrLB3jhOd;Ozf3;5wiK9|!)0v;WV5rvVM<0879kz=40#*&m?T!0&+P zf#(2f>n$L=G6pgRG6pgRG6pgRUMmb7Lx>Is`BI*D+EsPV=8)%0bR-E*B{NTobm$zL z!ao&LI8DCdDG|Dhj+@aFZP$V#gCMWe4GxWzO;=KZQigtws%~non+VBH;;%8APF4`h zknfwCw=)$plV?*ZmM#i!Ocg(g$2M%OFZ!~uVk#a>+I~QFT0Mx%Cba#yY@9pa&M0CM zMqQ}b$V4(PsZ_@w2w_-Q%8?9cog1YOns7jTUgicd(G+`=7`+q<%}jJ)7^e?JmKil7 zea@DdlUTicor=~c8+$K9RkFgMktjH#9tUli84p*@5T75gN-Nx9$SSIN610*@GHf+7 zP!;LKLRB&`7)$a6zrsZ1LcWC~R*7fkf=t494-yUi(V7So$r_2?(BQd& zA}KCIMe=#lh;+Cp$iCZvQDo2|!IGIFBdJ}9ojv10Bbj@rVWYSuuOM(Vv!6!QVL$9` zdVXb-GZYVt=xr50#Sz>A?MiB>2!zsnY987w00>)D`&qY%}IIWR~ Mr)J+sBb<8w1J9cQ%m4rY From f086c52fe9201aa9b57a5a1ee8693653e4f1bdcc Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 16 Jun 2019 19:19:47 +0800 Subject: [PATCH 029/176] add chain and matrix --- .gitignore | 1 + README.md | 6 +- README_cn.md | 6 +- conf/nebula.json | 11 +- docs/cn/user_guide.md | 13 + docs/cn/why_nebula.md | 61 ++++ src/actor/Actor.cpp | 10 +- src/actor/Actor.hpp | 33 +- src/actor/ActorWithCreation.hpp | 51 +++ src/actor/chain/Chain.cpp | 54 ++- src/actor/chain/Chain.hpp | 50 +-- src/actor/chain/ChainModel.cpp | 24 -- src/actor/chain/ChainModel.hpp | 37 -- src/actor/cmd/Cmd.hpp | 36 +- src/actor/cmd/CmdModel.hpp | 72 ---- src/actor/cmd/Module.hpp | 36 +- src/actor/cmd/ModuleModel.hpp | 65 ---- src/actor/engine/Engine.hpp | 80 ----- src/actor/engine/EngineModel.hpp | 40 --- src/actor/matrix/Matrix.hpp | 57 +++ src/actor/session/Session.cpp | 10 +- src/actor/session/Session.hpp | 33 +- src/actor/session/SessionModel.cpp | 34 -- src/actor/session/SessionModel.hpp | 44 --- src/actor/step/{StepModel.cpp => Step.cpp} | 17 +- src/actor/step/Step.hpp | 50 ++- src/actor/step/StepModel.hpp | 54 --- src/labor/Worker.cpp | 5 + src/labor/Worker.hpp | 28 +- src/labor/WorkerImpl.cpp | 389 ++++++++++++++++++++- src/labor/WorkerImpl.hpp | 34 +- src/labor/WorkerImpl.inl | 262 ++++---------- 32 files changed, 832 insertions(+), 871 deletions(-) create mode 100644 docs/cn/user_guide.md create mode 100644 docs/cn/why_nebula.md create mode 100644 src/actor/ActorWithCreation.hpp delete mode 100644 src/actor/chain/ChainModel.cpp delete mode 100644 src/actor/chain/ChainModel.hpp delete mode 100644 src/actor/cmd/CmdModel.hpp delete mode 100644 src/actor/cmd/ModuleModel.hpp delete mode 100644 src/actor/engine/Engine.hpp delete mode 100644 src/actor/engine/EngineModel.hpp create mode 100644 src/actor/matrix/Matrix.hpp delete mode 100644 src/actor/session/SessionModel.cpp delete mode 100644 src/actor/session/SessionModel.hpp rename src/actor/step/{StepModel.cpp => Step.cpp} (65%) delete mode 100644 src/actor/step/StepModel.hpp diff --git a/.gitignore b/.gitignore index 07acaa31..345f6b40 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ aclocal.m4 compile install-sh missing +*.swp diff --git a/README.md b/README.md index b77dee9b..7e1880fd 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,8 @@ MIT License build step: 1. wget https://github.com/Bwar/NebulaBootstrap/archive/master.zip -2. unzip master.zip; rm master.zip; mv NebulaBootstrap-master NebulaBootstrap -3. cd NebulaBootstrap -4. chmod u+x deploy.sh -5. ./deploy.sh +2. unzip master.zip; rm master.zip; mv NebulaBootstrap-master NebulaBootstrap; chmod u+x deploy.sh; chmod u+x deploy.sh +3. ./deploy.sh Run deploy.sh, the NebulaBootstrap distributed services were build completed. The reliance of Nebula was also automatically downloaded and compiled by deploy from the Internet before the construction of Nebula. The deploy path as follows: * NebulaBootstrap diff --git a/README_cn.md b/README_cn.md index 712fcd0e..2756e8b2 100644 --- a/README_cn.md +++ b/README_cn.md @@ -45,10 +45,8 @@ 构建步骤: 1. wget https://github.com/Bwar/NebulaBootstrap/archive/master.zip -2. unzip master.zip; rm master.zip; mv NebulaBootstrap-master NebulaBootstrap -3. cd NebulaBootstrap -4. chmod u+x deploy.sh -5. ./deploy.sh +2. unzip master.zip; rm master.zip; mv NebulaBootstrap-master NebulaBootstrap; cd NebulaBootstrap; chmod u+x deploy.sh +3. ./deploy.sh   执行deploy脚本后即完成了Nebula及NebulaBootstrap分布式服务的编译和部署,Nebula的依赖也由deploy在构建Nebula前自动从网上下载并编译部署。虽然不像autoconf、automake那样众所周知,但deploy脚本完成的不止是autoconf、automake的工作。deploy之后的目录结构如下: * NebulaBootstrap diff --git a/conf/nebula.json b/conf/nebula.json index 2f08b9f3..68b61877 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -77,7 +77,9 @@ { "path": "im/user/logout", "class": "neb::ModuleLogout" } ], "session":["im::SessionUser", "im::SessionGroup"], - "step":["im::StepLogin", "im::StepLogout", "im::StepP2pChat"] + "step":["im::StepLogin", "im::StepLogout", "im::StepP2pChat"], + "matrix":[], + "chain":[] }, { "so_path": "plugins/ChatMsg.so", @@ -88,13 +90,14 @@ ], "module": [], "session":[], - "step":[] + "step":[], + "matrix":[], + "chain":[] } ], "runtime":{ - "engines":[], "chains":[ - "chain_1":["step1", "engine1", ["step2A", "step2B", "step2C"], "step3", "engine2"], + "chain_1":["step1", "matrix1", ["step2A", "step2B", "step2C"], "step3", "matrix2"], "chain_2":[] ] } diff --git a/docs/cn/user_guide.md b/docs/cn/user_guide.md new file mode 100644 index 00000000..dcd07b8d --- /dev/null +++ b/docs/cn/user_guide.md @@ -0,0 +1,13 @@ +### 使用说明 +Nebula是一个事件驱动型框架,事件分为“请求”和“响应”两种。请求事件都由不同功能模块处理,响应事件由指定的Step回调类对象处理。 +Cmd类和Module类同为请求事件对应模块的处理入口,其中Cmd类用于事件中带有Cmd功能码的请求,Module用于带有url_path的http请求。Cmd和Module可以理解为某些框架的EventHandler。许多网络框架的EventHandler通过pipeline.add_last()方式添加到事件处理队列,一个事件到来从pipeline头部到尾部流过一个一个Handler,Handler根据eventType判断是否处理,不处理的就直接调用next Handler的处理方法,需要处理的就在处理完后调用next Handler处理方法。Nebula中不是通过pipeline来处理的,我们认为每个event都只会有一个处理者(Cmd或Module),事件的处理过程可能在单个Cmd或Module内就处理完毕,也可能需要经过几个步骤(Step)才处理完。因此,Nebula的Cmd和Module都是用hash表存储,event到来时直接定位到对应的Cmd或Module处理。 +我们来看看Cmd类的声明: +```C++ +``` +Module类的声明: +```C++ +``` + +Step类是异步IO处理的关键。在分布式系统中编程,任何一个任务,都可能被分解到不同节点,通过多次网络通信组合来完成。为了让每个服务进程达到足够高的并发,异步非阻塞编程模型是最好的选择,没有之一。而采用异步非阻塞编程模型意味着属于一个任务的代码被分割成很多个回调函数,在代码的各处被串接起来,非常不利于代码阅读,也不符合大部分人的思维习惯。到处都是回调函数,更甚者,使用“观察者模式”在一个地方注册大量的“事件-响应函数”,然后在所有需要回调的地方,都发出一个事件。使用“观察者模式”的代码,比单纯的注册回调函数更难理解,因为事件对应的响应函数,通常在发出事件处是无法找到的,往往都放在另外的一些文件里,而且有时候这些函数还会在运行时改变。而事件名本身往往也不是一个名符其实的名字。协程、Future/Promise模型、lamda模型等是为了解决回调函数对于代码可读性的破坏作用而设计和引入的。在Nebula,有另一种解决方案,就是Step类。 + +在Nebula,有另一种解决方案,就是Step类。通过Step类将回调的上下文关联起来,Step::Emit()是发出IO请求,Step::Callback()是IO请求所对应的响应回调,回调的上下文信息保存在Step类的成员变量里。这样一种设计让IO请求和响应看起来从未离开过同一个类对象,也无须传递上下文信息,与同步IO相比差异在于:同步的IO请求和响应在同一个函数的前后两行代码,而Step异步IO是在同一个类的两个不同成员函数。Step类实现异步IO比直接写回调函数、观察者模式、Future/Promise模型、lamda模型都更为直观,更容易编写和理解,又比协程实现更为节省空间,并发能力更强。实质上,Step类底层还是通过回调函数实现的,只是回调实现的复杂性由Nebula框架解决了,给业务开发者提供了更为简单的Step异步IO。 diff --git a/docs/cn/why_nebula.md b/docs/cn/why_nebula.md new file mode 100644 index 00000000..9235a91a --- /dev/null +++ b/docs/cn/why_nebula.md @@ -0,0 +1,61 @@ +### 概述 +  Nebula是一个C\+\+语言开发的事件驱动型的TCP协议分布式网络框架,支持包括proto3、http、https、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建高性能的分布式服务。Nebula自身核心代码只有2万行左右(不计算proto文件生成的代码)。 + +  Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建分布式服务才能真正体现其价值。为了能快速搭建分布式服务,开发了包括各种类型服务的NebulaBootstrap解决方案。 + +  Nebula是一个产线级的框架和分布式解决方案项目,适用于即时通讯、数据采集、实时计算、消息推送等应用场景,也适用于web后台服务。Nebula已有即时通讯、埋点数据采集及实时分析的生产应用案例。 + +### 选择Nebula的理由 +1. Nebula源于互联网生产线业务,却高于业务,适用于即时通讯、数据采集、实时计算、消息推送、web后台服务等多个业务领域。 +2. Nebula是一个高性能C++网络框架,却又不仅是一个网络框架,还是一个十分通用的业务框架。选择Nebula,不必在网络框架之上再做业务框架封装。 +3. Nebula依赖的第三方库很少,开发和部署无成本。所依赖的protobuf、hiredis是无论选择什么框架都会用到的第三方库,libev和crypto++又是轻量到可以忽略不计。 +4. 无锁设计,无须考虑线程安全问题,无须考虑进程或线程模型,专注写业务代码更得心应手。 +5. 详细的文档,入门简单,扩展容易。 + +### Nebula由来 + +  Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++11)是Starship(C++98)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的第一个埋点数据采集和实时分析项目应用Nebio于2018年7月底上线并稳定运行,Bwar还准备开发基于Nebula的IM应用Nebim。 + +### 大型系统构建原则: +* 可用性 +* 性能 +* 可靠性 +* 可扩展性 +* 易管理 +* 成本 + +### Nebula的功能 +* 支持http、protobuf、websocket等协议通信 +* 支持ssl连接加密 +* 微服务框架 +* IoC容器 +* 动态服务更新 +* 服务注册 +* 服务发现 +* 服务监控 +* 配置管理 +* 动态路由 +* 负载均衡 +* 过载保护 +* 故障熔断 +* 故障检测和恢复 +* 数据统计 +* 分层服务 +* 认证和鉴权 +* 分布式日志跟踪 + +### 还有更多 +  现知名度较高的网络框架以rpc框架为主,且大都是大厂开源产品,有较大量应用案例,更有不少知名度低些却不乏开发者关注的网络框架。这些知名度高的网络框架大都只有简单的rpc功能,少数带有服务治理、配置管理、监控上报、动态路由等功能,又或只开源了基础rpc功能未开源服务治理等功能。而Nebula有完整的分布式服务所需功能,且全部开源。 + +  许多简单联网应用只部署在一台或几台服务器上,启动个server程序,通过rpc客户端或配置nginx代理以轮询方式访问,写个shell脚本监控进程是否存在,挂了就拉起就可以满足业务需求了,这样的应用场景简单rpc确实够用了,大部分开源网络框架都可以满足需求。Nebula更是轻而易举满足这种需求。 + +  然而,对于需要高可靠性,高并发和高扩展性的应用场景,大部分已知的网络框架都是不满足需求的,选型某个网络框架后,开发团队往往还需要基于这些网络框架之上花大量时间费很大力气开发业务框架构建分布式服务。选择Nebula就没有这样的麻烦,Nebula的一站式服务不仅让只需布几个独立server就可以解决的业务变得更轻而易举,还让需要分布式部署的复杂业务也轻轻松松。 + +  异步回调模型在大中型高并发服务系统比多线程更加流行,除了多线程的死锁问题外,异步还能解决多线程下,线程反复切换导致不必要的开销的问题:每个线程都需要一个独立的栈空间,在多线程并行运行的时候,这些栈的数据可能需要来回的拷贝,这额外消耗了CPU。由于每个操作都是非阻塞的,所以Nebula可以只用一个进程,就处理大量并发的请求。因为只有一个进程,所有的数据处理,其顺序都是固定的,不可能出现多线程中,两个函数的语句交错执行的情况,因此也不需要各种“锁”。从这个角度看,异步非阻塞的技术,是大大简化了开发的过程。由于只有一个线程,也不需要有线程切换之类的开销,所以异步非阻塞成为很多对吞吐量、并发有较高要求的系统首选。 + +  Nebula每个进程基于事件驱动的异步回调,性能极高,单个进程做压力测试时可以将CPU利用率达到100%。另一方面,Nebula的多进程模型可以充分利用服务器多CPU多核资源,进程数量可配置,并提供CPU绑定功能,若配置与CPU核数相同的进程数量,并让每个Worker进程都绑定CPU运行,在足够的并发量下可以达到服务器资源的极致利用。 + +  分布式系统在业务需求的功能以外,还需要增加额外很多非功能的需求。这些非功能需求,往往都是为了系统能稳定可靠运行而去设计和实现的,一般都会让你的代码更加复杂,也会耗费大量时间,用Nebula将为你解决业务需求之外的功能。 + + + diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index e7d294f4..1a1dff14 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -96,6 +96,11 @@ bool Actor::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg, return(m_pWorker->ExecStep(uiStepSeq, iErrno, strErrMsg, data)); } +std::shared_ptr Actor::GetMatrix(const std::string& strMatrixName) +{ + return(m_pWorker->GetMatrix(strMatrixName)); +} + std::shared_ptr Actor::GetContext() { return(m_pContext); @@ -190,9 +195,4 @@ void Actor::SetActorName(const std::string& strActorName) m_strActorName = strActorName; } -void Actor::SetChainId(uint32 uiChainId) -{ - m_uiChainId = uiChainId; -} - } /* namespace neb */ diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index f728f103..e75a19bf 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -38,14 +38,14 @@ class WorkerFriend; class SocketChannel; class RedisChannel; -class ActorCreator; +class ActorWithCreation; class Cmd; class Module; class Session; class Timer; class Context; class Step; -class Engine; +class Matrix; class Chain; class Actor: public std::enable_shared_from_this @@ -58,11 +58,12 @@ class Actor: public std::enable_shared_from_this ACT_MODULE = 2, ///< Module对象,处理带url path的http请求 ACT_SESSION = 3, ///< Session会话对象 ACT_TIMER = 4, ///< 定时器对象 - ACT_PB_STEP = 5, ///< Step步骤对象,处理pb请求或响应 - ACT_HTTP_STEP = 6, ///< Step步骤对象,处理http请求或响应 - ACT_REDIS_STEP = 7, ///< Step步骤对象,处理redis请求或响应 - ACT_ENGINE = 8, ///< Engine链块对象,Engine(IO无关)与Step(异步IO相关)共同构成功能链 - ACT_CHAIN = 9, ///< Chain链对象,用于将Engine和Step组合成功能链 + ACT_CONTEXT = 5, ///< 会话上下文对象 + ACT_PB_STEP = 6, ///< Step步骤对象,处理pb请求或响应 + ACT_HTTP_STEP = 7, ///< Step步骤对象,处理http请求或响应 + ACT_REDIS_STEP = 8, ///< Step步骤对象,处理redis请求或响应 + ACT_MATRIX = 9, ///< Matrix模型对象,Matrix(IO无关)与Step(异步IO相关)共同构成功能链 + ACT_CHAIN = 10, ///< Chain链对象,用于将Matrix和Step组合成功能链 }; public: @@ -81,8 +82,9 @@ class Actor: public std::enable_shared_from_this return(m_strActorName); } -protected: uint32 GetSequence(); + +protected: uint32 GetNodeId() const; uint32 GetWorkerIndex() const; ev_tstamp GetDefaultTimeout() const; @@ -100,6 +102,7 @@ class Actor: public std::enable_shared_from_this std::shared_ptr GetSession(uint32 uiSessionId); std::shared_ptr GetSession(const std::string& strSessionId); bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); + std::shared_ptr GetMatrix(const std::string& strMatrixName); std::shared_ptr GetContext(); void SetContext(std::shared_ptr pContext); void AddAssemblyLine(std::shared_ptr pSession); @@ -226,11 +229,6 @@ class Actor: public std::enable_shared_from_this return(m_dTimeout); } - uint32 GetChainId() const - { - return(m_uiChainId); - } - private: void SetWorker(Worker* pWorker); @@ -238,12 +236,9 @@ class Actor: public std::enable_shared_from_this void SetActorName(const std::string& strActorName); - void SetChainId(uint32 uiChainId); - private: ACTOR_TYPE m_eActorType; uint32 m_uiSequence; - uint32 m_uiChainId; ev_tstamp m_dActiveTime; ev_tstamp m_dTimeout; Worker* m_pWorker; @@ -254,11 +249,7 @@ class Actor: public std::enable_shared_from_this friend class WorkerImpl; friend class WorkerFriend; - friend class Cmd; - friend class Module; - friend class Session; - friend class Step; - friend class Engine; + friend class ActorWithCreation; friend class Chain; }; diff --git a/src/actor/ActorWithCreation.hpp b/src/actor/ActorWithCreation.hpp new file mode 100644 index 00000000..1d2d9e58 --- /dev/null +++ b/src/actor/ActorWithCreation.hpp @@ -0,0 +1,51 @@ +/******************************************************************************* + * Project: Nebula + * @file ActorWithCreation.hpp + * @brief 带创建功能的Actor + * @author Bwar + * @date: 2019年6月15日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTORWITHCREATION_ACTORWITHCREATION_HPP_ +#define SRC_ACTORWITHCREATION_ACTORWITHCREATION_HPP_ + +#include "Actor.hpp" + +namespace neb +{ + +class ActorWithCreation: public Actor +{ +public: + ActorWithCreation(Actor::ACTOR_TYPE eActorWithCreationType = Actor::ACT_UNDEFINE, ev_tstamp dTimeout = 0.0); + ActorWithCreation(const ActorWithCreation&) = delete; + ActorWithCreation& operator=(const ActorWithCreation&) = delete; + virtual ~ActorWithCreation(); + + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); + template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); + template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); +}; + +template +void ActorWithCreation::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) +{ + m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); +} + +template +std::shared_ptr ActorWithCreation::MakeSharedStep(const std::string& strStepName, Targs... args) +{ + return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); +} + +template +std::shared_ptr ActorWithCreation::MakeSharedSession(const std::string& strSessionName, Targs... args) +{ + return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); +} + +} /* namespace neb */ + +#endif /* SRC_ACTORWITHCREATION_ACTORWITHCREATION_HPP_ */ diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 8131f231..954109b5 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -10,12 +10,13 @@ #include "Chain.hpp" #include "actor/step/Step.hpp" +#include "actor/matrix/Matrix.hpp" namespace neb { Chain::Chain(ev_tstamp dChainTimeout) - : ChainModel(dChainTimeout) + : ActorWithCreation(Actor::ACT_CHAIN, dChainTimeout) { } @@ -23,4 +24,55 @@ Chain::~Chain() { } +void Chain::InitChainBlock(const std::vector& vecChainBlock) +{ + m_vecChainBlock = vecChainBlock; +} + +E_CMD_STATUS Chain::NextBlock() +{ + auto iter = m_vecChainBlock.begin(); + LOG4_TRACE("(%s)", (*iter).c_str()); + std::shared_ptr pSharedMatrix = GetMatrix(*iter); + if (pSharedMatrix == nullptr) + { + std::shared_ptr pSharedActor = m_pWorker->MakeSharedActor(this, *iter); + // pSharedMatrix->SetContext(GetContext()); it had been set in MakeSharedActor(). + m_vecChainBlock.erase(iter); + if (Actor::ACT_MATRIX == pSharedActor->GetActorType()) + { + if (CMD_STATUS_FAULT + == (std::dynamic_pointer_cast(pSharedActor))->Launch()) + { + return(CMD_STATUS_FAULT); + } + return(NextBlock()); + } + else if (Actor::ACT_PB_STEP == pSharedActor->GetActorType() + || Actor::ACT_HTTP_STEP == pSharedActor->GetActorType() + || Actor::ACT_REDIS_STEP == pSharedActor->GetActorType()) + { + std::shared_ptr pSharedStep = std::dynamic_pointer_cast(pSharedActor); + pSharedStep->SetChainId(GetSequence()); + return(pSharedStep->Emit()); + } + else + { + LOG4_ERROR("\"%s\" is not a Matrix or Step, only Matrix and Step can be a Chain block.", + pSharedActor->GetActorName().c_str()); + return(CMD_STATUS_FAULT); + } + } + else + { + m_vecChainBlock.erase(iter); + pSharedMatrix->SetContext(GetContext()); + if (CMD_STATUS_FAULT == pSharedMatrix->Launch()) + { + return(CMD_STATUS_FAULT); + } + return(NextBlock()); + } +} + } /* namespace neb */ diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index aa942e42..77804afe 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -10,15 +10,15 @@ #ifndef SRC_ACTOR_CHAIN_CHAIN_HPP_ #define SRC_ACTOR_CHAIN_CHAIN_HPP_ -#include +#include #include "labor/Worker.hpp" +#include "actor/ActorWithCreation.hpp" #include "actor/DynamicCreator.hpp" -#include "ChainModel.hpp" namespace neb { -class Chain: public ChainModel +class Chain: public ActorWithCreation { public: Chain(ev_tstamp dChainTimeout = 60.0); @@ -31,50 +31,16 @@ class Chain: public ChainModel */ virtual E_CMD_STATUS Timeout() = 0; - /** - * @brief 检查Chain内数据是否已加载 - * @note 为满足数据共享并确保同一数据在同一个Worker内只需从 - * 外部存储中加载一次,提供了IsReady(),SetReady(),IsLoading(), - * SetLoading()四个方法。如果一个或若干个Step获取到一个已创建好 - * 的Chain,则需先调用IsReady()判断数据是否就绪,若就绪则直接 - * 从Chain中读取,若未就绪则调用IsLoading()判断数据是否正在加 - * 载,若正在加载则直接return(CMD_STATUS_RUNNING)(框架会在数据 - * 加载完毕后调用该Step的Callback),否则加载数据并且SetLoading(), - * 数据加载完毕后SetReady()。 - * @param pStep 调用IsReady()方法的调用者Step指针,用于记录 - * 哪些Step依赖于Chain的数据,在数据就绪时由框架主动调用 - * 依赖这些数据的Step回调而不需要等到超时才回调。 - */ - bool IsReady(Step* pStep); - -protected: - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); - template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); - template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); +private: + void InitChainBlock(const std::vector& vecChainBlock); + E_CMD_STATUS NextBlock(); private: + std::vector m_vecChainBlock; + friend class WorkerImpl; - std::queue m_vecWaitingStep; }; -template -void Chain::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) -{ - m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); -} - -template -std::shared_ptr Chain::MakeSharedStep(const std::string& strStepName, Targs... args) -{ - return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); -} - -template -std::shared_ptr Chain::MakeSharedSession(const std::string& strSessionName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); -} - } /* namespace neb */ #endif /* SRC_ACTOR_CHAIN_CHAIN_HPP_ */ diff --git a/src/actor/chain/ChainModel.cpp b/src/actor/chain/ChainModel.cpp deleted file mode 100644 index 0bb9acff..00000000 --- a/src/actor/chain/ChainModel.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file ChainModel.cpp - * @brief - * @author bwar - * @date: June 7, 2019 - * @note - * Modify history: - ******************************************************************************/ -#include "ChainModel.hpp" - -namespace neb -{ - -ChainModel::ChainModel(ev_tstamp dChainTimeout) - : Actor(Actor::ACT_CHAIN, dChainTimeout) -{ -} - -ChainModel::~ChainModel() -{ -} - -} /* namespace neb */ diff --git a/src/actor/chain/ChainModel.hpp b/src/actor/chain/ChainModel.hpp deleted file mode 100644 index 5a7aef56..00000000 --- a/src/actor/chain/ChainModel.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file ChainModel.hpp - * @brief - * @author bwar - * @date: June 6, 2019 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_ACTOR_CHAIN_CHAINMODEL_HPP_ -#define SRC_ACTOR_CHAIN_CHAINMODEL_HPP_ - -#include "actor/Actor.hpp" - -namespace neb -{ - -class ChainModel: public Actor -{ -public: - ChainModel(ev_tstamp dChainTimeout = 60.0); - ChainModel(const ChainModel&) = delete; - ChainModel& operator=(const ChainModel&) = delete; - virtual ~ChainModel(); - - /** - * @brief 功能链超时回调 - */ - virtual E_CMD_STATUS Timeout() = 0; - -private: - friend class WorkerImpl; -}; - -} /* namespace neb */ - -#endif /* SRC_ACTOR_CHAIN_CHAINMODEL_HPP_ */ diff --git a/src/actor/cmd/Cmd.hpp b/src/actor/cmd/Cmd.hpp index 33644861..7424145d 100644 --- a/src/actor/cmd/Cmd.hpp +++ b/src/actor/cmd/Cmd.hpp @@ -11,17 +11,18 @@ #define SRC_ACTOR_CMD_CMD_HPP_ #include "labor/Worker.hpp" -#include "CmdModel.hpp" +#include "actor/ActorWithCreation.hpp" #include "actor/DynamicCreator.hpp" namespace neb { -class Cmd: public CmdModel +class Cmd: public ActorWithCreation { public: Cmd(int32 iCmd) - : CmdModel(iCmd) + : ActorWithCreation(Actor::ACT_CMD, gc_dNoTimeout), + m_iCmd(iCmd) { } Cmd(const Cmd&) = delete; @@ -60,28 +61,15 @@ class Cmd: public CmdModel const MsgBody& oMsgBody) = 0; protected: - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); - template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); - template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); -}; - -template -void Cmd::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) -{ - m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); -} - -template -std::shared_ptr Cmd::MakeSharedStep(const std::string& strStepName, Targs... args) -{ - return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); -} + int GetCmd() const + { + return(m_iCmd); + } -template -std::shared_ptr Cmd::MakeSharedSession(const std::string& strSessionName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); -} +private: + int32 m_iCmd; + friend class WorkerImpl; +}; } /* namespace neb */ diff --git a/src/actor/cmd/CmdModel.hpp b/src/actor/cmd/CmdModel.hpp deleted file mode 100644 index bffa8ad1..00000000 --- a/src/actor/cmd/CmdModel.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file CmdModel.hpp - * @brief - * @author bwar - * @date: Apr 4, 2018 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_ACTOR_CMD_CMDMODEL_HPP_ -#define SRC_ACTOR_CMD_CMDMODEL_HPP_ - -#include "actor/Actor.hpp" - -namespace neb -{ - -class CmdModel: public Actor -{ -public: - CmdModel(int32 iCmd) - : Actor(Actor::ACT_CMD, gc_dNoTimeout), - m_iCmd(iCmd) - { - } - CmdModel(const CmdModel&) = delete; - CmdModel& operator=(const CmdModel&) = delete; - virtual ~CmdModel(){}; - - /** - * @brief 初始化Cmd - * @note Cmd类实例初始化函数,大部分Cmd不需要初始化,需要初始化的Cmd可派生后实现此函数, - * 在此函数里可以读取配置文件(配置文件必须为json格式)。配置文件由Cmd的设计者自行定义, - * 存放于conf/目录,配置文件名最好与Cmd名字保持一致,加上.json后缀。配置文件的更新同步 - * 会由框架自动完成。 - * @return 是否初始化成功 - */ - virtual bool Init() - { - return(true); - } - - /** - * @brief 命令处理入口 - * @note 框架层成功解析数据包后,根据MsgHead里的Cmd找到对应的Cmd类实例调用将数据包及 - * 数据包来源tagChannelContext传给AnyMessage处理。若处理过程不涉及网络IO之类需异步处 - * 理的耗时调用,则无需新创建Step类实例来处理。若处理过程涉及耗时异步调用,则应创建Step - * 类实例,并向框架层注册Step类实例,调用Step.Start()后即返回。 - * @param stCtx 消息来源上下文 - * @param oMsgHead 数据包头 - * @param oMsgBody 数据包体 - * @return 命令是否处理成功 - */ - virtual bool AnyMessage( - std::shared_ptr pChannel, - const MsgHead& oMsgHead, - const MsgBody& oMsgBody) = 0; - -protected: - int GetCmd() const - { - return(m_iCmd); - } - -private: - int32 m_iCmd; - friend class WorkerImpl; -}; - -} /* namespace neb */ - -#endif /* SRC_ACTOR_CMD_CMDMODEL_HPP_ */ diff --git a/src/actor/cmd/Module.hpp b/src/actor/cmd/Module.hpp index 829c8fb1..d2489854 100644 --- a/src/actor/cmd/Module.hpp +++ b/src/actor/cmd/Module.hpp @@ -12,16 +12,17 @@ #include "codec/CodecHttp.hpp" #include "labor/Worker.hpp" -#include "ModuleModel.hpp" +#include "actor/ActorWithCreation.hpp" #include "actor/DynamicCreator.hpp" namespace neb { -class Module: public ModuleModel +class Module: public ActorWithCreation { public: Module(const std::string& strModulePath) - : ModuleModel(strModulePath) + : ActorWithCreation(Actor::ACT_MODULE, gc_dNoTimeout), + m_strModulePath(strModulePath) { } Module(const Module&) = delete; @@ -52,28 +53,15 @@ class Module: public ModuleModel const HttpMsg& oHttpMsg) = 0; protected: - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); - template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); - template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); -}; - -template -void Module::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) -{ - m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); -} - -template -std::shared_ptr Module::MakeSharedStep(const std::string& strStepName, Targs... args) -{ - return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); -} + const std::string& GetModulePath() const + { + return(m_strModulePath); + } -template -std::shared_ptr Module::MakeSharedSession(const std::string& strSessionName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); -} +private: + std::string m_strModulePath; + friend class WorkerImpl; +}; } /* namespace neb */ diff --git a/src/actor/cmd/ModuleModel.hpp b/src/actor/cmd/ModuleModel.hpp deleted file mode 100644 index 398114a3..00000000 --- a/src/actor/cmd/ModuleModel.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file ModuleModel.hpp - * @brief - * @author bwar - * @date: Apr 4, 2018 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_ACTOR_CMD_MODULEMODEL_HPP_ -#define SRC_ACTOR_CMD_MODULEMODEL_HPP_ - -#include - -namespace neb -{ - -class ModuleModel: public Actor -{ -public: - ModuleModel(const std::string& strModulePath) - : Actor(Actor::ACT_MODULE, gc_dNoTimeout), m_strModulePath(strModulePath) - { - } - ModuleModel(const ModuleModel&) = delete; - ModuleModel& operator=(const ModuleModel&) = delete; - virtual ~ModuleModel(){}; - - /** - * @brief 初始化Module - * @note Module类实例初始化函数,大部分Module不需要初始化,需要初始化的Module可派生后实现此函数, - * 在此函数里可以读取配置文件(配置文件必须为json格式)。配置文件由Module的设计者自行定义, - * 存放于conf/目录,配置文件名最好与Module名字保持一致,加上.json后缀。配置文件的更新同步 - * 会由框架自动完成。 - * @return 是否初始化成功 - */ - virtual bool Init() - { - return(true); - } - - /** - * @brief http服务模块处理入口 - * @param stCtx 消息来源上下文 - * @param oHttpMsg 接收到的http数据包 - * @return 是否处理成功 - */ - virtual bool AnyMessage( - std::shared_ptr pChannel, - const HttpMsg& oHttpMsg) = 0; - -protected: - const std::string& GetModulePath() const - { - return(m_strModulePath); - } - -private: - std::string m_strModulePath; - friend class WorkerImpl; -}; - -} /* namespace neb */ - -#endif /* SRC_ACTOR_CMD_MODULEMODEL_HPP_ */ diff --git a/src/actor/engine/Engine.hpp b/src/actor/engine/Engine.hpp deleted file mode 100644 index 6db8fdbc..00000000 --- a/src/actor/engine/Engine.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file Engine.hpp - * @brief - * @author Bwar - * @date: 2019年6月7日 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_ACTOR_ENGINE_HPP_ -#define SRC_ACTOR_ENGINE_HPP_ - -#include "labor/Worker.hpp" -#include "EngineModel.hpp" -#include "actor/DynamicCreator.hpp" - -namespace neb -{ - -class Engine: public EngineModel -{ -public: - Engine() - : EngineModel(Actor::ACT_ENGINE, gc_dNoTimeout) - { - } - Engine(const Engine&) = delete; - Engine& operator=(const Engine&) = delete; - virtual ~Engine(){} - - /** - * @brief 提交 - */ - virtual E_CMD_STATUS Launch() = 0; - -protected: - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); - template std::shared_ptr MakeSharedStep(const std::string& strEngineName, Targs... args); - template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); - template std::shared_ptr MakeSharedCmd(const std::string& strCmdName, Targs... args); - template std::shared_ptr MakeSharedModule(const std::string& strModuleName, Targs... args); - -private: - friend class WorkerImpl; -}; - -template -void Engine::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) -{ - m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); -} - -template -std::shared_ptr Engine::MakeSharedStep(const std::string& strEngineName, Targs... args) -{ - return(m_pWorker->MakeSharedStep(this, strEngineName, std::forward(args)...)); -} - -template -std::shared_ptr Engine::MakeSharedSession(const std::string& strSessionName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); -} - -template -std::shared_ptr Engine::MakeSharedCmd(const std::string& strCmdName, Targs... args) -{ - return(m_pWorker->MakeSharedCmd(this, strCmdName, std::forward(args)...)); -} - -template -std::shared_ptr Engine::MakeSharedModule(const std::string& strModuleName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strModuleName, std::forward(args)...)); -} - - -} /* namespace neb */ - -#endif /* SRC_ACTOR_ENGINE_HPP_ */ diff --git a/src/actor/engine/EngineModel.hpp b/src/actor/engine/EngineModel.hpp deleted file mode 100644 index 55063d50..00000000 --- a/src/actor/engine/EngineModel.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file EngineModel.hpp - * @brief - * @author bwar - * @date: June 6, 2019 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_ACTOR_ENGINEMODEL_HPP_ -#define SRC_ACTOR_ENGINEMODEL_HPP_ - -#include "actor/Actor.hpp" - -namespace neb -{ - -class EngineModel: public Actor -{ -public: - EngineModel(Actor::ACTOR_TYPE eActorType, ev_tstamp dTimeout = gc_dDefaultTimeout) - : Actor(eActorType, dTimeout) - { - } - EngineModel(const EngineModel&) = delete; - EngineModel& operator=(const EngineModel&) = delete; - virtual ~EngineModel(); - - /** - * @brief 提交 - */ - virtual E_CMD_STATUS Launch() = 0; - -private: - friend class WorkerImpl; -}; - -} /* namespace neb */ - -#endif /* SRC_ACTOR_ENGINEMODEL_HPP_ */ diff --git a/src/actor/matrix/Matrix.hpp b/src/actor/matrix/Matrix.hpp new file mode 100644 index 00000000..0b2f53b8 --- /dev/null +++ b/src/actor/matrix/Matrix.hpp @@ -0,0 +1,57 @@ +/******************************************************************************* + * Project: Nebula + * @file Matrix.hpp + * @brief + * @author Bwar + * @date: 2019年6月7日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_MATRIX_HPP_ +#define SRC_ACTOR_MATRIX_HPP_ + +#include "labor/Worker.hpp" +#include "actor/ActorWithCreation.hpp" +#include "actor/DynamicCreator.hpp" + +namespace neb +{ + +class Matrix: public ActorWithCreation +{ +public: + Matrix() + : ActorWithCreation(Actor::ACT_MATRIX, gc_dNoTimeout) + { + } + Matrix(const Matrix&) = delete; + Matrix& operator=(const Matrix&) = delete; + virtual ~Matrix(){} + + /** + * @brief 初始化Matrix;重新加载配置 + * @note Matrix类实例初始化函数,大部分Matrix不需要初始化,需要初始化的Matrix可派生后实现此函数, + * 在此函数里可以读取配置文件(配置文件必须为json格式)。配置文件由Matrix的设计者自行定义, + * 存放于conf/目录,配置文件名最好与Matrix名字保持一致,加上.json后缀。配置文件的更新同步 + * 会由框架自动完成。 + * Init()需设计成可重入方法,因各服务框架在收到Beacon的重新加载配置指令后会执行每个 + * Matrix的Init()方法,所以必须保证Init()方法执行后没有副作用。 + * @return 是否初始化成功 + */ + virtual bool Init() + { + return(true); + } + + /** + * @brief 提交 + */ + virtual E_CMD_STATUS Launch() = 0; + +private: + friend class WorkerImpl; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_MATRIX_HPP_ */ diff --git a/src/actor/session/Session.cpp b/src/actor/session/Session.cpp index 6c810aa5..cd5308f7 100644 --- a/src/actor/session/Session.cpp +++ b/src/actor/session/Session.cpp @@ -15,14 +15,18 @@ namespace neb { Session::Session(uint32 ulSessionId, ev_tstamp dSessionTimeout) - : SessionModel(ulSessionId, dSessionTimeout), + : ActorWithCreation(Actor::ACT_SESSION, dSessionTimeout), m_bDataReady(false), m_bDataLoading(false) { + std::ostringstream oss; + oss << ulSessionId; + m_strSessionId = std::move(oss.str()); } Session::Session(const std::string& strSessionId, ev_tstamp dSessionTimeout) - : SessionModel(strSessionId, dSessionTimeout), - m_bDataReady(false), m_bDataLoading(false) + : ActorWithCreation(Actor::ACT_SESSION, dSessionTimeout), + m_bDataReady(false), m_bDataLoading(false), + m_strSessionId(strSessionId) { } diff --git a/src/actor/session/Session.hpp b/src/actor/session/Session.hpp index 5124d057..1573a09f 100644 --- a/src/actor/session/Session.hpp +++ b/src/actor/session/Session.hpp @@ -13,12 +13,12 @@ #include #include "labor/Worker.hpp" #include "actor/DynamicCreator.hpp" -#include "SessionModel.hpp" +#include "actor/ActorWithCreation.hpp" namespace neb { -class Session: public SessionModel +class Session: public ActorWithCreation { public: Session(uint32 ulSessionId, ev_tstamp dSessionTimeout = 60.0); @@ -32,6 +32,11 @@ class Session: public SessionModel */ virtual E_CMD_STATUS Timeout() = 0; + const std::string& GetSessionId() const + { + return(m_strSessionId); + } + /** * @brief 检查Session内数据是否已加载 * @note 为满足数据共享并确保同一数据在同一个Worker内只需从 @@ -52,11 +57,6 @@ class Session: public SessionModel bool IsLoading(); void SetLoading(); -protected: - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); - template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); - template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); - private: uint32 PopWaitingStep(); @@ -64,27 +64,10 @@ class Session: public SessionModel friend class WorkerImpl; bool m_bDataReady; bool m_bDataLoading; + std::string m_strSessionId; std::queue m_vecWaitingStep; }; -template -void Session::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) -{ - m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); -} - -template -std::shared_ptr Session::MakeSharedStep(const std::string& strStepName, Targs... args) -{ - return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); -} - -template -std::shared_ptr Session::MakeSharedSession(const std::string& strSessionName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); -} - } /* namespace neb */ #endif /* SRC_ACTOR_SESSION_SESSION_HPP_ */ diff --git a/src/actor/session/SessionModel.cpp b/src/actor/session/SessionModel.cpp deleted file mode 100644 index 40dc2d4c..00000000 --- a/src/actor/session/SessionModel.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file SessionModel.cpp - * @brief - * @author bwar - * @date: Apr 4, 2018 - * @note - * Modify history: - ******************************************************************************/ -#include -#include "SessionModel.hpp" - -namespace neb -{ - -SessionModel::SessionModel(uint32 ulSessionId, ev_tstamp dSessionTimeout) - : Actor(Actor::ACT_SESSION, dSessionTimeout) -{ - std::ostringstream oss; - oss << ulSessionId; - m_strSessionId = std::move(oss.str()); -} - -SessionModel::SessionModel(const std::string& strSessionId, ev_tstamp dSessionTimeout) - : Actor(Actor::ACT_SESSION, dSessionTimeout), - m_strSessionId(strSessionId) -{ -} - -SessionModel::~SessionModel() -{ -} - -} /* namespace neb */ diff --git a/src/actor/session/SessionModel.hpp b/src/actor/session/SessionModel.hpp deleted file mode 100644 index 7606a7b1..00000000 --- a/src/actor/session/SessionModel.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file SessionModel.hpp - * @brief - * @author bwar - * @date: Apr 4, 2018 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_ACTOR_SESSION_SESSIONMODEL_HPP_ -#define SRC_ACTOR_SESSION_SESSIONMODEL_HPP_ - -#include "actor/Actor.hpp" - -namespace neb -{ - -class SessionModel: public Actor -{ -public: - SessionModel(uint32 ulSessionId, ev_tstamp dSessionTimeout = 60.0); - SessionModel(const std::string& strSessionId, ev_tstamp dSessionTimeout = 60.0); - SessionModel(const SessionModel&) = delete; - SessionModel& operator=(const SessionModel&) = delete; - virtual ~SessionModel(); - - /** - * @brief 会话超时回调 - */ - virtual E_CMD_STATUS Timeout() = 0; - - const std::string& GetSessionId() const - { - return(m_strSessionId); - } - -private: - std::string m_strSessionId; - friend class WorkerImpl; -}; - -} /* namespace neb */ - -#endif /* SRC_ACTOR_SESSION_SESSIONMODEL_HPP_ */ diff --git a/src/actor/step/StepModel.cpp b/src/actor/step/Step.cpp similarity index 65% rename from src/actor/step/StepModel.cpp rename to src/actor/step/Step.cpp index 6b9f76ed..18416288 100644 --- a/src/actor/step/StepModel.cpp +++ b/src/actor/step/Step.cpp @@ -1,20 +1,20 @@ /******************************************************************************* * Project: Nebula - * @file StepModel.cpp + * @file Step.cpp * @brief * @author bwar * @date: Apr 4, 2018 * @note * Modify history: ******************************************************************************/ -#include "StepModel.hpp" #include "Step.hpp" namespace neb { -StepModel::StepModel(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep, ev_tstamp dTimeout) - : Actor(eActorType, dTimeout) +Step::Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep, ev_tstamp dTimeout) + : ActorWithCreation(eActorType, dTimeout), + m_uiChainId(0) { if (nullptr != pNextStep) { @@ -22,13 +22,13 @@ StepModel::StepModel(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextSt } } -StepModel::~StepModel() +Step::~Step() { m_setNextStepSeq.clear(); m_setPreStepSeq.clear(); } -void StepModel::NextStep(int iErrno, const std::string& strErrMsg, void* data) +void Step::NextStep(int iErrno, const std::string& strErrMsg, void* data) { if (iErrno != ERR_OK) { @@ -41,4 +41,9 @@ void StepModel::NextStep(int iErrno, const std::string& strErrMsg, void* data) } } +void Step::SetChainId(uint32 uiChainId) +{ + m_uiChainId = uiChainId; +} + } /* namespace neb */ diff --git a/src/actor/step/Step.hpp b/src/actor/step/Step.hpp index 371bed10..e38ea804 100644 --- a/src/actor/step/Step.hpp +++ b/src/actor/step/Step.hpp @@ -11,22 +11,21 @@ #define SRC_ACTOR_STEP_STEP_HPP_ #include "labor/Worker.hpp" -#include "StepModel.hpp" +#include "actor/ActorWithCreation.hpp" #include "actor/DynamicCreator.hpp" namespace neb { -class Step: public StepModel +class Chain; + +class Step: public ActorWithCreation { public: - Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout) - : StepModel(eActorType, pNextStep, dTimeout) - { - } + Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); Step(const Step&) = delete; Step& operator=(const Step&) = delete; - virtual ~Step(){} + virtual ~Step(); /** * @brief 提交,发出 @@ -40,31 +39,26 @@ class Step: public StepModel virtual E_CMD_STATUS Timeout() = 0; protected: - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); - template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); - template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); + /** + * @brief 执行当前步骤接下来的步骤 + */ + void NextStep(int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); -private: - friend class WorkerImpl; -}; + uint32 GetChainId() const + { + return(m_uiChainId); + } -template -void Step::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) -{ - m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); -} +private: + void SetChainId(uint32 uiChainId); -template -std::shared_ptr Step::MakeSharedStep(const std::string& strStepName, Targs... args) -{ - return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); -} + uint32 m_uiChainId; + std::unordered_set m_setNextStepSeq; + std::unordered_set m_setPreStepSeq; -template -std::shared_ptr Step::MakeSharedSession(const std::string& strSessionName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); -} + friend class WorkerImpl; + friend class Chain; +}; } /* namespace neb */ diff --git a/src/actor/step/StepModel.hpp b/src/actor/step/StepModel.hpp deleted file mode 100644 index df28ab75..00000000 --- a/src/actor/step/StepModel.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file StepModel.hpp - * @brief - * @author bwar - * @date: Apr 4, 2018 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_ACTOR_STEP_STEPMODEL_HPP_ -#define SRC_ACTOR_STEP_STEPMODEL_HPP_ - -#include -#include "actor/Actor.hpp" - -namespace neb -{ - -class Step; - -class StepModel: public Actor -{ -public: - StepModel(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); - StepModel(const StepModel&) = delete; - StepModel& operator=(const StepModel&) = delete; - virtual ~StepModel(); - - /** - * @brief 提交,发出 - * @note 注册了一个回调步骤之后执行Emit()就开始等待回调。 - */ - virtual E_CMD_STATUS Emit(int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL) = 0; - - /** - * @brief 步骤超时回调 - */ - virtual E_CMD_STATUS Timeout() = 0; - -protected: - /** - * @brief 执行当前步骤接下来的步骤 - */ - void NextStep(int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); - -private: - std::unordered_set m_setNextStepSeq; - std::unordered_set m_setPreStepSeq; - friend class WorkerImpl; -}; - -} /* namespace neb */ - -#endif /* SRC_ACTOR_STEP_STEPMODEL_HPP_ */ diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 57739607..7c980288 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -54,6 +54,11 @@ bool Worker::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg return(m_pImpl->ExecStep(uiStepSeq, iErrno, strErrMsg, data)); } +std::shared_ptr Worker::GetMatrix(const std::string& strMatrixName) +{ + return(m_pImpl->GetMatrix(strMatrixName)); +} + void Worker::AddAssemblyLine(std::shared_ptr pSession) { m_pImpl->AddAssemblyLine(pSession); diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 8d7d66c9..dde64f04 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -33,21 +33,19 @@ class Worker: public Labor void Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); template - std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args); + std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args); template - std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args); - - template - std::shared_ptr MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args); + std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args); template - std::shared_ptr MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs... args); + std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args); virtual uint32 GetSequence() const; virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); + virtual std::shared_ptr GetMatrix(const std::string& strMatrixName); virtual void AddAssemblyLine(std::shared_ptr pSession); // 获取worker信息相关方法 @@ -96,27 +94,21 @@ void Worker::Logger(const std::string& strTraceId, int iLogLevel, const char* sz } template -std::shared_ptr Worker::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args) -{ - return(m_pImpl->MakeSharedStep(pCreator, strStepName, std::forward(args)...)); -} - -template -std::shared_ptr Worker::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args) +std::shared_ptr Worker::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args) { - return(m_pImpl->MakeSharedSession(pCreator, strSessionName, std::forward(args)...)); + return(m_pImpl->MakeSharedActor(pCreator, strActorName, std::forward(args)...)); } template -std::shared_ptr Worker::MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args) +std::shared_ptr Worker::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args) { - return(m_pImpl->MakeSharedStep(pCreator, strCmdName, std::forward(args)...)); + return(m_pImpl->MakeSharedStep(pCreator, strStepName, std::forward(args)...)); } template -std::shared_ptr Worker::MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs... args) +std::shared_ptr Worker::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args) { - return(m_pImpl->MakeSharedSession(pCreator, strModuleName, std::forward(args)...)); + return(m_pImpl->MakeSharedSession(pCreator, strSessionName, std::forward(args)...)); } } /* namespace neb */ diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index e4dbf461..3b681c53 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -28,6 +28,8 @@ extern "C" { #include "actor/step/PbStep.hpp" #include "actor/step/RedisStep.hpp" #include "actor/step/Step.hpp" +#include "actor/matrix/Matrix.hpp" +#include "actor/chain/Chain.hpp" #include "actor/session/sys_session/SessionLogger.hpp" namespace neb @@ -113,6 +115,15 @@ void WorkerImpl::SessionTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, } } +void WorkerImpl::ChainTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) +{ + if (watcher->data != NULL) + { + Chain* pChain = (Chain*)watcher->data; + ((Worker*)pChain->m_pWorker)->m_pImpl->OnChainTimeout(std::dynamic_pointer_cast(pChain->shared_from_this())); + } +} + void WorkerImpl::RedisConnectCallback(const redisAsyncContext *c, int status) { if (c->data != NULL) @@ -538,6 +549,7 @@ bool WorkerImpl::OnStepTimeout(std::shared_ptr pStep) } else { + Remove(pStep->GetChainId()); Remove(pStep); return(true); } @@ -574,6 +586,35 @@ bool WorkerImpl::OnSessionTimeout(std::shared_ptr pSession) } } +bool WorkerImpl::OnChainTimeout(std::shared_ptr pChain) +{ + ev_timer* watcher = pChain->MutableTimerWatcher(); + LOG4_TRACE("CHECK watchar = 0x%x", watcher); + ev_tstamp after = pChain->GetActiveTime() - ev_now(m_loop) + pChain->GetTimeout(); + if (after > 0) // 定时时间内被重新刷新过,重新设置定时器 + { + ev_timer_stop (m_loop, watcher); + ev_timer_set (watcher, after + ev_time() - ev_now(m_loop), 0); + ev_timer_start (m_loop, watcher); + return(true); + } + else // 会话已超时 + { + if (CMD_STATUS_RUNNING == pChain->Timeout()) + { + ev_timer_stop (m_loop, watcher); + ev_timer_set (watcher, pChain->GetTimeout() + ev_time() - ev_now(m_loop), 0); + ev_timer_start (m_loop, watcher); + return(true); + } + else + { + Remove(pChain->GetSequence()); + return(true); + } + } +} + bool WorkerImpl::OnRedisConnected(const redisAsyncContext *c, int status) { LOG4_TRACE(" "); @@ -605,24 +646,37 @@ bool WorkerImpl::OnRedisConnected(const redisAsyncContext *c, int status) } else { + E_CMD_STATUS eResult; + uint32 uiChainId; auto interrupt_step_iter = step_iter; for (; step_iter != channel_iter->second->listPipelineStep.end(); ++step_iter) { - (*step_iter)->Callback(c, status, nullptr); + eResult = (*step_iter)->Callback(c, status, nullptr); + uiChainId = (*step_iter)->GetChainId(); Remove(*step_iter); - } - if (step_iter == channel_iter->second->listPipelineStep.begin()) // 第一个命令就发送失败 - { - channel_iter->second->listPipelineStep.clear(); - } - else - { - for (auto erase_step_iter = interrupt_step_iter; - erase_step_iter != channel_iter->second->listPipelineStep.end();) + if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + { + if (0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(ev_now(m_loop)); + chain_iter->second->NextBlock(); + } + } + } + else { - channel_iter->second->listPipelineStep.erase(erase_step_iter++); + Remove(uiChainId); } } + for (auto erase_step_iter = interrupt_step_iter; + erase_step_iter != channel_iter->second->listPipelineStep.end();) + { + Remove((*erase_step_iter)->GetChainId()); + } + channel_iter->second->listPipelineStep.clear(); DelNamedRedisChannel(channel_iter->second->GetIdentify()); m_mapRedisChannel.erase(channel_iter); } @@ -634,6 +688,7 @@ bool WorkerImpl::OnRedisConnected(const redisAsyncContext *c, int status) step_iter != channel_iter->second->listPipelineStep.end(); ++step_iter) { (*step_iter)->Callback(c, status, nullptr); + Remove((*step_iter)->GetChainId()); Remove(*step_iter); } channel_iter->second->listPipelineStep.clear(); @@ -656,6 +711,7 @@ bool WorkerImpl::OnRedisDisconnected(const redisAsyncContext *c, int status) LOG4_ERROR("RedisDisconnect callback error %d of redis cmd: %s", c->err, (*step_iter)->CmdToString().c_str()); (*step_iter)->Callback(c, c->err, nullptr); + Remove((*step_iter)->GetChainId()); Remove(std::dynamic_pointer_cast(*step_iter)); } channel_iter->second->listPipelineStep.clear(); @@ -687,6 +743,8 @@ bool WorkerImpl::OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privd { LOG4_ERROR("callback error %d of redis cmd: %s", c->err, (*step_iter)->CmdToString().c_str()); (*step_iter)->Callback(c, c->err, (redisReply*)reply); + Remove((*step_iter)->GetChainId()); + Remove(*step_iter); } channel_iter->second->listPipelineStep.clear(); @@ -698,8 +756,21 @@ bool WorkerImpl::OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privd if (step_iter != channel_iter->second->listPipelineStep.end()) { LOG4_TRACE("callback of redis cmd: %s", (*step_iter)->CmdToString().c_str()); - (*step_iter)->Callback(c, REDIS_OK, (redisReply*)reply); + E_CMD_STATUS eResult = (*step_iter)->Callback(c, REDIS_OK, (redisReply*)reply); channel_iter->second->listPipelineStep.erase(step_iter); + if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + { + uint32 uiChainId = (*step_iter)->GetChainId(); + if (0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(ev_now(m_loop)); + chain_iter->second->NextBlock(); + } + } + } //freeReplyObject(reply); } else @@ -714,15 +785,201 @@ bool WorkerImpl::OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privd return(true); } +bool WorkerImpl::TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor) +{ + pSharedActor->m_dTimeout = (0 == pSharedActor->m_dTimeout) ? m_stWorkerInfo.dStepTimeout : pSharedActor->m_dTimeout; + ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); + if (NULL == timer_watcher) + { + return(false); + } + + if (nullptr != pCreator) + { + pSharedActor->m_strTraceId = pCreator->m_strTraceId; + } + + std::shared_ptr pSharedStep = std::dynamic_pointer_cast(pSharedActor); + for (auto iter = pSharedStep->m_setNextStepSeq.begin(); iter != pSharedStep->m_setNextStepSeq.end(); ++iter) + { + auto callback_iter = m_mapCallbackStep.find(*iter); + if (callback_iter != m_mapCallbackStep.end()) + { + callback_iter->second->m_setPreStepSeq.insert(pSharedStep->GetSequence()); + } + } + + auto ret = m_mapCallbackStep.insert(std::make_pair(pSharedStep->GetSequence(), pSharedStep)); + if (ret.second) + { + if (gc_dNoTimeout != pSharedStep->m_dTimeout) + { + ev_timer_init (timer_watcher, StepTimeoutCallback, pSharedStep->m_dTimeout + ev_time() - ev_now(m_loop), 0.); + ev_timer_start (m_loop, timer_watcher); + } + LOG4_TRACE("Step(seq %u, active_time %lf, lifetime %lf) register successful.", + pSharedStep->GetSequence(), pSharedStep->GetActiveTime(), pSharedStep->GetTimeout()); + auto step_class_iter = m_mapLoadedStep.find(pSharedStep->GetActorName()); + if (step_class_iter != m_mapLoadedStep.end()) + { + step_class_iter->second.insert(pSharedStep->GetSequence()); + } + return(true); + } + else + { + return(false); + } +} + +bool WorkerImpl::TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor) +{ + ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); + if (NULL == timer_watcher) + { + return(false); + } + + if (nullptr != pCreator) + { + std::ostringstream oss; + oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << pSharedActor->GetSequence(); + pSharedActor->m_strTraceId = std::move(oss.str()); + } + std::shared_ptr pSharedSession = std::dynamic_pointer_cast(pSharedActor); + auto ret = m_mapCallbackSession.insert(std::make_pair(pSharedSession->GetSessionId(), pSharedSession)); + if (ret.second) + { + if (gc_dNoTimeout != pSharedSession->m_dTimeout) + { + ev_timer_init (timer_watcher, SessionTimeoutCallback, pSharedSession->m_dTimeout + ev_time() - ev_now(m_loop), 0.); + ev_timer_start (m_loop, timer_watcher); + } + auto session_class_iter = m_mapLoadedSession.find(pSharedSession->GetActorName()); + if (session_class_iter != m_mapLoadedSession.end()) + { + session_class_iter->second.insert(pSharedSession->GetSessionId()); + } + return(true); + } + else + { + return(false); + } +} + +bool WorkerImpl::TransformToSharedCmd(Actor* pCreator, std::shared_ptr pSharedActor) +{ + if (nullptr != pCreator) + { + pSharedActor->m_strTraceId = pCreator->m_strTraceId; + } + std::shared_ptr pSharedCmd = std::dynamic_pointer_cast(pSharedActor); + auto ret = m_mapCmd.insert(std::make_pair(pSharedCmd->GetCmd(), pSharedCmd)); + if (ret.second) + { + if (pSharedCmd->Init()) + { + auto cmd_class_iter = m_mapLoadedCmd.find(pSharedCmd->GetActorName()); + if (cmd_class_iter != m_mapLoadedCmd.end()) + { + cmd_class_iter->second.insert(pSharedCmd->GetCmd()); + } + return(true); + } + } + return(false); +} + +bool WorkerImpl::TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor) +{ + if (nullptr != pCreator) + { + pSharedActor->m_strTraceId = pCreator->m_strTraceId; + } + + std::shared_ptr pSharedModule = std::dynamic_pointer_cast(pSharedActor); + auto ret = m_mapModule.insert(std::make_pair(pSharedModule->GetModulePath(), pSharedModule)); + if (ret.second) + { + if (pSharedModule->Init()) + { + auto module_class_iter = m_mapLoadedModule.find(pSharedModule->GetActorName()); + if (module_class_iter != m_mapLoadedModule.end()) + { + module_class_iter->second.insert(pSharedModule->GetModulePath()); + } + return(true); + } + } + return(false); +} + +bool WorkerImpl::TransformToSharedMatrix(Actor* pCreator, std::shared_ptr pSharedActor) +{ + std::shared_ptr pSharedMatrix = std::dynamic_pointer_cast(pSharedActor); + auto ret = m_mapMatrix.insert(std::make_pair(pSharedMatrix->GetActorName(), pSharedMatrix)); + if (ret.second) + { + if (pSharedMatrix->Init()) + { + return(true); + } + } + return(false); +} + +bool WorkerImpl::TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor) +{ + ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); + if (NULL == timer_watcher) + { + return(false); + } + + if (nullptr != pCreator) + { + pSharedActor->m_strTraceId = pCreator->m_strTraceId; + } + + std::shared_ptr pSharedChain = std::dynamic_pointer_cast(pSharedActor); + auto chain_conf_iter = m_mapChainConf.find(pSharedActor->GetActorName()); + if (chain_conf_iter == m_mapChainConf.end()) + { + LOG4_ERROR("no chain block config for \"%s\"", pSharedActor->GetActorName().c_str()); + return(false); + } + else + { + pSharedChain->InitChainBlock(chain_conf_iter->second); + } + auto ret = m_mapChain.insert(std::make_pair(pSharedChain->GetSequence(), pSharedChain)); + if (ret.second) + { + if (gc_dNoTimeout != pSharedChain->m_dTimeout) + { + ev_timer_init (timer_watcher, ChainTimeoutCallback, pSharedChain->m_dTimeout + ev_time() - ev_now(m_loop), 0.); + ev_timer_start (m_loop, timer_watcher); + } + auto chain_class_iter = m_mapLoadedChain.find(pSharedChain->GetActorName()); + if (chain_class_iter != m_mapLoadedChain.end()) + { + chain_class_iter->second.insert(pSharedChain->GetSequence()); + } + return(true); + } + return(false); +} + time_t WorkerImpl::GetNowTime() const { return((time_t)ev_now(m_loop)); } -bool WorkerImpl::ResetTimeout(std::shared_ptr pActor) +bool WorkerImpl::ResetTimeout(std::shared_ptr pSharedActor) { - ev_timer* watcher = pActor->MutableTimerWatcher(); - ev_tstamp after = ev_now(m_loop) + pActor->GetTimeout(); + ev_timer* watcher = pSharedActor->MutableTimerWatcher(); + ev_tstamp after = ev_now(m_loop) + pSharedActor->GetTimeout(); ev_timer_stop (m_loop, watcher); ev_timer_set (watcher, after + ev_time() - ev_now(m_loop), 0); ev_timer_start (m_loop, watcher); @@ -1810,6 +2067,11 @@ void WorkerImpl::LoadDynamicSymbol(CJsonObject& oOneSoConf) std::unordered_set setStep; m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); } + for (int l = 0; l < oOneSoConf["chain"].GetArraySize(); ++l) + { + std::unordered_set setChain; + m_mapLoadedChain.insert(std::make_pair(oOneSoConf["chain"](l), setChain)); + } } void WorkerImpl::UnloadDynamicSymbol(CJsonObject& oOneSoConf) @@ -1885,6 +2147,31 @@ void WorkerImpl::UnloadDynamicSymbol(CJsonObject& oOneSoConf) std::unordered_set setStep; m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); } + for (int k = 0; k < oOneSoConf["matrix"].GetArraySize(); ++k) + { + auto class_iter = m_mapMatrix.find(oOneSoConf["matrix"](k)); + if (class_iter != m_mapMatrix.end()) + { + m_mapMatrix.erase(class_iter); + } + } + for (int k = 0; k < oOneSoConf["chain"].GetArraySize(); ++k) + { + auto class_iter = m_mapLoadedChain.find(oOneSoConf["chain"](k)); + if (class_iter != m_mapLoadedChain.end()) + { + for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) + { + auto chain_iter = m_mapChain.find(*id_iter); + if (chain_iter != m_mapChain.end()) + { + m_mapChain.erase(chain_iter); + } + } + class_iter->second.clear(); + m_mapLoadedChain.erase(class_iter); + } + } } bool WorkerImpl::AddPeriodicTaskEvent() @@ -2073,6 +2360,19 @@ bool WorkerImpl::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strEr } } +std::shared_ptr WorkerImpl::GetMatrix(const std::string& strMatrixName) +{ + auto iter = m_mapMatrix.find(strMatrixName); + if (iter == m_mapMatrix.end()) + { + return(nullptr); + } + else + { + return(iter->second); + } +} + std::shared_ptr WorkerImpl::CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient, bool bWithSsl) { LOG4_DEBUG("iFd %d, codec_type %d, with_ssl = %d", iFd, eCodecType, bWithSsl); @@ -2229,6 +2529,29 @@ void WorkerImpl::Remove(std::shared_ptr pSession) } } +void WorkerImpl::Remove(uint32 uiChainId) +{ + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + std::shared_ptr pChain = chain_iter->second; + auto class_iter = m_mapLoadedChain.find(pChain->GetActorName()); + if (class_iter != m_mapLoadedChain.end()) + { + auto id_iter = class_iter->second.find(pChain->GetSequence()); + if (id_iter != class_iter->second.end()) + { + class_iter->second.erase(id_iter); + } + } + if (pChain->MutableTimerWatcher() != NULL) + { + ev_timer_stop (m_loop, pChain->MutableTimerWatcher()); + } + m_mapChain.erase(chain_iter); + } +} + void WorkerImpl::ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData) { LOG4_TRACE(" "); @@ -2384,9 +2707,19 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const MsgHead& oMsgHead.cmd(), oMsgHead.seq(), step_iter->second->GetSequence(), step_iter->second->GetActiveTime()); eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); - if (CMD_STATUS_RUNNING != eResult) + if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) { + uint32 uiChainId = step_iter->second->GetChainId(); Remove(step_iter->second); + if (0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(ev_now(m_loop)); + chain_iter->second->NextBlock(); + } + } } } ExecAssemblyLine(pChannel, oMsgHead, oMsgBody); @@ -2452,11 +2785,21 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const HttpMsg& E_CMD_STATUS eResult; http_step_iter->second->SetActiveTime(ev_now(m_loop)); eResult = (std::dynamic_pointer_cast(http_step_iter->second))->Callback(pChannel, oHttpMsg); - if (CMD_STATUS_RUNNING != eResult) + if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) { + uint32 uiChainId = http_step_iter->second->GetChainId(); Remove(http_step_iter->second); pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED, 0); AddNamedSocketChannel(pChannel->m_pImpl->GetIdentify(), pChannel); // push back to named socket channel pool. + if (0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(ev_now(m_loop)); + chain_iter->second->NextBlock(); + } + } } } } @@ -2477,9 +2820,19 @@ void WorkerImpl::ExecAssemblyLine(std::shared_ptr pChannel, const E_CMD_STATUS eResult; step_iter->second->SetActiveTime(ev_now(m_loop)); eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); - if (CMD_STATUS_RUNNING != eResult) + if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) { + uint32 uiChainId = step_iter->second->GetChainId(); Remove(step_iter->second); + if (0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(ev_now(m_loop)); + chain_iter->second->NextBlock(); + } + } } } } diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index e35ff916..601bf766 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -62,10 +62,6 @@ extern "C" { #include "actor/Actor.hpp" #include "actor/ActorFactory.hpp" -#include "actor/cmd/CmdModel.hpp" -#include "actor/cmd/ModuleModel.hpp" -#include "actor/session/SessionModel.hpp" -#include "actor/step/StepModel.hpp" #include "actor/session/sys_session/SessionNode.hpp" namespace neb @@ -78,9 +74,12 @@ class Cmd; class Module; class Session; class Timer; +class Context; class Step; class RedisStep; class HttpStep; +class Matrix; +class Chain; class SessionLogger; @@ -138,6 +137,7 @@ class WorkerImpl static void PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, int revents); static void StepTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); static void SessionTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); + static void ChainTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); static void RedisConnectCallback(const redisAsyncContext *c, int status); static void RedisDisconnectCallback(const redisAsyncContext *c, int status); static void RedisCmdCallback(redisAsyncContext *c, void *reply, void *privdata); @@ -157,6 +157,7 @@ class WorkerImpl bool OnIoTimeout(std::shared_ptr pChannel); bool OnStepTimeout(std::shared_ptr pStep); bool OnSessionTimeout(std::shared_ptr pSession); + bool OnChainTimeout(std::shared_ptr pChain); bool OnRedisConnected(const redisAsyncContext *c, int status); bool OnRedisDisconnected(const redisAsyncContext *c, int status); bool OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privdata); @@ -185,7 +186,7 @@ class WorkerImpl } virtual time_t GetNowTime() const; - virtual bool ResetTimeout(std::shared_ptr pActor); + virtual bool ResetTimeout(std::shared_ptr pSharedActor); template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); @@ -197,15 +198,27 @@ class WorkerImpl template std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args); + bool TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor); template std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args); + bool TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor); template std::shared_ptr MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args); + bool TransformToSharedCmd(Actor* pCreator, std::shared_ptr pSharedActor); template std::shared_ptr MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs... args); + bool TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor); + + template + std::shared_ptr MakeSharedMatrix(Actor* pCreator, const std::string& strMatrixName, Targs... args); + bool TransformToSharedMatrix(Actor* pCreator, std::shared_ptr pSharedActor); + + template + std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs... args); + bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); public: // about channel virtual bool AddNetLogMsg(const MsgBody& oMsgBody); @@ -244,6 +257,9 @@ class WorkerImpl // about step virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); + // about matrix + virtual std::shared_ptr GetMatrix(const std::string& strMatrixName); + public: // Worker相关设置(由专用Cmd类调用这些方法完成Worker自身的初始化和更新) virtual bool SetProcessName(const CJsonObject& oJsonConf); virtual bool AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel); @@ -281,6 +297,7 @@ class WorkerImpl bool DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice = true); void Remove(std::shared_ptr pStep); void Remove(std::shared_ptr pSession); + void Remove(uint32 uiChainId); void ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData); /** @@ -341,15 +358,16 @@ class WorkerImpl std::unordered_map > m_mapLoadedModule; //key为ModuleClassName,value为strModulePath集合 std::unordered_map > m_mapLoadedStep; //key为StepClassName,value为uiSeq集合 std::unordered_map > m_mapLoadedSession; //key为SessionClassName,value为strSessionId集合 + std::unordered_map > m_mapLoadedChain; //key为ChainClassName,value为uiSeq集合 // Cmd and Module std::unordered_map > m_mapCmd; std::unordered_map > m_mapModule; - // Chain and Engine - std::unordered_map > m_mapChainConf; //key为Chain的类名,value为由Engine类名和Step类名构成的ChainBlock链 + // Chain and Matrix + std::unordered_map > m_mapChainConf; //key为Chain的类名,value为由Matrix类名和Step类名构成的ChainBlock链 std::unordered_map > m_mapChain; //key为Chain的Sequence - std::unordered_map > m_mapEngine; //key为Engine的Sequence + std::unordered_map > m_mapMatrix; //key为Matrix类名 // Step and Session std::unordered_map > m_mapCallbackStep; diff --git a/src/labor/WorkerImpl.inl b/src/labor/WorkerImpl.inl index 6b1665ac..959196e7 100644 --- a/src/labor/WorkerImpl.inl +++ b/src/labor/WorkerImpl.inl @@ -26,220 +26,110 @@ void WorkerImpl::Logger(const std::string& strTraceId, int iLogLevel, const char } template -std::shared_ptr WorkerImpl::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args) { - Step* pStep = dynamic_cast(ActorFactory::Instance()->Create(strStepName, std::forward(args)...)); - if (nullptr == pStep) + Actor* pActor = ActorFactory::Instance()->Create(strActorName, std::forward(args)...); + if (nullptr == pActor) { - LOG4_ERROR("failed to make shared step \"%s\"", strStepName.c_str()); + LOG4_ERROR("failed to make shared actor \"%s\"", strActorName.c_str()); return(nullptr); } - StepModel* pStepAlias = (StepModel*)pStep; - pStepAlias->m_dTimeout = (0 == pStepAlias->m_dTimeout) ? m_stWorkerInfo.dStepTimeout : pStepAlias->m_dTimeout; - LOG4_TRACE("%s(StepName \"%s\", Step* 0x%X, lifetime %lf)", __FUNCTION__, strStepName.c_str(), pStepAlias, pStepAlias->m_dTimeout); - - pStepAlias->SetWorker(m_pWorker); - pStepAlias->SetActiveTime(ev_now(m_loop)); - pStepAlias->SetActorName(strStepName); + pActor->SetWorker(m_pWorker); + pActor->SetActiveTime(ev_now(m_loop)); + pActor->SetActorName(strActorName); if (nullptr != pCreator) { - pStepAlias->SetContext(pCreator->GetContext()); - switch(pCreator->m_eActorType) - { - case Actor::ACT_PB_STEP: - case Actor::ACT_HTTP_STEP: - case Actor::ACT_REDIS_STEP: - case Actor::ACT_CMD: - case Actor::ACT_MODULE: - pStepAlias->m_strTraceId = pCreator->m_strTraceId; - break; - case Actor::ACT_SESSION: - case Actor::ACT_TIMER: - { - std::ostringstream oss; - oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << pStepAlias->GetSequence(); - pStepAlias->m_strTraceId = std::move(oss.str()); - } - break; - default: - ; - } - } - ev_timer* timer_watcher = pStepAlias->MutableTimerWatcher(); - if (NULL == timer_watcher) - { - delete pStep; - pStepAlias = pStep = nullptr; - return(nullptr); + pActor->SetContext(pCreator->GetContext()); } - - for (auto iter = pStepAlias->m_setNextStepSeq.begin(); iter != pStepAlias->m_setNextStepSeq.end(); ++iter) + std::shared_ptr pSharedActor; + pSharedActor.reset(pActor); + pActor = nullptr; + switch (pSharedActor->GetActorType()) { - auto callback_iter = m_mapCallbackStep.find(*iter); - if (callback_iter != m_mapCallbackStep.end()) - { - (std::dynamic_pointer_cast(callback_iter->second))->m_setPreStepSeq.insert(pStepAlias->GetSequence()); - } + case Actor::ACT_PB_STEP: + case Actor::ACT_HTTP_STEP: + case Actor::ACT_REDIS_STEP: + if (TransformToSharedStep(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_SESSION: + case Actor::ACT_TIMER: + case Actor::ACT_CONTEXT: + if (TransformToSharedSession(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_CMD: + if (TransformToSharedCmd(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_MODULE: + if (TransformToSharedModule(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_MATRIX: + if (TransformToSharedMatrix(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_CHAIN: + if (TransformToSharedChain(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + default: + LOG4_ERROR("\"%s\" must be a Step, a Session, a Matrix, a Cmd or a Module.", + strActorName.c_str()); + return(nullptr); } + return(nullptr); +} - std::shared_ptr pSharedStep; - pSharedStep.reset(pStep); - auto ret = m_mapCallbackStep.insert(std::make_pair(pStepAlias->GetSequence(), pSharedStep)); - if (ret.second) - { - if (gc_dNoTimeout != pStepAlias->m_dTimeout) - { - ev_timer_init (timer_watcher, StepTimeoutCallback, pStepAlias->m_dTimeout + ev_time() - ev_now(m_loop), 0.); - ev_timer_start (m_loop, timer_watcher); - } - LOG4_TRACE("Step(seq %u, active_time %lf, lifetime %lf) register successful.", - pStepAlias->GetSequence(), pStepAlias->GetActiveTime(), pStepAlias->GetTimeout()); - auto step_class_iter = m_mapLoadedStep.find(pStepAlias->GetActorName()); - if (step_class_iter != m_mapLoadedStep.end()) - { - step_class_iter->second.insert(pStepAlias->GetSequence()); - } - return(pSharedStep); - } - else - { - pStepAlias = pStep = nullptr; - return(nullptr); - } +template +std::shared_ptr WorkerImpl::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args) +{ + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strStepName, std::forward(args)...))); } template std::shared_ptr WorkerImpl::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args) { - Session* pSession = dynamic_cast(ActorFactory::Instance()->Create(strSessionName, std::forward(args)...)); - if (nullptr == pSession) - { - LOG4_ERROR("failed to make shared session \"%s\"", strSessionName.c_str()); - return(nullptr); - } - SessionModel* pSessionAlias = (SessionModel*)pSession; - LOG4_TRACE("%s(SessionName \"%s\", Session* 0x%X, lifetime %lf)", __FUNCTION__, strSessionName.c_str(), pSession, pSessionAlias->m_dTimeout); - - pSessionAlias->SetWorker(m_pWorker); - pSessionAlias->SetActiveTime(ev_now(m_loop)); - pSessionAlias->SetActorName(strSessionName); - if (nullptr != pCreator) - { - pSessionAlias->SetContext(pCreator->GetContext()); - } - ev_timer* timer_watcher = pSessionAlias->MutableTimerWatcher(); - if (NULL == timer_watcher) - { - delete pSession; - pSessionAlias = pSession = nullptr; - return(nullptr); - } - - std::shared_ptr pSharedSession; - pSharedSession.reset(pSession); - auto ret = m_mapCallbackSession.insert(std::make_pair(pSessionAlias->GetSessionId(), pSharedSession)); - if (ret.second) - { - if (gc_dNoTimeout != pSessionAlias->m_dTimeout) - { - ev_timer_init (timer_watcher, SessionTimeoutCallback, pSessionAlias->m_dTimeout + ev_time() - ev_now(m_loop), 0.); - ev_timer_start (m_loop, timer_watcher); - } - LOG4_TRACE("Session(seq %u, active_time %lf, lifetime %lf) register successful.", - pSessionAlias->GetSequence(), pSessionAlias->GetActiveTime(), pSessionAlias->GetTimeout()); - auto session_class_iter = m_mapLoadedSession.find(pSessionAlias->GetActorName()); - if (session_class_iter != m_mapLoadedSession.end()) - { - session_class_iter->second.insert(pSessionAlias->GetSessionId()); - } - return(pSharedSession); - } - else - { - pSessionAlias = pSession = nullptr; - return(nullptr); - } + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strSessionName, std::forward(args)...))); } template std::shared_ptr WorkerImpl::MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args) { - LOG4_TRACE("%s(CmdName \"%s\")", __FUNCTION__, strCmdName.c_str()); - Cmd* pCmd = dynamic_cast(ActorFactory::Instance()->Create(strCmdName, std::forward(args)...)); - if (nullptr == pCmd) - { - LOG4_ERROR("failed to make shared cmd \"%s\"", strCmdName.c_str()); - return(nullptr); - } - CmdModel* pCmdAlias = (CmdModel*)pCmd; - LOG4_TRACE("%s(CmdName \"%s\", Cmd* 0x%X)", __FUNCTION__, strCmdName.c_str(), pCmd); - - pCmdAlias->SetWorker(m_pWorker); - pCmdAlias->SetActiveTime(ev_now(m_loop)); - pCmdAlias->SetActorName(strCmdName); - if (nullptr != pCreator) - { - pCmdAlias->SetContext(pCreator->GetContext()); - } - - std::shared_ptr pSharedCmd; - pSharedCmd.reset(pCmd); - auto ret = m_mapCmd.insert(std::make_pair(pCmdAlias->GetCmd(), pSharedCmd)); - if (ret.second) - { - if (pCmdAlias->Init()) - { - auto cmd_class_iter = m_mapLoadedCmd.find(pCmdAlias->GetActorName()); - if (cmd_class_iter != m_mapLoadedCmd.end()) - { - cmd_class_iter->second.insert(pCmdAlias->GetCmd()); - } - return(pSharedCmd); - } - } - pCmdAlias = pCmd = nullptr; - return(nullptr); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strCmdName, std::forward(args)...))); } template std::shared_ptr WorkerImpl::MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs... args) { - Module* pModule = dynamic_cast(ActorFactory::Instance()->Create(strModuleName, std::forward(args)...)); - if (nullptr == pModule) - { - LOG4_ERROR("failed to make shared module \"%s\"", strModuleName.c_str()); - return(nullptr); - } - ModuleModel* pModuleAlias = (ModuleModel*)pModule; - LOG4_TRACE("%s(ModuleName \"%s\", Module* 0x%X)", __FUNCTION__, strModuleName.c_str(), pModule); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModuleName, std::forward(args)...))); +} - pModuleAlias->SetWorker(m_pWorker); - pModuleAlias->SetActiveTime(ev_now(m_loop)); - pModuleAlias->SetActorName(strModuleName); - if (nullptr != pCreator) - { - pModuleAlias->SetContext(pCreator->GetContext()); - } +template +std::shared_ptr WorkerImpl::MakeSharedMatrix(Actor* pCreator, const std::string& strMatrixName, Targs... args) +{ + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strMatrixName, std::forward(args)...))); +} - std::shared_ptr pSharedModule; - pSharedModule.reset(pModule); - auto ret = m_mapModule.insert(std::make_pair(pModuleAlias->GetModulePath(), pSharedModule)); - if (ret.second) - { - if (pModuleAlias->Init()) - { - auto module_class_iter = m_mapLoadedModule.find(pModuleAlias->GetActorName()); - if (module_class_iter != m_mapLoadedModule.end()) - { - module_class_iter->second.insert(pModuleAlias->GetModulePath()); - } - return(pSharedModule); - } - } - pModuleAlias = pModule = nullptr; - return(nullptr); +template +std::shared_ptr WorkerImpl::MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs... args) +{ + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strChainName, std::forward(args)...))); } } -#endif /* SRC_LABOR_CHIEF_WORKERIMPL_INL_ */ +#endif /* LABOR_WORKERIMPL_INL_ */ From 7ae214da5c141eab450081b0e391e87b022c28ec Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 18 Jun 2019 08:51:45 +0800 Subject: [PATCH 030/176] optimization chain and matrix --- conf/nebula.json | 6 +-- src/actor/chain/Chain.cpp | 91 +++++++++++++++++++++++-------------- src/actor/chain/Chain.hpp | 20 +++++--- src/actor/matrix/Matrix.hpp | 2 +- src/labor/WorkerImpl.cpp | 42 ++--------------- src/labor/WorkerImpl.hpp | 4 +- 6 files changed, 81 insertions(+), 84 deletions(-) diff --git a/conf/nebula.json b/conf/nebula.json index 68b61877..19973626 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -96,11 +96,11 @@ } ], "runtime":{ - "chains":[ + "chains":{ "chain_1":["step1", "matrix1", ["step2A", "step2B", "step2C"], "step3", "matrix2"], "chain_2":[] - ] - } + } + }, "//custom": "自定义配置,用于通过框架层带给业务", "custom": {} } diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 954109b5..2d55d7eb 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -15,8 +15,9 @@ namespace neb { -Chain::Chain(ev_tstamp dChainTimeout) - : ActorWithCreation(Actor::ACT_CHAIN, dChainTimeout) +Chain::Chain(const std::string& strChainId, ev_tstamp dChainTimeout) + : ActorWithCreation(Actor::ACT_CHAIN, dChainTimeout), + m_strChainId(strChainId) { } @@ -24,53 +25,77 @@ Chain::~Chain() { } -void Chain::InitChainBlock(const std::vector& vecChainBlock) +void Chain::Init(const std::queue >& queChainBlock) { - m_vecChainBlock = vecChainBlock; + m_queChainBlock = queChainBlock; } E_CMD_STATUS Chain::NextBlock() { - auto iter = m_vecChainBlock.begin(); - LOG4_TRACE("(%s)", (*iter).c_str()); - std::shared_ptr pSharedMatrix = GetMatrix(*iter); - if (pSharedMatrix == nullptr) + if (m_queChainBlock.empty()) { - std::shared_ptr pSharedActor = m_pWorker->MakeSharedActor(this, *iter); - // pSharedMatrix->SetContext(GetContext()); it had been set in MakeSharedActor(). - m_vecChainBlock.erase(iter); - if (Actor::ACT_MATRIX == pSharedActor->GetActorType()) + return(CMD_STATUS_COMPLETED); + } + bool bStepInBlock = false; ///< 当前链块存在Step + E_CMD_STATUS eResult = CMD_STATUS_START; + std::unordered_set& setTurnBlocks = m_queChainBlock.front(); + for (auto iter = setTurnBlocks.begin(); iter != setTurnBlocks.end(); ++iter) + { + LOG4_TRACE("(%s)", (*iter).c_str()); + std::shared_ptr pSharedMatrix = GetMatrix(*iter); + if (pSharedMatrix == nullptr) { - if (CMD_STATUS_FAULT - == (std::dynamic_pointer_cast(pSharedActor))->Launch()) + std::shared_ptr pSharedActor = m_pWorker->MakeSharedActor(this, *iter); + // pSharedMatrix->SetContext(GetContext()); it had been set in MakeSharedActor(). + if (Actor::ACT_MATRIX == pSharedActor->GetActorType()) + { + pSharedMatrix = std::dynamic_pointer_cast(pSharedActor); + eResult = pSharedMatrix->Submit(); + pSharedMatrix->SetContext(nullptr); + if (CMD_STATUS_FAULT == eResult) + { + return(CMD_STATUS_FAULT); + } + } + else if (Actor::ACT_PB_STEP == pSharedActor->GetActorType() + || Actor::ACT_HTTP_STEP == pSharedActor->GetActorType() + || Actor::ACT_REDIS_STEP == pSharedActor->GetActorType()) { + bStepInBlock = true; + std::shared_ptr pSharedStep = std::dynamic_pointer_cast(pSharedActor); + pSharedStep->SetChainId(GetSequence()); + eResult = pSharedStep->Emit(); + if (CMD_STATUS_FAULT == eResult) + { + return(CMD_STATUS_FAULT); + } + } + else + { + LOG4_ERROR("\"%s\" is not a Matrix or Step, only Matrix and Step can be a Chain block.", + pSharedActor->GetActorName().c_str()); return(CMD_STATUS_FAULT); } - return(NextBlock()); - } - else if (Actor::ACT_PB_STEP == pSharedActor->GetActorType() - || Actor::ACT_HTTP_STEP == pSharedActor->GetActorType() - || Actor::ACT_REDIS_STEP == pSharedActor->GetActorType()) - { - std::shared_ptr pSharedStep = std::dynamic_pointer_cast(pSharedActor); - pSharedStep->SetChainId(GetSequence()); - return(pSharedStep->Emit()); } else { - LOG4_ERROR("\"%s\" is not a Matrix or Step, only Matrix and Step can be a Chain block.", - pSharedActor->GetActorName().c_str()); - return(CMD_STATUS_FAULT); + pSharedMatrix->SetContext(GetContext()); + eResult = pSharedMatrix->Submit(); + pSharedMatrix->SetContext(nullptr); + if (CMD_STATUS_FAULT == eResult) + { + return(CMD_STATUS_FAULT); + } } } - else + + m_queChainBlock.pop(); + if (bStepInBlock) + { + return(CMD_STATUS_RUNNING); + } + else // 只有Matrix的链块(无IO回调),执行完当前链块后立即执行下一个链块 { - m_vecChainBlock.erase(iter); - pSharedMatrix->SetContext(GetContext()); - if (CMD_STATUS_FAULT == pSharedMatrix->Launch()) - { - return(CMD_STATUS_FAULT); - } return(NextBlock()); } } diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index 77804afe..447ef483 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -10,7 +10,8 @@ #ifndef SRC_ACTOR_CHAIN_CHAIN_HPP_ #define SRC_ACTOR_CHAIN_CHAIN_HPP_ -#include +#include +#include #include "labor/Worker.hpp" #include "actor/ActorWithCreation.hpp" #include "actor/DynamicCreator.hpp" @@ -18,25 +19,32 @@ namespace neb { -class Chain: public ActorWithCreation +class Chain final: public ActorWithCreation { public: - Chain(ev_tstamp dChainTimeout = 60.0); + Chain(const std::string& strChainId, ev_tstamp dChainTimeout = 60.0); Chain(const Chain&) = delete; Chain& operator=(const Chain&) = delete; virtual ~Chain(); + void Init(const std::queue >& queChainBlock); /** - * @brief 会话超时回调 + * @brief 调用链超时回调 */ virtual E_CMD_STATUS Timeout() = 0; + const std::string& GetChainId() const + { + return(m_strChainId); + } + private: - void InitChainBlock(const std::vector& vecChainBlock); E_CMD_STATUS NextBlock(); private: - std::vector m_vecChainBlock; + std::string m_strChainId; + // queue的每个元素称为链块(std::unordered_set) + std::queue > m_queChainBlock; friend class WorkerImpl; }; diff --git a/src/actor/matrix/Matrix.hpp b/src/actor/matrix/Matrix.hpp index 0b2f53b8..7554dd70 100644 --- a/src/actor/matrix/Matrix.hpp +++ b/src/actor/matrix/Matrix.hpp @@ -46,7 +46,7 @@ class Matrix: public ActorWithCreation /** * @brief 提交 */ - virtual E_CMD_STATUS Launch() = 0; + virtual E_CMD_STATUS Submit() = 0; private: friend class WorkerImpl; diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 3b681c53..4815d437 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -943,15 +943,15 @@ bool WorkerImpl::TransformToSharedChain(Actor* pCreator, std::shared_ptr } std::shared_ptr pSharedChain = std::dynamic_pointer_cast(pSharedActor); - auto chain_conf_iter = m_mapChainConf.find(pSharedActor->GetActorName()); + auto chain_conf_iter = m_mapChainConf.find(pSharedChain->GetChainId()); if (chain_conf_iter == m_mapChainConf.end()) { - LOG4_ERROR("no chain block config for \"%s\"", pSharedActor->GetActorName().c_str()); + LOG4_ERROR("no chain block config for \"%s\"", pSharedChain->GetChainId().c_str()); return(false); } else { - pSharedChain->InitChainBlock(chain_conf_iter->second); + pSharedChain->Init(chain_conf_iter->second); } auto ret = m_mapChain.insert(std::make_pair(pSharedChain->GetSequence(), pSharedChain)); if (ret.second) @@ -961,11 +961,6 @@ bool WorkerImpl::TransformToSharedChain(Actor* pCreator, std::shared_ptr ev_timer_init (timer_watcher, ChainTimeoutCallback, pSharedChain->m_dTimeout + ev_time() - ev_now(m_loop), 0.); ev_timer_start (m_loop, timer_watcher); } - auto chain_class_iter = m_mapLoadedChain.find(pSharedChain->GetActorName()); - if (chain_class_iter != m_mapLoadedChain.end()) - { - chain_class_iter->second.insert(pSharedChain->GetSequence()); - } return(true); } return(false); @@ -2067,11 +2062,6 @@ void WorkerImpl::LoadDynamicSymbol(CJsonObject& oOneSoConf) std::unordered_set setStep; m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); } - for (int l = 0; l < oOneSoConf["chain"].GetArraySize(); ++l) - { - std::unordered_set setChain; - m_mapLoadedChain.insert(std::make_pair(oOneSoConf["chain"](l), setChain)); - } } void WorkerImpl::UnloadDynamicSymbol(CJsonObject& oOneSoConf) @@ -2155,23 +2145,6 @@ void WorkerImpl::UnloadDynamicSymbol(CJsonObject& oOneSoConf) m_mapMatrix.erase(class_iter); } } - for (int k = 0; k < oOneSoConf["chain"].GetArraySize(); ++k) - { - auto class_iter = m_mapLoadedChain.find(oOneSoConf["chain"](k)); - if (class_iter != m_mapLoadedChain.end()) - { - for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) - { - auto chain_iter = m_mapChain.find(*id_iter); - if (chain_iter != m_mapChain.end()) - { - m_mapChain.erase(chain_iter); - } - } - class_iter->second.clear(); - m_mapLoadedChain.erase(class_iter); - } - } } bool WorkerImpl::AddPeriodicTaskEvent() @@ -2535,15 +2508,6 @@ void WorkerImpl::Remove(uint32 uiChainId) if (chain_iter != m_mapChain.end()) { std::shared_ptr pChain = chain_iter->second; - auto class_iter = m_mapLoadedChain.find(pChain->GetActorName()); - if (class_iter != m_mapLoadedChain.end()) - { - auto id_iter = class_iter->second.find(pChain->GetSequence()); - if (id_iter != class_iter->second.end()) - { - class_iter->second.erase(id_iter); - } - } if (pChain->MutableTimerWatcher() != NULL) { ev_timer_stop (m_loop, pChain->MutableTimerWatcher()); diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 601bf766..961df97e 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -47,6 +47,7 @@ extern "C" { #include #include #include +#include #include #include @@ -358,14 +359,13 @@ class WorkerImpl std::unordered_map > m_mapLoadedModule; //key为ModuleClassName,value为strModulePath集合 std::unordered_map > m_mapLoadedStep; //key为StepClassName,value为uiSeq集合 std::unordered_map > m_mapLoadedSession; //key为SessionClassName,value为strSessionId集合 - std::unordered_map > m_mapLoadedChain; //key为ChainClassName,value为uiSeq集合 // Cmd and Module std::unordered_map > m_mapCmd; std::unordered_map > m_mapModule; // Chain and Matrix - std::unordered_map > m_mapChainConf; //key为Chain的类名,value为由Matrix类名和Step类名构成的ChainBlock链 + std::unordered_map > > m_mapChainConf; //key为Chain的配置名,value为由Matrix类名和Step类名构成的ChainBlock链 std::unordered_map > m_mapChain; //key为Chain的Sequence std::unordered_map > m_mapMatrix; //key为Matrix类名 From 2a3f3307904f67559bf2758746eddc0a11c73a0f Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 19 Jun 2019 08:39:23 +0800 Subject: [PATCH 031/176] add runtime configuration and read config into m_mapChainConf --- src/actor/chain/Chain.cpp | 6 +++--- src/actor/chain/Chain.hpp | 14 +++++++------- src/labor/WorkerImpl.cpp | 23 +++++++++++++++++++++++ src/labor/WorkerImpl.hpp | 2 +- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 2d55d7eb..00adef05 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -25,7 +25,7 @@ Chain::~Chain() { } -void Chain::Init(const std::queue >& queChainBlock) +void Chain::Init(const std::queue >& queChainBlock) { m_queChainBlock = queChainBlock; } @@ -38,8 +38,8 @@ E_CMD_STATUS Chain::NextBlock() } bool bStepInBlock = false; ///< 当前链块存在Step E_CMD_STATUS eResult = CMD_STATUS_START; - std::unordered_set& setTurnBlocks = m_queChainBlock.front(); - for (auto iter = setTurnBlocks.begin(); iter != setTurnBlocks.end(); ++iter) + std::vector& vecTurnBlocks = m_queChainBlock.front(); + for (auto iter = vecTurnBlocks.begin(); iter != vecTurnBlocks.end(); ++iter) { LOG4_TRACE("(%s)", (*iter).c_str()); std::shared_ptr pSharedMatrix = GetMatrix(*iter); diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index 447ef483..07ed4f1f 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -11,7 +11,7 @@ #define SRC_ACTOR_CHAIN_CHAIN_HPP_ #include -#include +#include #include "labor/Worker.hpp" #include "actor/ActorWithCreation.hpp" #include "actor/DynamicCreator.hpp" @@ -27,11 +27,11 @@ class Chain final: public ActorWithCreation Chain& operator=(const Chain&) = delete; virtual ~Chain(); - void Init(const std::queue >& queChainBlock); - /** - * @brief 调用链超时回调 - */ - virtual E_CMD_STATUS Timeout() = 0; + void Init(const std::queue >& queChainBlock); + virtual E_CMD_STATUS Timeout() + { + return(CMD_STATUS_FAULT); + } const std::string& GetChainId() const { @@ -44,7 +44,7 @@ class Chain final: public ActorWithCreation private: std::string m_strChainId; // queue的每个元素称为链块(std::unordered_set) - std::queue > m_queChainBlock; + std::queue > m_queChainBlock; friend class WorkerImpl; }; diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 4815d437..9aaca627 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -1055,6 +1055,29 @@ bool WorkerImpl::Init(CJsonObject& oJsonConf) } #endif } + + std::string strChainKey; + while (oJsonConf["runtime"]["chains"].GetKey(strChainKey)) + { + std::queue > queChainBlocks; + for (int i = 0; i < oJsonConf["runtime"]["chains"][strChainKey].GetArraySize(); ++i) + { + std::vector vecBlock; + if (oJsonConf["runtime"]["chains"][strChainKey][i].IsArray()) + { + for (int j = 0; j < oJsonConf["runtime"]["chains"][strChainKey][i].GetArraySize(); ++j) + { + vecBlock.push_back(std::move(oJsonConf["runtime"]["chains"][strChainKey][i](j))); + } + } + else + { + vecBlock.push_back(std::move(oJsonConf["runtime"]["chains"][strChainKey](i))); + } + queChainBlocks.push(std::move(vecBlock)); + } + m_mapChainConf.insert(std::make_pair(strChainKey, std::move(queChainBlocks))); + } return(true); } diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 961df97e..445fd5bd 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -365,7 +365,7 @@ class WorkerImpl std::unordered_map > m_mapModule; // Chain and Matrix - std::unordered_map > > m_mapChainConf; //key为Chain的配置名,value为由Matrix类名和Step类名构成的ChainBlock链 + std::unordered_map > > m_mapChainConf; //key为Chain的配置名,value为由Matrix类名和Step类名构成的ChainBlock链 std::unordered_map > m_mapChain; //key为Chain的Sequence std::unordered_map > m_mapMatrix; //key为Matrix类名 From 1595a923ecc798818c8e9f02346725339b40eadb Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 19 Jun 2019 08:58:03 +0800 Subject: [PATCH 032/176] optimization MakeSharedActor --- src/labor/WorkerImpl.cpp | 59 ++++++++++++++++++++++++++++++++++++++++ src/labor/WorkerImpl.hpp | 1 + src/labor/WorkerImpl.inl | 56 +------------------------------------- 3 files changed, 61 insertions(+), 55 deletions(-) diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 9aaca627..aa741a24 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -785,6 +785,65 @@ bool WorkerImpl::OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privd return(true); } +std::shared_ptr WorkerImpl::InitializeSharedActor(Actor* pCreator, std::shared_ptr pSharedActor, const std::string& strActorName) +{ + pSharedActor->SetWorker(m_pWorker); + pSharedActor->SetActiveTime(ev_now(m_loop)); + pSharedActor->SetActorName(strActorName); + if (nullptr != pCreator) + { + pSharedActor->SetContext(pCreator->GetContext()); + } + switch (pSharedActor->GetActorType()) + { + case Actor::ACT_PB_STEP: + case Actor::ACT_HTTP_STEP: + case Actor::ACT_REDIS_STEP: + if (TransformToSharedStep(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_SESSION: + case Actor::ACT_TIMER: + case Actor::ACT_CONTEXT: + if (TransformToSharedSession(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_CMD: + if (TransformToSharedCmd(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_MODULE: + if (TransformToSharedModule(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_MATRIX: + if (TransformToSharedMatrix(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_CHAIN: + if (TransformToSharedChain(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + default: + LOG4_ERROR("\"%s\" must be a Step, a Session, a Matrix, a Cmd or a Module.", + strActorName.c_str()); + return(nullptr); + } + return(nullptr); +} + bool WorkerImpl::TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor) { pSharedActor->m_dTimeout = (0 == pSharedActor->m_dTimeout) ? m_stWorkerInfo.dStepTimeout : pSharedActor->m_dTimeout; diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 445fd5bd..891cd25d 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -196,6 +196,7 @@ class WorkerImpl template std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args); + std::shared_ptr InitializeSharedActor(Actor* pCreator, std::shared_ptr pSharedActor, const std::string& strActorName); template std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args); diff --git a/src/labor/WorkerImpl.inl b/src/labor/WorkerImpl.inl index 959196e7..eb2ccd43 100644 --- a/src/labor/WorkerImpl.inl +++ b/src/labor/WorkerImpl.inl @@ -34,64 +34,10 @@ std::shared_ptr WorkerImpl::MakeSharedActor(Actor* pCreator, const std::s LOG4_ERROR("failed to make shared actor \"%s\"", strActorName.c_str()); return(nullptr); } - pActor->SetWorker(m_pWorker); - pActor->SetActiveTime(ev_now(m_loop)); - pActor->SetActorName(strActorName); - if (nullptr != pCreator) - { - pActor->SetContext(pCreator->GetContext()); - } std::shared_ptr pSharedActor; pSharedActor.reset(pActor); pActor = nullptr; - switch (pSharedActor->GetActorType()) - { - case Actor::ACT_PB_STEP: - case Actor::ACT_HTTP_STEP: - case Actor::ACT_REDIS_STEP: - if (TransformToSharedStep(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - case Actor::ACT_SESSION: - case Actor::ACT_TIMER: - case Actor::ACT_CONTEXT: - if (TransformToSharedSession(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - case Actor::ACT_CMD: - if (TransformToSharedCmd(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - case Actor::ACT_MODULE: - if (TransformToSharedModule(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - case Actor::ACT_MATRIX: - if (TransformToSharedMatrix(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - case Actor::ACT_CHAIN: - if (TransformToSharedChain(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - default: - LOG4_ERROR("\"%s\" must be a Step, a Session, a Matrix, a Cmd or a Module.", - strActorName.c_str()); - return(nullptr); - } - return(nullptr); + return(InitializeSharedActor(pCreator, pSharedActor, strActorName)); } template From 64ac287fa1a700682d942bda57eb81c21194a6c9 Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 19 Jun 2019 22:47:56 +0800 Subject: [PATCH 033/176] remove ActorWithCreation, simplify actors. --- src/actor/Actor.hpp | 31 ++++++++++++++++++-- src/actor/ActorWithCreation.hpp | 51 --------------------------------- src/actor/chain/Chain.cpp | 4 +-- src/actor/chain/Chain.hpp | 4 +-- src/actor/cmd/Cmd.hpp | 6 ++-- src/actor/cmd/Module.hpp | 6 ++-- src/actor/matrix/Matrix.hpp | 6 ++-- src/actor/session/Session.cpp | 4 +-- src/actor/session/Session.hpp | 4 +-- src/actor/step/Step.cpp | 2 +- src/actor/step/Step.hpp | 4 +-- src/labor/WorkerImpl.cpp | 2 ++ src/labor/WorkerImpl.hpp | 16 +++++------ 13 files changed, 59 insertions(+), 81 deletions(-) delete mode 100644 src/actor/ActorWithCreation.hpp diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index e75a19bf..05f7d808 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -38,7 +38,7 @@ class WorkerFriend; class SocketChannel; class RedisChannel; -class ActorWithCreation; +class Actor; class Cmd; class Module; class Session; @@ -72,6 +72,11 @@ class Actor: public std::enable_shared_from_this Actor& operator=(const Actor&) = delete; virtual ~Actor(); + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); + template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); + template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); + template std::shared_ptr MakeSharedActor(const std::string& strActorName, Targs... args); + ACTOR_TYPE GetActorType() const { return(m_eActorType); @@ -249,10 +254,32 @@ class Actor: public std::enable_shared_from_this friend class WorkerImpl; friend class WorkerFriend; - friend class ActorWithCreation; friend class Chain; }; +template +void Actor::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) +{ + m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); +} + +template +std::shared_ptr Actor::MakeSharedStep(const std::string& strStepName, Targs... args) +{ + return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); +} + +template +std::shared_ptr Actor::MakeSharedSession(const std::string& strSessionName, Targs... args) +{ + return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); +} + +template +std::shared_ptr Actor::MakeSharedActor(const std::string& strActorName, Targs... args) +{ + return(m_pWorker->MakeSharedActor(this, strActorName, std::forward(args)...)); +} } /* namespace neb */ diff --git a/src/actor/ActorWithCreation.hpp b/src/actor/ActorWithCreation.hpp deleted file mode 100644 index 1d2d9e58..00000000 --- a/src/actor/ActorWithCreation.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file ActorWithCreation.hpp - * @brief 带创建功能的Actor - * @author Bwar - * @date: 2019年6月15日 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_ACTORWITHCREATION_ACTORWITHCREATION_HPP_ -#define SRC_ACTORWITHCREATION_ACTORWITHCREATION_HPP_ - -#include "Actor.hpp" - -namespace neb -{ - -class ActorWithCreation: public Actor -{ -public: - ActorWithCreation(Actor::ACTOR_TYPE eActorWithCreationType = Actor::ACT_UNDEFINE, ev_tstamp dTimeout = 0.0); - ActorWithCreation(const ActorWithCreation&) = delete; - ActorWithCreation& operator=(const ActorWithCreation&) = delete; - virtual ~ActorWithCreation(); - - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); - template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); - template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); -}; - -template -void ActorWithCreation::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) -{ - m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); -} - -template -std::shared_ptr ActorWithCreation::MakeSharedStep(const std::string& strStepName, Targs... args) -{ - return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); -} - -template -std::shared_ptr ActorWithCreation::MakeSharedSession(const std::string& strSessionName, Targs... args) -{ - return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); -} - -} /* namespace neb */ - -#endif /* SRC_ACTORWITHCREATION_ACTORWITHCREATION_HPP_ */ diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 00adef05..c2e7edee 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -16,7 +16,7 @@ namespace neb { Chain::Chain(const std::string& strChainId, ev_tstamp dChainTimeout) - : ActorWithCreation(Actor::ACT_CHAIN, dChainTimeout), + : Actor(Actor::ACT_CHAIN, dChainTimeout), m_strChainId(strChainId) { } @@ -45,7 +45,7 @@ E_CMD_STATUS Chain::NextBlock() std::shared_ptr pSharedMatrix = GetMatrix(*iter); if (pSharedMatrix == nullptr) { - std::shared_ptr pSharedActor = m_pWorker->MakeSharedActor(this, *iter); + std::shared_ptr pSharedActor = MakeSharedActor(*iter); // pSharedMatrix->SetContext(GetContext()); it had been set in MakeSharedActor(). if (Actor::ACT_MATRIX == pSharedActor->GetActorType()) { diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index 07ed4f1f..a3a9c592 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -13,13 +13,13 @@ #include #include #include "labor/Worker.hpp" -#include "actor/ActorWithCreation.hpp" +#include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" namespace neb { -class Chain final: public ActorWithCreation +class Chain final: public Actor { public: Chain(const std::string& strChainId, ev_tstamp dChainTimeout = 60.0); diff --git a/src/actor/cmd/Cmd.hpp b/src/actor/cmd/Cmd.hpp index 7424145d..1bc09ec7 100644 --- a/src/actor/cmd/Cmd.hpp +++ b/src/actor/cmd/Cmd.hpp @@ -11,17 +11,17 @@ #define SRC_ACTOR_CMD_CMD_HPP_ #include "labor/Worker.hpp" -#include "actor/ActorWithCreation.hpp" +#include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" namespace neb { -class Cmd: public ActorWithCreation +class Cmd: public Actor { public: Cmd(int32 iCmd) - : ActorWithCreation(Actor::ACT_CMD, gc_dNoTimeout), + : Actor(Actor::ACT_CMD, gc_dNoTimeout), m_iCmd(iCmd) { } diff --git a/src/actor/cmd/Module.hpp b/src/actor/cmd/Module.hpp index d2489854..ef639823 100644 --- a/src/actor/cmd/Module.hpp +++ b/src/actor/cmd/Module.hpp @@ -12,16 +12,16 @@ #include "codec/CodecHttp.hpp" #include "labor/Worker.hpp" -#include "actor/ActorWithCreation.hpp" +#include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" namespace neb { -class Module: public ActorWithCreation +class Module: public Actor { public: Module(const std::string& strModulePath) - : ActorWithCreation(Actor::ACT_MODULE, gc_dNoTimeout), + : Actor(Actor::ACT_MODULE, gc_dNoTimeout), m_strModulePath(strModulePath) { } diff --git a/src/actor/matrix/Matrix.hpp b/src/actor/matrix/Matrix.hpp index 7554dd70..9f65d84d 100644 --- a/src/actor/matrix/Matrix.hpp +++ b/src/actor/matrix/Matrix.hpp @@ -11,17 +11,17 @@ #define SRC_ACTOR_MATRIX_HPP_ #include "labor/Worker.hpp" -#include "actor/ActorWithCreation.hpp" +#include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" namespace neb { -class Matrix: public ActorWithCreation +class Matrix: public Actor { public: Matrix() - : ActorWithCreation(Actor::ACT_MATRIX, gc_dNoTimeout) + : Actor(Actor::ACT_MATRIX, gc_dNoTimeout) { } Matrix(const Matrix&) = delete; diff --git a/src/actor/session/Session.cpp b/src/actor/session/Session.cpp index cd5308f7..a621503b 100644 --- a/src/actor/session/Session.cpp +++ b/src/actor/session/Session.cpp @@ -15,7 +15,7 @@ namespace neb { Session::Session(uint32 ulSessionId, ev_tstamp dSessionTimeout) - : ActorWithCreation(Actor::ACT_SESSION, dSessionTimeout), + : Actor(Actor::ACT_SESSION, dSessionTimeout), m_bDataReady(false), m_bDataLoading(false) { std::ostringstream oss; @@ -24,7 +24,7 @@ Session::Session(uint32 ulSessionId, ev_tstamp dSessionTimeout) } Session::Session(const std::string& strSessionId, ev_tstamp dSessionTimeout) - : ActorWithCreation(Actor::ACT_SESSION, dSessionTimeout), + : Actor(Actor::ACT_SESSION, dSessionTimeout), m_bDataReady(false), m_bDataLoading(false), m_strSessionId(strSessionId) { diff --git a/src/actor/session/Session.hpp b/src/actor/session/Session.hpp index 1573a09f..4537a325 100644 --- a/src/actor/session/Session.hpp +++ b/src/actor/session/Session.hpp @@ -13,12 +13,12 @@ #include #include "labor/Worker.hpp" #include "actor/DynamicCreator.hpp" -#include "actor/ActorWithCreation.hpp" +#include "actor/Actor.hpp" namespace neb { -class Session: public ActorWithCreation +class Session: public Actor { public: Session(uint32 ulSessionId, ev_tstamp dSessionTimeout = 60.0); diff --git a/src/actor/step/Step.cpp b/src/actor/step/Step.cpp index 18416288..e2daa165 100644 --- a/src/actor/step/Step.cpp +++ b/src/actor/step/Step.cpp @@ -13,7 +13,7 @@ namespace neb { Step::Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep, ev_tstamp dTimeout) - : ActorWithCreation(eActorType, dTimeout), + : Actor(eActorType, dTimeout), m_uiChainId(0) { if (nullptr != pNextStep) diff --git a/src/actor/step/Step.hpp b/src/actor/step/Step.hpp index e38ea804..58646ba7 100644 --- a/src/actor/step/Step.hpp +++ b/src/actor/step/Step.hpp @@ -11,7 +11,7 @@ #define SRC_ACTOR_STEP_STEP_HPP_ #include "labor/Worker.hpp" -#include "actor/ActorWithCreation.hpp" +#include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" namespace neb @@ -19,7 +19,7 @@ namespace neb class Chain; -class Step: public ActorWithCreation +class Step: public Actor { public: Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index aa741a24..0c157397 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -20,6 +20,7 @@ extern "C" { } #endif #include "labor/WorkerImpl.hpp" +#include "labor/Worker.hpp" #include "actor/Actor.hpp" #include "actor/cmd/Cmd.hpp" #include "actor/cmd/Module.hpp" @@ -30,6 +31,7 @@ extern "C" { #include "actor/step/Step.hpp" #include "actor/matrix/Matrix.hpp" #include "actor/chain/Chain.hpp" +#include "actor/session/sys_session/SessionNode.hpp" #include "actor/session/sys_session/SessionLogger.hpp" namespace neb diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 891cd25d..4c19549d 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -61,9 +61,7 @@ extern "C" { #include "codec/Codec.hpp" #include "logger/NetLogger.hpp" -#include "actor/Actor.hpp" #include "actor/ActorFactory.hpp" -#include "actor/session/sys_session/SessionNode.hpp" namespace neb { @@ -82,6 +80,7 @@ class HttpStep; class Matrix; class Chain; +class SessionNode; class SessionLogger; class WorkerImpl @@ -196,31 +195,32 @@ class WorkerImpl template std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args); - std::shared_ptr InitializeSharedActor(Actor* pCreator, std::shared_ptr pSharedActor, const std::string& strActorName); template std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args); - bool TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor); template std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args); - bool TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor); template std::shared_ptr MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args); - bool TransformToSharedCmd(Actor* pCreator, std::shared_ptr pSharedActor); template std::shared_ptr MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs... args); - bool TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor); template std::shared_ptr MakeSharedMatrix(Actor* pCreator, const std::string& strMatrixName, Targs... args); - bool TransformToSharedMatrix(Actor* pCreator, std::shared_ptr pSharedActor); template std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs... args); + + std::shared_ptr InitializeSharedActor(Actor* pCreator, std::shared_ptr pSharedActor, const std::string& strActorName); + bool TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedCmd(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedMatrix(Actor* pCreator, std::shared_ptr pSharedActor); public: // about channel virtual bool AddNetLogMsg(const MsgBody& oMsgBody); From 4e13e1e61cf38679649a985f69fe5559e3ca2435 Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 21 Jun 2019 22:37:56 +0800 Subject: [PATCH 034/176] 1. add SetTraceId() GetTraceId() to Actor. 2. add cmd status check for NextBlock. 3. change Context from derived class of Session to derived class of Actor. 4. add PbContext and HttpContext. --- src/actor/Actor.cpp | 5 + src/actor/Actor.hpp | 8 +- src/actor/chain/Chain.cpp | 7 +- src/actor/chain/Chain.hpp | 8 +- src/actor/context/Context.cpp | 32 ++++++ src/actor/{session => context}/Context.hpp | 65 ++--------- src/actor/context/HttpContext.cpp | 49 +++++++++ src/actor/context/HttpContext.hpp | 47 ++++++++ src/actor/context/PbContext.cpp | 64 +++++++++++ src/actor/context/PbContext.hpp | 68 ++++++++++++ src/actor/session/Context.cpp | 67 ------------ src/actor/session/Session.cpp | 16 +++ src/actor/session/Session.hpp | 5 + src/actor/session/Timer.cpp | 4 +- src/labor/WorkerImpl.cpp | 120 ++++++++++++--------- src/labor/WorkerImpl.hpp | 10 +- 16 files changed, 389 insertions(+), 186 deletions(-) create mode 100644 src/actor/context/Context.cpp rename src/actor/{session => context}/Context.hpp (50%) create mode 100644 src/actor/context/HttpContext.cpp create mode 100644 src/actor/context/HttpContext.hpp create mode 100644 src/actor/context/PbContext.cpp create mode 100644 src/actor/context/PbContext.hpp delete mode 100644 src/actor/session/Context.cpp diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 1a1dff14..3e9d6830 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -195,4 +195,9 @@ void Actor::SetActorName(const std::string& strActorName) m_strActorName = strActorName; } +void Actor::SetTraceId(const std::string& strTraceId) +{ + m_strTraceId = strTraceId; +} + } /* namespace neb */ diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 05f7d808..030df9f2 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -87,6 +87,11 @@ class Actor: public std::enable_shared_from_this return(m_strActorName); } + const std::string& GetTraceId() const + { + return(m_strTraceId); + } + uint32 GetSequence(); protected: @@ -236,10 +241,9 @@ class Actor: public std::enable_shared_from_this private: void SetWorker(Worker* pWorker); - ev_timer* MutableTimerWatcher(); - void SetActorName(const std::string& strActorName); + void SetTraceId(const std::string& strTraceId); private: ACTOR_TYPE m_eActorType; diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index c2e7edee..f6df1355 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -15,9 +15,9 @@ namespace neb { -Chain::Chain(const std::string& strChainId, ev_tstamp dChainTimeout) +Chain::Chain(const std::string& strChainFlag, ev_tstamp dChainTimeout) : Actor(Actor::ACT_CHAIN, dChainTimeout), - m_strChainId(strChainId) + m_strChainFlag(strChainFlag) { } @@ -52,6 +52,7 @@ E_CMD_STATUS Chain::NextBlock() pSharedMatrix = std::dynamic_pointer_cast(pSharedActor); eResult = pSharedMatrix->Submit(); pSharedMatrix->SetContext(nullptr); + pSharedMatrix->SetTraceId(""); if (CMD_STATUS_FAULT == eResult) { return(CMD_STATUS_FAULT); @@ -80,8 +81,10 @@ E_CMD_STATUS Chain::NextBlock() else { pSharedMatrix->SetContext(GetContext()); + pSharedMatrix->SetTraceId(GetTraceId()); eResult = pSharedMatrix->Submit(); pSharedMatrix->SetContext(nullptr); + pSharedMatrix->SetTraceId(""); if (CMD_STATUS_FAULT == eResult) { return(CMD_STATUS_FAULT); diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index a3a9c592..3d086946 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -22,7 +22,7 @@ namespace neb class Chain final: public Actor { public: - Chain(const std::string& strChainId, ev_tstamp dChainTimeout = 60.0); + Chain(const std::string& strChainFlag, ev_tstamp dChainTimeout = 60.0); Chain(const Chain&) = delete; Chain& operator=(const Chain&) = delete; virtual ~Chain(); @@ -33,16 +33,16 @@ class Chain final: public Actor return(CMD_STATUS_FAULT); } - const std::string& GetChainId() const + const std::string& GetChainFlag() const { - return(m_strChainId); + return(m_strChainFlag); } private: E_CMD_STATUS NextBlock(); private: - std::string m_strChainId; + std::string m_strChainFlag; // queue的每个元素称为链块(std::unordered_set) std::queue > m_queChainBlock; diff --git a/src/actor/context/Context.cpp b/src/actor/context/Context.cpp new file mode 100644 index 00000000..e3b73e48 --- /dev/null +++ b/src/actor/context/Context.cpp @@ -0,0 +1,32 @@ +/******************************************************************************* + * Project: Nebula + * @file Context.cpp + * @brief + * @author Bwar + * @date: 2019年2月16日 + * @note + * Modify history: + ******************************************************************************/ + +#include "Context.hpp" + +namespace neb +{ + +Context::Context() + : Actor(ACT_CONTEXT, gc_dNoTimeout), + m_pChannel(nullptr) +{ +} + +Context::Context(std::shared_ptr pChannel) + : Actor(ACT_CONTEXT, gc_dNoTimeout), + m_pChannel(pChannel) +{ +} + +Context::~Context() +{ +} + +} /* namespace neb */ diff --git a/src/actor/session/Context.hpp b/src/actor/context/Context.hpp similarity index 50% rename from src/actor/session/Context.hpp rename to src/actor/context/Context.hpp index f72d56ed..04a01be4 100644 --- a/src/actor/session/Context.hpp +++ b/src/actor/context/Context.hpp @@ -7,12 +7,12 @@ * @note * Modify history: ******************************************************************************/ -#ifndef SRC_ACTOR_SESSION_CONTEXT_HPP_ -#define SRC_ACTOR_SESSION_CONTEXT_HPP_ +#ifndef SRC_ACTOR_CONTEXT_HPP_ +#define SRC_ACTOR_CONTEXT_HPP_ #include "labor/Worker.hpp" #include "actor/DynamicCreator.hpp" -#include "Session.hpp" +#include "actor/Actor.hpp" namespace neb { @@ -21,6 +21,9 @@ namespace neb * @brief Actor上下文信息 * @note Actor上下文信息用于保存Actor(主要是Step,也可用于Session)运行 * 过程中的上下信息存储和数据共享。 + * Context由Actor(可以是Cmd、Module、Step、Chain)创建,并由创建者 + * Actor和传递使用者Actor之间共享,框架不登记和持有;当没有一个Actor持有 + * Context时,std::shared_ptr引用计数为0将自动回收。 * 比如Cmd收到一个消息,后续需要若干个Step才完成全部操作,在最后一个 * Step中给请求方发送响应,那么最后一个Step需要获取Cmd当时收到的请求上下 * 文,而最后一个Step可能不是Cmd所创建的,一种实现方式是将上下文信息顺着 @@ -30,74 +33,28 @@ namespace neb * 从Context基类派生出业务自己的Context类可以用于Step间自定义数据的共 * 享(当然,Session类也可以达到同样目的,区别在于GetSession()需要传入 * SessionId才能获取到Session,GetContext()则不需要传参即可获取)。什么时 - * 候用Context?建议是同一个StepChain(共同完成一个操作的Step执行链)的上 + * 候用Context?建议是同一个Chain(共同完成一个操作的Step执行链)的上 * 下文信息和数据共享用Context,其他情况用Session。 */ -class Context: public Session +class Context: public Actor { public: - Context(const std::string& strSessionId, ev_tstamp dSessionTimeout = 60.0); - Context(const std::string& strSessionId, - std::shared_ptr pChannel, - int32 iCmd, uint32 uiSeq, - ev_tstamp dSessionTimeout = 60.0); - Context(const std::string& strSessionId, - std::shared_ptr pChannel, - int32 iCmd, uint32 uiSeq, - const MsgBody& oReqMsgBody, - ev_tstamp dSessionTimeout = 60.0); + Context(); + Context(std::shared_ptr pChannel); Context(const Context&) = delete; Context& operator=(const Context&) = delete; virtual ~Context(); - /** - * @brief 会话超时回调 - * @note 上下文由Worker进行管理,超时回调时一般默认返回CMD_STATUS_COMPLETED, - * 让框架执行回收操作即可(实际上因为上下文以shared_ptr存储,并未在框架执行 - * 回收操作时立即销毁,而是在所有引用该Context的Actor实例均已销毁时才会销毁)。 - */ - virtual E_CMD_STATUS Timeout() - { - return(CMD_STATUS_COMPLETED); - } - -public: - /** - * @brief 给请求方发响应 - * @param iErrno 错误码,如果正确则应传入ERR_OK - * @param strData 响应结果(错误码为ERR_OK时),或错误信息 - */ - bool Response(int iErrno, const std::string& strData); - std::shared_ptr GetChannel() { return(m_pChannel); } - int32 GetCmd() const - { - return(m_iReqCmd); - } - - uint32 GetSeq() const - { - return(m_uiReqSeq); - } - - const MsgBody& GetMsgBody() const - { - return(m_oReqMsgBody); - } - private: std::shared_ptr m_pChannel; - int32 m_iReqCmd; - uint32 m_uiReqSeq; - MsgBody m_oReqMsgBody; - friend class WorkerImpl; }; } /* namespace neb */ -#endif /* SRC_ACTOR_SESSION_CONTEXT_HPP_ */ +#endif /* SRC_ACTOR_CONTEXT_HPP_ */ diff --git a/src/actor/context/HttpContext.cpp b/src/actor/context/HttpContext.cpp new file mode 100644 index 00000000..beed9cee --- /dev/null +++ b/src/actor/context/HttpContext.cpp @@ -0,0 +1,49 @@ +/******************************************************************************* + * Project: Nebula + * @file HttpContext.cpp + * @brief + * @author Bwar + * @date: 2019年2月16日 + * @note + * Modify history: + ******************************************************************************/ + +#include "HttpContext.hpp" + +namespace neb +{ + +HttpContext::HttpContext() +{ +} + +HttpContext::HttpContext( + std::shared_ptr pChannel) + : Context(pChannel) +{ +} + +HttpContext::HttpContext( + std::shared_ptr pChannel, + const HttpMsg& oHttpMsg) + : Context(pChannel), + m_oHttpMsg(oHttpMsg) +{ +} + +HttpContext::~HttpContext() +{ +} + +bool HttpContext::Response(const std::string& strData) +{ + HttpMsg oHttpMsg = m_oHttpMsg; + oHttpMsg.set_body(strData); + if (SendTo(GetChannel(), oHttpMsg)) + { + return(true); + } + return(false); +} + +} /* namespace neb */ diff --git a/src/actor/context/HttpContext.hpp b/src/actor/context/HttpContext.hpp new file mode 100644 index 00000000..ff439c04 --- /dev/null +++ b/src/actor/context/HttpContext.hpp @@ -0,0 +1,47 @@ +/******************************************************************************* + * Project: Nebula + * @file HttpContext.hpp + * @brief + * @author Bwar + * @date: 2019年2月16日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_HTTPCONTEXT_HPP_ +#define SRC_ACTOR_HTTPCONTEXT_HPP_ + +#include "labor/Worker.hpp" +#include "actor/DynamicCreator.hpp" +#include "Context.hpp" + +namespace neb +{ + +class HttpContext: public Context +{ +public: + HttpContext(); + HttpContext( + std::shared_ptr pChannel); + HttpContext( + std::shared_ptr pChannel, + const HttpMsg& oReqHttpMsg); + HttpContext(const HttpContext&) = delete; + HttpContext& operator=(const HttpContext&) = delete; + virtual ~HttpContext(); + +public: + /** + * @brief 给请求方发响应 + * @param strData 响应结果或错误信息 + */ + bool Response(const std::string& strData); + +private: + HttpMsg m_oHttpMsg; + friend class WorkerImpl; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_HTTPCONTEXT_HPP_ */ diff --git a/src/actor/context/PbContext.cpp b/src/actor/context/PbContext.cpp new file mode 100644 index 00000000..c00c1a24 --- /dev/null +++ b/src/actor/context/PbContext.cpp @@ -0,0 +1,64 @@ +/******************************************************************************* + * Project: Nebula + * @file PbContext.cpp + * @brief + * @author Bwar + * @date: 2019年6月21日 + * @note + * Modify history: + ******************************************************************************/ + +#include "PbContext.hpp" + +namespace neb +{ + +PbContext::PbContext() + : m_iReqCmd(0), m_uiReqSeq(0) +{ +} + +PbContext::PbContext( + std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq) + : Context(pChannel), + m_iReqCmd(iCmd), m_uiReqSeq(uiSeq) +{ +} + +PbContext::PbContext( + std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq, + const MsgBody& oReqMsgBody) + : Context(pChannel), + m_iReqCmd(iCmd), m_uiReqSeq(uiSeq), + m_oReqMsgBody(oReqMsgBody) +{ +} + +PbContext::~PbContext() +{ +} + +bool PbContext::Response(int iErrno, const std::string& strData) +{ + MsgBody oMsgBody; + oMsgBody.mutable_rsp_result()->set_code(iErrno); + if (ERR_OK == iErrno) + { + oMsgBody.mutable_rsp_result()->set_msg("success"); + oMsgBody.set_data(strData); + } + else + { + oMsgBody.mutable_rsp_result()->set_msg(strData); + } + + if (SendTo(GetChannel(), m_iReqCmd + 1, m_uiReqSeq, oMsgBody)) + { + return(true); + } + return(false); +} + +} /* namespace neb */ diff --git a/src/actor/context/PbContext.hpp b/src/actor/context/PbContext.hpp new file mode 100644 index 00000000..9dc435b1 --- /dev/null +++ b/src/actor/context/PbContext.hpp @@ -0,0 +1,68 @@ +/******************************************************************************* + * Project: Nebula + * @file PbContext.hpp + * @brief + * @author Bwar + * @date: 2019年6月21日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_PBCONTEXT_HPP_ +#define SRC_ACTOR_PBCONTEXT_HPP_ + +#include "labor/Worker.hpp" +#include "actor/DynamicCreator.hpp" +#include "Context.hpp" + +namespace neb +{ + +class PbContext: public Context +{ +public: + PbContext(); + PbContext( + std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq); + PbContext( + std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq, + const MsgBody& oReqMsgBody); + PbContext(const PbContext&) = delete; + PbContext& operator=(const PbContext&) = delete; + virtual ~PbContext(); + +public: + /** + * @brief 给请求方发响应 + * @param iErrno 错误码,如果正确则应传入ERR_OK + * @param strData 响应结果(错误码为ERR_OK时),或错误信息 + */ + bool Response(int iErrno, const std::string& strData); + + int32 GetCmd() const + { + return(m_iReqCmd); + } + + uint32 GetSeq() const + { + return(m_uiReqSeq); + } + + const MsgBody& GetMsgBody() const + { + return(m_oReqMsgBody); + } + +private: + int32 m_iReqCmd; + uint32 m_uiReqSeq; + MsgBody m_oReqMsgBody; + + friend class WorkerImpl; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_PBCONTEXT_HPP_ */ diff --git a/src/actor/session/Context.cpp b/src/actor/session/Context.cpp deleted file mode 100644 index bd7301e2..00000000 --- a/src/actor/session/Context.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file Context.cpp - * @brief - * @author Bwar - * @date: 2019年2月16日 - * @note - * Modify history: - ******************************************************************************/ - -#include "Context.hpp" - -namespace neb -{ - -Context::Context(const std::string& strSessionId, ev_tstamp dSessionTimeout) - : Session(strSessionId, dSessionTimeout), - m_pChannel(nullptr), m_iReqCmd(0), m_uiReqSeq(0) -{ -} - -Context::Context(const std::string& strSessionId, - std::shared_ptr pChannel, - int32 iCmd, uint32 uiSeq, - ev_tstamp dSessionTimeout) - : Session(strSessionId, dSessionTimeout), - m_pChannel(pChannel), m_iReqCmd(iCmd), m_uiReqSeq(uiSeq) -{ -} - -Context::Context(const std::string& strSessionId, - std::shared_ptr pChannel, - int32 iCmd, uint32 uiSeq, - const MsgBody& oReqMsgBody, - ev_tstamp dSessionTimeout) - : Session(strSessionId, dSessionTimeout), - m_pChannel(pChannel), m_iReqCmd(iCmd), m_uiReqSeq(uiSeq), - m_oReqMsgBody(oReqMsgBody) -{ -} - -Context::~Context() -{ -} - -bool Context::Response(int iErrno, const std::string& strData) -{ - MsgBody oMsgBody; - oMsgBody.mutable_rsp_result()->set_code(iErrno); - if (ERR_OK == iErrno) - { - oMsgBody.mutable_rsp_result()->set_msg("success"); - oMsgBody.set_data(strData); - } - else - { - oMsgBody.mutable_rsp_result()->set_msg(strData); - } - - if (SendTo(m_pChannel, m_iReqCmd + 1, m_uiReqSeq, oMsgBody)) - { - return(true); - } - return(false); -} - -} /* namespace neb */ diff --git a/src/actor/session/Session.cpp b/src/actor/session/Session.cpp index a621503b..11414834 100644 --- a/src/actor/session/Session.cpp +++ b/src/actor/session/Session.cpp @@ -30,6 +30,22 @@ Session::Session(const std::string& strSessionId, ev_tstamp dSessionTimeout) { } +Session::Session(ACTOR_TYPE eActorType, uint32 ulSessionId, ev_tstamp dSessionTimeout) + : Actor(eActorType, dSessionTimeout), + m_bDataReady(false), m_bDataLoading(false) +{ + std::ostringstream oss; + oss << ulSessionId; + m_strSessionId = std::move(oss.str()); +} + +Session::Session(ACTOR_TYPE eActorType, const std::string& strSessionId, ev_tstamp dSessionTimeout) + : Actor(eActorType, dSessionTimeout), + m_bDataReady(false), m_bDataLoading(false), + m_strSessionId(strSessionId) +{ +} + Session::~Session() { } diff --git a/src/actor/session/Session.hpp b/src/actor/session/Session.hpp index 4537a325..345cba16 100644 --- a/src/actor/session/Session.hpp +++ b/src/actor/session/Session.hpp @@ -57,6 +57,11 @@ class Session: public Actor bool IsLoading(); void SetLoading(); +protected: + // 这两个构造函数专为Timer而用,其他Session子类不可使用 + Session(ACTOR_TYPE eActorType, uint32 ulSessionId, ev_tstamp dSessionTimeout = 60.0); + Session(ACTOR_TYPE eActorType, const std::string& strSessionId, ev_tstamp dSessionTimeout = 60.0); + private: uint32 PopWaitingStep(); diff --git a/src/actor/session/Timer.cpp b/src/actor/session/Timer.cpp index ca4ce4f9..f984039c 100644 --- a/src/actor/session/Timer.cpp +++ b/src/actor/session/Timer.cpp @@ -14,12 +14,12 @@ namespace neb { Timer::Timer(uint32 ulSessionId, ev_tstamp dSessionTimeout) - : Session(ulSessionId, dSessionTimeout) + : Session(ACT_TIMER, ulSessionId, dSessionTimeout) { } Timer::Timer(const std::string& strSessionId, ev_tstamp dSessionTimeout) - : Session(strSessionId, dSessionTimeout) + : Session(ACT_TIMER, strSessionId, dSessionTimeout) { } diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 0c157397..cc8a91b4 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -453,7 +453,7 @@ bool WorkerImpl::OnIoWrite(std::shared_ptr pChannel) } if (CMD_STATUS_RUNNING != pStepConnectWorker->Emit(ERR_OK)) { - Remove(pStepConnectWorker); + RemoveStep(pStepConnectWorker); } return(true); } @@ -511,7 +511,7 @@ bool WorkerImpl::OnIoTimeout(std::shared_ptr pChannel) { // 若返回非running状态,则表明发包时已出错, // 销毁连接过程在SendTo里已经完成,这里不需要再销毁连接 - Remove(pStepIoTimeout); + RemoveStep(pStepIoTimeout); } } else // 关闭文件描述符并清理相关资源 @@ -551,8 +551,8 @@ bool WorkerImpl::OnStepTimeout(std::shared_ptr pStep) } else { - Remove(pStep->GetChainId()); - Remove(pStep); + RemoveChain(pStep->GetChainId()); + RemoveStep(pStep); return(true); } } @@ -582,7 +582,7 @@ bool WorkerImpl::OnSessionTimeout(std::shared_ptr pSession) } else { - Remove(pSession); + RemoveSession(pSession); return(true); } } @@ -611,7 +611,7 @@ bool WorkerImpl::OnChainTimeout(std::shared_ptr pChain) } else { - Remove(pChain->GetSequence()); + RemoveChain(pChain->GetSequence()); return(true); } } @@ -655,7 +655,7 @@ bool WorkerImpl::OnRedisConnected(const redisAsyncContext *c, int status) { eResult = (*step_iter)->Callback(c, status, nullptr); uiChainId = (*step_iter)->GetChainId(); - Remove(*step_iter); + RemoveStep(*step_iter); if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) { if (0 != uiChainId) @@ -664,19 +664,23 @@ bool WorkerImpl::OnRedisConnected(const redisAsyncContext *c, int status) if (chain_iter != m_mapChain.end()) { chain_iter->second->SetActiveTime(ev_now(m_loop)); - chain_iter->second->NextBlock(); + eResult = chain_iter->second->NextBlock(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } } } } else { - Remove(uiChainId); + RemoveChain(uiChainId); } } for (auto erase_step_iter = interrupt_step_iter; erase_step_iter != channel_iter->second->listPipelineStep.end();) { - Remove((*erase_step_iter)->GetChainId()); + RemoveChain((*erase_step_iter)->GetChainId()); } channel_iter->second->listPipelineStep.clear(); DelNamedRedisChannel(channel_iter->second->GetIdentify()); @@ -690,8 +694,8 @@ bool WorkerImpl::OnRedisConnected(const redisAsyncContext *c, int status) step_iter != channel_iter->second->listPipelineStep.end(); ++step_iter) { (*step_iter)->Callback(c, status, nullptr); - Remove((*step_iter)->GetChainId()); - Remove(*step_iter); + RemoveChain((*step_iter)->GetChainId()); + RemoveStep(*step_iter); } channel_iter->second->listPipelineStep.clear(); DelNamedRedisChannel(channel_iter->second->GetIdentify()); @@ -713,8 +717,8 @@ bool WorkerImpl::OnRedisDisconnected(const redisAsyncContext *c, int status) LOG4_ERROR("RedisDisconnect callback error %d of redis cmd: %s", c->err, (*step_iter)->CmdToString().c_str()); (*step_iter)->Callback(c, c->err, nullptr); - Remove((*step_iter)->GetChainId()); - Remove(std::dynamic_pointer_cast(*step_iter)); + RemoveChain((*step_iter)->GetChainId()); + RemoveStep(std::dynamic_pointer_cast(*step_iter)); } channel_iter->second->listPipelineStep.clear(); @@ -745,8 +749,8 @@ bool WorkerImpl::OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privd { LOG4_ERROR("callback error %d of redis cmd: %s", c->err, (*step_iter)->CmdToString().c_str()); (*step_iter)->Callback(c, c->err, (redisReply*)reply); - Remove((*step_iter)->GetChainId()); - Remove(*step_iter); + RemoveChain((*step_iter)->GetChainId()); + RemoveStep(*step_iter); } channel_iter->second->listPipelineStep.clear(); @@ -769,7 +773,11 @@ bool WorkerImpl::OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privd if (chain_iter != m_mapChain.end()) { chain_iter->second->SetActiveTime(ev_now(m_loop)); - chain_iter->second->NextBlock(); + eResult = chain_iter->second->NextBlock(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } } } } @@ -781,7 +789,7 @@ bool WorkerImpl::OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privd } } - Remove(std::dynamic_pointer_cast(*step_iter)); + RemoveStep(std::dynamic_pointer_cast(*step_iter)); } return(true); @@ -857,7 +865,7 @@ bool WorkerImpl::TransformToSharedStep(Actor* pCreator, std::shared_ptr p if (nullptr != pCreator) { - pSharedActor->m_strTraceId = pCreator->m_strTraceId; + pSharedActor->SetTraceId(pCreator->GetTraceId()); } std::shared_ptr pSharedStep = std::dynamic_pointer_cast(pSharedActor); @@ -905,7 +913,7 @@ bool WorkerImpl::TransformToSharedSession(Actor* pCreator, std::shared_ptrGetSequence(); - pSharedActor->m_strTraceId = std::move(oss.str()); + pSharedActor->SetTraceId(oss.str()); } std::shared_ptr pSharedSession = std::dynamic_pointer_cast(pSharedActor); auto ret = m_mapCallbackSession.insert(std::make_pair(pSharedSession->GetSessionId(), pSharedSession)); @@ -933,7 +941,7 @@ bool WorkerImpl::TransformToSharedCmd(Actor* pCreator, std::shared_ptr pS { if (nullptr != pCreator) { - pSharedActor->m_strTraceId = pCreator->m_strTraceId; + pSharedActor->SetTraceId(pCreator->GetTraceId()); } std::shared_ptr pSharedCmd = std::dynamic_pointer_cast(pSharedActor); auto ret = m_mapCmd.insert(std::make_pair(pSharedCmd->GetCmd(), pSharedCmd)); @@ -956,7 +964,7 @@ bool WorkerImpl::TransformToSharedModule(Actor* pCreator, std::shared_ptr { if (nullptr != pCreator) { - pSharedActor->m_strTraceId = pCreator->m_strTraceId; + pSharedActor->SetTraceId(pCreator->GetTraceId()); } std::shared_ptr pSharedModule = std::dynamic_pointer_cast(pSharedActor); @@ -1000,14 +1008,14 @@ bool WorkerImpl::TransformToSharedChain(Actor* pCreator, std::shared_ptr if (nullptr != pCreator) { - pSharedActor->m_strTraceId = pCreator->m_strTraceId; + pSharedActor->SetTraceId(pCreator->GetTraceId()); } std::shared_ptr pSharedChain = std::dynamic_pointer_cast(pSharedActor); - auto chain_conf_iter = m_mapChainConf.find(pSharedChain->GetChainId()); + auto chain_conf_iter = m_mapChainConf.find(pSharedChain->GetChainFlag()); if (chain_conf_iter == m_mapChainConf.end()) { - LOG4_ERROR("no chain block config for \"%s\"", pSharedChain->GetChainId().c_str()); + LOG4_ERROR("no chain block config for \"%s\"", pSharedChain->GetChainFlag().c_str()); return(false); } else @@ -1394,7 +1402,7 @@ bool WorkerImpl::SendTo(std::shared_ptr pChannel, int32 iCmd, uin { if (nullptr != pSender) { - (const_cast(oMsgBody)).set_trace_id(pSender->m_strTraceId); + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); } E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); if (CODEC_STATUS_OK == eStatus) @@ -1424,7 +1432,7 @@ bool WorkerImpl::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq LOG4_TRACE("no channel match %s.", strIdentify.c_str()); if (nullptr != pSender) { - (const_cast(oMsgBody)).set_trace_id(pSender->m_strTraceId); + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); } return(AutoSend(strIdentify, iCmd, uiSeq, oMsgBody)); } @@ -1432,7 +1440,7 @@ bool WorkerImpl::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq { if (nullptr != pSender) { - (const_cast(oMsgBody)).set_trace_id(pSender->m_strTraceId); + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); } E_CODEC_STATUS eStatus = (*named_iter->second.begin())->m_pImpl->Send(iCmd, uiSeq, oMsgBody); if (CODEC_STATUS_OK == eStatus) @@ -1462,7 +1470,7 @@ bool WorkerImpl::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint { if (nullptr != pSender) { - (const_cast(oMsgBody)).set_trace_id(pSender->m_strTraceId); + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); } return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); } @@ -1481,7 +1489,7 @@ bool WorkerImpl::SendOriented(const std::string& strNodeType, unsigned int uiFac { if (nullptr != pSender) { - (const_cast(oMsgBody)).set_trace_id(pSender->m_strTraceId); + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); } return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); } @@ -1508,7 +1516,7 @@ bool WorkerImpl::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 { if (nullptr != pSender) { - (const_cast(oMsgBody)).set_trace_id(pSender->m_strTraceId); + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); } return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); } @@ -1538,7 +1546,7 @@ bool WorkerImpl::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 ui { if (nullptr != pSender) { - (const_cast(oMsgBody)).set_trace_id(pSender->m_strTraceId); + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); } bool bSendResult = false; for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) @@ -2516,7 +2524,7 @@ bool WorkerImpl::DiscardSocketChannel(std::shared_ptr pChannel, b } } -void WorkerImpl::Remove(std::shared_ptr pStep) +void WorkerImpl::RemoveStep(std::shared_ptr pStep) { if (nullptr == pStep) { @@ -2559,7 +2567,7 @@ void WorkerImpl::Remove(std::shared_ptr pStep) } } -void WorkerImpl::Remove(std::shared_ptr pSession) +void WorkerImpl::RemoveSession(std::shared_ptr pSession) { if (nullptr == pSession) { @@ -2586,7 +2594,7 @@ void WorkerImpl::Remove(std::shared_ptr pSession) } } -void WorkerImpl::Remove(uint32 uiChainId) +void WorkerImpl::RemoveChain(uint32 uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); if (chain_iter != m_mapChain.end()) @@ -2616,7 +2624,7 @@ void WorkerImpl::ChannelNotice(std::shared_ptr pChannel, const st oMsgHead.set_len(oMsgBody.ByteSize()); std::ostringstream oss; oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - cmd_iter->second->m_strTraceId = std::move(oss.str()); + cmd_iter->second->SetTraceId(oss.str()); cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); } } @@ -2633,13 +2641,13 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const MsgHead& { if (oMsgBody.trace_id().length() > 10) { - cmd_iter->second->m_strTraceId = oMsgBody.trace_id(); + cmd_iter->second->SetTraceId(oMsgBody.trace_id()); } else { std::ostringstream oss; oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - cmd_iter->second->m_strTraceId = std::move(oss.str()); + cmd_iter->second->SetTraceId(oss.str()); } cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); } @@ -2673,13 +2681,13 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const MsgHead& { if (oMsgBody.trace_id().length() > 10) { - cmd_iter->second->m_strTraceId = oMsgBody.trace_id(); + cmd_iter->second->SetTraceId(oMsgBody.trace_id()); } else { std::ostringstream oss; oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - cmd_iter->second->m_strTraceId = std::move(oss.str()); + cmd_iter->second->SetTraceId(oss.str()); } cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); } @@ -2701,13 +2709,13 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const MsgHead& { if (oMsgBody.trace_id().length() > 10) { - cmd_iter->second->m_strTraceId = oMsgBody.trace_id(); + cmd_iter->second->SetTraceId(oMsgBody.trace_id()); } else { std::ostringstream oss; oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - cmd_iter->second->m_strTraceId = std::move(oss.str()); + cmd_iter->second->SetTraceId(oss.str()); } cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); } @@ -2758,14 +2766,18 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const MsgHead& if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) { uint32 uiChainId = step_iter->second->GetChainId(); - Remove(step_iter->second); + RemoveStep(step_iter->second); if (0 != uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); if (chain_iter != m_mapChain.end()) { chain_iter->second->SetActiveTime(ev_now(m_loop)); - chain_iter->second->NextBlock(); + eResult = chain_iter->second->NextBlock(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } } } } @@ -2807,7 +2819,7 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const HttpMsg& { std::ostringstream oss; oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - module_iter->second->m_strTraceId = std::move(oss.str()); + module_iter->second->SetTraceId(oss.str()); module_iter->second->AnyMessage(pChannel, oHttpMsg); } } @@ -2815,7 +2827,7 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const HttpMsg& { std::ostringstream oss; oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - module_iter->second->m_strTraceId = std::move(oss.str()); + module_iter->second->SetTraceId(oss.str()); module_iter->second->AnyMessage(pChannel, oHttpMsg); } } @@ -2836,7 +2848,7 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const HttpMsg& if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) { uint32 uiChainId = http_step_iter->second->GetChainId(); - Remove(http_step_iter->second); + RemoveStep(http_step_iter->second); pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED, 0); AddNamedSocketChannel(pChannel->m_pImpl->GetIdentify(), pChannel); // push back to named socket channel pool. if (0 != uiChainId) @@ -2845,7 +2857,11 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const HttpMsg& if (chain_iter != m_mapChain.end()) { chain_iter->second->SetActiveTime(ev_now(m_loop)); - chain_iter->second->NextBlock(); + eResult = chain_iter->second->NextBlock(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } } } } @@ -2871,14 +2887,18 @@ void WorkerImpl::ExecAssemblyLine(std::shared_ptr pChannel, const if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) { uint32 uiChainId = step_iter->second->GetChainId(); - Remove(step_iter->second); + RemoveStep(step_iter->second); if (0 != uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); if (chain_iter != m_mapChain.end()) { chain_iter->second->SetActiveTime(ev_now(m_loop)); - chain_iter->second->NextBlock(); + eResult = chain_iter->second->NextBlock(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } } } } diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 4c19549d..72993f05 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -297,9 +297,9 @@ class WorkerImpl bool RemoveIoWriteEvent(std::shared_ptr pChannel); std::shared_ptr CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient = false, bool bWithSsl = false); bool DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice = true); - void Remove(std::shared_ptr pStep); - void Remove(std::shared_ptr pSession); - void Remove(uint32 uiChainId); + void RemoveStep(std::shared_ptr pStep); + void RemoveSession(std::shared_ptr pSession); + void RemoveChain(uint32 uiChainId); void ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData); /** @@ -366,8 +366,8 @@ class WorkerImpl std::unordered_map > m_mapModule; // Chain and Matrix - std::unordered_map > > m_mapChainConf; //key为Chain的配置名,value为由Matrix类名和Step类名构成的ChainBlock链 - std::unordered_map > m_mapChain; //key为Chain的Sequence + std::unordered_map > > m_mapChainConf; //key为Chain的配置名(ChainFlag),value为由Matrix类名和Step类名构成的ChainBlock链 + std::unordered_map > m_mapChain; //key为Chain的Sequence,称为ChainId std::unordered_map > m_mapMatrix; //key为Matrix类名 // Step and Session From ecf49fb0ecdf99c1f2df84f3ce6b661bd8a2ce34 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 22 Jun 2019 16:49:31 +0800 Subject: [PATCH 035/176] optimize HttpContext and add features to README --- LICENSE | 222 +++++++++++++++++++++++++++--- README.md | 48 +++---- README_cn.md | 35 +++-- src/actor/Actor.hpp | 7 + src/actor/context/HttpContext.cpp | 15 +- src/actor/context/HttpContext.hpp | 3 +- src/labor/Worker.hpp | 9 ++ src/labor/WorkerImpl.cpp | 4 +- src/labor/WorkerImpl.hpp | 17 ++- src/labor/WorkerImpl.inl | 22 +-- 10 files changed, 298 insertions(+), 84 deletions(-) diff --git a/LICENSE b/LICENSE index e94a6c89..fb33f326 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,201 @@ -MIT License - -Copyright (c) 2018 Bwar - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (C) 2019, Bwar. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 7e1880fd..8fe0886d 100644 --- a/README.md +++ b/README.md @@ -3,47 +3,37 @@ English | [中文](/README_cn.md)         [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
1. [Overview](#Overview) -2. [License](#License) +2. [License](#Features) 3. [Getting Start](#GettingStart) 4. [Documentation](#Documentation) 5. [Depend on](#DependOn) -6. [Related Project](#RelatedProject) +6. [Main Projects](#MainProjects) 7. [Todo list](#TODO) 8. [Change log](#ChangeLog) ## Overview -Nebula is an event-driven TCP protocol network framework developed in C++ language. It supports multiple application layer communication protocols including proto3, http, https, and websocket. The purpose of developing the Nebula framework is to provide a fast and high-performance distributed service cluster based on C++. +Nebula is a business-oriented IoC distributed network framework developed in C++ language. It supports multiple application layer communication protocols including proto3, http, https, and websocket. The purpose of developing the Nebula framework is to provide a fast and high-performance distributed service cluster based on C++. Nebula is a production level framework and distributed solution project for instant messaging, data collection, real-time computing, message push and other applications, as well as web api services. There were production applications for instant messaging, data acquisition and real-time analysis on line now, and a recommendation engine application for a large user base will be born soon. By the way, using Nebula for toy-level projects is also good for learning network communication. Bwar welcomes more developers to join the Nebula project. Nebula is a proactor development framework(a framework-implemented proactor, not an operating system support). The IO-intensive application on nebula with be good performance. Nebula can be used as a single high-performance TCP server, but building a cluster based on Nebula will be truly reflect its value. In order to build distributed service clusters quickly, Nebula Bootstrap cluster solutions including various types of services have been developed. - -## License - -MIT License - -> Copyright (c) 2018 Bwar -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in -> all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -> THE SOFTWARE. + +## Features +* Protocol communication such as http, protobuf, websocket, etc. +* Ssl connection encryption +* Microservices framework +* IoC container +* Dynamic service update +* Service registration and discovery +* Service monitoring +* Configuration management +* Routing +* Load balancing +* Circuit Breakers +* Leadership election and cluster state ## Getting Start @@ -106,8 +96,8 @@ A simple testing can be start with a NebulaInterface only, and also can be start * [http_parse](https://github.com/nodejs/http-parser) integrate into Nebula/src/util/http * [CJsonObject](https://github.com/Bwar/CJsonObject) integrate into Nebula/src/util/json - -## Related Project + +## Main Projects * [NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap) * [NebulaBeacon](https://github.com/Bwar/NebulaBeacon) * [NebulaInterface](https://github.com/Bwar/NebulaInterface) diff --git a/README_cn.md b/README_cn.md index 2756e8b2..759513bb 100644 --- a/README_cn.md +++ b/README_cn.md @@ -3,7 +3,7 @@ [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
1. [概述](#Overview) -2. [许可证](#License) +2. [许可证](#Features) 3. [开始](#GettingStart) 4. [文档](#Documentation) 5. [依赖](#DependOn) @@ -15,25 +15,36 @@ ## 概述 -  Nebula是一个C\+\+语言开发的事件驱动型的TCP协议分布式网络框架,支持包括proto3、http、https、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建高性能的分布式服务。Nebula自身核心代码只有2万行左右(不计算proto文件生成的代码)。 +  Nebula是一个面向业务的IoC分布式网络框架,以C\+\+语言开发基于事件驱动型的TCP协议,支持包括proto3、http、https、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建高性能的分布式服务。Nebula自身核心代码只有2万行左右(不计算proto文件生成的代码)。   Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建分布式服务才能真正体现其价值。为了能快速搭建分布式服务,开发了包括各种类型服务的NebulaBootstrap解决方案。   Nebula是一个产线级的框架和分布式解决方案项目,适用于即时通讯、数据采集、实时计算、消息推送等应用场景,也适用于web后台服务。Nebula已有即时通讯、埋点数据采集及实时分析的生产应用案例,很快将有一个面向庞大用户群的推荐引擎产线应用案例。 -  把Nebula用于玩具级项目,用于学习交流也不错,Bwar欢迎更多有兴趣的开发者加入到Nebula这个项目中来,Bwar也乐意解答项目中遇到的问题。Nebula是个proactor模式开发框架,不错,是proactor不是reactor(框架层实现的proactor而不是操作系统支持),应用于IO密集型的项目可以达到非常好的性能。Nebula现在不支持同步调用不支持rpc,以后也应该不会支持rpc,做全异步的通信框架目标不变。对了解异步回调编程方式的开发者,Nebula是个非常简单的框架,比写常见的异步回调写法要简单多了。Nebula网络框架的技术分享和交流见[C++网络框架Nebula](https://zhuanlan.zhihu.com/c_216558336) +  把Nebula用于学习交流也不错,Bwar欢迎更多有兴趣的开发者加入到Nebula这个项目中来,Bwar也乐意解答项目中遇到的问题。Nebula是个proactor模式开发框架,不错,是proactor不是reactor(框架层实现的proactor而不是操作系统支持),应用于IO密集型的项目可以达到非常好的性能。Nebula现在不支持同步调用不支持rpc,以后也应该不会支持rpc,做全异步的通信框架目标不变。对了解异步回调编程方式的开发者,Nebula是个非常简单的框架,比写常见的异步回调写法要简单多了。Nebula网络框架的技术分享和交流见[C++网络框架Nebula](https://zhuanlan.zhihu.com/c_216558336)   Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(也是Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++14)是Starship(C++03)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的第一个应用Nebio(埋点数据采集和实时分析项目)在2018年7月底上线并稳定运行,Bwar还准备开发基于Nebula的IM应用Nebim。 - -## 许可证 -> Copyright(c)2018 Bwar -> -> 特此免费授予任何人获得本软件及相关文档文件(“软件”)的副本,以无限制地处理本软件,包括但不限于使用,复制,修改和合并,发布,分发,再许可和/或销售本软件的副本,并允许本软件提供给其的人员遵守以下条件: -> -> 上述版权声明和本许可声明应包含在本软件的所有副本或主要部分中。 -> -> 本软件按“原样”提供,不附有任何形式的明示或暗示保证,包括但不限于适销性,适用于特定用途和不侵权的保证。在任何情况下,作者或版权所有者都不承担任何索赔,损害或其他责任,无论是在合同,侵权或其他方面的行为,不论是由软件或其使用或其他交易引起或与之相关的行为。 + +## Nebula功能 +* 支持http、protobuf、websocket等协议通信 +* 支持ssl连接加密 +* 微服务框架 +* IoC容器 +* 动态服务更新 +* 服务注册 +* 服务发现 +* 服务监控 +* 配置管理 +* 动态路由 +* 负载均衡 +* 过载保护 +* 故障熔断 +* 故障检测和恢复 +* 数据统计 +* 分层服务 +* 认证和鉴权 +* 分布式日志跟踪 ## 开始 diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 030df9f2..b2b6ea47 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -75,6 +75,7 @@ class Actor: public std::enable_shared_from_this template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); + template std::shared_ptr MakeSharedContext(const std::string& strContextName, Targs... args); template std::shared_ptr MakeSharedActor(const std::string& strActorName, Targs... args); ACTOR_TYPE GetActorType() const @@ -279,6 +280,12 @@ std::shared_ptr Actor::MakeSharedSession(const std::string& strSessionN return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); } +template +std::shared_ptr Actor::MakeSharedContext(const std::string& strContextName, Targs... args) +{ + return(m_pWorker->MakeSharedContext(this, strContextName, std::forward(args)...)); +} + template std::shared_ptr Actor::MakeSharedActor(const std::string& strActorName, Targs... args) { diff --git a/src/actor/context/HttpContext.cpp b/src/actor/context/HttpContext.cpp index beed9cee..232cb357 100644 --- a/src/actor/context/HttpContext.cpp +++ b/src/actor/context/HttpContext.cpp @@ -9,6 +9,7 @@ ******************************************************************************/ #include "HttpContext.hpp" +#include "util/http/http_parser.h" namespace neb { @@ -25,9 +26,9 @@ HttpContext::HttpContext( HttpContext::HttpContext( std::shared_ptr pChannel, - const HttpMsg& oHttpMsg) + const HttpMsg& oReqHttpMsg) : Context(pChannel), - m_oHttpMsg(oHttpMsg) + m_oReqHttpMsg(oReqHttpMsg) { } @@ -37,9 +38,13 @@ HttpContext::~HttpContext() bool HttpContext::Response(const std::string& strData) { - HttpMsg oHttpMsg = m_oHttpMsg; - oHttpMsg.set_body(strData); - if (SendTo(GetChannel(), oHttpMsg)) + HttpMsg oRspHttpMsg; + oRspHttpMsg.set_type(HTTP_RESPONSE); + oRspHttpMsg.set_status_code(200); + oRspHttpMsg.set_http_major(m_oReqHttpMsg.http_major()); + oRspHttpMsg.set_http_major(m_oReqHttpMsg.http_minor()); + oRspHttpMsg.set_body(strData); + if (SendTo(GetChannel(), oRspHttpMsg)) { return(true); } diff --git a/src/actor/context/HttpContext.hpp b/src/actor/context/HttpContext.hpp index ff439c04..ff22fa82 100644 --- a/src/actor/context/HttpContext.hpp +++ b/src/actor/context/HttpContext.hpp @@ -10,6 +10,7 @@ #ifndef SRC_ACTOR_HTTPCONTEXT_HPP_ #define SRC_ACTOR_HTTPCONTEXT_HPP_ +#include "pb/http.pb.h" #include "labor/Worker.hpp" #include "actor/DynamicCreator.hpp" #include "Context.hpp" @@ -38,7 +39,7 @@ class HttpContext: public Context bool Response(const std::string& strData); private: - HttpMsg m_oHttpMsg; + HttpMsg m_oReqHttpMsg; friend class WorkerImpl; }; diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index dde64f04..54fce95c 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -41,6 +41,9 @@ class Worker: public Labor template std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args); + template + std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs... args); + virtual uint32 GetSequence() const; virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); @@ -111,6 +114,12 @@ std::shared_ptr Worker::MakeSharedSession(Actor* pCreator, const std::s return(m_pImpl->MakeSharedSession(pCreator, strSessionName, std::forward(args)...)); } +template +std::shared_ptr Worker::MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs... args) +{ + return(m_pImpl->MakeSharedContext(pCreator, strContextName, std::forward(args)...)); +} + } /* namespace neb */ #endif /* SRC_LABOR_WORKER_HPP_ */ diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index cc8a91b4..03855c95 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -816,12 +816,14 @@ std::shared_ptr WorkerImpl::InitializeSharedActor(Actor* pCreator, std::s break; case Actor::ACT_SESSION: case Actor::ACT_TIMER: - case Actor::ACT_CONTEXT: if (TransformToSharedSession(pCreator, pSharedActor)) { return(pSharedActor); } break; + case Actor::ACT_CONTEXT: + return(pSharedActor); + break; case Actor::ACT_CMD: if (TransformToSharedCmd(pCreator, pSharedActor)) { diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 72993f05..ba701bdd 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -197,16 +197,19 @@ class WorkerImpl std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args); template - std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args); + std::shared_ptr MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args); template - std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args); + std::shared_ptr MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs... args); template - std::shared_ptr MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args); + std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args); template - std::shared_ptr MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs... args); + std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args); + + template + std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs... args); template std::shared_ptr MakeSharedMatrix(Actor* pCreator, const std::string& strMatrixName, Targs... args); @@ -215,12 +218,12 @@ class WorkerImpl std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs... args); std::shared_ptr InitializeSharedActor(Actor* pCreator, std::shared_ptr pSharedActor, const std::string& strActorName); - bool TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor); - bool TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedCmd(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor); - bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedMatrix(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); public: // about channel virtual bool AddNetLogMsg(const MsgBody& oMsgBody); diff --git a/src/labor/WorkerImpl.inl b/src/labor/WorkerImpl.inl index eb2ccd43..8aeddfe7 100644 --- a/src/labor/WorkerImpl.inl +++ b/src/labor/WorkerImpl.inl @@ -41,27 +41,33 @@ std::shared_ptr WorkerImpl::MakeSharedActor(Actor* pCreator, const std::s } template -std::shared_ptr WorkerImpl::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strStepName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strCmdName, std::forward(args)...))); } template -std::shared_ptr WorkerImpl::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strSessionName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModuleName, std::forward(args)...))); } template -std::shared_ptr WorkerImpl::MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strCmdName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strStepName, std::forward(args)...))); } template -std::shared_ptr WorkerImpl::MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModuleName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strSessionName, std::forward(args)...))); +} + +template +std::shared_ptr WorkerImpl::MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs... args) +{ + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strContextName, std::forward(args)...))); } template From dddbbebaa72bd6f11c76353043a64477c1d96703 Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 25 Jun 2019 08:49:11 +0800 Subject: [PATCH 036/176] chain test passed. --- LICENSE | 222 +++--------------------------- src/actor/Actor.hpp | 7 + src/actor/chain/Chain.cpp | 16 ++- src/actor/chain/Chain.hpp | 9 +- src/actor/context/Context.hpp | 18 +++ src/actor/context/HttpContext.hpp | 9 ++ src/actor/context/PbContext.hpp | 4 + src/labor/Worker.hpp | 9 ++ 8 files changed, 88 insertions(+), 206 deletions(-) diff --git a/LICENSE b/LICENSE index fb33f326..e94a6c89 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,21 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright (C) 2019, Bwar. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +MIT License + +Copyright (c) 2018 Bwar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index b2b6ea47..5db2a1bb 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -76,6 +76,7 @@ class Actor: public std::enable_shared_from_this template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); template std::shared_ptr MakeSharedContext(const std::string& strContextName, Targs... args); + template std::shared_ptr MakeSharedChain(const std::string& strChainName, Targs... args); template std::shared_ptr MakeSharedActor(const std::string& strActorName, Targs... args); ACTOR_TYPE GetActorType() const @@ -292,6 +293,12 @@ std::shared_ptr Actor::MakeSharedActor(const std::string& strActorName, T return(m_pWorker->MakeSharedActor(this, strActorName, std::forward(args)...)); } +template +std::shared_ptr Actor::MakeSharedChain(const std::string& strChainName, Targs... args) +{ + return(m_pWorker->MakeSharedChain(this, strChainName, std::forward(args)...)); +} + } /* namespace neb */ #endif /* SRC_ACTOR_ACTOR_HPP_ */ diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index f6df1355..08902f64 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -9,6 +9,7 @@ ******************************************************************************/ #include "Chain.hpp" +#include "actor/context/Context.hpp" #include "actor/step/Step.hpp" #include "actor/matrix/Matrix.hpp" @@ -17,7 +18,7 @@ namespace neb Chain::Chain(const std::string& strChainFlag, ev_tstamp dChainTimeout) : Actor(Actor::ACT_CHAIN, dChainTimeout), - m_strChainFlag(strChainFlag) + m_uiWaitingStep(0), m_strChainFlag(strChainFlag) { } @@ -32,8 +33,17 @@ void Chain::Init(const std::queue >& queChainBlock) E_CMD_STATUS Chain::NextBlock() { + if (m_uiWaitingStep > 0) + { + --m_uiWaitingStep; + if (m_uiWaitingStep > 0) + { + return(CMD_STATUS_RUNNING); + } + } if (m_queChainBlock.empty()) { + GetContext()->Done(); return(CMD_STATUS_COMPLETED); } bool bStepInBlock = false; ///< 当前链块存在Step @@ -70,6 +80,10 @@ E_CMD_STATUS Chain::NextBlock() { return(CMD_STATUS_FAULT); } + else if (CMD_STATUS_RUNNING == eResult) + { + ++m_uiWaitingStep; + } } else { diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index 3d086946..fa0d8722 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -19,7 +19,8 @@ namespace neb { -class Chain final: public Actor +class Chain final: public Actor, + public neb::DynamicCreator { public: Chain(const std::string& strChainFlag, ev_tstamp dChainTimeout = 60.0); @@ -28,6 +29,8 @@ class Chain final: public Actor virtual ~Chain(); void Init(const std::queue >& queChainBlock); + E_CMD_STATUS NextBlock(); + virtual E_CMD_STATUS Timeout() { return(CMD_STATUS_FAULT); @@ -39,9 +42,7 @@ class Chain final: public Actor } private: - E_CMD_STATUS NextBlock(); - -private: + uint32 m_uiWaitingStep; std::string m_strChainFlag; // queue的每个元素称为链块(std::unordered_set) std::queue > m_queChainBlock; diff --git a/src/actor/context/Context.hpp b/src/actor/context/Context.hpp index 04a01be4..697ef226 100644 --- a/src/actor/context/Context.hpp +++ b/src/actor/context/Context.hpp @@ -45,6 +45,24 @@ class Context: public Actor Context& operator=(const Context&) = delete; virtual ~Context(); + /** + * @brief 动态处理链上下文操作完成 + * @note 一次客户端请求可能需要若干步骤(Step)才能完成,当处理完成时, + * 对于预定义的静态请求处理链,通常会由最后一个步骤(Step)给客户端发响 + * 应,响应信息通常来自于各步骤存储于Context的数据。然而,对于通过配置 + * 完成的动态请求处理链,最后一个步骤通常是未知的,如果想要由最后一个步 + * 骤发送响应,那么无论处理链有多少个步骤,都需要固定最后一个步骤,这是 + * 不合理的,配置动态处理链的时候也容易出错。因此,在Context中引入一个 + * Done()方法,把给客户端的响应放在Done()方法里,系统在处理链调度完最后 + * 一个节点处理完成时调用Done()方法将整个动态处理链的响应结果发给客户端, + * 那样动态处理链的Block节点顺序只跟链的业务逻辑相关,而无需固定最后一个 + * 步骤。注意,该方法只适用于配置的动态处理链,预定义的静态处理链无需实 + * 现此方法,所以该方法是实现为空的方法而非纯虚方法。 + */ + virtual void Done() + { + } + std::shared_ptr GetChannel() { return(m_pChannel); diff --git a/src/actor/context/HttpContext.hpp b/src/actor/context/HttpContext.hpp index ff22fa82..bd1acca6 100644 --- a/src/actor/context/HttpContext.hpp +++ b/src/actor/context/HttpContext.hpp @@ -31,6 +31,10 @@ class HttpContext: public Context HttpContext& operator=(const HttpContext&) = delete; virtual ~HttpContext(); + virtual void Done() + { + } + public: /** * @brief 给请求方发响应 @@ -38,6 +42,11 @@ class HttpContext: public Context */ bool Response(const std::string& strData); + const HttpMsg& GetHttpMsg() const + { + return(m_oReqHttpMsg); + } + private: HttpMsg m_oReqHttpMsg; friend class WorkerImpl; diff --git a/src/actor/context/PbContext.hpp b/src/actor/context/PbContext.hpp index 9dc435b1..53a735c5 100644 --- a/src/actor/context/PbContext.hpp +++ b/src/actor/context/PbContext.hpp @@ -32,6 +32,10 @@ class PbContext: public Context PbContext& operator=(const PbContext&) = delete; virtual ~PbContext(); + virtual void Done() + { + } + public: /** * @brief 给请求方发响应 diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 54fce95c..773a0ac2 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -44,6 +44,9 @@ class Worker: public Labor template std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs... args); + template + std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs... args); + virtual uint32 GetSequence() const; virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); @@ -120,6 +123,12 @@ std::shared_ptr Worker::MakeSharedContext(Actor* pCreator, const std::s return(m_pImpl->MakeSharedContext(pCreator, strContextName, std::forward(args)...)); } +template +std::shared_ptr Worker::MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs... args) +{ + return(m_pImpl->MakeSharedChain(pCreator, strChainName, std::forward(args)...)); +} + } /* namespace neb */ #endif /* SRC_LABOR_WORKER_HPP_ */ From 9e53d01d20b3706f29d04ba9da4dc54533ba42e0 Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 25 Jun 2019 22:39:39 +0800 Subject: [PATCH 037/176] modify readme --- README.md | 2 +- README_cn.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8fe0886d..5a908ce6 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ English | [中文](/README_cn.md)         [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
1. [Overview](#Overview) -2. [License](#Features) +2. [Features](#Features) 3. [Getting Start](#GettingStart) 4. [Documentation](#Documentation) 5. [Depend on](#DependOn) diff --git a/README_cn.md b/README_cn.md index 759513bb..aa202014 100644 --- a/README_cn.md +++ b/README_cn.md @@ -3,7 +3,7 @@ [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
1. [概述](#Overview) -2. [许可证](#Features) +2. [功能](#Features) 3. [开始](#GettingStart) 4. [文档](#Documentation) 5. [依赖](#DependOn) @@ -26,7 +26,7 @@   Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(也是Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++14)是Starship(C++03)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的第一个应用Nebio(埋点数据采集和实时分析项目)在2018年7月底上线并稳定运行,Bwar还准备开发基于Nebula的IM应用Nebim。 -## Nebula功能 +## 功能 * 支持http、protobuf、websocket等协议通信 * 支持ssl连接加密 * 微服务框架 From 4a7d8d775a4ce4d7e085c7666ae5f7cef0f7965f Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 25 Jun 2019 23:12:07 +0800 Subject: [PATCH 038/176] add banner to README --- README.md | 7 +++++++ README_cn.md | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/README.md b/README.md index 5a908ce6..13c4021a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ English | [中文](/README_cn.md)                 [Bwar's blog](https://www.bwar.tech). + + _ __ __ __ + / | / /__ / /_ __ __/ /___ _ + / |/ / _ \/ __ \/ / / / / __ `/ + / /| / __/ /_/ / /_/ / / /_/ / +/_/ |_/\___/_.___/\__,_/_/\__,_/ + # Nebula : An event driven asynchronous C++ framework [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
diff --git a/README_cn.md b/README_cn.md index aa202014..63c1bd15 100644 --- a/README_cn.md +++ b/README_cn.md @@ -1,4 +1,11 @@ [English](/README.md) | 中文                 [Bwar的技术博客](https://www.bwar.tech). + + _ __ __ __ + / | / /__ / /_ __ __/ /___ _ + / |/ / _ \/ __ \/ / / / / __ `/ + / /| / __/ /_/ / /_/ / / /_/ / +/_/ |_/\___/_.___/\__,_/_/\__,_/ + # Nebula : 事件驱动型网络框架 [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
From 2e05a650ff054f723b7b72286ef5897e7dcb4ec1 Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 25 Jun 2019 23:14:17 +0800 Subject: [PATCH 039/176] add banner to README --- README.md | 2 ++ README_cn.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 13c4021a..9e4375c1 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ English | [中文](/README_cn.md)                 [Bwar's blog](https://www.bwar.tech). +``` _ __ __ __ / | / /__ / /_ __ __/ /___ _ / |/ / _ \/ __ \/ / / / / __ `/ / /| / __/ /_/ / /_/ / / /_/ / /_/ |_/\___/_.___/\__,_/_/\__,_/ +``` # Nebula : An event driven asynchronous C++ framework [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
diff --git a/README_cn.md b/README_cn.md index 63c1bd15..4fdd6858 100644 --- a/README_cn.md +++ b/README_cn.md @@ -1,10 +1,12 @@ [English](/README.md) | 中文                 [Bwar的技术博客](https://www.bwar.tech). +``` _ __ __ __ / | / /__ / /_ __ __/ /___ _ / |/ / _ \/ __ \/ / / / / __ `/ / /| / __/ /_/ / /_/ / / /_/ / /_/ |_/\___/_.___/\__,_/_/\__,_/ +``` # Nebula : 事件驱动型网络框架 [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
From 57f428f6410f0110c48a04d5f35ff3783998f1d3 Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 26 Jun 2019 22:58:57 +0800 Subject: [PATCH 040/176] add docs image --- README.md | 1 + README_cn.md | 1 + docs/image/Actor.png | Bin 0 -> 166107 bytes docs/image/Channel.png | Bin 0 -> 111942 bytes docs/image/Codec.png | Bin 0 -> 54887 bytes docs/image/Labor.png | Bin 0 -> 105252 bytes docs/image/dynamic_chain.png | Bin 0 -> 18677 bytes docs/image/merging_step_call.png | Bin 0 -> 16054 bytes docs/image/static_chain.png | Bin 0 -> 23825 bytes .../step_create_before_and_call_merging.png | Bin 0 -> 51362 bytes .../step_create_before_and_call_serial.png | Bin 0 -> 45334 bytes docs/image/step_create_on_need.png | Bin 0 -> 51831 bytes 12 files changed, 2 insertions(+) create mode 100644 docs/image/Actor.png create mode 100644 docs/image/Channel.png create mode 100644 docs/image/Codec.png create mode 100644 docs/image/Labor.png create mode 100644 docs/image/dynamic_chain.png create mode 100644 docs/image/merging_step_call.png create mode 100644 docs/image/static_chain.png create mode 100644 docs/image/step_create_before_and_call_merging.png create mode 100644 docs/image/step_create_before_and_call_serial.png create mode 100644 docs/image/step_create_on_need.png diff --git a/README.md b/README.md index 9e4375c1..fa5a4213 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Todo list - NebulaMydis Data Agency Service. - Developing an IM with the Nebula. + - Complete writing user guide before September 2019. ## Change log diff --git a/README_cn.md b/README_cn.md index 4fdd6858..beac3004 100644 --- a/README_cn.md +++ b/README_cn.md @@ -134,6 +134,7 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) ## 开发任务 - NebulaMydis数据代理服务 - 应用Nebula开发IM项目 + - 2019年8月底前完成开发指南 ## 版本历史 diff --git a/docs/image/Actor.png b/docs/image/Actor.png new file mode 100644 index 0000000000000000000000000000000000000000..5a4105561e8979eefb0d4008f69d8a0ebe4d114e GIT binary patch literal 166107 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%0AS_~PwHVZ8TgRaRbtPNJvkNdxtkty{|gd0hD> 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 Y74|(n1{_UO@H1}IB!jST=l}SB0pp<;qW}N^ literal 0 HcmV?d00001 diff --git a/docs/image/Codec.png b/docs/image/Codec.png new file mode 100644 index 0000000000000000000000000000000000000000..68de12f340b53e0f7ef3f7c5b0a3a2ecebc7464c GIT binary patch literal 54887 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 literal 0 HcmV?d00001 diff --git a/docs/image/dynamic_chain.png b/docs/image/dynamic_chain.png new file mode 100644 index 0000000000000000000000000000000000000000..3fb234be213ec1fd4496e415aec5c269124437c9 GIT binary patch literal 18677 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�$ zN?KYJ+dDe0d@FEu;W&M|xS~S*_Q_WY3JRV;ya`@z?pu?+U2_h3!&X9FLjwaW!PUAV zA|l`V`n1TmJ@oV>RxT%JzL}YnVL{g_jFI9J6Y<(J zI{3SBT(|a=|D-41HZ?oDZ((7f-iW^sljzQ_F1$5!XJ_ZT=8^rPQvyi2aSIF6H#9VG z^YT(#T3X)1Qd?W|(HuAssXV`OkmOe2F}VeSxmWl0#kqW}N^ literal 0 HcmV?d00001 diff --git a/docs/image/merging_step_call.png b/docs/image/merging_step_call.png new file mode 100644 index 0000000000000000000000000000000000000000..072f8c23e5be6ce27e583fe3b24e095126d62149 GIT binary patch literal 16054 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 literal 0 HcmV?d00001 diff --git a/docs/image/static_chain.png b/docs/image/static_chain.png new file mode 100644 index 0000000000000000000000000000000000000000..eee112d78540b9c3de75da519bdd9f5edbcf0fea GIT binary patch literal 23825 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 ZxF=-g_ly{mr6Ab_W literal 0 HcmV?d00001 diff --git a/docs/image/step_create_before_and_call_merging.png b/docs/image/step_create_before_and_call_merging.png new file mode 100644 index 0000000000000000000000000000000000000000..d5fe1ffb12640771cc2938587824ff2549861336 GIT binary patch literal 51362 zcmeFZcU;eH-#`AbM?@itiey#FsHAB$MJnxSroHzjBrPJ*5JG!tZ=^!ndoQIuXwdvV zkDTXqU)OzI_x*eP#-G0)=ZHSN$MHUn*Xy~Cw}-5Z#QL=iYbg}UdMQa!c?xCOF#cV= zdL{lw>37~P{BMn^q^bplLbZwfyMz)Lyp2NHNs$sgsbCvC*y5m3eQkbWVc$cVdn_#X zblfNIGjfY`Jo&;Fc+vQ!w0>M;FH@|2Jdf9n5$_(~DU|Yi4T1#(Ep)XI*Z#+Vz zX*|suKZx;m?bjjj(a7gApRMIE#3M92n&01&m9}!-w&%Zpey{tTZD(hfxiCM&o};Cp z@M;y4?Flh4F)L#-1zf+IdD7cF&nyiUiI0fbpJLpiR{Z#2RlM^Adx(&O(!%U_5yo1b zGiUCdI(@qP`}gk&yyS%GTsqAia!T=92Sr3g4r*p9gz#I*hlzVvXIdGo-@#XY$;h}R zQ!(FtgH8Q!GQZM~(c0LqOKRYF`1zH{{Fv{LA3vg!g}AuRi-*Y_IB6Qi`@2?8?#fKg}eX80Qyzr|?^0UH|@B|8YvfM5MaISm~n6* zx8BQ7pCm&BY-Hr*l5O2!yGIwU@h}IkjbzicAT^o1e0(G0y6YZylV{O`;(n`PU z*Ty_$?fdAlskV-DLCr7Ku;C@mZt?02GOH-hO2TNT`l`YQ`ocp~?P8OY6>{t+Dg${8 z47J)4F1f9w-m%}aKt4Kka;RxvSVcr-Wu;mX)r_Z)Pi+yiOU$iX%Na$SHM+aI`K)t#V+h%|bLR=oENex1dB1!2?v3Yf5b5tY8qZ zjv@Dk(`#V%@(9;`xJ{)fEBpMU+e*FZ@jm-+4`cy6-H;M(nekNE zXG*{lNJfncM!o`cvUuheA%zt}${?U^s5oN+NXFa!a)z-$UPfQQQHwDk&(Uh-Z zc9iW)ytw_CarNVa>f?^L8X^!a z7bf0c8ykf65_3>JwLaKk_Se{tiHQ_D4i$0nJ4&%?ueY!(Mc|3}y|VaTdf?!}bB7Nf zUb}X!AJ$Ew_xAAd@$rFY8_4>e>%Qa*0K|GTe)67fKxLj zK7`Ng6h3*S&?_=|0naeb?$5nmA4{jVF)=Y|aaov~9IPcv;sIHmM!qWoF=MmzF(zaO_BkGOgB=G8;9QwOuvR*V&RZXLk7seW@VC?;Mp z#!2SfIYn#h)RMgICfQ?w-}5K1;7E_)<(-Ub3O^V0W@aQWPv(1k>DU%Ca>pk$Z8bVM z=j1i#!+eG1F)eE%FYFr_*tKidk*8;CRqdqKu3M*oa!1Fh5iycGBZI?D*X7N>u8CGQ zz%bVt4Pad;IXY(JDZfch=9ZF|mA#LPj>C=ifBXE9jf+bebJ%J~s6P(R>C2Zd@={VB zB?DNVwJf*D6_jtZz1lfHpuN!eM&I%F{remJA3ajJ-oehHl5hg0{k+h`qf@6&ahtRX zo(txyuorN$YV`E>#vBe7{{3x}lswM8Zl#;J&yGl}+Fs0=fP%}3#SF(C6j*zRJ z|599B)msr}^lVD2qqD$ME!Qza{=x-CC8ak@maXi~`2OrdSD`oK#A4$#UB-00i@NNP zZuw59sTP|rWn}@Z-QT`_x_Br;J@#4AQ&Z_tW!@c=ReoJ32aW zuYS%?ja+_pxEoEP+sfJ7XTA&z>mC`2fBt;uxj=3yW~cE7ue5c#YzFIJpi}NgX~$x$ z#uPBvA+4tuiL2=;2@1!jfB*ii_ueFHE-I3cl9KAL2y~9h*{(NIP+w|NY(Xs?)c&nnv_k-_EWsD{@o*{(Vg*c{%zti$FSk zhlfCOD=KqMlp_1>J9o6K9Iy_I(>qp*iHi1CNH8l-4Y%-^b&3v5-lp0bg`q`=e$#kx1uosGJxIV$27K#=D zQ-zvw`Oj?6<8LWtmCx(avuJCXvW`t}B_B(=|2KPJ7?1ryK0-+s_%GU!6|&7+{NA06 z!~gx$`(+l{hbz^U!ha_wX`Ai^Wc=F+{-6Dd?`7VgYe$}vW#P7JLsUXS`L3Z~Gc(a> z(+p_jEW3AKJ2Xm9Pwu607l72Epptt&K1%q?No|VP7Qy8A`bIACL@c+tHRXiU;iqRw z^}|%#ylE5ZTg7#Cb-RpE?e-r#ri>1%q@(i&s{{+7=YHzqfE;^68M@VZEsl$aN70Y` zR(tzJ_dP>?-v$Pv@hv8+wBMu0k9!jS`Zxfx7cTaUAHU78hKZ3>qx=8cI}$A zfQXZm)p*KWd7XbnU1^7plXv59^b`MQ&*F|-^ys#~yAjB16euq*k9+zySS~98zAJdI z!B7bxhG6%gx(o91mfu5c_+wgt5&D6n8IGI3GS2MVo1$NHM#y0@*RXm3Ehi>1xwV$6~O=NET;Y9uTxaGt^UD>n~ z&VJ;`k+|tr$;!klMdKar%t{$^XBKBhRo(am7sF-k{9H853BX8As;!*WCeA;%gN*9s z-&xOZ)&D5z^l2U*EIHSG9r=vk8`I6gSoO-EpHxdVeEQ?(Ppc-QGy>vQx}9vvv`T8o zOHWrxOiaw|@)1bZDPh>d%BoT(cd6m+HMRo>DvG)CINaVpC)|bKQgS=5p)A_)mhIc4 z{n?fK8?J}eB$WwAi+R%ZMaSn>0$JDOIynSc%+0hdC;@Ri=F*NM$n$)taLv6gk%iyB z03*9R3(QL_x5A@$Dv^T(g`)+^b&A0&2LQv^w=>X1J3#-0LvB=0s#mP22*Vi~tng2Xr9^G_c?wj~N zgXzYJnZP{X{s6G0+V5&_uX+sUOljSAcW39|I16fNVQm|+^yn~I>(4uMK}kR4=f@u9 zZAvq#1F+Q4%S%W&WYu3SO36@68x>JD-4+D>Dj#{LC4OY zJb4lysJgXg)1zn4IHd?aYIXd14kbeiBNxp*bM(@O<-dRZ+Rwsr%6$WKob?XC;s?II zz8rMY{zCzeAL|$SFamGr@))!8*^8Gj&;7Qi1$dQRD|`et;aG`A;jz z%I=f`e@PhLzH#G4r#XEl=UbCS6)QuQaZ{XtM8W2h6)M>t9 z|IuBb6a#vF&b~h3QlOAS>fWv%d~yfn{(_r5_R`R&eUYI0u}`guyb;zHcb5xq0P?DhA*E%G(XJq$`q_lKZbB0BHR_ObSvZ!r%T09ruQ6RcxGQB*lNeZJRP+_Q} zqvNATk6wX71B$)t@y$JYy;OIizvhgTlw1(6QL@cQ>luTeT8?94L~_u~w3JITZmE7L zMg^vPVE69R&d$z?sYZvkaqCy_7aIGxsm?oz-)!5uyk{ z%#8QfNTbZZ0{MA^f(s;k-f4PV4vau1R5r3-j+er^WOTSmZUxcZAfJCEZpQ@_cT_dYVZ7BTQ!MyRFezb-E? zKhp%R#|=Cu^z-Xc`@zH_&|mW!3orVk;ux&Ds`DbTq>2Ev-3QsYOS8K=%|x5DvqgyD z<+W}-{Snc;-z6rNogdc-EzJI`^G?h(Q;3Lil2S`IeE|;44Hj`OR9Mqj@W)-Nrio6f zwQDOuL?D1&vuI9feE2GBBv&1nrv^)!9Ne2(q&4#(Du=47YVAnU+qc{o-?_5I55>F8 zMbmED6mK`D_#(b<;GRKP{K9r-H1lJRa>XaG;z|1?1gNsA${#BPQ$*`0CoT6zx_757 zM_OAdp~Cs~fOE5cK9g&rj z7*F~BJYHy(Q_CSpjkV_Uvv$|rm#n(vEkJp9pfOo5Y~M56Q__z)r#DOQ+Bo>u*N;J zktMwbWhcpRFqjIlqGX!)O#gvP+@hPq7uE20w-XCnWS*Q`=e2bX&9nC+$^*?FsK!TQwzaz2pZ|E%-Tq^Syx;0&{^B+|B|2pD7 zP~GP6q)qp#76tnqH|YOOr&sSDdiC-UW7^kc>CL~!x>8&goUglg(9VyCi|BQJ^eClX z#`Er1PUd#w^posLF|zSmIl+u;gtkg_9I7AC}^o$G>#;WQ~9Z6BjEk^+Sb z*!(rUY0c3gN?gH17jZ{WC4jv7R}c%xseXJLb9h~+?V|bl^XIRr#}0X2>bt#Ft31`H z$+C#ihEHH~^@DJq?+_QkF7f~R(QGvb&?KiqEvSaJ8;_-KfXXZ5S!BpNFc6Jg^EPMtIIr(m4GJ_ zM*6Q?d#X99E#+fPYbhFp z26O|abQcFG+YP9QEoG#oy?jBr3qs2oNy&28CCf-_|=+8}05Yl39VG(cl6;KHL^39Vc+i<$iZEe~BaTg%{lsI2_b5`!e30Ji= z<5-VP``C^it$Gl!bVJXVu3xVMwEW;qFvDJ9(B3NO9_`(sm1ExrC{094%gI4ijB-ui zbUVF<(GD&!4wql~%)olZ{F(vbm%YJao>05yKs7u2aNqe_Oq?wszC;;e6Ujz|oQIV+#lc?*^7u*ipGJQX6skx2~F6EC7T!{N! zoba;utiFB}ddh|F-}gK{YhFohlB(>;cdvn8HQ(!&pYer{=q!pq$OZfp9RHJ7S%C_(PGX*=l3_P-w>t#$qKjSqr zNNk|Fd~SN2TNoi#HLlw z%q$UWFtWT{7Ai$?W*Nk~I9N%#{ne2~bs$7Y>YlG}9o8U^`8P@6PbpBW^6JP7Mx|}B z>c?OYoF!M&{@x7|WW+{i8p`pcU z=V^hoDnHzNj@z{TgqVax)53=iCCsz1VKl&ZV#`;o&_gAzYcYWtNP1I=0n9cOf%G0T z8X6i*{HxxVSx!ovJM2?Yp3%6?xG6QZ)$V(U{>TQ&xMM!?mXL*iBxT%z^l|IPs^>2< z8^bsRtOp~2^Z9o%Jh$Ffq}#hK-*lDNl_JtUHWaP$C4b%rT4x*EPX939HrXMo)PupZnzu33`6RZGa zmJ++fZ~SzXDbMD3(bI*$#7-A&yCZN(&;gHV$-5iBx~H{;?8pD^yDg^q>oXw@2Q#jW zy78xb%j408Fs{`17~?=$wHusizm}-NQA)iuVnb;r`^~Jw?Pn!?c2p!>%C`iE)>~}T zt0Mdd2Ms~K|MsCfC_H{opGN9cy@_;Lm_Kv&tfe*IiP)0h;}@V0kb?E$!-rr-9}JIs z(H|UBzR@gNaPiL3i(6#8#KVf>(iZ>BM@`mYB3!Iby4?D|MmtKWS45=XeDvPpIOTDH z&Cd%Vh+N}l*uuy-G$`xJ|Ma-U8yo=gK``SziB2<9>kre{$gx~^LmOdo`-7kuSd9Ju zkzpWAWASW0Cy;P3_Ug1WU9~y<^l5Q3{&cE)RmZm$^bOvcC|>-C#B)AI!IghL&n?=_ zw&)p=L^DZ*!~RW>pvlYpJL%Krzr~BxGhcuo`|jO|RpQs8qoyuH!dbh1y&_7}(q+s3lEV65xHG+N;DSh+ zk&$6Plu|cNyjF6K4I7j(1Spi(>Qqc}CX|F#MBT1)Gtd|5CIE>R{kpA=;))s1gcOUe zE7aFy^X@aR-@J)}<^#ozG=G%VaS+4-)EV4fWwzZjle>EL)wy+eQl`{^ksS}TBfJ}9c*;skYhgMQ4iKxE zr378222hlB?#QMcP@n{c(@&E&a_ZDgUKq(k7F#!O)*b6Cu>Vrjnsyj8dFNB-(yo`e}H4=X!xojG&nQD|s9r)H*# z_CV!pPYggbV=++`k=7VMPf|h^i5^`jYDoVXWWn_z zu+sXkWg-hP7zsV#q|CX=rae+bY+*eBMoiijXe|-vf$kVWX7DT?0SV!B)hPzbEs%)n zn)48w*!xfl3JftyS&0!w`Vfy<=pSv1RSM0ka`yL6gUuJL;5QgRxt+$vrdamLlZANC z!y_?Q#Ba!*6=dhf)KmZ~Mp6c97+o=c&whI_`7EsYnA}m#pXf;# zRqHhG5O!377cLKRkKIW9-=sAjej6;e6x|AzoY^16_5-nL+}BHY1krzEC;k~0O)YEA zsW9<{xeECQ<5qw(GqtDzFrztRCKZU?g5r0(e z`C+i*!nt45(`-l~BQwwFDBvIud)uGSXPy7XCYMy)M%vhs4Vz+7+i67gc^(* zlPk#bg>+r}YnISJaEwR6!7<;TImqmN82QCXJy~}b&VKFc)lX$*Wjg9&muDg7sMaMj zLubaXN1C0jhjv)iur)^MSDq!8lAGR}wazed}ATB6fpUVJbUnIUp+xZi z^~;t#Ff1c7WsPc`xwLaNUcY`#kf`6ohg<0BRi}=1tR<2i1~3MS=v0|MO9i51_G`nyl1nSnK8LF?YDA*a z_2;23(mySn@WDDLxe=%g_R_8>09dV6@Hh$ELUsOp@sRFl`+F>-3fMEG(GjOQZ((j^ z0`g^TZ#EnxQho7EYcMM?(NxpGee@v8qZP>Ge2Y$xin!OdOF6d}eteIIzuZD3JCsaZ;nmuJCcGwY4Rt{+<_gyw&Bk#qq;^9#R27a{|A^S}eQ!I;40Whe<;tFr-G`i0FGs&bgx&>*H>ATIzlR z3$;0E#{c~?o!wG$*ON6rD`nFou{YOr1o#t*gL!1quWB{V3jgXP3$aZj$guQ?=V zi!zw)Jk!($*gK|ZReVUs&7SM?gt(-(F%EJjk|EAUrg-9Tz_UK9_lptbb?1B z_I7TzpKYkH6U6CnW@R5HKhoPsTq0iXyQITi)V`MvZ6mTZynZ&Ao*??w=SAke-A_f# z>}`L}HioJ!Hyv4}ARw;vr= zhOdn9#hG*GYCzJ^nR@SA&(6)&yg0pz@cIW29vs=GlCpTh2(OQGM=*VH#eh2Iw(5{9 z;EKx0$oNY5V&BZq`%#yhEDp^s3G>@5y*p5CY@-wa;V661T*n1_&CNSP!k&q3#>jiY7 zMXLY?zy#fCF>~i@r?fX=7)fGc0vWbQT*;z*0Q>`nnxH{6UsE~O;eg{fHfznm;1w}- za3}v%XcWYJsC_yylDClC+Ice;A~Mo*XE*IVD~k@r8VQ+6zb!ZC;qK_Cd9&SI{f#Nn zB%&l$2`Ea=)d#>+d3uA;w{N|a_I@w%-8PS3yzj4zXYSf+jCj`jhF!MYZ|XTO{7Wh1 z4Jsj_MJ?yw*GPhQQ8I*i=x4pp08|#9P%M%LwtnawVu%p*_(D5BVPB|c@I z-YOs@Bo=Lic!|VdEc9WFMk$U4qN#NvS}>!Ky#ge$@^i;pUXgA}qzRy<*croyw_>nn z0@BjbE?>Da@qvn~7dpfP){;;WZ2%h*V0xvRq*JrM5XqQ+L<+!76~Is|+utoI`1mm* zCgvK{Z<|EiT~!8L^`Wle%P{AH8J9+tLoy^f92#Xc$OaKH zuv*Ue5r+v+5jDL+mGww<8YP+U>jq7RUa)A z32f0JG5$UomVJH` z1IYe01{YsWB!dVs>Je*;b(2x;(3ax;QC%<*z+d~zU$~VFq_tQFl|B<Syl^A}F51BGmCy!ldr~(!f&`FRkA|zLr4>i4RuGH+hNN&v*#DDK_e?)z z0R&GGlQ8*m!CD|}B9oJ-MM@Xd|C+k=bCw#03Lgt(S--;p6BrZq^|Z;#e$9fA&0D08 z_MaA0N3DrT<3*7-wPGD!FNu?kLv9~vv1zM8Dr5qA(}$)toBk+vBy>k2woDF#hf(|c zA>qCq-o(tzh3o^$eH46adw4FfY2nI6U9TC5GDaMOsrV)paFMvk;LH9rPa zpMx*iJ80^v76cUuD>mioXId=6;{rQ;^S!dR@6?m9N-=DZx9Is=LB*B*9Nw-0VtDXT zcS&V=9^JMM1*IC!Jme_G%;9E;6ni*TI_v1&8s?~!8+38((BPb+_gOR-uSjDl>Sc?Hg*x0@_yrA$r23G?! z!_8iIUt0YcP7$qc3R*)8lvQ;_~vm$Dlo58#-)*#Y?H*Y2GjYN9^xV z*P?%ifZ!z_a-qcGTT~UT7`dA_%u&622*xY(y(|=#BXBlHZF+u~f*{Y%KWH;%?836B zpzN7{1pQW@UvGK=D<3lcBhX-@Tx&7pR%-8UVIN0=tPh4X%4?v2O&nIazEkNvgIo>V z1kkXDerOkFL!hADTaq+DOd51&s3A!LqFfCUnZI|&J|l<;j;8|PFo4V(h~-#)wdynf zqCWyfruQ=s{^I(_j6R+ni=-~(iT%hi5$l4?uvP%3hx>T0ec5c(@V4N^kmKxF5h*|< z$b}>X*{K2<3DUh{NSS6ps1q1uf>dL)y~zKh>V1q)m>&OGCc-WxqzUcz$K<3E_FimZ zVv0ld;xkxlL&p+Id+o32RZRI;kHB)2CL4Ibn-M-7OrQ_@Pw){li6QQ<Cv;B!`-r;^MzPESWI4|GZo$)AUY1T)K@+L zgpH{%387R;0c`mr7l=Tnf#oo&4Vjhpzqc@ukG_{ z(h(o^bGEb8C|E^@7K)93*K!JPM3J&a$~R_RO`gkw0bRJoq~D%*^#71;61P%(zhnCa zmiIxeY!y_BIuIL39Zc9W@cD9LFR}|{qXD>yf#WdQxg;Q<4s{@{+1!&;BmH7aEu3s9 zdPrlG@joOXHf*2SFD$Hu-4%ou=gv*42U)fKOWN(*K%DY<8JSu_6oP{_!tS;G2Ptpv z-=sWF_rjY7KhZyM=ir#(Xu$g2U;Lrt{IV+AD*YIm3?mU#IxSD+ry|^i{WHWL!EQzd zLE8jq5`QP)kn}7^Cc?o9Q3f9Vc$if$#XQO5BO9jB-D-`HKP7ep*iSWXZ>T;ofA3@X z3o0fZEYT_%HSwsSa4n3XsidNKc@Kl4b=hflq)HhS#0Ha%46qI)|r&Yb3nZ*BATy zp8vU-xUlDwIgDBA1q6;0lh!YoG==MUN9miP!E)jDO5ZFu^ zmHgl6BniHyJ8dDDt9>D&<|q-xuHc&l($1%>L_~_z0zxIG&57ZHw?6O; zi;9YSmGt5Jf*#AO9QDC6DuP|DCvErErjJeR!NzD7mKO=fLuk_NC)frD2XD!^-M!oU z0P@Ro1-4xQ#k=S{*VA5jE&slyg^|`{IkbY69#W#B)fIe-xt?eU`NJ()hp}t$EX2cJ z%e&81#KcxW4_t;Fm)Y2aYHDL6p{5qt+KFmJEg!i#B_)M2Jat%iC|U^4`yu?IMwaz@ zH0%8+q7u`hntnMsLX>UeBd0bMzkmP!;$hh3{0!k=Y&M?e=3+l^;H0rJC#Hv4$A=X; z1uq`sZu0sE255HdShr!rh8=vS6a-FBs;bgE|L)%E*8SYhs4PUleFYU=JJMoqk&${? zg|ck^cfGv}uu1za^vH{7qj$Z$KEh(#MLWiK;6Z#m*XFe=5HR1!#6&G2A)x{-c+cZA zuTROz(SWcmp`aAmdz=;(y}FvCnfbX=^sa}W@B91T#%#9M<~&=MWPwqdoSYn;n7ADp z+ICu``WP>7Cw2>Lz-2!O2yjQ5DusRP%Q#rp+mz!p-a}TqbnV&-$~IoZZ2;qYO?8HT ze%=>)5ny!NadTEmJKFZ*)go<0iw3iWl@JYhmRG>-KYjhWnROM*dI$``f_Aj91y)m2 z^QveZJ9g}BsBp{0vAcnO#oa2oj>nr@S|0fMu~_np`XX1n4$o2Y?Acr4;o;?dmI55j zL9)>`tJkj=Lu|42$^ri~`!}z}KaMb&C1qspNV48ki!DU(@4>@|Zt;2Z47l(L2hM8V zXL@y90s;b0o<6+T}P zD4=1lUM+_se$vvC8#Qx1CEaO;7cIyX8k35uYRZhCtCf{ieTKz8ut7ku^(yM>*up?> z3)3=rRckJ0mQKN4TvmAaKKy~|{aTxsrJ}5~yY}o^apdVb$Pa+&##2koHKkZ-`S|$W z6&0<*Bs&BbReA?z{OOAwt+&qZMf_eEDNP?>AU=Nn{#ZL1D=QuVw~br3uFBBxfj#mv zHI<|v(}6X!iVQ~hHC~p!dj6a;Ha3QQ{D-tQXHV=YSiXEYH+%b)D_7*Sw0r>9yCTD% zeSlbdKRDP6JyU<`^QTYmpx#~7*T0AN5`6sl@glTfiYtEhGBc9_7fiuyYmM7+%&^`S zoD(A1yVh1F;1n$36HZP-*cI&sC8`4`Zrf3vvq>}1b9M;`Y({U6SZ8nXEHJgUuBF=m zlV6B3>P4|q#{>HaAN>&a(hjy{Z6#EI*FP#M>XfxLFGlU6fdQ3==uP0|Q;UaTu zczJmfl9NSGoSfA zXAAWy0a_j)Kg+&-tMGjI&-;IWpfPvZfvxp{f@1uh$`qd&47sRhjb zJ3U_lH!wL?%{JH9e`;eUWz(Jmh4hwANXU!0gH*E%ohLV`v(jF|x>Z4z0pR@(yoZk1 zwAK&ExEdq`-@T=yr{{qkubZId9r<|n?Ab$*(>YY%E=S<(JwSB= zJZiVn)z4?R#aQ}^fO8WpzRRHDy?pUv38GJ;Iy!r?k$=?#$>r+m>a$27i^?6;;Q#Z?9$^o)pE`I7P;P_5XM!f562zvKTY(2%VwKK8}atB_Gbx8Z!QTSO}GXbX5K$dsyt( zVf;QqRvQb*n_WRoP5ldLC~&7c(3V&8Dn47B>cTSq$Ch$5PMl}+{~@ndy%#r=4I+8h z!{a@26$#k-Xjq?cLreg(nns9*4vn3WxPSFI4#z-0+!3g{4FCLQPd@tmy82+B%p<(v zyRW?OvHZ}77$-I*+0sGxxK$kyLfK7y7wP))@W;MQ%-1T z>_7)rVDoRi9I3#DF7&aeXc+~j+Blu(vmHTIi=TfWn_if+=6`N`5i?|Miw|uU@m}h60-y!qw${8&f`qOHvh|LaHqmgjo1I*SjWdT|J(6; zEURN9YVSrvKy~&eIWN6s!;pT zkT*-m%0Kjn_v+xc)Z&ORlTPhs2(lP!0 z`j|YsI60}!Gj^bFf=JkVh>31cmXo^yv2r(d)Ww@&7~RmLo+FO{;b#?w-V{Z7AJdf7 zeCYU4(^g`_%{Mn%$f>G&;1;?ddh9=VPz+E6JnZH(q2FV!UipYTKL`r)!~}j47852V;a@&!stC69-EmMR7vjx)UI8#x6{^^+fP#7&xETbdqp$CPC4UQb+X{Y3RL!zf8*{j}U~Li- zZUxmJVNCFavgLMQAj8n+3D`OLZEeh) zoSZUb0JkJO9>#6uGtri`m=Yx`&Ucz!dAIPYj}ZN*A^4;+1d z7RGo1LR#1^tKbMkQ8H;XkbyOOJ18h<^18CJvNhEkW8_YkxGc=HC0@#3nf>DnJJ$Vs zG#E2@R7xZHdu&8mS)ZAO8=wBUK}ox+i@6Y}YkStw_=-JU%8&=^M=oM?0O?J!(`~RZ zg~}SfE(gbQ(}lC9SFf@nm2?A_&a!LQ5)z%U8~pO+%*TR)OW0RVP&}u0ZcK|c!ePLL z?cf=@)}hd!HL`7K5HFK}x=%w-?}2b*M@pU6GRh9z9d;KUW?*0d2=oCq-hJfA+nOCv ze+Y4*P@rJ#iBV6BE6JW3YI28^(2kv4q@5i`qpOrG@B+ z1c4*flU(4$G^wGWtQ?k{yjey@rm+dB{^X>G{9O)@d)}IgGD^iN8JvH|x_56UbT>D& zXcWFNB(`*?y8HzChaoWEl6j%tMUNs@8I4U&oe-3f(_4>swyeEy;X*c2ffUO5^XJK0 z-O8nfg(?m=KdW%KCVHzV3r(Cx#?nL;FGN)Y%@?pSVhffn-pZAbl5+d?>pjp~eX+)d zgh7jL`}=PpjSMp4r|usTKEInS%8>1IJ|Ql?QeR&mpuA3}6;(G{IbIx1g1qB}Ln}MI z;p8exNnjOENCin$JqZfRFDoM`l9c+4jN@n=rtocfuNWY6xAa8Pfe(0@L1U9gCi|vM zn}(DxY~akO`tITSMtYdC6WSe0T!cY8{S@^FR8qQmV_6%k-#@NZ*VKTGz9>kA4BLTv zu}#35#lH^c#2XtQ=~v@_tiNZ)jwc z(x$Gedh)e=%2Rhq)(<#JBCJmWl`$OXo+vPt!?>npP^NUI1~7V9!KQd$Ky$8BRzvEl zwxitA#;mk!D9^E}7>KYjwB+koS#9k$owyD9YS_W{L^u4~4_0O;C@11YFk9P6(p-BxFcze?0McPbbRr2# zY82Ff3LrI5WDMJOhpAzssd`iw39RR8eIC57>lXe_FAa3*M643A4jYpi9zT9;3n`rj zm=T7_a}+;p?so}{ooju2jmmj$LRll%ksjrC^yg0lo_VaYQM{Dk#hW)P$${0>81PE0 zW}Q}~Y;Gha9R=A~$IQ%}vvnv|Xe;2Ip>@k|R2MJgd*U-Tt|_Rl-i%wi>*FIXBSQ@v z;|_V@U9;S?yONTET?B8f+`!a)fSN5%JC6x8^NElHFCL>7HpK&yU3`CY1z{JM8He!l z5l3X7h8UD%m9C%%lc$JyiyL^-5HA2ast@TDbaZsTu$>^V`v=5wz3@>k_0+Qtc}V+Q z!6qxPe%w?(3a*;BiH^=4*h9N`dFgt3dY&A)b|xuN?IDsXLPy&I-<+%8D-(Q|M7~SR z)TgBOX}=|{=$)Mk%u6RlcJ?#Coj8m|V?2>&Kh(&xeC0~`*~>s_32j5eY!Zw(QN2Y$ zQSk-l{Gp>q2kwvkI)3x+t&c~C8E=_HiHa5uT?*I*YRu@2Vo!DdT+y~e7b&m}Zo$e1 zb75g)yC)_tZzihz_aceI}Lnpnn2 zBWKgm-u`aN9O$9{kxKd|8v4Z2%3z;fsf6Q z=|2~nc$|@b8DKmMEmKg{8qW*ee?a?P{=;+_0dm>oOOko<3J(*!|E|YURP@6Y3;s)H z-VUoe8U_AbQRON^7>E-1Y1@C@LNMF;MW`=Y?Ey--^Kaz;Kb+m5SzvWttCcfq@R`@N zH8nMVqy6N>KbOU{ipr*+7hLUy?0#7Vh2;<$+&*%!u`Q)QgW1N)RN?j)nE(s%#e^t` z{84R$A8cqIc?`)T6`SzTNhjVA2kSjCjj_s1~M%?M3+V?+(S5N=keq8 zC{FgCVxm_ENhy)ECCu#^hF`F%mFKP~3)D5B6zyJxKf`Nz`LBsv?Tk0O+ztr|Dc6IJ zLSsdB>Y(R0^Rp~VA%;rBoBwZOXap6)-S3d8C195zGc0`aVhQzZL_YA~iMaG`ZbNXa zQ;GyUU$}bikSX5d{R$2REAiM~4!eRfVU6DZeDxEg0x79AkIuZ->Xc-qt#4||udCY% zO>3ivnus&pSHiWv_4IT?aIydF-dGscQoW_7u5LF59;KVlw4H)l?}oaF>_6Aa;*&p* zS>P>0gyzxJ48W1`xMa5B>Kjusq-u^eOMnz0=~*= zYOYUIF}(jIkiES(iE9Im)%w|wKSlIY7y!CJR&ShoQLbJ=-Mp|dN_PLoO`F{Cau(ED zgESrpp|Dj5HaU!K471qvc>>kb)0#y;4a&VnhWQGxdiRtR9{b5b5A2HQfNf33pJM$R z??5~RFTiWZaazhKidiSNp%(;bJ0CFBxw;>dHsr>lYHV5P`!}gfxkIMc(cy*NfJULA zzOYty1E>4=_}B{ss%dI^L$3g2JINW)1&**y*pbiN+WJYBlca?XWJb`8vYQR2~i1& zE8yc4SG-$e_ujoL@k*+j{{D(t+eK_7{zeu+LVf9ZFH=H^Fg<20jN_Mu3tryAafj68sPO@n6)scQ)ZKl21b1NWqyu(h?#GH*_} zv<%x+AUrRHBt+yoJcEge36*Ty!`M)Kn_W5XA_9&j!{5Jsy9LxjapW45;lAGLF+K#< zTMXnUsR^vr6g&N~LF1UflL!;YnooqCGT(R7r0o4m+LZBM2t^Er;u;nf_WrW|Wz72R z9D8Z3xc-BjDU%cPALPuP$Bv~nY$LCrI5vwt$iTVeX0NEe%qwU+b zb0FgLz4_6ZJwD7LJ_sl>V@~xpB<*|VGH;iStxBIheL@H=&zX@@9@_w&1n8gzoqzu&4QLzD%@sQ?uMQt>)qznlV=mvJ|)H!nyey=jpx5vS;e zD@WGoBfPo_k}qDSS7jF^7;dtin&>f;NEdctca=D3H3l}penKBRfVLsodHTwg-5_h~ z&EauzGBY_TDOO2|4BNNMX>0r5ySJ9|?*6XR$xTg1jqqldXkA0Y>bUJSgrVY*tVmH? zU$RA-dA2<2cSd631D62NtA|(~tOkc2(xQVJ_X>6RA%VZpv+p3odI?H4QRPPmlgieH z(9zJ-VED4J%_QBjj~0jvNQKg@&88qLTRsSwre)+4exQ=)dchK1PdiQB(wEw~ju{O@ z*nC6?0R(+;UP?PEVaX(+#6SWvcJg)T%+>y%yUitE%ip?lXKl#ZicOc$;Lx(J{tqgq zOh@=*?t*o4%I0v22AeZB;Yhl=y6-@*9z1>O0c?2^E3CHm|Frk!@mTNc|L;xnph!td zgXTF&gEUE+2bHok85&7tMS~^{Dj`|TbJ1Xy6&jVz@mp*;^$W32x_ejd3@7Mt1z~E^u z62J%?p51H$B0l}ach+R?vuCcz>T=m>QkG< znrJ40@X~Pyd)HtAWro}YWMCP^%~%?BdU=c2nKUO2k4@f>NIEfRPVc%^2&QuEk_mqL zx4oK|ut3h8Kc6~sjIr_2n_Rq#jr&>84I4Lat|o57Q>Rboq1>qKBOWV5)R}h6R2d6` z*~r*fIV>_Vk#Wci1PSAKH9XwWP+aEW>hw$(V(7-U8$=!|ck-jF)>~BDsGzMO7cG=};1A`qpRcqqH2XjwMYilXJr1adtM_gEITT z=ET#*dGku^2Qp+Zawk$Ycd|l1y?fl84Upks$>+hCrert(EVL-Q<{Pg6oEqnV{Cdy+ z{Wh3m7cU-VGW_HuOC%l@J$MuPi6ub49Ub@aS>?TND3$-Hn9SOG6g|6l*C?OfdWc|qjg z5U(0HHhLuSI2V!B8hhuC3T@j~LD9N(%hcZrs{#i{ouXoX`gZ*Rj|PSk29nLA;zw_7 zS04%vSzjE?Gg+u$U7sFO@pggA(cG_ccCLGL!i6|cWB3S&czU*aN9#KEHcVa8pBYwY zDI{sdCqxdDS5VM>+5F3ou5TE^#AsEyk#A412HCg7kYjY#(5W+LbO5yO z=nhG+mJOP9CGw6g z@4#Ic7XFzTn@{l3PzMKHNXZo6FOZF4z?G2Ti#}$=r>vdx%P(~l2T-?9xYWqWi4FG) zWM>f?-~93)@5_7Pb6>T3^`i0>Ewr^&_PKZ1*P$=l;q-X6LjzAR%Hi!Af!kNd@v`>i zV06c^FmuxVvR(@gWko*B%(T6J;rHKdSKLT?kQn6WH{+3Q6y`WZ=Fizr`Zxnd=(0<`Yn%p+@fzaoiQUTr1asVM=^16)mR}9FFSQr zytH@z9=C8`{L8>PElp?0z*a*>Pne6mgXS<78NEn*_e4&d3*eK0q}5{a_0=Ccz~n9a z{>4EC2mVR|2!NwjDIlisu}TU729F+{b=ao(RcbB8q}j87B>M~mY2Zz5*}Yp`nkRwg z6_d=&P7j*IlmQx~qM(;rm~=ixx6iC+fcBk6t?k`|2_cD!`|$btdxJ_^AXCYG_3B17 z6YjjW3#gV;7lsh?bCycC@7&P^Ap!JiMMJdN=wvi`@5z(5o5rRtUKviQ{i>9Fvb_wi8#xel)IO4^Xnl^2EG)Y_EwlP-lt!Oaf($kyZj!<&x zn?##ANT48GWm4fGcke{js#!CZ9TV$aj~}Hu<-joKADs?CdrOocn;g|-K&2Ems)>Q1yItm;n|zC8uI02-7S0Ic7TAEDIN3U z{?5K}U(~$M{cEA$d1X*L<4&`F*REC6^mdH}e$mm{T+vDRB*poEsI2wIM>XpSOgvBC zOT{0l<642Mg1zYISpDm6A5L{r4LPj0WbL|&#$=QXth_=^fKLA5we2USW8e>s0MK0L zXw5o*;|WKVgOWv*FFm||VuA1KcZb%m`;;=WK+88Epe|aR%M(gl()Ghz)~;Lk>1~Zc zg5DB%A)E`W?7LGk>5 zH4TleLMqT%iYDV}w*P>=V_buRx(Oc$_DgpZO&F>xuztGb090gL#gNT~FHuduAxUs< zb|u+Wz18sPB3cLw(;e0A#OI_)qAqyAViOZCG_R?5HgB(FqAP z=#uO;EU|6HG0&+rZd`rJl++c2mhx@v60XeB1bl4g?OAia30UWF2uX>Uc|852rp21Y zD7HkP5UxuHgN`gI_VG|;Rt}8DqWmKDIGzoT(WJ)8l`GjwFQk`JFaJdY1K3S|aRA-S zvAd;gZ-0f4`sbB8ge{Z)**D$Mvrop@=F|QM^6Rbrf7bhz9!xX2C|u&`^1?9Rto?AK zqr&CC5?)qcKRoO2{HfF6^6$$%S`N~}#28O;Qz&53L_@Tm(mpbg4b2#O2P?CuU_kKm znfAe+DHekd`M*Gaoy0jK9`Gt#wr#WFS|Z+CJGUi^Hx9*(XpC(jd!RpmyL&I|f5VJD zl-|d}qHf2I9SiKM8jtAf1BM6Eih(IQkU$F|p1bmTsWuN4duYny6*R6U3m0}`PyKvs z_hkPqMkigUFDQF96WpQk-pfsD<>~nz&F9R4TJQK5)U2DM!KnqbaS4n$VaQ_YYa5{V z#YKyldC(w-_fG~1O-TR3xa!pe_renw@F$_LaLX#9dED#Lzp*T3bXD{sqgoUqqK7J^ z*J=Z3$~ht3&?$fT1kDs+jY&NLL3GPY<-Q+M8o#Sz+Z3%q2Z0>z^%^49 zpdpa>c|4!Hws> zfA7_1_}11~246b*9X^kG!RX{r^p|j1Dvf|nk0Ns1TB+g~W_^F%{+@G8DtjP{#aR({ zP0_AqFJE5p?DtAWZmWi8nsf2h@~{k^N!St2Vlo6j&D1_m zSN`y#LtEK7c%E_zm0Y?a@#`@AXVr$zNVaRad(3I&W#x-c@87?F#f)*|>T_ETdCD~g z<(KXe4daUQ_6?6MLwi_LVXu^ft((hVaBu$n`CjSfA&VRqa#QX^H}vGJFeG{=t5)@A)bvL599yO*bb0< z4cytp=D;Dg?%A^;d|3C$%4O*~HpvNrj;d*8L2My8&3EVWU!&Hc+Pc5bk2Eqf+gqHwnFj`Oz_5=m z@AkR#N*cJ=uLGtmzx)%yyOx@IKmPvryHiSKQ|fIk@U9yQ8$qQ7R)ajL>23$dgh^Sm zTg%5hUcAB%W*6fTBVuUU%Q-nk1_C5}qP}2_KIBGhON{EloU)FL#sOZb963k%;nV9zWP-esC z`KDhN&aW@!Ls~;1-CaB}Lxx=M+v)}335Dd~>KGVcnE3SaK*#rL?B&pc@lf-px@RNJ z&?Wjg>P70&gLI|b325Nhu|1=zFJJy~#p5}t+wblgyRT2JMOmrUy$;zdP_Rw+-cD@N z=HPYq&d-ZbUxWD^9`Q87men$q-lZa@>~c3NGBW${P19j zoxR6{4=I|1ocmO@rSjenkV$gbOBZyQ!FTQ4lpcG>M9g8Yv0r|BN z{5Wj1SH~SAy2HXo!(uSqO9z12SwVQkilzl=P6xSred`|GqH9^DmU?-ES%!A88c<>P z$kC@KbGNNlr_-cU9tlh_W6ZK?4SXEp{S}EHD=^au;sl)U34EF$I(TKY6FV zk)fW)2@#U-h?F1Tq1J3w-7@_jA37HHV67zi8=Xz}|H-VNFry)+OH1&H+WhF@Lr3$J z3A37JTwJNza=_208_a+fLbiP5X>ipeuLJ`KakdGUYZ&0cc{Y|`U)8_R3t2 zjT0(a;ApRFDLwnM1ho zitXFAZ;v#&^rYP*z)d0hCv->uSX<-WEOq&CcP@AAJwaOGPr_ee@=Zj_W3d=lP79a^ zk^K~8mPp^)aKo2sWcOG!E@*4w5zV}H4z+|v0bS+PpJbxw}!RSy+K}_#NmT`(g!>D`AcIU9-!MbSajNl<0w#-w6m`(=#5E-5g8ChFh zjsoB|XC9I;ez2Ih=tum-teN>w^eZ)%P6jF}Dh;E~lx{99{9cWJSBt5n_Tl}N!^T#J zkI%;k&Ailz#khXq+0~wxFB_aXd)DS%_NCwnz8!UZ_VtM}rd?6dz>lHtKE5ESg3o5q zaB2@Ec|hMzgWS?L8d(V7s?i0qoAC+eZO?7kR1E)Qr1FUwj{KukB zhs?RpHQg62Hov%X7gSJ4Mlb7%`||>FO0)*JIZ~f?Dw#&>Ag2aYnzm>%U{`jAr?VfyOX)zSds} zOEM~W8p(W(u{_lD6att$BMrT?qxuQ&zWt9}WXUYann%ccONe9!$|t5IhE(VV-hQ8N zY{Y$M0@KM4SuEy3&=h2j`@C}D6C?$YmBTyi7JWSsYw+xt{Lz0i5qPbgcy^=>I)PQI zt5pa&{qpC|pRc2@|M_I9-s#Dmc`bnkjwS|#ge3CWma!5v7UYc3QIiGEA~qz!LHjpW zkGJZoKQ<%(lsnjgrISFlP;K5k%V#0}jgeG+Nn1GTzCyu>BqkLhPU+0%XHko9sSs$RNl72py)QLLn7ZH{L!zh5f^@V~N8 z@MOx2*|9=F(=#y2e8OnM(AY+2q$$Rsr z9&+aG#)eys=8l6fw4}vh-PSua5)BDiRJu^gth2g@>Y+_(xBpfc5knWRTxm-X9=@xV zGtA93#bI>dKy9>mCsSXoFz?>?xcn+hQ6e!rGghBonDgUOs#-M}TiM32# z(5eO;(4n9>19=vEmq?Q6-ROk{r91CGIKWPA!k=IXKb(8d%Ev5P7cyt#%r+BWK?-T|S_d;PjDOfX`m zq1`mv@=S;=%OGNjXafMQ_>XK@eFiXqUO#cm86W{Y&l_xKH`)}#2d6>26<0sfW zK;NOS-$Y0iclUarWQRPn@M4G(nynG`8qi?b^5v;ZRysMcDa>ATtmggA%+BArxRU%( zNRGSAap+Q$FhR_SYPkPH1zR~soGs5cpSyM@X^00ZrM`Wy++W!Pa727sl$X@kgP9G8 zMH3f=Kp@QfEpQRv3G;##2cOlX(8*XTvQ%)xzDeW8jEM!j471DV0p5I4j2;)M+-SdL zkDLVR!|^uorHd{M{tba9bW3txuKeAM*L4x z0^+%}9pLy%(QPv}uVI*C<`gj^cb8KxowU|~3xMSsU0+`&s6GnQzDZZtl=KkN5klS) z?>lnE1hK?N{M2*7K|$dcro5O-Kw6pYYwqVa!{~>Oof|4x zG(1T%v_*I>$R4QHx5ulQC{|lHa+`!OhXp&>@%VozM;*1Y9z2*<8igk0y#I$_&tK56 zQ}Izs89Lq;UAotX?>6qA?~kuH6d<`Ee8G=vucl+fl6w4nJpe8ohltX*ZL7udEKe@G zxMv&M)t{SQH}qb?FCSm&$1^RRYGb03?83@GX^OBU+cKZV5V2gLA^Ng zPM*WZk7u2I?r7BWSsU5=^4M@w+QM``r3RZA87YU%j@rRW6l{Vb-|RnRo9U~wIm;$j zgg$f~aI8B)W2!~BP1-Z69oqO2AVl&KLfl}h$t(~woAP;%T@eB_?*9EoKZmTxXV0Be zWw3eevS4fGt^ZMeNn?AtC)doY7e{7^;DTBLqKfig{oJxVqsC2}#(}vAqmCA&kOWj9 zy%8@QMtW+w62oovsyats=pyh+vSW ztYRWbGnNqW0@#?OZrC=hB`dA4xHyrvOPmLE{CH~bSY`+`m-lz^L!F8;|E$T5>OYlf zM_td{iaeRorcRUn9y$05RVl+*>ySWN6Boy9w1p8V*}qv-KtLB^k@;KNy<6rS=R)M%cF2h9cLr2%htiIWx?EovXF zDeGQsPzTEJeUaKtXx2gQxK8hOqB!Ks8T!1^c#KToRJD-tmG$uCxeCxx=f3*Ss`_;5 z>Wbgg@e!kpIvPC^BrNR3^>^rD+5o}nFUGch;`|or~$8mTxq!O)JEr)TE|8YgOq^ z)7%*JYUTaX`KL$8U^BD?*Rd9MHs_$yz`uaX#xKZ|XzxABa_Q2BShw(n3~TpvKz_%V z1zx;qzoSB1)N=Y?&=$QP#;m$DvjOr<{6}}yqu-hmTe*b-!(I&m0T7ch^L6XKD=`R)=QH)zHS@Ps{8NM)} zU|r6Z;(kP@q=4U=_Z%<{#|5(&c^TN^5O5NBmoYZzor~yGE-S!ObAJpOHr&*I>K$u! z(-$W|?1%*ZqIo$WD5%e<7M>4UX8pU<5U-^xzKRJGc0T#&uzdM&N@C9%Bb|SeB|H=Y z8xfZ!R&=)V@Q3~WYWAh`!$nkOPErgdSm=qh%%1X25auZWSG`3!Hvl-eTLMhdm0Z~@S1%^t=!yA3doK4n9=-OM{v3jxAf(0?Qn(YFloNkK703J`!0%3x@i zq>aXpSK*KlcSK5y&hATuZz0S`;Q~-H98u`meM4@dTPdOu5sCy3Fy+aUOwHhDzN=d> zq;18)Ck`Ynt#8S(xx<7zq3ILa`rV81dhH)#>y`nih*LG1pwEb9Oeg}j?U!{SIBDPE z!}VzW3hwbfL^(I0qff{QIy4rPG20qwe$k5|<;@5R8)0VFL~-I@CgUT0!dST~8mXEN zOD6ra*KK)DgO2`(nHJ_Dj=TI9v+(8P7Pb!Xu!_#ji%fm z*8b+DiMJQ0wjAC-*S+`rE{U;=i_M-N`g(KUtTTIz!#X^h8vMC5E;QoS>vJ~^99Inv zu?y+?_`Oq3Xi4egH0R*bYjfV~Ef8C;XyWMUYbsc?xm;7>W$M?je-0^z6hGaI1hr-R>Ec3eqgq>}$WR-W%qE{(&Y5Fq=2i*?&!|^ZQTXZ8o={~!FL*$ayn>_| zo0td+1(fo^P9<&J5dsFD%^+a&V71}Hhcip(l2R&4+WIOgJlffQWnay1eGaq^{brGR z>x*0XXiR-sZIWkLJ`K@1;%5JN)rQeoWd7M*jY&&M**LDllw0P?8&8-#%izhpv0p_! z!UQOb89vl$#viC3<#vnx-NeL%{&(?xO|v@CDQ(D0QBu(68O@t_+OTuTWV3GeqA=^* zHxYH<;qW1snS~@sf+tK4(OH~*F)Y#J<6*5GwOkBOh8mm=u+YRlfM)hXB*_WLBQjo& zoIbrN{fJy`gkZMeQD0X^+K#q2Q(kImsn|HQb@_#B*9HUCyuPl8@H=+#<;$0UY{>Bc z`2IT(Um?;O8`J~YFD7)6ef;f{s4Fq~g!6ilM)v$Rw!8q+U&@oER z3yCEGzpXeqsW!?nRIqP?6Y&cRF~f6M+Dwk-tfSb)Ilnd< zTzO5xzxuB!?nWy3P8>tIU{hV1UjTPaPDvSNYug1;YU9kM_`jHG5Sxh22ugCYZxSs+ zEYJ%ekGn{-+BMSed@R8D8?7xFWo;GmAijJOHP9ua&Wc$#&^q@Zq)kvkA&2PSwKg1E zo^I>P4)({8wYx^Od&yx7nMomC@MG6;c6Jsto@pDSe9!Xoy%vKt5H8q0udb}btLPFF z>KY00gRU!&(5mt42czwr&L}Gkwd*h6$xeR?5Hl{*-%L+m-xWbU^W-EnsnBarVG1av zE`W8EVwq{!2KVl?v$ozOgaAsbB(zbJ7+_$L^678;5oqAb{S}>n2*(k)t=*UT@S!H6 zj_YN`hhuL$IW=rG*ckag3RGtT7xI6(7xCGYCxYZRS1_J7jMW?%yUf91QK4Z}RFu_% z1>4{(WMVHEy=K;n2j>?|%)ab2cKuA;frxU`wCZ_x(A3hh=7@Mve&N?fpx6KhN{52r z`U|qnwj|uL==WsK{k~8xtdiyoP;#b_>5Z?9rS!_iXwiUK>~3p5c)|S;=P(Pvch|WK zb{P%0wXQfLKYjW%)wzf9ofIe)$%jFJSG&5hGz2`P2Zn4I3=yZIr>A%O=)k62wrRI6 zBL3UmGh+^JChh_4LXSD?m)O{B#$7BpJ5#U=YDnTe>ho~RL2VJm-77L1c1Y9!d3&|% z*WYs?J$>Y?qN&aLEL0WGBeEHmpyiXRt5uHKz|m~JJ~E(ZP8k^VxnG~%tXUd7cGipM zxw#5X73#UOqlPdhNr)5-AJHnep#cU~;FGCg2r_^lN$E#MMGq{}A~Y+PMjT%1c{ z8<}SPhJ11Q^bC!%m2%>h=pgWrvWW`51s*%5pw)z5y$1SQz~~Z+Ej@4vDiyd>Q_*;;q~#pBNKGTU}i>8aVxZH?OTKQbv(QB8|z=p+h}=CM#qqlBhzd z=zRNk;wIRHTe6WZzJFLr=l-$Gx)bc5Ty4g){J^Nsohhh{MWuxER*DFU_L-)}-G2ij zi5%X#d#=YoHPd?2X3q4W1!V>{Jvn~j#D?^Fa9S;SgvglO4qyA=+ZSJn4Wbjtem~kc z!d3-|Q+%_MeI9Q7e%EFAg(;{*b-X);!|6n4XSX0K$MnFwjP&#b;(KM1bbTaKj_s@i z7cb0QyZ?;NcdNzh&Hk4T_p(x!qeW;h$m_jilC|N-$Kw`rSQwibwFtnm(=nFu$W~`? zgPq&`_KHLVjF{*;D_dKO7Y>&%UpBw&*cm6)0R-0mk3{{@xNaDyE!%H1GVZP^&wyT7 zaP#*)Bid0&RR*U<{r>x8t49+45V7RUnKNuC5}LrZHc3Q}zSnE~_#|AvKGP(s2#$w5 zqdA^qJ}&Ljv*$>_b4ZpMFM2!c=u8$pXzT9XJ-s&h&W~a8R_r1)G)y-z5&q`>!vn!; z>hdbBJ9n<5H~)4tEl=Z2OpYGH(T^N?HnD^!22^^L#)h<-FJ;SjhA_R@cZUzi(nUd>bJ$VQ|{~~!nN)| z4VwZ3?SeO~-KNrfE1CyOb_CVhv&#dBKTncQ=;>3R!fkrA+JXzPc^AD-@9G&p(Kt=x zTz_r-j{J4eykq@RIyuSj!MYb*W!1W_q!;x(2dLShV5wWh9~x(6)rKc|87>3YzSXK# z6Mh=a|8E`NMqDC91Qy+t!*Y@zaNxZJKLJ&B@jSfUuR8}1HL4KPXb;?1n(aU0BVr8M z`0TT4BIdT=mioLG*tqR5kpfdTElN(2=fErY(khMlY&LD$R>XKmI7{SY?A4*IU9Mai zNLQS~)!Q2{xV`cCxcH;n{b~pZM`Ohy&1aJ5FI>1CLzg9O$assbY}WdO9pp2r)U_vi zi};}-;EE4UT@*Cl{M>G6AM?PHlTrZ85-Et=?dyW=TCp7DOVz`|4gisX-0zoc$QRVwuQT{5eiGb4(ayz$tBn(9=I4-+kJn;lB?G%Ug}hLX=?dO z+mi6gFT&EnpupO}povw{UtcNV)f0r1ZLl37yu@IIb1=fg#@4njCLMXekOZjKzW8M$ zFEeV^F~il$=wCH8H5U$Stm9*16KU>Q=}rQ5*urBN(VjWddlc%1oz!*#k>OFzvT$gc z9UHPilCNxtACS~CFsyA-59UA>w+}6vl#vl|f6{S~Mi&Avh$6exXzO;ZXSf=+BK}23 zQ^?SUtYTbN8#T4go@1W{s<|0MdcDlKK7`vVT3}f92oG7Yko(9o^2k4+`#u1@XOZZ0 zVfx-ty-ZK+drK*oju=t$RuEKQTWeY-!+qumdg3xi*M*RFf4a?}Cjg7N-NclrOjD{7~kD zwIi-{U^gAxlhQRImuSZnhf3n2RuIrr62|34&Pj#UO#ULUm zI2&c5U;vCGUpNjz>HE@oXAI2d97Me&)K7d5^hVOE7 zE81dqeC|JnjF8FeLMLv5H+zLC~Q{$}fzIUu} z)!Nm?I?5%o8}uA)s*Iq-9LY*TOx`8^0))+XJBlhLv+u(N)E0&|Iy5u85qdms=I3D>jdg|*k*2k>&GzlKDO3YV z7U34ryavT?+iLvja{pO>uX5)C480o#6kk@0n2X;#h7Lp44q^ro*TX~#pGL$RKpk&z z8%i?_be$i+r=S(SeWZsAs5MpufO;ht=h8m^ZTR_6EaN087=nxN{w15M@Zj#+fY;O7NurVi$s?z~MUbLLHjbFr(RE1+qL|(DWN7F;_>tDY48U8g zhy(o{bikH7FGaW47sbeCNcg9u^wh!{0`^ygX+~KA7bY3qm+KiD2Ondo9{eS@nxrGL z9c|sZHM4=6UxL(;``$O^_O`E;GIOC_E4`A5>T3FL|63zpmyt8Gf#-G$7Y5Sk=xD{a zZ{Nl{56bD-$h)sY;?YrczWa_owT@9|8>17C+EyIuG#`44I_jJ@OTXD#$JXtDICI?d zo$AqV%!pHVN zISqV;j~6Jn4UjfR?|JLCZ{)Uiapmhh-=6R)+?~qwikiepLFs{7%|GH5S8(QB&4r7{ zRs58W?STIIv+v;Jfbo{$h+;+K9tU^U-G=jkboBI?VUM^lP4P47y&L$Om{}S0I9U0f z*<@yWBKe}?(OWja%WK}&1}N4BOiB}S)g-Z!II$(WxE1;DCf zYD(Rzvfqr;f~0ptEKqK|$D-AfIsBHbJ^lQj@0SK_81Fg7sUV=dWCQ>4e4S6xz9<)Cg;D6WQTQIAKb#R2siom7RzvTeTkb>dUUF0m7@$UrY2C!Lo~ zV8aZ5vh8=g@Rn@I-eE2rhm1W-!q12SLbz--ckUKwleOQAk_4%9baK+Qj+fw1YXGE| zoXeGH-Qlw}AQ4|__08s7ev;g~7{r9P^7qonXWEF`QrHP}S2D!Y_6qEHIv-^LBSTl3 zd1>(JxkX`(5Tcgis`KZ%G`U9T{=l+?B_+*IiC#p)fVkf@lBz9QcyM|^bsRkD=@E&m z)BBD7m4mn?)|Qkm8KgPLvyQ^4*mSyK$@`pb{2`GMS5O zPM$rR;rB*lIRwMJeD$gg$^iUM(J-qjbbI_U>-g)g9)eqFErulJ zjV|s{IWLh~nkBKP)CS~((P z;TGghfg*o+4^@@RUlGU}VE|97_^8-Shj-cg6uWT}TaE*{NkOxtv=o2dSet(T!{x@L z#vxXb<^Y_B1gt;poj!9J&7^9H--M6Q$n8|(rLcRtp1BB`BSGivPV;~tk zvRk5z(Gc{L;7f26HEr!#Z|rOb(kAt;>NM6L;R&W9*cQmUVKIvyQDij;@|cut2%d*k zMoM})v*kJ!1z0G}2H%MVHCqX^BXjLC0YUtBR!#(d_UEy(Iy)N>hXV+9)?2q>i~f?g zRom4F_sWAcZ;@{<6PrRp!pRDqH?9dK1>1h%hi7A@^n&0bld+#{C`9|G&!2?{!mH9i zA;k}Yx=6txGe{?I*bux{jTS48epS-Q00m>Idfb2|X$Jrpr1iLWFNyqsUaJaooxMVP z9t#+hFM@EmPDM0E9S#OjNd%Cn%Z!#y0uwD7vOnSnL%&;8)1N+qw|sNRTXNnS2yFfI z=_Wy1IsC3ypFUeiPom%5b4>%8kfOC$)ay3^?=wbXz=5AfiyY zb_RujL=b?7EF8G1u(d=x0MkeR@kedi>+w4RyI2tpfMv)xfYD14DPd_?^=GrAgFa9S0?rIkr7+8;9 zO>Ptqxqjx8Be^#)zZPCk*Q!~wCKvp}hBt5C+^Tm=f9uYj6Ffr?_ugHQm)E9z=&)IE z6PCcDuq)zEMix9SJ4cyGH*efx!9>`_A}@gyVt@wyXamX^6y|_A7;@!HO|QU1`gh1a z*R2P?=7B;?q#!&vB41xdFwEYB4EAT)z}N<0;_DWONs@QZM(V)7XL|Hcu|cq7g?&l_ zwHf=l1&10>G}9^j^lHl2W5pQPeP}opvpYU?ih6Mir532sk|$T|uy~1wHOpTU891%0 z;pz{@Kk_yk>v+GP73$7BFX~mO3xO3FFrsmupLuX9%78RfMfsHv-$dGU?vbQB_5*BXuwY6>{*Z1*JCUXw-Z#V%ubmLRC0^Qu|{9snm zp?#94nch%*077c5`uw_y=roBJ4x*#Sob}-sTQE2t&F>g}>*{5s4_f<0l>q|i=p`X- z{FTm21B1Q3J@x+`ag$&f@F-jnS9kZZn_s*tC{Q6G`v{ehrDi)Z^NqoUpPgfVRp(gf z%&<7_m|-)RTVpy~sz)vp2Gdow=r@m+jrX@W=ySL1lf6<^w!v$97u-WnEV8y3wE?qE zK`?wlOKb^Z#CLkVC-(k}e-juBqSlEu0pLtBC22i~bVF_E{vW;OvkN;SLLAPHKipwX z$aTU*cvNF)*{*%h#;7ML1a$Ur!Ai7h*0)A!SUj{QnV{({IF2xY~oGy;X&%X6ml52wm_e zYq1tXb8XjHt*mN(&r++}Qmrb?Pu4d%{5n)v@GP^1dcAsyHU|MmP=`D5%+gKpim**> zG@3z3i5h}^OdNeXFZp)7U#m+G8Vy=isK9L}VkCr<;%b7r8V2Y)**`qy_z?T3v(MJN zx{!3b((b83nUDVSaMatMS^y>qUh+iY?f2;1iEL~nlAaqk{8LuEy5F9?85fDk$$N0v zI?(3WoTrY;#GJH^tDXLkKZqm8XIbsq>fm3I_-m@p^eyK62TyGUY?e#JSx)I+*7KmqyiYDl zznrcA#?`klYgpKvjdUCPY(|fevx3j**lxo1o5z~^U}Tj}{>@UVcoG--&xGfKnX}XD z=<1rbS=5SPHemz-W@Q4XafJ4A{f;z{e&Alyb&?DS3T4r1^*rn-__a`mjc8-_7u!G zg|VTwMclnsEp)-7ri8?iuXzGpp4QHtnZ54<$5(AFr;OP3KddYGtR44NPiL}56{gn& zUzd>6ES2eYr@wZgV#vnFp!Q~_$wcF+V1G^2>bibt-#YDEVdba7O4p0UJoDnRVJGb& zeT9D!bY|@kCIMlL;lUZs^_z52ix^tfd#4%8mQ-!~ql(4dIK3HIvlsSz`)kVP$InyNfQ7k_=W!{{&=kT{88i-)!LTQgnQ zL(X0BAm+w3l2yZ+ZG|z>Y*DuHxa=7{-c{D%$@j^v#h+zicfUvDx(*y7gC_3ElT+FX zV*foQDuNIJvxlYYOWw^QSxFUoXVupar^n=X^w|eCPLuCl+2s2~N_ZY&=@(ZN95Omg zoj~-nk=50uUEWQ;rv*svQ2t|`Yb4NdH1CB^Z8tgFINc#J>e7=Sj*6@4k(r2OBnL7}=oLkI z>4~t&axEE8aRocrQMEqb1>-M%sa zh*}7Nvo8$?^JV;rVVe~)j>^me#w0cc9`U|i5}90OQl$-D_35P=)YK+X9pet)vtj#M zcu_PAH|Mlftu3t$jwC%c2-_G&LFPB4Z#6+LJw1LQe7V)(Lp%9c3}i5O3}O;p9uyp$ zUR-U~zu^C2-8A~Wic{7Yi@;$v6Qq#wC^;mjm2!yWJN? zU!LpZMi?94$-?{SA~P8TsRZClVCaTa!u{1nOUExUpNTphc`*r5lYPCI#p58CY0UYs z!{^WMz|Pik$a)Xh11kX#`}GshgJ6eKj*D3olE@<68&QL7S3)3~|30@4mw}H43b7&_TH@bfTN*gc=3D}`g&<10M4^H)7yW%>33+vJ)Ek{zklZ3 zyLD?b{$R8Lj6_Nbykcm0e7_%nWx_Kubo%rQDGs2U9|3mc>;?vH?qwSe@C;74h?5}q z?c4eY(p(#E@pm|Iv7a8Wzh?hst!p-DQ5&ZT51edpuu*Hb_E0m`TZzjPG(t^cVg|my zS`E8C-KrHH2daj&rgMILXbRtC{rIvIH-G>2MOuT`uG(By{%YceP2_9~P6Q@x$frk< zIwonsk|lDw5zV*+w+m8(xIuhDt3N(hT6Hvg<(mg8to)Z8y7oPS{{c#G|4F}%j?eQE z5V2%tdG=T`CNp8syE{jCbqU;s9+yLw;=n||CXTMhWa6%?X1WZS~DC_}{h7DNOx&!^o~4+)!`)G1!Nd)(5(fs5XL z`SRVZ^wy&k?AY;EdH2c;bQVWH+isMk$tv2v*gWYT{;QeW!80ZA^yg*62f%m@pVcO% zdHCn^!;DX55HuMQ@lCm>C)6=+x2lhM+$JfGC~TraK_Y$&eeM6o&TNt2J&|95 z!m`1Bj~XcmDU}p8q(~nm`vMb@-N1JD6&hy)&#>Q6a<-AG2S8(zh>+p~5y5`**8KQB zKn^n^Y0`>|#q#PYyB0_ZD1GPC=%t0B;vR|vg7qrVCIwj^3zXGNmw&`LBEhdBEyQ|X zo#hL6ae{`{wwafJ^BdeMzc%N!BBVmKgSRn&bTxi`xP zr)Mkx&EUYp&4lDZ01DNNis{38ra`d`!tRR)lZ^#>`QX*Z{A)1v3VrM@S`lJ&Bmf*N zeJcr%K%8_J*?&&dd>9)Pk-28fosibq~7`Fd6hG$^DINLoI{-i2xB$r=SDPC9n_LsGv)29Qn z#S03lt?el%&_6`?(GKv)jegIEnvSW=FXcVO%(PO)Vz}W)3#44^Q+qFDk@A9iDkWfK`vf{>mdOAg9i_wa3|k-IB(uOKtW?Xj-0((-Fws)Dp|n1 zphy?D(&lF-9^VP>moJ^v&6|_6?yK=&QECYX@;|+S_BRN8TzP0zX^i$iVOO(&_lTTt z*oD$LrgtK;?>J!@(FhFHnl|m2^BxkQ7uQ@#@n2}+G`}Kix-06*yq-!_XDo3UDVE1M?OIuZW+>glil@Coh6ccnC2a>f`f3U^D>~&T z(A%Bhc6eTa&N^jA&$z$oSy1z!qK%8wZLOWUdxefS8?WIMJaq%QcKS?Bg9I^SQNMnqc*_c z5!3dZ-RCaEnfH!QoQ76m6WXzZ`?-^AXU%Da!Z6Wx$iW#B*hB2Y6z79&3!O*j3rS3! zg}Sj3k~rD&xRgzRc@m>UP|qyzV4={%LQo4m47}>HdGm13F>!R-?D=clyJBE^{Q*7G zQ{oDfn63-&ZTO*1w>~{fFMD*U+^ehiIsCV zj{&3Y|NIh=$|KCL2K}Fx;Bv~f=wo^{2&{!kaZ+jjQ}EsuQ@hac{gw-gUOB>wEWN|7 zO1!|nJ{pxylqbrHghNOwkahzOz6PF^G_#5TOme%Tlao~?e+{E%TJI7Ny9Q4dZ{UgVpf-}Xkhd&Y4;tEN3> z{{IdN+&gm|(x4xTb&lcHGp-FJ2OL_3BNzV$WgCTkwYgK++OyW$Mk&&bDn=h#XF0w`n3oUMr1< zP|+OE{`~-(zZ@}zme(8m)cyVP+T{&0#a!cp@YK9ns8CRDZP1MU|LH z9xOdzA*Xgy>K>l68t!RqN4g-RZ!HBxhZV^`f)R{rCo%Lt;011G3E&TG(zVH9W1SdL z6oQ`LtanF+2tELc82B6sS2u**QzYY{)(Qm^=}LdG342-i(mAEalbMZgl&|RH7y=V6 z5azhD; zbji47ppT6QHMsxP+S-!?o>zGPu;-kKa+D9m`72C+A#eJM>1p3(1X22W}N+dd%a zUW092oMt6mPF&JyKE4lwdr=1Stk>!-x_j^5NOrBOAuhe2u$n@w9r6B*NkR_!n;dB% zqlTQ*DbGLU zqXQPrDz^I7y0yqsx)~o|T}D?vxJbeD{$Mzv7hU698aU>DtHEnKm0#|1aJor)H)rSo zp7zZDJ^);pL*y20r#XGzB+AU@!kO$jB>FzZj7F-o=d&ka{>D!4O0Xs^@2Q4x(4o-mvbveHNTOGwRI!QUf zwCl8>XhIH}A5km$D7L_$4o0Oda4DV zx=;=-r5el5Nzx-bq4c?@zm=yR|52U&NGw&L!c6@mXU;rwzlbzDG06&hwERT_5eS$6 z$SEHVyh2H=mFsl>5le$jENzuN`V`;wt@=5JaI?PfP@eGh-8`i1)kLUxGtWogiDEnf zwJkYY*hzb>{xJ9Sy_0R6JRk2VwINSYA-{qT=>DIY9BeJ=+JIVQN23>`J z@WEEScXwtYozBh6Q=-S_6vl-ETj==IJP4T3y}ODWoWdxda(Zl2p4E&_t((G0vZqxA z)(O5w2eGH6)D$$ci6dLB+k$S;60HLQ<}>$0b2w>ChIZuD!x(0m?8QzdFz}H1INInT z_Dzr3dt)wsh6Kiyy)A3`mCuxsp0WphAn8`a&;oik^J2@k2A_jPYfoUGx$mxBym9egB2B)d5}C(_9(1 zRuye5<%{#-Z9S_DgjT4SQ2b2`4)huPy)e$X6uCebdNLT!YOaxR20EOpWq<2Kma_IQ z>zaSg)U+?KdH!P&=;2wcM9g?e9Ntrhx6dy(BzbYz+O=2jmtH;m`cBeOm?r<qzCYa{dn zN)}1hW*m;?ElFuBJ_Xk`>M@EJi7Mw#bqy~g(0wm}42F)d-3X#Cp>d4ELj zL}RNz5FV(gfZNjZINCIp+gE*w=l917MeX{djfYKDNJF^Km!y-)M-dfEk^s92rotf8 z{Ct=g(cvRIqom83Fj}GAImjM319rG^qhc@&VSng^zMq@ndU5JCtt!)pPvjY{7TzUd zYktz^_aEOr-De#QAq zA@(mSgZ|^hAvsoEQpl$I**Exk4oK!Xx<`4Ufv-*m!%#~-pf6)v`?px)t1gX{oXPy` z{CnKxhw5J+k0H>zQ59ur(Uvw0OCx`k)hJu2WZ_odl;Q7-PnMNhQ;_^eB>-rW+%8%f z8mc~xHq3~r8bSR$I};EOHm-&clh2@KkGP9IwtIsX{as+JJ6(;0?1X@dX8l-NS(*9< zV|IYI+QeHG|4PKM;WK>N+B6$_DWI9z_@+9k4=z?xwZ&K}MW#eri`hc;x~{;3BiX3- zye8UGPL`CVY2>D8FVj1B<*kNw^65otHf2fs-$vX`EPXedD+G5giKX*G59C@(dl`6JAY1 zSLCP$n$Vjians3rib1YmH~#hL0pLPE5$m+yupHnN_DL*(8-RP@O`^hh7Di8;uKvPnf5W>Udvtl+Fe{5c;J5&Lkw#dpi4s}5YB*D{Nw0(o~T9(*e zOseF&EzYC8mD9dBMdx>u#48T@HCYl*)|Jfi^vJM?$XFrC2z;Gt#_HA@o2e9i_wEE6 zn~3@UBGW&WM=Vc@bgN<2*knVHr<(qOMt^ixjP0?tOY4)G%37_rD(`w3qcp*)?Tl6Z zR^K0d;L5}oeR_>jUw0@;r9<|~YWa=|#n7&?+6skY(^s#7j+XsZ6^exk?gRNob@g}r zx9Yfy!)qnJQYc)eZK?RK-h^7(nrEvi6c$BK8}I`$mETr>m%P!zK%qF%B(LJT9;w5s ze(mZ-et2lF%5QJsZyezV7LKU=ZocEsU+Y=**D8N_{es>Hr<$b`1)6&I>dYQP?snK+ z_3Gobd0xvd&3wJe z@PTtwNI*b>bu%-^6iZ?-bC!XHLswg^z5;L$M4ylf0VYc zkMrwGyyBng?>1bV9UAd?iQR>)^sj3}yy)+a=p7tqYjE^|b(mjCNs067q9Wd}VuSj- zL0`3cd>rt(*QGgf4enj#yQ;Sx-TE)k{-Cu4&^?&}P z?(Dvc-!=W~)%wfJ{=7XU@v9!4tNgP+FTIjn@V{TsC+hk0;-0Tp`s;n<#bW+`ch*ew z*spT<|HT`7HvLVjUXAmME`FYvyD^&t@#GzHpA@y9PgyPh=#b5OX?Ef$bvm6*UXD-udQD1w3_h-4K74=9pzP!T04h~$iAK#7V3B}h9as&-4Ac|E|y5uNIuJ_u6yKIp!E+uGmRbpURJix&4OZUwP{Ib3z(zwo_ zH;ljZ+3ap`_pkIv3bG;1NiUMs8h;m1R9?G&+1~Z%U0g@+YswroW4*tkW6Vj+;n)4O zp(8mRtXIoIEuDG!LQ2o$I`C&N#oU&0;a?2g(yh$oUv5|LW-liHIDMLeyTG4Kn{JCP z{A0=gdkNOz|NgoNUa@7UJv-V}QI~$fH}BP6%Ds0nF)r#>sS<71d( zX@5sYM^BUE;GPM)GiM&MJAFI6e!~VmOAfq~K}`9Yh6Q6{E8gpI`?liyz&!!?@5k4h z<(-T>6;YGb?4oVz{3s@dYxnLGixw@)tRdm%cGG*kt(})DO>Ng7e;kqv;#Hj<@2`$m z+@5*yv(oI;7%fvmaB0UBiME4H9$sE_A2!jF%Gk>IiPLgn*GOEik7O@|W zsjRGAw|e!%_V+=eKH85@UtPX@`REsSj-LLk=9(1Uypqf!lZAbv{)dIT%t6<=XOslR zXj+i(|HbzwnW3#|~*17Z-9h>M{ zp7~Q*w5X`4uE05O2@{J7Pbb-s&n@p{zuKOG6>4f~61m*+T-A5Q%9Z{UL9+%8{KRL_ z+1XiDON*OIt~E)GW|lvuR@@sbVBAXoD8u0#qnGa>thb8XuHdm&X5aE`3xlzqYPrFJ z=>fs4>I`FLYUNN{F`KXMR3_z8T?SR^=E|Io0`mIj(Q76Wl;cb)W2o}J0sj8+7s|r5 z@*Og(+TOi;*HfDsaq)n*_text-rza@2P#TRN^N#uw3Hv6sECVjNxXjjS2j_b6NZL{ zA`YYR5;NaknPjeAw=U*mn5gOZ;dbubyXn4?bMcv`wIbooS>_S4vaWi!LY|Z!RWGgf~PlV;#^m9|ET0Qx!%ETwS>dgzQb1XaQSTt-k&D|SLhKW>{u6|W* zme+rPlS0uM680==oy^tEvzh9lrE<^Ab%dDLY%zcKWpwK5V#Xfqj=HCpLJpWUbhW-E zQEkWVt7Tzcofv>0_h#f4Sa!;Lvk1ycNJx~Fl=x8$FI=EKeyou1Fy=?meP*YaS}>J} zP>2Z^cjV^hSE8w>23DH{j5c2y!mjx8<%?FXm2pmidWud0ZnF2=msfrm|SZQXj< zd3szmLc&?V;LXbQ>({I1h;LGEd*dB&_wK{U$Ukh|$tp)){TBaPcgi~N*=%?{<0^JK z9yUGw!l|*YidT2bUB{}`O=?rZ{=9aLb2fL+RC<|2)f2<_qh+ph%AOncR5EfnMb_%O zHYW7*70xJexJ;hO(Y}_fU#J&y;8LY&YW}0vKS0v?lLyTeQ&SzrdfJaS6yibOxOr374?E5u?b8pnO2vPQlZ{Q zA!e1i*1alPT8U-==HR;EdSv@g4^vA%7SecT2XQPFB5Vh3|=m!Ms6WCze zI+=|~dvr47k#W@{-L(9?!GgSoUS8LW<&lS31dQ&qO-DvXE+5?K5<9M7jWV>Dk=g(A z#hR8E*Vuf`KR-DZE#q4=(3rwWBAg#> zG0Q&eI{Au&HMO*~RMpBV!ypMMO3-QYa@=%&y>9m9!#rw93MM9rvT}0OS?2oU^D`qR z4cQi#@|noxvn=)1v5K((ojX^i^D;(mBl6Ebe{r7t;_firpJnPKEOq3_pX2@YabLfli+%JcF*5QHk9tbd_hlp> zeA`4BFuFrZ*6P(;(?9R-uB{%IVYh$XeZRH!EH;g4iT|$UD_5!@9Pur2$g@W)Q)xq} zrzj&;>yX?#N<&OvUSC%26B`qA6dB60;{yraUw-*T;QWXCSF+|w1iJAIy-gnew(=U| zsv57&`|+Di&QslS!D9ALK86an*-1!B>Z{SJyPrDl(SAzru8QNN?mQLlUu{yIoYmB0 zS3teDquQtK>$`j0JUmr*wn~Mvo6~Q(x{!6^mg#U?(i`qLa2Jm@kWW7a4~U10>+LQJ z^O~dwKoC}apY}CcRh{&UAyprrk2-p(PQ}k^X3~NLhfivM-|dMTD>i;@Ym1JGisE^5 zW^m{B?NWd+DUyohE0|cc+bbfE9Xn|!Vgdc#W-q z(X}N^N=UlvHf*pd>TGLkvu?Dm-GzdHETEia)+k`pr%^nD9joBtQZO|!n2Jo^lUB@b zDTWtEBfgF2X1=okkNo-PpWI#sP0#Fs99j6!-6Gcw=#kUMO7gM5MZKpEP9vA^y`xYY zMEd!e`kjr9I+fQVg9eK`K3%eMQzX)Po+vB1FzZ|EZOPvZE0Yedi%U;^8+FDYbfLsW zIpnv>Z&OdZIoc!;W0|u-PfzdJM!e9CF|oEbd&{!gGeSDT!on;EF5X9ui$o~ex-TG| zS9ktuLB+Vdp)Wa{&D(r(qG^HuLda3Qz10vOVmL&@~O7l zEEbxk=g)t926`p}*$?SH(MskIDV05WvKlpCML|JPVTFYVYs zK0@%7hp5<1gtGrGW&Z~cG~;HQT<2$HPn@9GlaE%|o|K(;D}Qeokh~=&{?_M?D@&CWh(-SGG%4&eY^9Wd1obNt3NNt)_TbDu9nROe4LX}8-X_CCM5n6wQ>M)4%{fg@&v>+;lca&qK5 zwH2K*je%7>yuGX6-rcUNwf}mW%^QvVHD1==Z4y1xcvGLgVG&FRaw0uIT3XuFWSg(5 zj!xgeot9rH1H5O`G$s%KM&AxxrF8!M`5gW8-@ktcx=PG+NmirHHP27GE?=>tilk-a zO`2(KN@k6|wd#iE+iQ0UoO}Cw(D(y@Ms+>=WBBag1DAr4)#55Em4Y(MH1szc^ow{| z+k0g?3cOB}E}a>E&nJ88RPBcc`>Rv+CB>)v(q*YsMRZGfQc_IlsJ|UY=>QO>1M7#e zpQ2JL&^G9q&aw+%Ia=(?QCt2{YAmFAMtYA{h5~SmPMv${dD@UEk7sIp{BieH95%=9 zGXrhof|V~{ehiGT!`0OlfmLeQzrRNPjYb}`fiAm&uBDftPXEgrE9%k=+|jzxTxO@b zyW=8iP~NPjMvZOVZ<_pJ!Ps&a{lOAuR<88VeWR0sd$a{kKWB(5w&|~nUA8@8_)mr&R0N#Ai*Q9o%&_WK8tboDKXc|x@cs)v!0~Y{EvLUvO_?<2TCbn8 z7zZjz&@Xgx=z8c*Kw^63!M^(aLgr0Lk00+Pkn2++W&OsDrX3$1^fcyWZQH&*)1a7H zh9jV8BOBWuTD4Y6ip;Uu&j!=L-U4>tP6PRwpx{h>?~GteJ&84*ooF7Mlt4eZdGls< zCv!@7S4A|MVP9^ixhr0R2BjoI#!`FQG+t%mX^*R{Yrzh0Uo{btF9=3YFCyR@RM-JUpPi3rU)ey3M zu%KxmNmkV~b)KzTUsT-G#cKg8$8fbJ_4V4zmMs&p`xXh5Ta8|U;1$$Wx@DPoTxYnW zJb?jN*5u9m&&Qt(IUs*&wo`ImQC8MNElJ}c0jv3r6DdlpNQ!m2*0hTU>Q$%pap@n! z#c6036%K@yc8e%VN*2=IWVy^bFmXue=sS-ED1TT#cA?qt0idx^lbsZhYOsWJuAW0T zC;5Ifk|pooE62%)v0ef$r6wdKtoLaqS3%n4_PK+FEN_C-&L?d)eN#7SP}(eG{oW)~ zgm*&s$+_DD1=D87jvnO_6VnlDw7Qq~>Xjjy{g$*J*x-vs+UF;Rp6vEt?_Mds;qFdD zuR(5pW_C6pD5!IE1{-D!kB#(s!((HrRO;;y=hN}rqwn1L{kLC#HTrOWA2v-8g^68E z!@S`8BN9uB(b56hvsfT6CuisNn>Qy&xihN@`yejg*4C;Y2uXf9CnqbLVcn~S1w489 z@L`jtXE{4g+;5@F8xHBFJQ)dyd3SFoiv4kHB{aEhtUO1t@BL!X?i8ot^*+U^U+>DW zSK#7OS*qGMnZB7KVRNZiV~cB&q7c9V>--Bl4GY`VYITZiA2v>ul$KgMi6atA@9&c` zc;mIu*5W&QvDxK|Q#I42w;Z^r(D^Z}eQ+=p+a<13-@bjl_|WUMK9V}};bI9-o;Clhx2AU&wp&EwMzGqtRHle0wF z&Czz}Omvyx=D0hztXb1)<$^erFV>rq`75y}~*U>N$ zYgw9FvJ%1)U_Ta}cTnB|a{vdD14XQRVt93P<2BRIV}~e7OWy>jT% z#agIwGMf+T3n}U*yE<+SZY%cfeY3^f%E@`pkZ!W%v^zy|ekL8C1*QVQg0E<|OCIeN zztHcxX~Vj8@qi)f{pmaKT-EkYE&lj&c*dOSYmZr+? zR_uvXg{1_&qB@X+^o;(TO~Oe7OpaETnQo4O!+2jpT)2a5nZ$IG536vLn;XN}K%+Lg zhZ-z17?Z@o!g)QikC-_mk_u=0%|$Fbys)K7n@XC2%1yL2?v2$`tClZU!c!)T1^TO} zn8TG`mubqDn!LMU$H`zYNXs#k!cyR>jU`Bd<<530LQ`lu^sfLyzy_YfBQ2d2T>e;F-`NaNLKDb|Kte8jMOddZ*M+Z*HyLPL|6E zK_ucho`j&^8B+RatJKZ-QxOuuLgwKVf(ejG2|^($vU_r<&Ed<9jojX8i58z!>4lU% z8jlZQ2lX@;7Cw>>ODtx0{4k_rICSIsbt5#ysNNP1Se<(@qJz(rfln*%?=zsso#OCj z7q1)Av9rtO-@l)&`NH}0iRR4(>%;4@Zb^X1x??juFOjOpEe(?9w{ReSYlG6Ht5HW1 zQ9QJ=FF&lTszR{xGqDJCCFUJJefdE4Q9#^_s4|weo3fYNdat%z9KL(!&bZ3&u7LCP z*H%dwiJ}?r*PovX+QZLZX@i(u?i+!d?7w$P!ooT}TiPqL^K_Cpqk*nun1pkk%|L^& zt^1;lZ7jAMAKqWWKtF-F7jT)e+oSp9D4Hu$-<+JBxW!zW3!D-9CT!A6)z6Uk_X}g6 zrPZ<@dew?v@8SUSFYznJzB$2f^w^S?(yHGd*0HbZt`1(%&V^#kSN3A=!0Q^rVGry z!WQIa^4sUn|HiayaAQ9}Yvh5yLG`*g%hf-@uF2y6fsYp`0{W7F!_&M+EEd4sp-ZQW zet!A(e|?|s^&f~E7cMx!px_CLAX(gTJX>^S7E)gCDDV*txB`rI_4Ap0{tnC87pyiN z?d`E3b4XlU+j7M(>PWv(c18^~`D~g(B=aHR@VON=U`I}*7S847y*ebg&o$NSyN>H# z_b_bo(g?}s#xs#O@0W#(yK7o7x{0*B7n&*Q|k{ZUr32 zFEVY~`@HdC3yXb^)A*?Hhkvaw=?MPMJ2u62D5nZ8^aONXwZFQAzgOgSYf6_UySd4fGScBqb$n-Ie(O+ZmQ9BE?y>z1fA5IG8^XPA*BP*{Q1AH~rZ&F(M=6 zUjA|P*PkhlYYK5@m_)A z{3Z)#+wRI^p!t*>9C8P;+n7m3MunE2lAb;H+Wz8%uj`DI0K^0KYqjReczv&fzMS1e$p9Be`eDu;J8F!Z z$Jh#!bx4e+sG|I~_x94(^Qgtg#C-KA{LLh?|<@*Jixz2caAK2xN+*_xdohKVLez90uW!+h(a=YODrX4nm#B|V&?qM}2a zw@}IK6KC*&oK#WU0jtF1eXes(3rO+Gl_#LrtpK5}M4W?)hY~~R2ZUFxAE%tjY-o$U zmxQmJoZQ$%;k>S|__#)?{O?TRt2@6Tpb|iWn6^B>O6Zap>b;cNqZN^xE!aET+l@y$ zNGZUSWAplUhi$Xbi_3Bl^ zCLmLX`Gg#_hytxZe_|*HzD`ZlrZ2u!`6}c7VX?XQ`g7ax00|_jmf*8vjxPNIA{#t; zLFG-REu=9iDlXQq`u<}7rc3O6;16A=sv_vm>_<#R_)bBrU|3QGed6T(y@%1`W;NP` zfJCyWZu8}kXrn$I zD?rLOI9__+>y~|92i)`X^Q)g;()Q+%)T@7bsjX8e@io_o9ptE~p<)j3a^FlPcda^x z%(pilz+txglxroncB=G5#&a>&iV&d9f5&&+s2EjWDB-Vx4aBd9$L+nrNt=`na6o3-)CCiza zZH6{SKipTxDsj2RO=51+AWYI#k94Q#dPd8oR#m@z5WRI@otXTI6LdmclQ&|i9FVd& zsT|IutErCb)5zmm{rm5Kcoszg-O2!~5`_NpO@)J5Mj$l=Rbyak7_6=+*z|`P0Cd6+qcJ=*hDh4_6?;oRTqg#J735UZU9}EKk`vj zRf7GFmyj=?E%L%hdQLEk?ekZ<-TV>gpN*kjn%!Cwzqw;yk7&nUmR~w7`nRG=t}5Qy zWVG~tas1%@SLE+Zca)F52;O~~i*l!)t^CUiu!*U@qWt&0zKYHJ+$->RqkG~osp!z) zc?Ni2?mR{Q{Xek5t#{{gTl*ITK8EZKI&I7h)^4lD6(aVzE_~XZy%FMT@gZPExD*&8 zSzU7GdLVUHiBB}N4eV7o`JtJ|K%$o8!7(n$ zjz~SnbCtFb5m*O6#P@eyZ?*ci=?5B6;7I%NafnDPmj;2irD;%3k!4E;RUga2%czZl#&Zl*B{GY2%HGTP-dpzin<$Uck>oA zC`g7Lv^cVMr#u}5kV$3)!W#7x&{wNWY{ct*U4G!I8tm;yC`V;O&c_Cifi~7d^Lpvi zS$IsUXQOUKwpHjQZ_`Y15J?V zz`m*~D&9rD@xOB?5@csLN&Mhr*R5F-C2ZLd?>I3~ZQ_WvBoa0{C&9C?m!1q3p!%%? z{gg1HQ+WBypX4s-bQOFgX<8iSzIToRgm8<9XhGwAf-km0iUl#nEg_)?(3AXlo-{vT zEvr53;oI==@u>l0+1}Qs99s+Ole`hKl~#dM4%&$fz^(qKd~Ndd2*<&}k&0?4;*%L+ zi6;Ok6%Lpq!vTJ+z3z>)~{QqGJ7)d=JV%=GB4GuL9J53pPYjTVTCNDDxt)8nH?KEk3lc0#@A+?FJ;Lz zYt+K31dR5|eoN9!r$3ZpOxDZS0+ppz=;9nu{}J$l$c>P+6LGhsscp+aUNuGgN7U|6 zy?a<{aL?Sr!kXS&guwEqXIyEQW!lVt)QN=eT2cM1j=sAz6GHp3<;@-|v zHE7^8+H2Va4xo(2)y zfh5x7Bo+tx`@g+-z!YN4@@32JQoJ|sm&We3q72~f)KFU}#*p%YMQzP8&n9}{0TDu} z*>yfYkXJVmTkw>Hg@wtMxj} z&CEDauY}TOO{d$c4V(GR=L`QyCJ;2q<0lm0$B#95h8dt*A@724%T}BECGq9iG~!2r z+KN9UX$OXevXaNaCSs)ku$lL2k;(1@2h_T|yR|?EzI=u3>r;$^))2pgW0Fi9-J zJr#hA67ehq5=k3jqPBsBg@uG?uO4R-YKR17+(Wh?;+-^mBFTT~5Bw1AEGgYK-Yl~d zGM?fOgIrFY1Cx`u{vKGUs~~w;o9DlmxvHHz7gMzlWMw{}rPa3mGl#WUW}V9+@W9vU#?HHSpM|^p8)E^~M!APs7ziEt8yo_Tjwcjp75QH7Ah7u$vV? z165g$zn%novI(7kagDJ$QI%#=OqmTntv-5KMo%vsbWz5IGIneudDrjn^?RI5ZP8Is zW*vGDPP?y%MF?o>yQ+Teqmx@?ZhWV$j}rw}qa?tU!0R>D)90+3)xpqWvMx^QU!{## z%WC!X?87rUK4W6Ev1kgiqvo=Q2lKY3$!E3FJD?pRp!k(1<%HX=Y zdvAz&fg(tGB7bO`5oE@g4?OX28TFY!gd zt3!bcAwHf9G}u()KAovW8mfaek0HUz(%$LUnZc67T0LwU6*aL9mkq6m& z_Ip%4d>DB!%n|V2*Mf-32w>Og`ey&Yz(?@(97bmgW${^lKHJ0#mzkkrabDw84<22u z;g>7;NJEjoCCb7ZJHmKoa)_h~ki!CSZUj;Kaq-_Sl4nEQ4WNE4lQSC8&H0}CBxX^R ze}ME03@teXt<7a8yArxBd2&x$QSu{Hyy8MaXg&Weo_hh;5^rkWt)jDMtkbSOX?6{$ zwqdxu%Cl%2oPa>2Ff*xW|GOmYX1Zp}7Ge|h@>*%@-g@_MRt&K4kochmI;*V6BhT~x ziAOw5P0PnPv>Whs4B2b{6E6PHiU-gI(l7KiNJ$;G5SjQ733!ZQftM3g-)_rzTHuFO z5i$!*dTyXOOkyMcqZs=?%l|xcS_L8xaops+B463^@OT5?nM^IgABC#MK_5FEIaCRy z>Zy5i%Inv!S#}N<`>-j4z0hUdO5XE)DS6LGTUD=Nde=E~_J49!Cb7-12g)P2R708poUyE~ zXHvT`v!9=zfB|mU%l7Tt2k%*eh)rLY$DKuyY8tyYU|YOB$BzJd*Z75@8cJC7nO8T7 zjI%EHSpJ4BTL|$*2v)@R0CREMooxMWXr%p6(BojEtTEAtxJ?9$M+iB>TOqGkzMh3p z46R5V@MwInS)VraLetndf!I%wZF`_5SCLwS+v_F#a{8veci-EWIa|YwVgweLxLWCn zsyw94M>bWua)m$0l&s;BW34W?fsu|Yg{4T;O+)J0GZ9*Pz6!>RK@H7$V(;9|d+>c} zX>~=kj368;cq~*u7($~DT&l|!e$w-Ox_LeYi_;5_5rix>sj2zn!4j}kiNoq>=EKa8 z@&V8_edSo-?9(TQoP+M%X&Lxkj79EZ(BuC|VUVQZ#1ze+Z1r@2J~aw0yVqN&=?`Pu zXw+l^(NN>G($3xhyP0CwfT~LHEp9%M5c$~GkZdWlxw2;b4@TRA^i96Lz1vew3jzzh zkW_nDtXMJDU|H6UFR6+=e2u(Djprk}?H=%r)%Xc<`~g{*fC+$)!N*{3$k!DTF8dXy z!T$c_Z{IG@wsFkItLc+v>tW^@Sn}lDyETUA&UF!&=eTLA&FuaZ_!TNp1BhqK8K9X( z_(}}i8N9$O#rsA&%WCg#m+=n`O=_O$5F((OIECS=6!H0-VSEpmdn*SA2azmY=SCvN zK8g+=_ajLhrXxTWY9&kywDoQ9a%R{L>Ve@o0h*v1QVPUmBFht%nplqUpsZ_kZH4nw zpm~BUz4uy-f18>yuKKx0-U~Z>`yA0~7r3FN81KV#tp>T&M1#lykwn%cT(% z9Gr+^p_(V|=r;#&Azbiu=ZNYV{bQ2oEr6%{>}JqE5fF{?OpK>g$k$51Wo*S?J3D!R zHUJ5oGuxyam%)byzzF8AH;LIjUs~$)>C<7Oj4Ggd6Y`b7efqW&k*b_ZARkIqI|YErnzhU%N=)JPzrZ%BIi@(7!_ zg9=hK?TQx~T#s9~%1OM!hX+2cyLo;ti-1N%sN9rb4$OnG;{%Poqji8zi3nCzSE-f_ z2DrB;c@z*J}Y_RiL74+~hq5NQr@ zMyzLHNkpG))b}Y(zLq|QWQ!)>(#bsiGYK!WW9l?MdOjLz*(vMrbB2b(YOhSeTi+!e z6cfW*BK;F{(}T6J9CL*(%X*@*8;T>SAn@RRXipM3 zckYxS?l*U4Hl&umfy-_zp5ET7s1t#^HKUNWY~9-|G%U;+j@K!w!skOQI%A;nHQ^mH8s9lU^PV4E#2awG7+TcAixk?mN9_H<^5_6e2V(&Kcpb0 zjXVQ~IJ4)u@r3@(5_Y`ds_du1LfONc=`v`u(ORKILiUQ^2TE|i&A`Klx4gXraQ%?Z z^Q4B3{~Kq-3t&M$%&h{tjmn>U%*cN<-2O}IgWAT8SI{r~#a+GmDE?pERS?h|dE0X% zh=JTMHW2K{FmRNBEPODT?FsmS_Z%K>Jq=#Xpp%_txkpvb#=jwtMXuaWLGiQzoNxiB zJ-3ke{>$`6?hvmS+x!>hlxDeVfpYTv>q?^!Hgf({H}|rr;pLBuh<(*fbfL-mF+y_) zuW+C8iqc!4Maw?r{Mf35?I$n2su()3=BFsPiHWRqpLT4;Qf7Stg_#YzM*1Er-B$y{iu? zv%{5;XB2-u|6tgtX(jvGwb5W6xcBW-Bua&RghaA=;cQK*qT0#-MXpUMDr_XL``P6z zU%Ip$R7gDNFF5d}FcP5doD9wIr&X}m0xk(+OVZTTBoiWQ)~=;CeNjWKA<(dgfe)_O z9z2x!_3Kx{8}^hxTztb!F}3K;8*U&!xxn3u(B$)84IP}xWL9~FEw}FX-y>t?g3utz z`4RO7y+>K;UDcE5|G}cIV`WVu{;uh1Q{1=%9K?-m!Nt3F?}iCF5dVi>pS%im9TL+i zV3c+rp^(y&4nRxlrva0IHge&C?Bgv|@l$2~qN`{20K7GQ6sA_1$BZO!#4S>o7Olx)%UK~RkYLFLeQuXzX>j966(5C`d zJu!y858|r@YsP)avb!>t@T}m_qTvNH9c;>%@87XwMtM_7AeTK3j+pD#M8BG z>qyxqm+$Qx!L<`;@5jnB1R~Ne2J2pB{7`-Ug2AV?Wl^3u7% zyB`wm@c4KQ3W*gg>H0(W1x;&=kjXJCla3mOc>}fBr?9EB>NEcKoA9N8)c8_t?k*HH zOrtceuXkz>P#=B0_S8Yn9>371-yQAkh0PS-%+Pd+$qwv;ezygpv8O7|7qk^v@7KKN#_W&VcfjX^0 z(#TJJ`1mm}X1V&wGrP&46&$j{_km;T{c)h=?H0vUVGF zFxzmvs-gN}Qp+#;Pg~f4Z(LgPFDfWh*bpr^W`a8tS(snm5?{3eOMDPz(#lv z6?kh10Rw4YeB{G^2apkfEL!an#DM^_&~aGw6g4#NL$u(e20RruZxRC--vtUD7>`eV ztIO+Ks~uXdG71=1Qo_U?)sXV#Cl-|!I@btw=jg2S%k^b7CL@mKd)cY+5- zRwurDlIiQ1lD1y5Fh%ga@#RS6lnXS1z6PHXGu4p|gnfcVC9|E#e^4SJ&YeyFDVvq_ ze>>XP!ZPvR&e4(NU>{Sjt2fVs%%2$|wU@sAVgRF?gIh9@9>YDILwL{fKVWrji8^!U zzo7u}>O2m}xcX#coSeM8jV%{L%iV>-9_IL8nixYQ`vN*~>-?dK)m~2fZ(INYF!!b> zj2bO^WSjmI#$ixcwSZ8bTmI1JOtU|=l2Pj)UVgKC(9|Z&M=Dbl#m6TGD(CfFXU9x3 zEjm7I2D6iKre+;wC1&CH_{F`C#L z!;$ClaSvn?s5_*YgrSYp7}7u(U&H`Iu=D`%Px)ogZvTJ9ELAfhAt7~`FNw(+Ap(6O z77Q&-#+Q9z2+#-=ED^=f-4SOCm}fosRS6{oyEEwz@Zw%%Rfv7+7)XjqT;Ea;UREtt zFB$y|iD9^1{9=~Z$8&{qxXvizn;gEk2L*tAKtAR|(FG@p+}Ewzd<~rq4>lT-p1-f} zsm!LV$w|9C`UN_`FoZ-U`4alS9P%>xHb{zW#dT#5qb%iM>qZ;c-?jaRt((A%G5EY` z=s!`Na!qvpPf{(>N@6@Rdsj>ziCpn0@CV<3Zw z?8%ou>en`eu~xVrPC@G?Yl{w44YQY^H%h>fLn^ERP8q|TL-&IV;dd!A{Q93`<>8?u zCWkd^=OrC&**93{&(F`lh`%0r>v$BKq-OlNv(A*dQQI|tc>&~kIXB3KuzcReZD?{^ zyFGAQ!kJquTBLc~6?8V1b-2D;_lL zgo!v2mziv0(OBr^4XXfOcSKjjgKEN4HYVeah_dwnjoI1Ry?U1Uov1FUC`{m&H9w-w zCT_3=I|?y_f{affBUZI}7TyRsjFw_Y!~#lj@9Y*0 zLpg(;ogEi`391q>A?2PhNXJC$IRMp+OZ5kc(nQEk3aN1mFbxQ*7ef^AAt>3{j58ID zVn(p!#f!s8`n5YFoVHP?5Kp*ISk89jgL1K|zm#PR#)Q=G z%#Mqg6kECE8W!vkY(q3mx2+G?R}|xPe6#apFhyNmovMVX6%+?$V^&^X-e8zWTxQ4A z_vk&-K#!pUklmgcsM53eD{zg5#5enaoX)RZU1%fza7M{ufx(@Xjjg&6Qyi$+C8@`# zIWE*bPJMmdH?E^yisS~#Hl@*MkQRBFSPv?oenjGeyriSi`_gkPFTisDzN$)DUS3|+ z=b;GF3z_Qe>M|lKj&!slmOn8d2MO*ZWkB%y8m{_dx3~0MgVTsCD|Rs<4aLO8t5J)! z9uO@ROHF!f3h_{?%GwY+G=>HNH4{Fh5fd2r7t$PshK52>wvtK(*V1xX z%DX*<_eNHFI*%>qG*Bh!7RE3H`yTHFpdy1KoK#f#%(<=p_wL<;h9L+pjHvhf_T7EW zW<3DNMrPnJOME0+TfhjZ89fw%C`irb{g+wT;0-ZiAhHmdd7j5n1M2`dE{u#L_Rz2# z4fuEo^*DxyCq+H=mk2-#ApII-lIj#)(f-CfbuxYHUGxd|q0gT`??Uqb`0=Bm&mew- zErb4aVFU@!Ma9tv8478_7$hj6tj`Ism~(~u9i75NPoQ`|SqNyY6kY1$_tTMkVecX- z3#o}vHXwv+zK8ewncM);sx9HP_rVDhJg^(J3yuLWX#o0qc3;3=$HiUfU*Z_xmm{m^ zijh*5@$AKi7N6d`35GjMs$Y8gi1k`}a3$FCMyHWd%$HSxC9+0a!mZoc+PY0&U;mhl zOfNKpT&M3(3g+h=fvrh*LI#up&A_WEL=TCGiuQuFQ9p6Qi*&XqB(r!bQxJ-hCFdp= zY>t5M1cCu{YpNw^W6&9=Ent8oKr^SH4r7b{!NJ`42|RUO=0 zW|!$?z{6PVZLRT)S~dHbF@KdfdWVZcq*#O6hNs&gc|*TRdaAvQXFxE9TlaRwUd7g% zQ!OC*=<52;oKy$}0nyRX1lA$btJl5A69G{~MktAYeql)57HZP6{dKCu z7K;Y)>%c$?_&j2uHK~ZYF$alU&-NnL2`p9@nS+AWG72uA_i4j#xcI`?ENGDngI-BU zJYTgSzOE9ymCo0vCcNy>OU~R;~`opIZvYZz*3%^!7+>-l(5ai_@Rmcxu-ZL zV~24ej6@kA7p3oI=no#SMIB1We3htVvErVu zFFBq*01;l0c!^O1?WKkfIg^1*kdSh^dbJ`1-ebHv*@Q;v0gl;o z@39c%ai~$X=*+gLD!pNRthyg9g&*eLdw!1{W+;Ciij0Y!s*Y6iz<`;VnY7(#Yq&4! z1;Q~y9w%z0p%0nZ!t`{)x4;X}y`xmv8QglJRSItU`>VVhS3pR1pb1xluYq*zkMd?n zc|?lM|MqePY&r@LOf5lwp_|;L{K%hLhh`)ZB#I*VZwV^K3l51N0o}$#cU!bL!+zwv zob4cufSRpaw>rQnzT2_c7G80}xNP0}@Px4w8rKYroe>`{Vw2|i4b+#yG5?aqZcI?E z_`sw%HcqS5fByXL^Jh(P5Qb~Uk;0AX@rn*ZMH{O#c3nE9FwGSpWALic_%si3Xu%SI z@f8a`TOKk~aPj$eU$H2($-DOMef%K}y3Z^U#-4i_WCNfWmSZugb@HXu-m0y*vV`K zQ1)I-M7Vh$#8?c0@#x06G4a|k0?C%RqR4a|7!Wzke4q*6a=2Ab3e3)~!gNJ~i%d+o znG6+D;UDV3PN_grQ0>qFnkU0HZ$Exap-%v>5DJTQ%_x!aG=)WxiVv@>db$k|7du5( zUjC^Km-m{+TMUtl$k<9pSwt!pgumD|}_O)H6tgu*G?SeTCkL5O9F82CZZ zU!KQYxSPYR4l9Q8Lz}&&v8(%X4Ts+ro>^rRL{OrVLA(+2&yZ6})yvPYXp<&>sSmQVNYv1P z@AvK)BbT=Y?D(O@kKw3-kL%U&r+4qbbDgXNUt;}g+4LA@uT2ITa)?8$-4VPNEbLV# z7nudj$auMeb5eHg@4;1{d4th1E=3n}`g9~E0jeaKWF5s3617H^yt9SQ!_dtGUYsUL zmCVr8c9EI;c=iFAO?PB0dSins!x zU0B~#vKKfY5O*y(QaH?7@rR${khnAw95%5WG`AD6j2DlfrOlKwqH9>BZWV}2*A*ZH z6vJr%Nc;t6zyL^+A}AQZ&xNGcDlN4c@6}Zqc%gRv#*KFv4M96p!yK#l(;N}W9xY77 z2n9JKc}DHSmLk1pf9^)bAN@M9PLt1#FFxL2y0oJ?UHzChqoBY{fRa3-1VS2yx zJc1iX>=4R($Grr#4m5Cg-QC@ZYX>!L^Gcz)%_UHgzxMQKU8`95yq*<#P(06?GAioh z?L;62VP`F3Ug$ELaqgX;3Fjb!oH!Rr0)qI~BRT*5)E?=j^e_-l$vQbo#MLwhWxu4N zLXkW=c*;!-(EyDbLsnln=*Zk$|4gb!k4iOV=gwXb1Ik!A42(pB#sVf!-*IFKONL)W zT5gI`dd)Me+^=d4sb>XP$V?$9Tpj>`@bGXnD?sS~2;9E~W0w-Fh;YT+zkc%T8&6b*7~T$ua2gik^r~~vlL1vQ32bhAF~e>YT06$_!t=(3Euz+f5pmbx8+9=77tIWZK4~2gAXKDPnaKiU^?)pw8OE>%`MyPG~%tJtILyY zm7|6Rp9qyW>V|YWa8p%4uS`HKt#}eV2X`ZA5c(DG7pLz4Wfx4gt{`Jo4I*pq2ml!6 zk5C538P=1Wpn#lD=oL&>lfea*UnIeM5-}R&3>hFBtuswE{y`P` zw1=Wr5Tp5%tgKp`R6;s(GFIY899tl_-Wv(X`o;FdCX$jw%sSvl@N9_v23;+&Ok+e6 zMNppjkx=twbumMvYG%k(u0hCgL}KE-9FR+KJHzdwXVUP(i9oOnno=_HL24^>useBqd6e#)&hRQ6CZ)!0;R^Bz zPiO~~;6LIhzzH?*coC;J$NZ#w`y^oS7(z;Y*8%((c&0bZwkb{JeFNsWScJ?%iMJcB zxL&|3zZlRHIrL1FsPvB!lBw8H+y~3M0vcX$LcT+UWjZhhArKQ>=jX`0T3DEF@`uGL z*Gt4dd0@K1)V(~O+}P^XtKqu~Eco8B#TlnBEvQwYZ)>f0&b*-@X<_hB0ZI)~wlzgV zr5|GWm0SnF-a2;n6u=G>>``)B4Dd1vm$IzFeh+cy0X(ui!5&Eq`>EY&ogdncLpimjvrEG4sKSeZ|^3 zz$*6cIrN^1;(Am+7pNfbYuC@}=+e%up9Q-~Sb_Fw)ZG9)D^L`Mobw1PqC~WZ!T|ji zaF75-n+D{1qlPYSeP3XC(y<{&$R0mljv+7tl^~M6sCy{L&B<>cPRNf;2=IMfxD(>_#N1!SY8m1aKe3*CE?BUp7DED<&qEakgkV0)^zGy2odv z0@{FumBBEOs3w9g4%U#I9lb}0PC|-S27E054~r=0v%Ulh3o2IP+*yXl$4{ON0D3`k zH|SSzGZDqiI!ua0ATV|RWo8%l?eNf$BKBpbl(GNuhAm4)MMcrq69tkS`GyWf3Lenh z6YqG-?+A;E^1$mxA_CIM7@??u8h~LlLf69EeeFdgncjx^tgs1(5Sau-$e`7RVS${b zK@2?N=ni4l0Ia}wR><+?$6!#A3#25v$=zc&`|wmrOS0S7)6+8mMk@>sWNTTHn&@{< z{liT;Pd{yeYU{;!B}E5;W#CLI4qmzJdBkN*Uy9F;R*}(9Y>S=F*kWWl0>{-IUjPV+ zI3Q-rp-R0xE(05Af@anKqCpUmUkp*Xp*y4ZXG$a3!^wWcZS3v0h6(Hq#t`WRNUPuJXgR3|k%rj{Ge*A>gIae$Tj}iZqJ1a7YJW!V`JkN1OFQL-o51|a30{u0}RHH zBoAo=P|X;H#sR0@C9|=7i_3=uNe*zKlXG@3l}HYt1fW+;-iIL=^yrw*fu6_-6Aw>X zZg%$DSFfZNFIkDhnC`4x;<54!8#&+*r-jjDwrlBFhCqrLrbxqWnl3+5u}W zgVg~KMvw?OsVOi+&JPSJ))URpD6nhE#RIiY_Z$i#H z4<1h-0-|o_+6_hF`9fYY6vl`}?Dgx{x7Z82ImDssiDlo26ERRsD^N)_({{v~AxyBUAF5HY5yh=v-erF_Lf;0Cr`8O)8CSi>kzht%}(b%(*P1)cY zDXQ5V4Vc>q;?pOitnjzQ^!4>|dy9ib%*oA_^~32G+KHca?G5yDt*A&mRj4a;G=)?p zoTx;m;;AGd6O=@TZNRyZAqR?P$FA>F$gzGggcg)d`(;e^U0K%{8i!eQ05yg4v*vI# zcln48lmq9}pe0lek0=jsRRb0RmuxDiwsYrBQp`{xFu$vM_UtxzsUlxMwMX$LZceO% z>80OVv^d*yArv4267LBsiuawQiDJq9(cA%mLcI3r4No^~6d_i9a^?!C>R78RevnUr zsk*wlaseelLg!KI07Q_Z<9dB^OscsF(G$DIni^X&)8E&(n===uCRzoY{f#A;uqQx4 zQ}FqYk~w@lRZ{%grT9*rDiiUHJ(uE_vxvdy*71ha)-PWkVx}9gls+YU=r-K)RBoYt zZexnAYPKNSQy_l#zFH+o$8n~L9g|R|GabfEE;SeELTISMFNx5lYTSo177uLW7lW2H zfiSr~pFtv1D>5)LOR1M#o`6 zeMnM^R&9oPvmOR;uzXb>>v<>%A0=3W&KZZRhfH^0^?1y{u+UOsnM=T5kvRoOA7r8m zjaBtPptdTCFd4Z4^GYEAE|Y&%dHD(Sf<&DJcS}sX$T>YAVabsSz!k(ak6*q;(+9RG zemI*@5pZ3R0d+8sZ_c)HAezaZC&i%Nl$NY;-qlgfN+Pp>EELEverblc-(n;`f&i4a^k)6Qr8JKE}} z-mho5k!|X7EaTAR{1hB86+#4uuu?|)1u`20FHtI~zo3d>-@;Z4!C_3R2_HaQzdKHa z?}koAC?5jhE!0L^Kbpn!e({pr13I54mZn#i7F zccv|mfRsp1n!;Y-{RfUXA!p;#$5F#iAAJJthf{zm?+^4O?>b~4^Ru?TM}EovRCk!Z zlQSzCMoJZ&#FpHo{}cu;aP^WCLCKv2Q-q4ylQRsfKmhpz?-5W8`Uw3=mW|Q#h;g`U zOrgR0QGk$ejHx*MVKuVXrkz^&lKB=NYKoyJ;m(?NrDVEgOEs;6{N< zcVT`3Y7Y5ectuLMux(VDK=}a;g4%RMT`qT2L;EPV- z4rWSAA}^{?Z*O5T1Vpe_%wg1C4Qmp7@9?o|i~39d3u!E%fJ1wLM}B~m^%E?B7DU7& zwf)~g(Z6vuLLUDBN$_$vExh*!R)M1Nlj|d&^8*`QzJ^13_HZ=l&k82Sea+09Y9@#F zns{4~!TZhpF|wUzP0toQ3(JYkE=i+_AV~k>?OVimaSY_CS8weNV zMRH9h_=^o$rj%IMV6MrvGt|aHI8Uw;&_H3pqEy(@fulUen|Dd2aY(foZuSA@~ zbRz77a@4cV4ICVN5T3v(cauXsks5J4$W|(Gp#^}wh_m?Rkm>^RYnfL^3C3ATWHto} zfb3H+Geo9B&wB!v$t$+DWO#NCDxR8s!-o$i5cH-P5h2J1G*%4P(OUr3;G!*P70RD4cV*g)z=K-BoBm(t;yht&?onyhqJy>j#k z6CZ?eTXZy%`~ki2&Qo}U&!vm&gG&qt>ieXhh$AyU8ga~Rmv=cuTFz>cPGn;R=k)w6 zs@tHszeb)x2758I${V0?)sQVCPj2qjI^_03U2otrKMuG2xo7Xv|I%T|&rSB&YE3Sn zIdohWwzg&a3cboD>#>4U#i<#6hi)!%-MT4ry_}q4D8^RYVRTqZW|tsR%Eo;WJ24+i zgi7^@ADJ7rkt(K$c)%2~0hsj)u07|JY4o?1EPB=nTxI0G6_~weW^n}7XA2BiEH6vM zrdGY@y&nN69rU2WSHC-~z7kh9beHt5``d5FQo^c#`Et0KgZNMST-0%9vuDqC$i>Pb zi78>j-lw+?7v-uBT?_*6UsA!>jNV^U|+gj7(Fa0P<|9wc`dhY-HLcXrMdk2e+6^#{s8| zpTe7WOQ(0@vFBsL+6si;vctP*zKOyA?;yThU8ejtQ>T{qST%#*gCARyIX~B^%X?8d z3Z=so?|coay;SUiGVQpIw9xxD%yDcqh##8Q_5BFle7b16@VwPsGb*|!0=tV%#aNaM zf#w=~LPo~#K7L+?0F_u|B2k{x_PhH2`u=vayXGg`(^}_7nfy#5Oil_OPfQY-d2j1F zBiB$0Oi@wM$gHGC!E4lXM%UR7FAS9vokh>tEzceLKPj)!#p5WAL;;uDe{PFbt$a$K zwC?!~WpzSYBDr7D2ljsf49*5qe@pl`UEA@myN?pai(z&HLrh+_S54n@if*lqgKtDu zY5TJ|A#h_O;}1ZgsT zfd#el*R}1~kyPO@&m9xOeS&YXsU+gC?!FTvW8ggXyJssQ{-K^+yliyIe z5;DFhzVq_I#Xd?wCtOb#1>8K>_{NzF4nbu4Yv_ zgo)7HL0ywS`Q(#UVfQaPo}2;lPoOiNHaN{Tv^I~(3IN~PNTVGD_}#rn4}ZD{d(-th zcI+t2d=f_E?pN{4QT3V>xG2)6o;>>Li3`YuQZAS*i_wX!h@}hJ1EC;n>7gXnhKq1qe;tDtVdoYc*Kv$R z_IG}EiAldciyYA#Kuo*L%ATqb7LVgcRnDD`Lxu5f8@4+*GUR}ZTH9>FB7Vc%o)uVo z4v3aPvj%?~?>1fONO38i1L1qExBxPo0s6QCKNVfLAnDprBE=MSh^%SUm0x+!+<@li zm2BH8rsz2TH#aKuB61qOvoNUq)BrbCq}-_Kl~08QG6ChZJdZw~S3o;J9uv&Z379)Q zA-z-fvaR2Pr*1>G?T?UF|GaMg;|GrGuTaIrTe*F;bIb+nKz!LzpLu*yv2G2BI1Q}z z3Z`tlTW;6pG4!iLokzeG@l09_Fsjun2mEhpT1srW$R;*B(fw3MjOxELcTHNiMuuaE zd58IVUd7S3f_R$kvH;=aI^cEtZCiJ`L3s^0@Ji0@?goXNkMJOf>(0NKrUgvhTkyBF z|7D9gsXYXo1dH23ADJgqu3J&Hy5AM!uA7np3|t}@(ZKCGmcsuk-MrE#7x*CiX7Dg_ zo;mw0Cv~jCSmsX;0f;+()i~Kcq#0Ir zOTBPbE6)QbrsssH44yn$1{sH~f`cELhT2w|IC$s3(z3_heWNdx?DJ@4a5gdax+g?t zfZcUSE!?w+w@i}a&p-dXUgK@#8YuRW7r=5x0&gM;<6(<;_pUvq4xgB?aD~Y;x-RCL zWJ27#(@3v!X1<$AVH1tp)Mw5+kd~G+CL;=BP9lOM(#|26{z*}}B``rk49~nGTx+a7 z2+b+3Zsny<8bo);@Z3j?GUN7_I84m23XCFM1oK4yRk`Kt8Fw<~oh3QX-^)x}t-9L^5%b zfXuEOJ%|rXe1i&z&?VCLY^(|3uKGz1~vy0OtSlV`M;U-W9|-)}HQ>f}b|UBLsX!?;@8ay87|cjA(Ux zzW4V0NeJT$X&RrhaT>s-As_}7%Tr;7!QY41OgRrf{O|+M2<1%caKMCi4av1tI)5JB z$8{*tP}wWI;sHsDsTZrffk#I!LchNhliEJt$QzOpx9lS;nAgAh|i_wD^D zSOyLkVKP7t8}d>vUAknts#;Fz6n5|^3A_14Gb}weF|RPFyc?b9L=Zp z&*tP2mSf)aYw}?ll7wUPo)FNFMQ0B3C0(~whVgf9h$nY}l3UwcbW6o390iZ?nx zHvQN6rl8WrJ81U5hzIf?zH${Uqx>wXN#BLnp#P>ljiCNR7iJMf-oli5uE&lcu`*b! z2)}CJi{Mdj}XtGy|ZFxvJ6TuNlJM;~gdqnE7TaPZ>kFYme!_ z{Dbkx=c7)MX4ebb-Oc~NZk2}Fj7Rs*YuAr*gu(f&`t;d<&SVH^$8_C-ix<4i8Py`7 zEy?37H!c)_zPfnvqNK!T?cq!#oWan>ggqDt37m@(E)n&ZvIWgORpC=KQ)5B|G!Bk56KEfD7l*>Bhr*87)pojFR}`yL&9F-S5h&{h`53QJUx_ z1Wyh&N&|R9xQg2}6C&TfI`j$}4{iKlX0aeo5vs%$F`>rPoJzn4>BM!2NR1s#thDQL zW}tbbba%njq#`b{Ibc2jo6YqQG;iYVtXI^p`w4?xB{Bi$09!+>ZG#349I*cmT6^5X z&W#dVGHIBh^5xAHZtW);H!Kut7};dCeiJ(i9*s#&x_WO$kQODRU-P?-1dSA^jo?^{ z5aS3G)`Tj`=K3*qzfg%>AWx(qlY2>+2A#;b?jLbo6sW~BXHO0;O6_7X>t@GSb$*Wv zz|<8new%e%9KD!4g!B?onzv4$=7CF+3xsDy^u1MN z|2mx%FDU1HisjnjZgUOY;{Im|{HWNc1pdgSot z9oB`G70f0sOA%}YPmJK9NF~cFlDSBI5PVb)R}q1HZq$UejMDM<=9oKFLn~O1S!s61 zWyJQSVfSx;!2Eon;t`1w*%vN&*Zt(X?Gf`~iV*?kPCo%e`wJ=o1fDKErx;>i1)1gi z@vi!e%h{ai(rJ#BQZ>%mcT8F3`Bt`bud*?$~C^1 za_QCMB72~9;#X2kT4>afC+nX)Xgzn%9C?}`x-x28TbRbx&LA6bg4)u~ zE~VHhg=>@5(NGyE*RAs@IftNyr`P4tVG4%d8}ApDcISPBq4xyE-=N?Y5W6qBZ3#+1 zR)M$2H??Kg?+`FZaEz)+TS_!D11reUboFasbHoh2aU$vzt0Z65J^OV0o4BBac)m-> z0=o=ukU$^jx}2{lz(SiDDvM9QN8bQUKYrV2JIR=}vg0b+wiJ~E=2|zM6_br}^nX%} zMtDyPx)^?TPlWCFK^fgD23_loKvEL1(QRt=%b4{asr&`=7Te7J)?{YXZresx@m)62 zh?=G;ZAIyF+9UIHTHi;^X<|z@)$|K*J1w)`lAcMQ4$l@3?cO-}56Rv_M16 zzi|1hJy93KAiG96m({B84b)eoD*O+ez9Deq?`Zq8R^VwfVg9>p5iHI5|Py-Nj=H zBeRbbrO(|V|9+!J{m-^v;QxT~hCeKpc|id_hzpZQGTG40B(C zIz=7nxaK&ONPs(p=#X}!qOSL)u158}J@a3hwuIDL;VsMO7-AZ_qf!C8NhNp$Zw<*z zyC%oup|N_;)t*ra^dHP;4KS9-Vj7PdB+aMJ@-vvmz%agxo+%ejE)Q9>mg7BTgDl+K z3QK*)bzNf*l!y}|Z(a!CG;JR^?`f?~!F2n1`k|5<)df44rYVgCCL3t8Wc&&0Urtf% z`hU`DZhXhmqs_h^6Eh1ae94rL_H-=}yUK@QSwW#=)?-wXCL|GX;6crYE?vId8M&Zk z%N=OGCHo^c03YNp#Flg9c^^>sEgeE1S;BMH6yo3^TDwPiWm<-+dvN+J3Pn zmMw#ydJaM1ls}``GVt`Ina2b;L-HwkQ=6{t-n~2WiY+-|G$;aMr9ye?GW-m)e|vSJ z_xILu8dsBjDh2{CK{3%CqB&@|Y%*ss7X>^D{pK%RUw*&D?%gZm*vtVd9Jh8y!ru%3 z&dt-W*sk4{f@=nYg8;wZRR<_u{z`RV3`9g>Mm0-v%u0WX>hqX*^SDKlEjSu!t zq56>Wvl~_NFPARG0I|P6a$AK90A5a2t(aYuN{`2^?Qw_z5G|ebIASkoW$C10F}-Rg zpX5du^7#Q!#tmdoVo+D3l6huv?)1U#wJx31n~v&^0G^o$YrU$z{x`Fp^X8Nb7cSV0 zN!zzCy86J5^iDITPiH?_4BAHT*~b_NiqX1Q>^OP1D(dkrBS7BZ5|6s`V`rakoy*UQrgDG&qeLlS=0ikZIxV zV3J>Tt9*X*0+U(>$EYPwH550Wwd07r3 zX5Ki}h8VN`xt&I*`kOl#Q&%xKOgILxJ`w2v??x`~O(LM*;C<6uUyJx%Q!px{>2rpp zjr2t&jPj7|@@GVz|N2$Gn>Fw6GrqX0Vbsl(@mI6%iPs%VlO+b7##*}>6{M@c1jIMm zCPQ!5lC@oUAC0=gEN?n%g%!cR$g~yx(^@i)uYOki%q^!inn_7X5?xv^nS-aG3yw8; z(Mk)G6`!F+Q~wMQ*WcgYVA>d(T73d+cmBSXEe!52Q+#6@?qNtvMPQ(vrVIc&+qG?j z(F%AZ0D-!))5RL`xm&ZZFdS-UWgaw<&JU%HL|C?*0RfAyQ(h@y>vj*XB9__mLaRCb z=uPu9xN=ym-1qz_C_F01#&mf&G#}?e><-ccr>l$S4vNpEEA6_7~(foAoa+e;siVP(gPetqU zYe-twu;Dwouf}9M&hy0_cG$SS!|5fVZFD68|DdzF+SF=N4kIT~g&RS$J$p7$8xsDv zr{PAG&4kKb9lR@^PVBcTGOGG*9Ava57~uoteKo5L`z?N|Jv#DV;|riy>BnR%(l=-+ z72(r0h(>dgzO9MykCZcZ%tK0m+Qj>I*PImpe;XICZ)-xRkzNaM?svg?mK@!t;CxMXSKkT;XBgo5hH^p zR@8jX?(P<3w;1C%&=_*O2(_Z=)(10yt61_Fn|Wm4v5ND$Ktm&`eo>K^fIpt*$W7C; z@IvAdvLbgJBc&7lD3S#&DH2gbJvP~2S91c$p-H1gZvvY8XH0=v2i5+PBV5lrP=G&; z%5>fUB8dU)e7o=drF&#m5xorh{th13ZX-2-hG`XgcLayPVE!|TH(lXMdaDq|TQ*(^ zn*F1niaBRB&WpIaA&x`V_sMX)zV}~*iUrG`yyBBlBlrMp{jO-$(#w;l-5CM&x+ zq6WuF=)i=ua9e2Ewp|~&PF4afjesTD$$M6kKB*dhgl<9$Z54u}Eh*sFoW^8+&b|$Hb+2h{BV@RsZW#`^>L=&Y9>&I%gsiyI`b2E|W{tjy?Y=F>&vDBk zQ%gDT)dmIKg-tLrkZ0MvUt07HV zhV%~^-^AMF)v6V*UH`b3Rp74MVTt*kf%p16th(?*#GUS$KRqtf25Z8&gru2m1Cxm` zljtpd?kI`~Dx|fP-5|Gd%ZYgAEmZ>Xetd=wx%?l6pwc~M(-1_+9WAk|wsdh~ZpGG1 z4fGK|&sgcaxf2MA1;04mljd_Or_Jmq6(#M{Cxr0Q<9~@Xlf1#qY}3IWEn2it?7R#i zC>GeL>4F}Xe1fI(Pc;HO!ZzcOi*eaEAixSOl3IV(UBykBJXrxD)MdBoR12@9G48>a z3w>>_5by<6EYeW|IAprKxpc}BGN4Y}-pjQyT_7RQTY#xTnJmQ)k>?mNKB0QSC7+>w zL)>PYbd|NNawV)2Cp%sG=yC5{A0}LM-Mo_aAa(36-%u2aiE`${dy|oOMxVKdH`x#M z0tz0M1+3+fDUVxs@PJ=HKs-x|21lqtq*OHu&ok|EzxRYW^XA2&G?M)v3T>(7inD|+ z$&IPKOj>TDYkPRY*X7P5dPi6<+<*IP&IGeZectWG(2=u?l|YC(oAw+Ap?np20}<}> zkssc5;Xzm@$sVWF%<>C`du}SIip`&i>6ZM_o=m#?;on>&qbs zLlm8$O?BGK2O%xFP0}vY*7I((IeX>|`_xAu5HjB-Hzg1;zm9fzD*w@S?sC8x@%ZZP zv6Je>bE|Apx0nuObkfAWxKH?+=EYSDybH!32M3ZSm!rS~5$`;0jDEEG*1`OGZpl8W zA@@H=NXG-|_P||!M}Wy?p)n&yF=d%OdQ!R~?GOvVsI1S|tNjr|D{;R&cG{~{K{Rnz zM{a7gWB+Eg5;Xg&1vD;tH=f+j>MQXV7f-Y#@ZP8)Lx!Xl_%kU=x6XMi2{NtTO!6@v z$>c#e`DX5uj$64>LAI@TZ|C3CVd_T*DCY|cCO95lDGi3|=z z0d1%epCDM?dhiZ3(-w7jd?;eZv?K2Zdt7ICYIEAX@3)VkzLF6!vMh!7Lf#F3z~tyG z1>BAveSUTO$J&1twaGMN-Tppfk7TTN-qN;R*>IhN+K5LJTz2(UEZ6Ey2-g}UB|_qY z1q(cz@8*aATN<$W9#GvjjJK?(QfxKiB?+*2@(NIxOGB?iknt(L!1U#;P!Lku4IJ4s=j61 zizwce=F9Eml4kA`cf0ze&q?9FPFd`~d>hS^1Y=r0!L?*{$r-yf`-7{dltuY`+E8HF zg6U|*;i`iVw@RCKjp&kjIRbh_D~tN{*J z?>~6Jl85B)uPnN^FPv}yy*xAsl1kEFN1iDFpb)@KX3R0~#asD1Eo24s>5!5LqE1Zi z2uy(GaZ_R}qO*U>yzjncHQ_mt`_f;i%B1VbIdo`a-tew4kdH2b*eW)|%Qm8M0hzE8HG(@-?O~*x18e}akZ9h zoW}|E;fgV7vy=oxFjR+zXPS5MixHR&JVY0l@Z-4=eN&ttKR&!%KSW?tF#%j$kFssv zdA#UaysCb8!cSa>c=YB0AX4W2gJ}|s&@&v!qFDy>j^Z&5MH{J<6EQR(ud_`_S1>rEysOjNucl?r?{tNdHOy_cvDbJ0MRtGYOhF@!&MSD4mn3Auj1yF|V)OG@cq z^TmwiWY2qmU&=z*hwyi^Q|J?`4I>)}?zL9x*N$r5q_+2k(A=L1{6xyP;X>eKl( zc5mOkoU&TW!Y0)DR-JMlGcn&tE%Z5!U?o9Z3QKmd_W7oSaukEU*X7C&qnn&DIr`O0 zefkGt12dHM$HqzXkG{wG!_lY%q{b78)b4co)V&X3FCg`Q;K=>{W-wMe4bME5H@K{+ zBl|9R_(GL&v1HsEz+?pXK&{SSGMODcKMM1L8h3v#c%mo-CKynYs51aFFX(-fpv|+} zfzYDzF*VmLNy06hp^7CByNXWPI*5tl+f^23K2H7q=0oyUBumlgvX75S zEogA*@%P4u&1oU^Nnt_VyYKzJ>*&#=Rc&;VG(2e47JoKmKTNgPlF6_rTFnjKGtDii z_P|b8!k&Cc6*%>EQkjvSu3SS^LhCiS{w+yynL)Tf<(7mDAj8s>#B;myt8FKL_~8`A zVsQORLrH_0cTU|c5=4_=LLZGQr0GNQu~G!LI#f`A=-W3 zAFdIpH=|@25CQ}z1?diO?7^}Xe{66UVEnx z-maLbEIH~|r>cfr;`#~R+-gH$-@utO6H>XR3~mXjDTZdv3;+B) z5g$+ojSqibS-!aXG8j5`Tz%T5 zaX{A&IV3gG1txY4^M0?!YF98IF!K|3-|);S8-tP0qG!*vK}YB@KBuqZu&|`5T{XmI z^lyP^UX&$3tHdMQ`MXAjSKj0yGg8I(FTWtiPjq4;$rvr;0Z$5ln1igefHSa?Y8gwR zyXv#cB`VVdrc%30q;sNIl0}QRIe&zmln);VH>BBM@NuE_311=mDrzd);~SC$XiwA! zbNWKr$frhzo~qlNa6&V{OJ&fJtP@&VifyIYLk;>T{{3KJ*hewROMyQD1t!X%Mc|Y| zOF%reywT-x44#pqo_ufcXnD*~3@XhP{~Wk@{txaDsB*c`kVy-VBv45Phwrfz^pyNL zol6yK<-h5*$a2O2g_A*-ZlYTnoz}uoHyZN_Q-(jHKI2#+YYQ6XZ>RgZ+`);b>4QT( z+DvRCx8}4;Fa43{-X4Yn7c?@Xb7LY1u)bc@{;jvN?7Eh6Rg|wI6~`6cq`ivz9{fZZ4Zq zT3{oAznp$bd0^JbS+kmq0*c?ev_`BNb3<(}08HkBb@OkXvlo1zW8m8l4v`%0wb>Ma zF%cYBU64RE%w3gO{~$_7-#d##Piwhys^9_`P zIFhjJt7#pu%ruT5M*OzPXUG*tD3pMQMw?cnRvb#a;eQbvf6m%kk);@Io&p&3IB!u* zIrfhh6clV9uKon@h99$(J$jQh%qou9U4OS2* zHsa=9QdUp#gpg~o6}I}~`DJws->#+`QA8I=c5G@V!Ur5kxcsKzNc^%F%j()m0;2`O zAS#Suk*ufSv3D)mJCg{ND|3iORKNS z^SIJtdLr&3FLdAK=DWoVzzf7GdUM2L8MVAQVmT}Ln+V6S&%N%4zVlA4`7;bLullhu zlleB_uB|7yG5LqlD^@gU99i{`OH(@X074xx1Q#-`FfdLiHai(qRsip5n4NR+qL1=` zk!m3*u&H0F^H7Tx9?V(X=8;n5l3zX);@jlJHy@2357B<1;(iT}PlCeuRYx=7J6eU@ zekZ37{dfc&z1o!7lT~NJ@Dz1iOlh%e{k^mCvrcaypBl>J(C>1uF-15u&0rR+fHuwH+ zi&~O{^2-5ZOz?I!_r6-*7V(6Z0;?C1!(CA*SBEz7SBymvT8RngYdQbG1(ZRU4#~K~ zoYsb8NfWTm!vQpL2uu`b_4AUqwCf^Sd1I|Lt$t%t2oWF$0iAT@!>^@jVd8rj6twNe zS8_h%v(Qi^)pY3@V$gnNO_v{T3g91C8g5X!1n)-v#1+u7w9^t;-Ov1Im0nvJ#{rK?>1m6kGeTlJW*vj{28PmUpch}-`dLLRYLqWo>K}d{(0}c5$+^eK zIm4!{p_$WMs|^4VTahIX=#YNE+U%LMKFW|k7Ruba z6U`6z)IrH9>SpPooafw3-e2t~H54Vtp3_;OK6Dvzx4&XO7oJ)P5*rK2tdn+D(QLLn z(_RWz)dQ-}`m-rp!db9)INfjQ{APx9BY7bECe=(g=FU zU%Is&hmwm;jziH#DR)7p4z6(lfKV+?FMtAZ;QS-O&yjQ!ATyqA$7yL+bT^0G@c0 z=vR=E5>_3QqQ`q;R*XO#>F7g+z}fTAp^k_c|N7S3rMwd^_sm($T*RDU{^(+x{afC$ z#DcFaTH31!a6u1Srz*H#V26W*A;?jM@`bb3Kjvtp(xf1Z!USqfM-E9PcIzh3nX z46t^S$3k6}(9X32(sszD7qDsFgy$k2p5G<7jlp1V+o419vB`^%7uO}#boT7354&&d zcs?($;(l(Eq_IgyON&cufBdi|zY-e*Jg)k-SX}tHQ`-?$P4+*SoV)3z!a2`b6heV& zJ=NKB=d>P6DMjL>nbD-+r5BT<3xwC$a9p8X_0&c;kqhJtzpEa=SYB_DQX8Dc@N&j&$w*RpSYa4*fds+NzJVfpREjFJ9I-=@fv0 zghY+Zy2Ysrkkt;XS9256*NCt&{l{GFL{k?B7zG}pK8C$droHFw^nJ*|hPt&AAt0l5 zjLP~7s;eY|h)vM^;imR+)jn?+`lafj;E>(O8O^XdnB!(qWsehE5TYJ9H;_tQ+LQev z8lCw$6t88Be7|{S1Bz3Rv@V8z4V5;NvnB4JXZ#|MwIStS3FV2{B(o)7R;>tc?`AC0 z{d*dkow~4SR?$x{mgO%R{zXX=#U+69CG9H)Ts?d-gET0eJbCh*FA(gTe!jx?*c&9Y z?yHH(MrXPRcjGtVe;hEv8rr1(U54OMchkHLQ@WFMPM_viP>0VwVEODrju*$hbUdeo zn4y{uWI~PTv17+)EeE%#%hvd3-{t@Q&(z){M&(5By8Z`vuTnYC6uq2gR7g~Q{uHX$ zH8kWU984RVKkG@mfbh{PYd}*jERauIZeW3}E{VY@hk0o>Xo}J@B^hCgPVj!!;Oq#= zPj*1&M50`(@2r24AmouUpPce5!FAS%$awh&=avty$gWPdL?~oG?dJi4`IU)^lX^+T zh?f@ky~2;Hz(E8c70@UDkNp({gH`2)h=&G$HxaGhI=#xfPH}@kg_K48qbZz zy>{DrkPncyuj`6#O$^JG`~JTMdZ9T<7#R+7Eg*sDnqPkY`5U4?s4a4i!{VGmZ!r_p zOMTdxJ3NS6?%%&ZeFgEO2~Vn%pNzbG!8NC6p@OxT-t?p(n_;o?(GRnrRz~6YVWLw8 zlo&`1`pJ3So$vOD?l#Letj|DR$dtUnjVwox8-a2(4>jplT18m|d9X+FXrde3Y&Ygk z@S{-6c%HKWkttL^qH`@6U;#3<<{^A}6h5tJDa>NKZ}cH3h{9)ce{!We>uqxkU01y;%FZ_?R2M36(K zg?rp4piL|+Eq#n|1pIZx(YRZy#V2BQaLA3-*BT+-Rebj0BChJ&E(lX~X~&M$b7xNwpc%ZR*AJGbtQC;>lU?)8zct+Yb-k+B^}#_SMp*C-TvjAR?_^@#KC35qIjo zL0~7IRWll$40N`E*NpXSZY3@1MOF5Qwx2c7z8kre$7Xbhv$MbW`f4-`@ys-X$1Kd= zQ~1-??Gw&C2!1&x$eE6j$Y(^zTnVAV5i52yF+2&q4Wac}nc6j|VhzjNq~? z&6pPV(jU05mWpVNIvuA=|NhSv?yVg=N|q&-B5Qz}u;Z-VC+VQZw3cz%SyrzU%Mcyn z-Rzpx#=zHlBTQCUR@TX{ySK$-4$72s>?SUKnTkE{?M0f)*UG7%Jn&GM?};F_KVAGj zm+q7ct)<~9lOWMkygvM#nUYhI9?7Moh2_SbDdm;dN8Om>AA*_7?9s)mk51&j;JYnp zUO5^G@5x!u88+UB7O{Dk9US5w+5s;Ik$MyMZak*gEK$Bt5ud_3hm~tKfsl66_lZJ&8Iu9(C zHH2Q1^3SYQcqhAegr`$iHkXlk6%RwNgg|rGt;VIpBp`Eu%_x6sRD&N~_W$GHpqZ&b z0d;tXKU>=MJ0c{ExtUabBS4KpJ^t>XhK7Wr|0BrX&(l|~|EWFAKP|P<=7>P8H>M?* zxM>`&6}NAn0{phd)~0YF&TR(i9XC7Axjhy`aaIK|uNF1g&$)fcGTi0j-fINx@o7d> zu}1f4vgX$tOhMXu_EO_^vY(%+{6|#xPb(t3&2pLFhpKU!5SFc2({C`2XKpn;bm&ke zDx--o-O59hcM7pwbg=F31#;zAL)sw|iHGAb-z7l|x=pUqs8tyXu>(0zuuD7C`t87e z(D!VW3|=z-6GT#LujLo0+jIIUxS#9-dbXag!tK7%DI79aBOV zN+~48`LI8dCqo9fR`)1R3&KdIIfcXU@V;LGA6ue=S2n9RgW~tQI5jPhW%4V<=cOc# z$xMP`Zj!dqaYNtVE;o=P9h&Qich9nhoy)2kG8p7Z3s;Jo1;1XbrZV}+di6>_@-ET< z`{Rt-Je4j;iv~}62sforHGq{(2YY^CZf<^*SR@EC^ZscBwMa!u8CiD9r#^(u!5f{+ z$lP4``fRYuv!t$pNjcuKeWG<8C?kO?EbiUsdnwvew!2}14uujq+)nnbfhJyQVMv!9dDjT6r}u1$$l2cl{SUGt6@Pa z0nyS}nF6RX<<@RbNojv?4&W4Z zU=tUzL>!ije@2aRm<)K`Ocqmeo1)I#Lv-NB!whe?5_b*A$;El4Hr{&zzuP{X06TUm~{Y@MUq;jWb+7RxJ%=&M=?w)9KTv zRaQhWKz+qURhPb8cEzVojqt#!HG7SCv9WDGZ?d?y563%ChY~g(*`#BkCNp1j{=sQt&*q^ zTX0@>lvNNP<)4_@2hG5XHNQ)q3vZbcCBf;IOs239cM*-!-}}u)ZiCQX0-4N2#oy>W z9r;#%2v%WTNh+z!W6U3?>(FOJLS^Kgx(MaM%gCzEs-*sDW`cB>lbHQyE?HF#6h%IM zYM#@I>>7Z+U(1j1f^!~xxPsnmBGEkxGt+^3a@-koR4`v+8fZ|KO}tq^G1EjRAJY+Ci^o+Dxo`gzX+ zZ`QIX7+drO=#R~KR}h?}yB>*;H+4_buVmz|bdC3exc1o-o3@xW6U;F)URkBMEv zlz+kjjHHF3cH2K#0(}HBg-idtzR1A0-+uf32vOsck3nsHIL`R%I5qh$<3%NHk@o7z zDD!lrc}YLqp80<(TGkBDx0%WhipuD-w^x5otF8|I_f~z9wLbK~qnxMMOtZy*bnWq1 z@VI^{Mi^w?KZpQXP?KL7?#FhO7kew*tTxF$6HstaQXmj5w*7L+;ReQ!j(CQ4oKDwY z2vhk#e=Yz2PaPx# literal 0 HcmV?d00001 diff --git a/docs/image/step_create_on_need.png b/docs/image/step_create_on_need.png new file mode 100644 index 0000000000000000000000000000000000000000..562b42ff2d520b146ecb7c9bf1cb0e7b51a8e857 GIT binary patch literal 51831 zcmeFZ2{e{p-#>acmqrbWLLrqgg^Hpo0OpA4p%~TWR>tCO&#pn?IGyQeIo6fZbbTPoznP zR(R(l#*sHM$`?~L^s*B)w**Gty>)A`I>Y`J$(#V|3Z>WQRr1O|-%->~U0X)Jd+Wv6xPW|8*%CX z|4;sRj7#d(-i2mG0YU~=t#5CZ2owrTPEPJQbSN{qEJHu1lrG{s&V=W2w6C$6vwv|` zY}v<;)ZX=1Z;fYUMGiGD#IK@XKDRH*WjIz67#yr?;IC(yQ|d2V{P$DkX5Eaot@bY@ zU3ARzCL`JGelU*q)G8Dd6xeq~_?u-kH8x&ovTbpq2MO-kQ}OoZA{8a2>xE8a0vI_5 z7P*O^)B5=(Sna}v7l#iYPPs97akle7ens7zE2F{lGf~Y`XXND0pF8*X!i5Xn@oUMa z#vU>I!V$yqRA!MW!j+R;|pBzvKJ&R|U71)oKMl7d5R6dnBQ0-nWRHrG9z8 zk?0xjCmzyaL5@SRhYlW8ZU}n*oL5p(G9n_v_;+go!|K&^HC?BO{MkNpmV?(q*zAAp zaGIO^={Jk97-@4CT?}zQ$^=z2VBZ7?Tb+emKe1+BbO8?a7#SUvnom< zHauJk_iF1o?KIwGyN|`QyCpwA^6X36ty>FC8`9)=?AUSo^y$~V2h5ufZ`!=sq^Blc zGutM9tiMSso?p6wtdqvwL&iF>de(L8(hEW^r`=M@#b+uGWG{P+?6 z_3Jru2A}Yuq3eC$l24qEI_vxV`Sbd8^TfgSQmxeUs;ZHXBLYU%BcE2mDKQc+fx@nK+KxU}zCb%fmJG7HoC zlw|6a>9M}<+Qi_F8{}3q*cRIgZ;jVVSIu{x9qG>=XuhU}JM-o0`1w;SD0?QE%d{~= zk@J*S&i6d>_rm7oJQ)hl_MP0Y>uidBLQc+wxw$#b%K;h2~hmcuuaJ{b;MkEXiJ zJ7ZSH|k1Z|p4`Uy-chXZ$)N*IX8hH5ls-qRdsuOi3XS)Wj-<*znIUvDQ5``<|D7{P?l*>7Mf}nHQ_?x6E`NIDO_!PFq@B9ABA%prD}BVDV

NdX?P~MjMZIX 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 Date: Fri, 28 Jun 2019 21:57:09 +0800 Subject: [PATCH 041/176] 1. SerializeToString() replace SerializeAsString(); 2. change NextBlock() to Next(). --- src/actor/chain/Chain.cpp | 4 ++-- src/actor/chain/Chain.hpp | 2 +- src/codec/CodecPrivate.cpp | 16 +++++++++++----- src/codec/CodecProto.cpp | 7 +++++-- src/codec/CodecWsExtentPb.cpp | 13 +++++++++---- src/labor/Manager.cpp | 24 ++++++++++++++++++------ src/labor/Manager.hpp | 1 + src/labor/WorkerImpl.cpp | 10 +++++----- src/logger/NetLogger.cpp | 8 ++++++-- src/logger/NetLogger.hpp | 1 + 10 files changed, 59 insertions(+), 27 deletions(-) diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 08902f64..148fe44e 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -31,7 +31,7 @@ void Chain::Init(const std::queue >& queChainBlock) m_queChainBlock = queChainBlock; } -E_CMD_STATUS Chain::NextBlock() +E_CMD_STATUS Chain::Next() { if (m_uiWaitingStep > 0) { @@ -113,7 +113,7 @@ E_CMD_STATUS Chain::NextBlock() } else // 只有Matrix的链块(无IO回调),执行完当前链块后立即执行下一个链块 { - return(NextBlock()); + return(Next()); } } diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index fa0d8722..e16b47f6 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -29,7 +29,7 @@ class Chain final: public Actor, virtual ~Chain(); void Init(const std::queue >& queChainBlock); - E_CMD_STATUS NextBlock(); + E_CMD_STATUS Next(); virtual E_CMD_STATUS Timeout() { diff --git a/src/codec/CodecPrivate.cpp b/src/codec/CodecPrivate.cpp index 5289c2ec..d13efb74 100644 --- a/src/codec/CodecPrivate.cpp +++ b/src/codec/CodecPrivate.cpp @@ -56,6 +56,7 @@ E_CODEC_STATUS CodecPrivate::Encode(const MsgHead& oMsgHead, const MsgBody& oMsg return(CODEC_STATUS_OK); } iHadWriteLen += iWriteLen; + std::string strTmpData; if (stMsgHead.encript == 0) // 不压缩也不加密 { iWriteLen = pBuff->Write(&stMsgHead, iNeedWriteLen); @@ -67,7 +68,8 @@ E_CODEC_STATUS CodecPrivate::Encode(const MsgHead& oMsgHead, const MsgBody& oMsg return(CODEC_STATUS_ERR); } iNeedWriteLen = oMsgBody.ByteSize(); - iWriteLen = pBuff->Write(oMsgBody.SerializeAsString().c_str(), oMsgBody.ByteSize()); + oMsgBody.SerializeToString(&strTmpData); + iWriteLen = pBuff->Write(strTmpData.c_str(), oMsgBody.ByteSize()); if (iWriteLen != iNeedWriteLen) { LOG4_ERROR("buff write head iWriteLen != sizeof(stClientMsgHead)"); @@ -82,7 +84,8 @@ E_CODEC_STATUS CodecPrivate::Encode(const MsgHead& oMsgHead, const MsgBody& oMsg std::string strEncryptData; if (gc_uiZipBit & oMsgHead.cmd()) { - if (!Zip(oMsgBody.SerializeAsString(), strCompressData)) + oMsgBody.SerializeToString(&strTmpData); + if (!Zip(strTmpData, strCompressData)) { LOG4_ERROR("zip error!"); return(CODEC_STATUS_ERR); @@ -90,7 +93,8 @@ E_CODEC_STATUS CodecPrivate::Encode(const MsgHead& oMsgHead, const MsgBody& oMsg } else if (gc_uiGzipBit & oMsgHead.cmd()) { - if (!Gzip(oMsgBody.SerializeAsString(), strCompressData)) + oMsgBody.SerializeToString(&strTmpData); + if (!Gzip(strTmpData, strCompressData)) { LOG4_ERROR("gzip error!"); return(CODEC_STATUS_ERR); @@ -108,7 +112,8 @@ E_CODEC_STATUS CodecPrivate::Encode(const MsgHead& oMsgHead, const MsgBody& oMsg } else { - if (!Rc5Encrypt(oMsgBody.SerializeAsString(), strEncryptData)) + oMsgBody.SerializeToString(&strTmpData); + if (!Rc5Encrypt(strTmpData, strEncryptData)) { LOG4_ERROR("Rc5Encrypt error!"); return(CODEC_STATUS_ERR); @@ -170,7 +175,8 @@ E_CODEC_STATUS CodecPrivate::Encode(const MsgHead& oMsgHead, const MsgBody& oMsg } iHadWriteLen += iWriteLen; iNeedWriteLen = oMsgBody.ByteSize(); - iWriteLen = pBuff->Write(oMsgBody.SerializeAsString().c_str(), oMsgBody.ByteSize()); + oMsgBody.SerializeToString(&strTmpData); + iWriteLen = pBuff->Write(strTmpData.c_str(), oMsgBody.ByteSize()); if (iWriteLen != iNeedWriteLen) { pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteLen); diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp index c7b6c306..9f6c30b8 100644 --- a/src/codec/CodecProto.cpp +++ b/src/codec/CodecProto.cpp @@ -29,7 +29,9 @@ E_CODEC_STATUS CodecProto::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBo int iHadWriteLen = 0; int iWriteLen = 0; int iNeedWriteLen = gc_uiMsgHeadSize; - iWriteLen = pBuff->Write(oMsgHead.SerializeAsString().c_str(), gc_uiMsgHeadSize); + std::string strTmpData; + oMsgHead.SerializeToString(&strTmpData); + iWriteLen = pBuff->Write(strTmpData.c_str(), gc_uiMsgHeadSize); if (iWriteLen != iNeedWriteLen) { LOG4_ERROR("buff write head iWriteLen != iNeedWriteLen!"); @@ -42,7 +44,8 @@ E_CODEC_STATUS CodecProto::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBo return(CODEC_STATUS_OK); } iNeedWriteLen = oMsgBody.ByteSize(); - iWriteLen = pBuff->Write(oMsgBody.SerializeAsString().c_str(), oMsgBody.ByteSize()); + oMsgBody.SerializeToString(&strTmpData); + iWriteLen = pBuff->Write(strTmpData.c_str(), oMsgBody.ByteSize()); if (iWriteLen == iNeedWriteLen) { return(CODEC_STATUS_OK); diff --git a/src/codec/CodecWsExtentPb.cpp b/src/codec/CodecWsExtentPb.cpp index b661041a..580b0645 100644 --- a/src/codec/CodecWsExtentPb.cpp +++ b/src/codec/CodecWsExtentPb.cpp @@ -76,9 +76,11 @@ E_CODEC_STATUS CodecWsExtentPb::Encode(const MsgHead& oMsgHead, ucSecondByte &= (~WEBSOCKET_PAYLOAD_LEN); std::string strCompressData; std::string strEncryptData; + std::string strTmpData; if (gc_uiZipBit & oMsgHead.cmd()) { - if (!Zip(oMsgBody.SerializeAsString(), strCompressData)) + oMsgBody.SerializeToString(&strTmpData); + if (!Zip(strTmpData, strCompressData)) { LOG4_ERROR("zip error!"); return (CODEC_STATUS_ERR); @@ -86,7 +88,8 @@ E_CODEC_STATUS CodecWsExtentPb::Encode(const MsgHead& oMsgHead, } else if (gc_uiGzipBit & oMsgHead.cmd()) { - if (!Gzip(oMsgBody.SerializeAsString(), strCompressData)) + oMsgBody.SerializeToString(&strTmpData); + if (!Gzip(strTmpData, strCompressData)) { LOG4_ERROR("gzip error!"); return (CODEC_STATUS_ERR); @@ -104,7 +107,8 @@ E_CODEC_STATUS CodecWsExtentPb::Encode(const MsgHead& oMsgHead, } else { - if (!Rc5Encrypt(oMsgBody.SerializeAsString(), strEncryptData)) + oMsgBody.SerializeToString(&strTmpData); + if (!Rc5Encrypt(strTmpData, strEncryptData)) { LOG4_ERROR("Rc5Encrypt error!"); return (CODEC_STATUS_ERR); @@ -183,7 +187,8 @@ E_CODEC_STATUS CodecWsExtentPb::Encode(const MsgHead& oMsgHead, else // 无效的压缩或加密算法,打包原数据 { iNeedWriteLen = oMsgBody.ByteSize(); - iWriteLen = pBuff->Write(oMsgBody.SerializeAsString().c_str(), oMsgBody.ByteSize()); + oMsgBody.SerializeToString(&strTmpData); + iWriteLen = pBuff->Write(strTmpData.c_str(), oMsgBody.ByteSize()); } if (iWriteLen != iNeedWriteLen) { diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 04d32411..fe184c85 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -1440,7 +1440,9 @@ void Manager::RefreshServer() LogLevel oLogLevel; oLogLevel.set_log_level(iLogLevel); oLogLevel.set_net_log_level(iNetLogLevel); - oMsgBody.set_data(oLogLevel.SerializeAsString()); + //oMsgBody.set_data(oLogLevel.SerializeAsString()); + oLogLevel.SerializeToString(&m_strDataString); + oMsgBody.set_data(m_strDataString); SendToWorker(CMD_REQ_SET_LOG_LEVEL, GetSequence(), oMsgBody); } @@ -1974,7 +1976,9 @@ bool Manager::OnBeaconData(std::shared_ptr pChannel, const MsgHea oTargetWorker.set_node_type(m_stManagerInfo.strNodeType); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("OK"); - oOutMsgBody.set_data(oTargetWorker.SerializeAsString()); + //oOutMsgBody.set_data(oTargetWorker.SerializeAsString()); + oTargetWorker.SerializeToString(&m_strDataString); + oOutMsgBody.set_data(m_strDataString); E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(CMD_REQ_TELL_WORKER, GetSequence(), oOutMsgBody); if (CODEC_STATUS_OK == eCodecStatus) { @@ -2034,7 +2038,9 @@ bool Manager::OnTellWorker(std::shared_ptr pChannel, const MsgBod oOutTargetWorker.set_node_type(m_stManagerInfo.strNodeType); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("OK"); - oOutMsgBody.set_data(oOutTargetWorker.SerializeAsString()); + //oOutMsgBody.set_data(oOutTargetWorker.SerializeAsString()); + oOutTargetWorker.SerializeToString(&m_strDataString); + oOutMsgBody.set_data(m_strDataString); return(true); } else @@ -2178,7 +2184,9 @@ bool Manager::OnGetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) ConfigInfo oConfigInfo; oConfigInfo.set_file_name(m_stManagerInfo.strConfFile); oConfigInfo.set_file_content(m_oCurrentConf.ToFormattedString()); - oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + //oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + oConfigInfo.SerializeToString(&m_strDataString); + oOutMsgBody.set_data(m_strDataString); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); return(true); @@ -2226,7 +2234,9 @@ bool Manager::OnGetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBod ConfigInfo oConfigInfo; oConfigInfo.set_file_name(m_stManagerInfo.strConfFile); oConfigInfo.set_file_content(m_oCurrentConf["custom"].ToFormattedString()); - oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + //oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + oConfigInfo.SerializeToString(&m_strDataString); + oOutMsgBody.set_data(m_strDataString); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); return(true); @@ -2300,7 +2310,9 @@ bool Manager::OnGetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) std::stringstream ssContent; ssContent << fin.rdbuf(); oConfigInfo.set_file_content(ssContent.str()); - oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + //oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + oConfigInfo.SerializeToString(&m_strDataString); + oOutMsgBody.set_data(m_strDataString); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); fin.close(); diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index 29ccb317..db9cfdb9 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -301,6 +301,7 @@ class Manager: public Labor mutable uint32 m_uiSequence; CJsonObject m_oLastConf; ///< 上次加载的配置 CJsonObject m_oCurrentConf; ///< 当前加载的配置 + std::string m_strDataString; char m_szErrBuff[256]; std::shared_ptr m_pLogger = nullptr; diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 03855c95..72d9536f 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -664,7 +664,7 @@ bool WorkerImpl::OnRedisConnected(const redisAsyncContext *c, int status) if (chain_iter != m_mapChain.end()) { chain_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = chain_iter->second->NextBlock(); + eResult = chain_iter->second->Next(); if (CMD_STATUS_RUNNING != eResult) { RemoveChain(uiChainId); @@ -773,7 +773,7 @@ bool WorkerImpl::OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privd if (chain_iter != m_mapChain.end()) { chain_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = chain_iter->second->NextBlock(); + eResult = chain_iter->second->Next(); if (CMD_STATUS_RUNNING != eResult) { RemoveChain(uiChainId); @@ -2775,7 +2775,7 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const MsgHead& if (chain_iter != m_mapChain.end()) { chain_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = chain_iter->second->NextBlock(); + eResult = chain_iter->second->Next(); if (CMD_STATUS_RUNNING != eResult) { RemoveChain(uiChainId); @@ -2859,7 +2859,7 @@ bool WorkerImpl::Handle(std::shared_ptr pChannel, const HttpMsg& if (chain_iter != m_mapChain.end()) { chain_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = chain_iter->second->NextBlock(); + eResult = chain_iter->second->Next(); if (CMD_STATUS_RUNNING != eResult) { RemoveChain(uiChainId); @@ -2896,7 +2896,7 @@ void WorkerImpl::ExecAssemblyLine(std::shared_ptr pChannel, const if (chain_iter != m_mapChain.end()) { chain_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = chain_iter->second->NextBlock(); + eResult = chain_iter->second->Next(); if (CMD_STATUS_RUNNING != eResult) { RemoveChain(uiChainId); diff --git a/src/logger/NetLogger.cpp b/src/logger/NetLogger.cpp index 2b3f0824..df835148 100644 --- a/src/logger/NetLogger.cpp +++ b/src/logger/NetLogger.cpp @@ -59,7 +59,9 @@ int NetLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLin oTraceLog.set_code_file_line(uiFileLine); oTraceLog.set_code_function(szFunction); oTraceLog.set_log_content(m_pLogBuff); - oMsgBody.set_data(oTraceLog.SerializeAsString()); + //oMsgBody.set_data(oTraceLog.SerializeAsString()); + oTraceLog.SerializeToString(&m_strLogData); + oMsgBody.set_data(m_strLogData); oMsgBody.mutable_req_target()->set_route(m_pLabor->GetNodeIdentify()); m_pLabor->AddNetLogMsg(oMsgBody); } @@ -94,7 +96,9 @@ int NetLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szF oTraceLog.set_code_function(szFunction); oTraceLog.set_log_content(m_pLogBuff); oMsgBody.set_trace_id(strTraceId); - oMsgBody.set_data(oTraceLog.SerializeAsString()); + //oMsgBody.set_data(oTraceLog.SerializeAsString()); + oTraceLog.SerializeToString(&m_strLogData); + oMsgBody.set_data(m_strLogData); if (strTraceId.size() > 0) { oMsgBody.mutable_req_target()->set_route(strTraceId); diff --git a/src/logger/NetLogger.hpp b/src/logger/NetLogger.hpp index e36a2d2a..80c9c715 100644 --- a/src/logger/NetLogger.hpp +++ b/src/logger/NetLogger.hpp @@ -58,6 +58,7 @@ class NetLogger: public Logger int m_iNetLogLevel; bool m_bEnableNetLogger; Labor* m_pLabor; + std::string m_strLogData; ///< 用于提高序列化效率 std::unique_ptr m_pLog; }; From dc108cf94a2200c6b767ba1c97356e6295ddd883 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 29 Jun 2019 11:33:59 +0800 Subject: [PATCH 042/176] rename Matrix to Model --- src/actor/Actor.cpp | 4 +-- src/actor/Actor.hpp | 8 ++--- src/actor/chain/Chain.cpp | 32 +++++++++---------- .../{matrix/Matrix.hpp => model/Model.hpp} | 30 ++++++++--------- src/labor/Worker.cpp | 4 +-- src/labor/Worker.hpp | 2 +- src/labor/WorkerImpl.cpp | 30 ++++++++--------- src/labor/WorkerImpl.hpp | 16 +++++----- src/labor/WorkerImpl.inl | 4 +-- 9 files changed, 65 insertions(+), 65 deletions(-) rename src/actor/{matrix/Matrix.hpp => model/Model.hpp} (52%) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 3e9d6830..58313d09 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -96,9 +96,9 @@ bool Actor::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg, return(m_pWorker->ExecStep(uiStepSeq, iErrno, strErrMsg, data)); } -std::shared_ptr Actor::GetMatrix(const std::string& strMatrixName) +std::shared_ptr Actor::GetModel(const std::string& strModelName) { - return(m_pWorker->GetMatrix(strMatrixName)); + return(m_pWorker->GetModel(strModelName)); } std::shared_ptr Actor::GetContext() diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 5db2a1bb..b8c6af7d 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -45,7 +45,7 @@ class Session; class Timer; class Context; class Step; -class Matrix; +class Model; class Chain; class Actor: public std::enable_shared_from_this @@ -62,8 +62,8 @@ class Actor: public std::enable_shared_from_this ACT_PB_STEP = 6, ///< Step步骤对象,处理pb请求或响应 ACT_HTTP_STEP = 7, ///< Step步骤对象,处理http请求或响应 ACT_REDIS_STEP = 8, ///< Step步骤对象,处理redis请求或响应 - ACT_MATRIX = 9, ///< Matrix模型对象,Matrix(IO无关)与Step(异步IO相关)共同构成功能链 - ACT_CHAIN = 10, ///< Chain链对象,用于将Matrix和Step组合成功能链 + ACT_MODEL = 9, ///< Model模型对象,Model(IO无关)与Step(异步IO相关)共同构成功能链 + ACT_CHAIN = 10, ///< Chain链对象,用于将Model和Step组合成功能链 }; public: @@ -114,7 +114,7 @@ class Actor: public std::enable_shared_from_this std::shared_ptr GetSession(uint32 uiSessionId); std::shared_ptr GetSession(const std::string& strSessionId); bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); - std::shared_ptr GetMatrix(const std::string& strMatrixName); + std::shared_ptr GetModel(const std::string& strModelName); std::shared_ptr GetContext(); void SetContext(std::shared_ptr pContext); void AddAssemblyLine(std::shared_ptr pSession); diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 148fe44e..dec5dbbd 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -11,7 +11,7 @@ #include "Chain.hpp" #include "actor/context/Context.hpp" #include "actor/step/Step.hpp" -#include "actor/matrix/Matrix.hpp" +#include "actor/model/Model.hpp" namespace neb { @@ -52,17 +52,17 @@ E_CMD_STATUS Chain::Next() for (auto iter = vecTurnBlocks.begin(); iter != vecTurnBlocks.end(); ++iter) { LOG4_TRACE("(%s)", (*iter).c_str()); - std::shared_ptr pSharedMatrix = GetMatrix(*iter); - if (pSharedMatrix == nullptr) + std::shared_ptr pSharedModel = GetModel(*iter); + if (pSharedModel == nullptr) { std::shared_ptr pSharedActor = MakeSharedActor(*iter); - // pSharedMatrix->SetContext(GetContext()); it had been set in MakeSharedActor(). - if (Actor::ACT_MATRIX == pSharedActor->GetActorType()) + // pSharedModel->SetContext(GetContext()); it had been set in MakeSharedActor(). + if (Actor::ACT_MODEL == pSharedActor->GetActorType()) { - pSharedMatrix = std::dynamic_pointer_cast(pSharedActor); - eResult = pSharedMatrix->Submit(); - pSharedMatrix->SetContext(nullptr); - pSharedMatrix->SetTraceId(""); + pSharedModel = std::dynamic_pointer_cast(pSharedActor); + eResult = pSharedModel->Submit(); + pSharedModel->SetContext(nullptr); + pSharedModel->SetTraceId(""); if (CMD_STATUS_FAULT == eResult) { return(CMD_STATUS_FAULT); @@ -87,18 +87,18 @@ E_CMD_STATUS Chain::Next() } else { - LOG4_ERROR("\"%s\" is not a Matrix or Step, only Matrix and Step can be a Chain block.", + LOG4_ERROR("\"%s\" is not a Model or Step, only Model and Step can be a Chain block.", pSharedActor->GetActorName().c_str()); return(CMD_STATUS_FAULT); } } else { - pSharedMatrix->SetContext(GetContext()); - pSharedMatrix->SetTraceId(GetTraceId()); - eResult = pSharedMatrix->Submit(); - pSharedMatrix->SetContext(nullptr); - pSharedMatrix->SetTraceId(""); + pSharedModel->SetContext(GetContext()); + pSharedModel->SetTraceId(GetTraceId()); + eResult = pSharedModel->Submit(); + pSharedModel->SetContext(nullptr); + pSharedModel->SetTraceId(""); if (CMD_STATUS_FAULT == eResult) { return(CMD_STATUS_FAULT); @@ -111,7 +111,7 @@ E_CMD_STATUS Chain::Next() { return(CMD_STATUS_RUNNING); } - else // 只有Matrix的链块(无IO回调),执行完当前链块后立即执行下一个链块 + else // 只有Model的链块(无IO回调),执行完当前链块后立即执行下一个链块 { return(Next()); } diff --git a/src/actor/matrix/Matrix.hpp b/src/actor/model/Model.hpp similarity index 52% rename from src/actor/matrix/Matrix.hpp rename to src/actor/model/Model.hpp index 9f65d84d..d872b989 100644 --- a/src/actor/matrix/Matrix.hpp +++ b/src/actor/model/Model.hpp @@ -1,14 +1,14 @@ /******************************************************************************* * Project: Nebula - * @file Matrix.hpp + * @file Model.hpp * @brief * @author Bwar * @date: 2019年6月7日 * @note * Modify history: ******************************************************************************/ -#ifndef SRC_ACTOR_MATRIX_HPP_ -#define SRC_ACTOR_MATRIX_HPP_ +#ifndef SRC_ACTOR_MODEL_HPP_ +#define SRC_ACTOR_MODEL_HPP_ #include "labor/Worker.hpp" #include "actor/Actor.hpp" @@ -17,25 +17,25 @@ namespace neb { -class Matrix: public Actor +class Model: public Actor { public: - Matrix() - : Actor(Actor::ACT_MATRIX, gc_dNoTimeout) + Model() + : Actor(Actor::ACT_MODEL, gc_dNoTimeout) { } - Matrix(const Matrix&) = delete; - Matrix& operator=(const Matrix&) = delete; - virtual ~Matrix(){} + Model(const Model&) = delete; + Model& operator=(const Model&) = delete; + virtual ~Model(){} /** - * @brief 初始化Matrix;重新加载配置 - * @note Matrix类实例初始化函数,大部分Matrix不需要初始化,需要初始化的Matrix可派生后实现此函数, - * 在此函数里可以读取配置文件(配置文件必须为json格式)。配置文件由Matrix的设计者自行定义, - * 存放于conf/目录,配置文件名最好与Matrix名字保持一致,加上.json后缀。配置文件的更新同步 + * @brief 初始化Model;重新加载配置 + * @note Model类实例初始化函数,大部分Model不需要初始化,需要初始化的Model可派生后实现此函数, + * 在此函数里可以读取配置文件(配置文件必须为json格式)。配置文件由Model的设计者自行定义, + * 存放于conf/目录,配置文件名最好与Model名字保持一致,加上.json后缀。配置文件的更新同步 * 会由框架自动完成。 * Init()需设计成可重入方法,因各服务框架在收到Beacon的重新加载配置指令后会执行每个 - * Matrix的Init()方法,所以必须保证Init()方法执行后没有副作用。 + * Model的Init()方法,所以必须保证Init()方法执行后没有副作用。 * @return 是否初始化成功 */ virtual bool Init() @@ -54,4 +54,4 @@ class Matrix: public Actor } /* namespace neb */ -#endif /* SRC_ACTOR_MATRIX_HPP_ */ +#endif /* SRC_ACTOR_MODEL_HPP_ */ diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 7c980288..51d5f30f 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -54,9 +54,9 @@ bool Worker::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg return(m_pImpl->ExecStep(uiStepSeq, iErrno, strErrMsg, data)); } -std::shared_ptr Worker::GetMatrix(const std::string& strMatrixName) +std::shared_ptr Worker::GetModel(const std::string& strModelName) { - return(m_pImpl->GetMatrix(strMatrixName)); + return(m_pImpl->GetModel(strModelName)); } void Worker::AddAssemblyLine(std::shared_ptr pSession) diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 773a0ac2..a1a65241 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -51,7 +51,7 @@ class Worker: public Labor virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); - virtual std::shared_ptr GetMatrix(const std::string& strMatrixName); + virtual std::shared_ptr GetModel(const std::string& strModelName); virtual void AddAssemblyLine(std::shared_ptr pSession); // 获取worker信息相关方法 diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 72d9536f..2561d856 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -29,7 +29,7 @@ extern "C" { #include "actor/step/PbStep.hpp" #include "actor/step/RedisStep.hpp" #include "actor/step/Step.hpp" -#include "actor/matrix/Matrix.hpp" +#include "actor/model/Model.hpp" #include "actor/chain/Chain.hpp" #include "actor/session/sys_session/SessionNode.hpp" #include "actor/session/sys_session/SessionLogger.hpp" @@ -836,8 +836,8 @@ std::shared_ptr WorkerImpl::InitializeSharedActor(Actor* pCreator, std::s return(pSharedActor); } break; - case Actor::ACT_MATRIX: - if (TransformToSharedMatrix(pCreator, pSharedActor)) + case Actor::ACT_MODEL: + if (TransformToSharedModel(pCreator, pSharedActor)) { return(pSharedActor); } @@ -849,7 +849,7 @@ std::shared_ptr WorkerImpl::InitializeSharedActor(Actor* pCreator, std::s } break; default: - LOG4_ERROR("\"%s\" must be a Step, a Session, a Matrix, a Cmd or a Module.", + LOG4_ERROR("\"%s\" must be a Step, a Session, a Model, a Cmd or a Module.", strActorName.c_str()); return(nullptr); } @@ -986,13 +986,13 @@ bool WorkerImpl::TransformToSharedModule(Actor* pCreator, std::shared_ptr return(false); } -bool WorkerImpl::TransformToSharedMatrix(Actor* pCreator, std::shared_ptr pSharedActor) +bool WorkerImpl::TransformToSharedModel(Actor* pCreator, std::shared_ptr pSharedActor) { - std::shared_ptr pSharedMatrix = std::dynamic_pointer_cast(pSharedActor); - auto ret = m_mapMatrix.insert(std::make_pair(pSharedMatrix->GetActorName(), pSharedMatrix)); + std::shared_ptr pSharedModel = std::dynamic_pointer_cast(pSharedActor); + auto ret = m_mapModel.insert(std::make_pair(pSharedModel->GetActorName(), pSharedModel)); if (ret.second) { - if (pSharedMatrix->Init()) + if (pSharedModel->Init()) { return(true); } @@ -2231,12 +2231,12 @@ void WorkerImpl::UnloadDynamicSymbol(CJsonObject& oOneSoConf) std::unordered_set setStep; m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); } - for (int k = 0; k < oOneSoConf["matrix"].GetArraySize(); ++k) + for (int k = 0; k < oOneSoConf["model"].GetArraySize(); ++k) { - auto class_iter = m_mapMatrix.find(oOneSoConf["matrix"](k)); - if (class_iter != m_mapMatrix.end()) + auto class_iter = m_mapModel.find(oOneSoConf["model"](k)); + if (class_iter != m_mapModel.end()) { - m_mapMatrix.erase(class_iter); + m_mapModel.erase(class_iter); } } } @@ -2427,10 +2427,10 @@ bool WorkerImpl::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strEr } } -std::shared_ptr WorkerImpl::GetMatrix(const std::string& strMatrixName) +std::shared_ptr WorkerImpl::GetModel(const std::string& strModelName) { - auto iter = m_mapMatrix.find(strMatrixName); - if (iter == m_mapMatrix.end()) + auto iter = m_mapModel.find(strModelName); + if (iter == m_mapModel.end()) { return(nullptr); } diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index ba701bdd..47c0147e 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -77,7 +77,7 @@ class Context; class Step; class RedisStep; class HttpStep; -class Matrix; +class Model; class Chain; class SessionNode; @@ -212,7 +212,7 @@ class WorkerImpl std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs... args); template - std::shared_ptr MakeSharedMatrix(Actor* pCreator, const std::string& strMatrixName, Targs... args); + std::shared_ptr MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs... args); template std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs... args); @@ -222,7 +222,7 @@ class WorkerImpl bool TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor); - bool TransformToSharedMatrix(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedModel(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); public: // about channel @@ -262,8 +262,8 @@ class WorkerImpl // about step virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); - // about matrix - virtual std::shared_ptr GetMatrix(const std::string& strMatrixName); + // about model + virtual std::shared_ptr GetModel(const std::string& strModelName); public: // Worker相关设置(由专用Cmd类调用这些方法完成Worker自身的初始化和更新) virtual bool SetProcessName(const CJsonObject& oJsonConf); @@ -368,10 +368,10 @@ class WorkerImpl std::unordered_map > m_mapCmd; std::unordered_map > m_mapModule; - // Chain and Matrix - std::unordered_map > > m_mapChainConf; //key为Chain的配置名(ChainFlag),value为由Matrix类名和Step类名构成的ChainBlock链 + // Chain and Model + std::unordered_map > > m_mapChainConf; //key为Chain的配置名(ChainFlag),value为由Model类名和Step类名构成的ChainBlock链 std::unordered_map > m_mapChain; //key为Chain的Sequence,称为ChainId - std::unordered_map > m_mapMatrix; //key为Matrix类名 + std::unordered_map > m_mapModel; //key为Model类名 // Step and Session std::unordered_map > m_mapCallbackStep; diff --git a/src/labor/WorkerImpl.inl b/src/labor/WorkerImpl.inl index 8aeddfe7..c0b8e67e 100644 --- a/src/labor/WorkerImpl.inl +++ b/src/labor/WorkerImpl.inl @@ -71,9 +71,9 @@ std::shared_ptr WorkerImpl::MakeSharedContext(Actor* pCreator, const st } template -std::shared_ptr WorkerImpl::MakeSharedMatrix(Actor* pCreator, const std::string& strMatrixName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strMatrixName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModelName, std::forward(args)...))); } template From b23942b11b5b12e6d59a37d29aff99d7bbd7cff0 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 29 Jun 2019 11:45:10 +0800 Subject: [PATCH 043/176] rename Matrix to Model --- docs/image/Actor.png | Bin 166107 -> 165681 bytes docs/image/dynamic_chain.png | Bin 18677 -> 18660 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/image/Actor.png b/docs/image/Actor.png index 5a4105561e8979eefb0d4008f69d8a0ebe4d114e..cdf847868fe41240eeb9487ea19addff73379dc2 100644 GIT binary patch delta 102009 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ðm#KER8&;oo14$wdIeh*)WwprGDb%wcz$0Pz#FAL4@hnF z_V>$IdAMiWfnr_@QR*!)BkDWT6?i=M?m)7ky?+8W^WoZC7<>`Ker)5`` zR#xQoEaBNAQG$p&DTsv0-=PbmqSkJ@A_I8*Prbd*fEhLcX`BRDx8nU=TwG|$1EJY^ zeMTvJRlV0auc&AOz?Aan(D(1YM7PxW^ADbVh=s7i9H_!?;J4+o)j4spva_MkG;H>v za*`s^UTvZfoV_=jX48xGO6Y zJ?##udHEhJE%^XA5&s1y1iB-vfR6kpGJxGbAuj6-oF<*un3?@S12Y%_9Th?-0ytol z|A4%gY`S9Y2L$>ZJm&HF~|T{_?<59>^#shi+N}0LPJFr{3Ifvl?yaQ#LUd>V{Y!3 z?(TPD0V_gr8p-k~k%9i(+S4PJERPnHsHmud;muivr)Z^+5Y8V3a# z1bY+rHXP(M)I2zIkM>bs0&N_F%ZxZ6xBr6kJbI9sKo9mYV(VuUX1`+e% zkz&-%!?)Ycf_co+sfqz@mOyvN&dJkN3e%ea2m^}RmhJkQVh*`8JSL`2Kp=7<4# zwur=tz9lWo7>akI-0;{g7dV+4KW(kf`kXmY3?*UzLZ2X4dL|2K?0rwM1 zO4~5&C)K^t!A~+FKAv}Jmv(SvZMZ0FeFpiH@Ll`6i0e#JZug-C;GxrLsHrjOuZd7l zQo^|5jre%8r%Qo@N_rJEzVY$nW>dkva_x8OA^B_pB{3*qPyb-N_G5RJgBq(c=%Fw1;-zVoL4Ds zzT9A@QcTE3d=L+J`4lEBAh1uW(AuIx8E!8U?09Yns!B#itXf)Hm^QD1q1ny5ck8#s zdruFXM%b-_#K5w%P@G#mj+2KcNxLR>w<6wLp00O}2KW5^ziM{gqCdQTcgVh*AN7!k zK>q2Bn^03zKY^=O4KpW3B4?1%s)!ATESo-doNaok#f?eNI*?#ETl-O~!kRS*1KE*c zjUZSlNYWPW5??bd=#VLzz7;=lkTOiT9!$O-M=pP)I2N_XoUBXVI)gT+1(sh0!J%HE zo8Z}3+H44J;B!pTQpZw9oUtq?8&h<7$gWojP(q{tHia(bHFc4hl1rPg5E% zaMj12fm!M$3yZM(_e((uMM&PI39K&!7Yjb4w4);ieRbp^gI9!eT#$tNy-LsHkGI7y zlMnNRqb;&6&h|xQ;afhpN%|rJwsgAQnEt+b>sCl)Z`dq3`Z0}AP>DmMx(}7x$Wu_ z3e@{+IO-FLr$slz;K`V&tkydI0auj)MJ<3DW*E_hmxiOm#dxUK$ zm)8ac3sXo+N|J>jrb8Y|xeH1V-Ff1fqnY~Jb}b%$i#`m%EuQ3 znc_~5EvP#Dcj@8uP?ndM3%HK!lWh%@V@go8qPeXhzE3Jto>Boeq1Ln@jNNoH`M&DeN?)4gZhRYHA{2GQnY}0L7D% zikY0E@lI&m<{uEiFYx$%E#j=Fs(g=})JgUBLkFtEK(GN&j z>K2`QVW2HS<57GKd^7}ne11*TY;hZ!LVhvU8_YH|Y?_>#lT%l}XW!eA=RSRS4OOe@ z#WS})JsUh~XZ?E?eQn4{R zUt6`(SlcZ14$bpc-kUJ%5c{jc)=gijZj?>$fz;t=tKH48X0J!0jo#E+PBXh*#`WKU z7<*~Z9;5Nxe0p)j<^1!FJw0hXKYrA~Lbnp%)`xH7=ik#iTIkPp+OT<{V@Kq!V-61N z(S%6;)*a4kw3&pBw@&=VElsPDZRbDE-dKje0+>D zZND>-+>4*mJ}^=igAHbiE}R5d`_qU7HN@)3>E;BxtAh<)HE*MYMRD=^-E>1>tUi2~ zrT_!+y`Z(aO8KnB+CYbyp)E2Cm)70B{rT%_V-`Tc<~XPn5C{sG*Xi#$;uiJ2pa>A= zU~>BGj)!WOlVqC{QlDKpD=%XGuy&x{wk~!gv3B@?NIEN_PaK0~yODQ|V_U(Q)g^GQ27Y^MlS{Ar-=hW-$F?G!*s>R=q zAF8TuZed;oQMp*#HeD77@RTOKlJL6NlMIyzrg)syFo z2^OWw;g&R;;D7+$i9+G)7jn~{Y4*z~Qg{+x2^^N$Nmk=6!;yB%fhUC|UEK+$?zPA*&rOy24)q4F+DN(RK zezW%xraxt^tv!BBlmup5$yPQ`x0=j-0t*gvY}*=;)0|@Si6)W^G)S+a;%-+tf_9FT z*Sr!39VqT8G8E9{@u=|5T`P+F58ZU#V~P9ptEX!4kpFtpfBnB_PSq_Z8cRW8(_+%+D-@_jDDymi(`jn5?1*Xrat-83!)tYUu)M5qybLL!Z29H{Z2v89IC7UYxGAIRC+D6FXTZyNTT`FHM6%3$k;Rsg8-A z41Ew(+iFd8a`AAWAQRqHb+kM;k$67rh-r=IKn;o6}Kk=5s8QEGaes+9rT zC`a1|@A_3th@JlGQ|dGxG;fGS?EMAq{Tb|r z3T9iC7$l#uhy8U?xrxLP&O_zXq$@T`l5kf+PV;hM4UM0q8J+1?)g*k|$O*D)j#vKE2jhoqg*1xVZ2-{KKv z;FFWu4FO0SX=f2YS>Huqv|lwQxcrt|NMDhN)JkH$&d~WmzC8iQHTJM`a6~+G8%fO_ zYRlw?zGwi2Q@>6E$Q{HvMof{Fp_C!3!TbnCC{mhG>+|f`vW0A|-(bc=-@w56n*{`b zV&dYIWPu4Auahv7{UQQThU`iq@aZOohN|GtQxDuhSzZ0`Z&Uhb{K_fRlb1MM6wD&S z!xd1f!}fh2d`uQlw#=!-@IBIvYBXAt3d)L$yiZToJ5YZuY*pI%aT3PA6Bfq3%Pw1~3-d(2*<~ zbaZ&o=a&v$$f0$0b%mv|B05}D(KgpCJv}^n#>X2-768CbVVOr@3v=I$|=M@9(AG>?b@|#1t3k4Yla9&5FVO|C>p^D*u&=sW(@aYW1(HC zKOi6*ut`HBqVL<2$@!zto@A*q@KFHTGlAtVAjqEX?ozbOSzq9b83Tx*87oFWvpp2E zX9s|N-rer&Yc?Zyw6E04w1J7wquvlaM}Jq_W*s?tv>y`-tMe#(Bh0Yn#S5B3S|+jk zK#!VA2m;t;;+>rz``n?vJ`v0mkeYgsY&U?`YByEY)%5{PeTrELvU~?s9|p^w6gnE3 zXRddgsT2OGCBRAzPgK9qN`guCc-BiV7k+Sj?cL)vu_Du&Zq#CVVe%7LF&^PzDi&}#x%ef&;ilZL~;qQmeW zDn*;bfLq9sQyBaq7txR{H3S4Cz7Fc48-3@x1P*()1J6SB_p)>;Io{>rNewNhDa9Dq z{xL;Qn6&IjG0L&tjY%?NxJZG~cnCo#5Zzk%=n*v1z+`v@d1~7E(hEuI zC_MQ3{*#g52;Kwupx(5p49W#GfL7m=DDz_tNRmfPj1e?vM@L7vGkiZt@B=@ObRLJ( z3n*YaO1EF#l;{L;3sw1wfT6`!RaIGdWy$$Bory2pbmkMp4k+*LWKPTdA(o5KFPWmq zwe9>9R9gC*kx|=tX2>1bXKIxbq^NCK)pwJUwmp3G1%%_=*MipQqgHATy&v}f-E>q` zRVnaw;jc8aU$G%ckABO(E^Oh$hEO8fHToe*Ba^JQ(5?KVlzyenyBK^EYJ|SQ$GY}Q zE(GoG5z+V6mNNCxgInhA1k02)#PUHfd?4E@TKGzfHK{=34SJx5yVWFEV%au>l z9~ay^pUTd2dE3*cD-h=0dueIFMX0Zc7@aS0P3IyNP((4M5sga+v#!Al6s;&mkw+R z$j9^-`54*4!iIh?`FU(AsvxK2KEK}iEJfuXF`h7+;}oQ)r}tHj*UX^;lkyHstFJEw z{M6sUA(jMzQ+1kgjF3%0aB+j8#Q>m|ic*i9wt~8c%}2LCdqQ0E6B<0--G`a@fWH3# zGKMf{a_Q1qUs%mfd0hwg>syOCoR}d#fWW%M+|B9Ju2$_ zQyco{4W(q)D`K+;qXgig9J)&>@q5GH%KjH@o(CPguTE@hp}Mm2J_6L&fwO%Us%!^@ zpp5X(%j1Je-A^ag8oZ$O}g-m^cV zc7}C#_d-Emve|WRnREaC*QQG2_l8|33~{s04%740e0nZ9-xs#S&11b*x*?T_oncA} z-XG(-hxsYzi5;p1F4AaL4j+mAc!o$gec@K3g@Czvq;=0cl~_uA-Hylj%8UOM0J0=$e~zZJ=ZEdbVK`jm9HRotth#D5>7?S<+tq>DW1j zX>36A8eg!MVLjq4NNYR@D8N-lynzLA9#Xh9%h|gAZsmx}@!FFF15lnl*nF|FvSbGs z7#QbfkUe;fNm)M62w=8!Cw2-lKV!!aQV9)zSoss4gCy*Unk0&y5B0{cU%wb!*^iBD zD?S=@#E8V+?DMD5ooDA*cH{}tu}FRNV03`PP(kxn`STq{LQpOs z7Ufw;Xs8_M8eB`2w~oBwqq#@F<@^IlS*5X^EG-AeHgx1+;WNIl;l_{zu$^M?QB)BJ z5sf2`|?t0P~|_OJPvW}psHt0VpcUCPx7Lna9I)+GpEs?vr8 z(GP9n>}*-Seeatux3G8N?NpmZk&2ocz$I;~SV%+Jxz)!9S6A$|K*fGw{ra<)JO7+! zV&a-=w_Bljk-%l&SLFhIXJhT*+;1`z>{6CB9hXMNrz-y_6kbo*5>ude>jVoK7$^hwg#k&z=APXnk<8^~fz0!0)9v8yLvBh*U1(|gL0gGM!(GXqtk zyqA|0sHA9}K?77)2ptVNZ zKW=Qur#mMkYL~(;s&?zb7Ys^j{Cjt79e66)WsLB zX(gvsA6q)t8|%MGcOVa`sCqy#-2;>WSB;~lF(inNCN#J6r+jll`Y!j;o@!ED0+lQ( ziiGAI@5LF7ahijdyL<^>3X;Hl`qa4iH;CNl5S37X4mtxyjeH|XPdqcmWu!#U3;)C6WW1hsoK42I`R)lzmF1C zw--8alo(>pKev;$OsOwPVeIln@jKZdIRQZ~1G%0JW_Atma$SJ*XE#h7)rK!XuLWQe z4H}7tLTA;BJi*DuHHA_|ivVX1W(Hfe-J&Q55JT79V-tN8tky1b5mwJaPT@c##h zXhP*fJh%P6er8nsj#({NpJ(@*1T793nMm{c&W{I9#!BpjQbH@!oOz$?V#0d~8hw4K zb480lL8Q#WIl360sW*_GTdOvVR5(nylD zFi}T;w9x;6RFi;0B;EN7{}5BPm(0y!==aPT3I%&dM^orwps0Qt9<1q~$o;RmheLu- zUN#~(BYi08jraDJVW^*^ae$f?y-DTT!a^LjXN%kQ)^P8UBhKxWm5HVOI59O=1}5ve zQNfx4s6#J73WKncst#hS2>14b!0QP-64{k`4$Q)RfVBrI+)ViXU1_OW9=Efws6;r~ zedIDdWE7=Sq-VhtW`w zDZ;R=ML=;Mj(>2~#pT1NPa1<>#n?FrhdLnooP2yl5J(JR5aDLtFyj}4weQqNdvu5p znlWN`R)ae=Ta9`=rWH>99w||{vHlBQ)E}dTzAY`BU|sk|Qd1sjras!ptu~ry*Clw# z%r*(NQ^$N((4lXao10fT;#V+n#x%bHPjpJP#RmZ7FhE1lXaxO z(e^oM@1=p+tF13hk4KAqhrnkeFloz=rH@e7XeZB3_y;GP zHZtughmL5V1OO8A1)_aL-N0q?QjTrCk)2vxiw}!3!gqG0bnyx-*I;OyC3$m?h;>q3 zSyFm>BgiUp&J5DagB4AiG+h3f`-MCUZDkKlysgZQ$fJ)qZY-#&y?pqP4h3oFivOFI zn`Q>T0tGCe(=wc$%TeEDMN0c9<(3mqc=+}YUro|U-2<>W@9o>4F<*LXVV6D+&v}}9 zQ?!*3f-dP7dF+4;(`Wg&E@qsD;1i-|wp}j#Fc6>S#jTA z`#_!R;K6lJ(|Y}xWJq-)42hy`&y^bc6Cdx?z(KNUpLOeb6BL*T+yY_|KfGVDXdBlc zx`letnK3+j6LxMZ;F3}5kjLe>==)EaoY7v7uQkKT#|xI>G^|;?G%g7ZVgCZPoD_H5 z2%qNP$lYnZnIpI{Xks8A2}75A?$4H1SUN;;kmFS#hhGM`V~&E{ceH!;>5JS@l&7ylOrGb49L`Qx86OHvPo zD$)vr_WcKBHgu|F{`7zR4Yh_|5k0}d5a;*uu0sEBsk;yV9R>BTtoC2a480!@@fJqe z7ULar(Dql@?%is?)^$!0qYHizh1p~7BJp4veLvb4LA>i`YG0a!tiJF=)iMvukWKoe z++Mh4)t@*yxW%wmL@`__;y{##XPw_wN<2Eo7wQE0OrJ(|5e_=VFB z?VSIP4C?tLZj+OjU~m#}95_MoB}M#Y^X$J6CI|HiP%1=}q$@T0=JW0Qxw+q9ItOxL zsO|UPSbzFr+3ox!?F!dp)EXMg7^?@mpQ+t^N>!DelXLC1ZDDKnvZBf7*UzKRK96x> z`IiF*J4x8{h$RD*6YoBK3V~cE8VMXGIfmfS8t<2@R;#3F8y<+D(bd%j#s5&+lMn%Y zyPt>09{{9@v9ZSJ%A>E0M=qav8U8;amzqY3mU|(M1YC^I%THYO}Dg#ypS+WHUwmfroa$q^-<>xD;RuaG^gkJ&_xLUB`evqg8TNfY zVKP;LZHi?O#G)u5sL+XhC@)_NPf!wb0AHyJ+Q?aT5cBu?C2^~{sYv6bUJ@+;PW!u$ zAL${wgCa&5pI3G1Y8)?qZ>L-D=JQ!q%du%jB|cDpGBEL%G;gGg$m3x9rwT$BQ~9CFDu*a%d0h-m7pZy$VI(38aSb}wD-5{Of<`hdwqBnEbTFKhmuLcCK_rI z2&2zIAPlJNkU%OBKz(eysUj8g-wQ;6E}qvi#pn)< z!fGHlhQh7v`*&tMU!bZ3@9UcxSD3HWAk}L?MpT~YCe>6}@W8DLN(YlrUQKG4#EtWis)4RF>U*S_l z0je@aT?e~_>;xCyL+c3~B9);%yO8IkAqheX+i}4t@Kx`v&y784(iQO+np|3XD^pK) z?D%5d;)Y5BI0Uqtq^SzP0CKtc7ZHZAMmmGUP4EdLB}f2;GQNJIM6F7i{zI7`1|UHI z9#&9;MEq`OXrRF5^EPLiTUcbeP7sDNkT#i&RAIWInw)md&hAIyvk$TvJy+xoz_#Qj zG0dWO!wmR*B?&;Ab&>NAfIfd_89wN(;Y0N5ELvTTRfAf~({m{?j_b7lF2(pUWuDDI z`c6gX(5N?FtgGX1E6ne9cb!(d`@&to39Kc*Cmc#7eK@t5w-x*|geo1FihlKHiMal(XvvUa%d| zkW*nTS-f^eC5knQRWaqhk;=kvy#b{@zLbrua+i~AmPC&f zgTSd{HJfV}?RAc)Who2f)C$8P%_g(I+5uB`#aD@_dJuMWn#l8X3!zklvr zqVommB7o0;{H=(w4$=n$T|ikaQ5yuBhmY<`^cd7Go+s`Qw?sMp{*Uep`oS zOVUI%uU98OTIK{ z?pS)rrd>_;d#A`?T~yeJNNn@0E3TY0%RoLuTG_qtD1nYp8IVE#_^1PYIA3T{bafD3 zj!(!XnCYmuBrP%BmB`5|kaWhuHcUcxkMA!RbEn^X-ZXI4cKT3(=?TxMEcf|}U4+pu z6w?_Xv8QWhaDK_LMJVr(V4pg?fn1gP>QD=8ms%XpldCu6H$=r2S%2G-OHM^L#8?*J zyK|=xO(`gWYq+}T_Gx&Z2%_!)Vfolh3Oj%(w(OELGXtJs?umoTonAOaBNDwi`Lin4 z*X2PTOpu@#W(Z?sydPlz*|AO6(EXNbgl1!jG+g@&XCL)0)~_$S)v)$z#RU&+%)lrccR1n$ zW_i|#jsCI`*$dr7A(mDQ98aq^2cpAV2>#>P&$D0ltA_6r7mvb})MO1`I_YEMXGV!z zqJ;MXVC^Mr!XRT0-d}pys$hIafc6Fim)rL4J&A26Ohgau#*LIu07jD4E!v_fOh-(^ za_W+#H{N`F>n_-6gH3n`+KDXfMWXMbf)Rq2f4V#gmx3?K55&d}7;tWa1%98v1bAhH=-;3TBq2h)=;ZF@t;YwuYU>UAWs6H7F zVRrm`t^Zbzx&`PJC2)YF>py`1bw@U5?t~5O-rZB!=AB_&wq9745)t=PYTvPMIk9a{ zODbU+#2{voAV0N^tGn5v_6roPWZxMb@ykTIe#mIYDMNteSFuo9?9_!qHzGq% z2_M1HxiCy`=@eh4w>sdD=^3#DHe0%sJI!3{9_q0iI``Ta65{AsYJ7MIF>iy?G!WOj z`GR>W5%uP1NB%a^+>}O3+(ewL8jvS-Km4)v?KB#=dI<{ba1AnML?I$9&x{kPC!_Qf zG4>q)P6&Rl5MLfgWN{DGhV$MqX2gNg0zrvD3?I6Yd{}(cHRNY8uXF8Om|Mt2;E1Xn zI3kY+Mos1gXp3k@-LM8%;M@aRqAD^xZ^cU4wh4|Ra0843ri83%UQoo2}%08c3_vmO4^pWj_ z@uJIbX8E=OV<$5cKs|D(0Sn#kqCX(*xS&+WQM6;)QrJ8-HD!X}TDUyhj7?I!q4Hl2POUB24Z`A1H)H$a9&#y#twDms5<@9Vn0{1c-{j9l)ze zk+E`*}ui0`V3}J<5!YaGu|QGjun)n$j~mGq<(w=i3l6=FP!w zikx=ceLHy`62-}1zn+|ap{EGb{(TfG3dgmhMZ^a#e#?vC3k};_9fU)Rix4`6aKwoQ zu(eV$D!Z}n#rL_s-}c(y8oe>#!ZdwIwDQhy?kzH1-iF^lB^9aQJ;6Pur$DI(8TaK zc|gfQ0hC9Wl5vZyjpWk}hCZPAsr-iNi?U$lVFY;wU*!1kA^tjA_7M7#%Z4vsqiTnf z^C^_Bxcq0B0BTb+$}}f7Lj|qCC$yH32BQg|Kq_#66bS-gD`iVt+>%hcLq|rtSP7QpGT#&zP_GGh97chTpPolJvHE19)f|^Nj_T1w6W)7|lv{FlO?;dyOzIFAac-RF%K7FXdX<21r$kFNW@N#^Ld00o( z3E{?qhZjNzidPhY=Lg!GEsZT1vb)CoSbYRo8En&6ONnb#cfcK>U`Ipx9^d;FlhjBr zwSqF9_aeek+?$dThyZf7* ze;$FgOOpO(tL+R;biQozYu+4wxbATJdElcF_+dh}`l60+O9&X}~R zMXOV1xKalf_Z)I8v=A=PQIRi=N@PgKF1&S+paen`?||iY`uh4veduQa1WS?@n&jv{ z`S~+rw!B1diUI(`FOJy!j$>+_R$E!=hj(=H%LThniR!E%`OxmataD-F;OX;euow# zIC!t5V5&>YBK45K1H8JtNSdHo=PW^55r*{9gm($cA2yK1=oNSsHxRJM^}QqhVAk8* z9j43Dq|+YhcM8y#Dye8Em!*%C(N%LMy=t2+U(LR(he#zvWV5&(AuTNptrJxE*MUXe zN7Lefp>*^ea$v!(jXHi*%O^BbnAXDLtm^bZFAn!fSj`I}wUb{0>Eh+qG-kXxi68|B zgm<7H`Nt7`P^mHOD4AGTTQ6veF)Okiz}ENGO2qnWExj3t#6mDVLk8UZg2nIK1Ku=o zOS@#9gPs+qh0$Wo$^qoGczh1QJM7YJ1mU&dTIH;x%?O);=F=c9Z1T3R%=k#zd2omgI05py!a}DU$Dkjjah1-Z>&w2ZgK(rkc@cHVAN; z+FvR;C{i+gB46`YINv&M+3Bj*R@V(5J+Mm(3e`U6--?mntbSnd|1>})N*4^vZ5VJ~ zRJzlTt==_0}n?e_7I}Br;w`g@5aDxC<@Uz<$k~RwnL&n&>>y`yXlN?8)t3VxbYKR*O3;( z*a=LPk?<;cE-1bs3s+zFI;aNWOy4@|le zz+249!p}(_FI_$QI|#yQ(D-5-P$X#@{^Qyy2P=Hjf&RdbK(7XlezEeAt& z-#>r;{P9{L){^d>+h>>ksJH)N-biFD&d*EPWt{V%y6daDF3&ckgHT={j0NfwHX`kL zFvbm23sw^6aDVwa+b+L;D0uBw-m5yv5n!gpCGSLkfk!mVBF9GsUND|XQ)V*{3J&(s zl=X_Ie@nrH9e)^v8!+mV=f3WLsdneKZJE^P`bLl0a$_hq_2AAR9GRl#whZMst%knP zd-v3>p17Vmb%(I+qPJ&o=Jdy(s``&~Mq9UrthtzdzM$P&gzAja_3amQlCyJ-85@`8 z=L@k1(6O^^(to>>j>U$32rGa6iF&i=_NUKRvRxDhufCWk|AU#wIOE(~F-gXd9#zSw zOiZ#X!c^<$1wCi2^cqg8zKu&4R8S)7!+E{udx?9Qg}=XS`!m}f;$`rd`bF>pc10O<(9=-(dKMWVib&%Fw#mhU{>%T8XOtAOx=YJ=@=H82{FA1@ksz*{mDM2n_ zX7T!U9Pq1qUx<=FMm2MtzgaHvhhj}aF7KJ|0(bUcgmp#y&_0K~i+pq)l(c2@<<&n3 z|LqrkDp$p?u87{QsyaNK3Y5oZzu5ds+E@laIh_2~3`HJy-<6dGamO)MJyU%5)jG#3 zNU_{J6Svcv98SYNIoR7v0dUoe1eSgXWdIqlNiR+O=NmBpI5TRvMUL4G_#~6>HyyqT zGf=-_lpyCdN=d<2oYRDd=!wd>BzLTIbmu7l?lw6(Xd zVu1v<7P5d7sz0mW=FP1xMNBG0*79~Kmg_kbtST3Ht9ON55bJp;4cNRbDQj}@mV>@%GM?fkF&#?$W)i z_BX@~^G4A(ryEqv+dq+-3Alj(CpaF!u%7~|x~oHZRo3|z;>R-msjydV*qm{|vA2Md zyLqeh5*t=MoLzA8asC!$6^QZ>2OjU8ZVbI+2okIe1Id# z#jfTk0Jm_T5YEH^H>ebOvMvBggY^4c+zu@$Y3xPdW$(t1L0+LG4R`TyanHn^*k54f zr4iD1IZ#x~PtVrj>-r!qEu3jONebnGF=eJ0Gp6Y0hk{;!fVLFP@<})&EZlIzyM}mX z2P@ukeJ(aO%6*lM+!ZH{Ya?(&N#Y8)Oj@g`=F}PERH8>9Ks^4NgRejX9!6iqm6?E zYFq&8jk>BqWmG}-?}jCDWC4W|`sf3#;vj$Fj-eHDY_pPJC1UXAJD`#G8*}<*BXp@K z7Lqh_nFw0=HNNPQKS6&l^XHfRzLBvZbbVwFBnRYYGB0K8yJQ^uS!{xvTR7byNiKbq zf`>lDo3%0NW0_T5kB(SQ`5H5Jj*pM$6%HHspUCGIDq$OZYb=Q=if(uA z+nk!#JKBS9?#1%1p>|g(OqqoZ4W&j!Zrl`I4iyBW=(upgsfEz_c`-O(ZYh-X93+Tg6BShVkn(~*21;aU%!0$;u=zL~Z_^DWl2$U_t zu(v`$3jBpiskm;_;f>_zE*rzCyHL1)7k+I9fkt+sIu zV(vJ~TnwQ*=5ZLKU?Zaf0BA>m&Nyb&phCPrH9NYajc$AE%=g%Pb~lL#R)>}x{#kv< zs41=aPyw;f=AD*hh8aEBl2VW{9=_QFuSp*utdUTIF61LlU5@Rtr)7XEi)2B`V5c8ic1 zYmp10zGz|}V)B~u3>y2*cAOP~_2wfRgWWG?oI8)P@T3$is%^Zt1HXSfabv@V`!Rp! zj{ib@p+1&+eh%YPO({F6N}Drx66!MI5BqE7FMlkZ+^sY@X%D#nT88NZtqhZ&51)>; z&zZct{%K}Rk|n4@?y=0fQ*j*+820Pu^#%)JLV+Xm^TYp*wBQc+!VdIWbae8UFZ1BM z!~^nhbGsxH-ii_M%U@qVEgS1UTu1;ZuOJN?$Z;0DY-Yd=`7zXkM@<39^rygwF){~r z7K%skt$zQ4mLdA-$_ymdmLH!`l4f+NI<#e_7&}Ang&Pm8K_B!#RNX;`i@U+4lW<)4 zA%i`>{{gMDq5Z2BpsmH(CoVU=2q?R!7xF%wkK`2=7Jk612ZmDhk_;~69V+NsG0?^f z`2=aLL3&lApg;%y4gnLZSZFCQ&-x5Z2(ZI7;)AtFHmI0HE30xUjg2btU-%rQUAk1zgt%kgtS(tsT1x8j z$9>KZOA(XEN`cq*(&|<)AGRkck!EF}fsE zn!dqzha#_~*;P&n4?aF!H4cGamX3>vw!q3e-4iWtn}LtMYESU0e17HpYuc(Z;q+sM z?OCN#)QvS=z55alTYw_y`vNyHQd~0ajh48+l@!T#7P{|_M8EU{groh#9Jfi6zv%-k ze`#R6{SM71+OK%Hs%Q6H<3y_x8>ihg0wdd7tdcuZ7H78i$e*J|C6yBCvPXs zg|x5J-C!@Mo?!Pl>FNP?lk_-6@FC9#s0-S?B6a$IVe&5Qq`vqy0tv>YLQIL`udU+H zo6_2oU&59EHnC_FIlVslN^?6D-}q1!7(hbp_8OBc$j9Ny6XiakH7u*7h7`s4cG0)1 z$|c?#E+f4`qXP&%6U@LgBUj?`hGWRY;vO?R=we-m6smrT)ujP>KlNB9mZUI%BC5p_ z5*1jQaqrZF+A$H?u~}%XqM$byf(Zkv%O+rvmed^vPvzGA`J?G43t&hJ5{S2zt*vu$ zLqkKWVxn>(f97;qv}-x(_Bp$8ppV-A#H2OkYBK*$zU9T!fS}^E)AY+?4Ab;~Mkuhc zZAAp|sU2IGE}87TC;98Q4N-Y}bI@*y7SVR+vq@(*`ks8?Q(2!HBfi`yd$@Z*hh^7; z(A-6P1qxDK0_>e1R0IMvNzg(qV>~-=!G$Z%%gdu}2EnePto#IZ;=sA$F0bjkWh%8< zv!iWg;33_Hp87E*a3ene?6=5Y0SI~(K3b@O2-7rCLBUTc++c+fen{^p;3{Ze;>kQP z1(k=r|1@xLR3u_4rqIA(8$fVrX(@m2{hs*O{ejYSqa%j_z9p(Z=7J6aFI^8dS3vkx zi(h-WIRWn5G2JAN&QeEOTJ-+-0cEO7>>iQwhZ2)>temt0)a?-Y3MfGOnA>yu##t7#qAsqoDt^hS<*tzorNCtE!+u_wfG1^(wE*13_ zwFd%Dy$53Val~w8Ry)vtU*8hWd`4cA>yUk63<_hN`yOUSC`{_x2Qc?n|NC2)>{Jy; z_<^&5Xa*@rOp+Ypg;@(md_`(k|AytSVS!+?{wko=C{)mZk{j&xJ*zLE0?#QaJVTMp z*OYJ4ctdhLk4m|S!x&f|-uHJ>`C%fW9UVjmwoxOjobFvNXDosw2&PJaAo^fQS5m zJ4HA&@kt9-0Jdfxks+kTn-h2Dh_-@nBk5B1PrVx$~MXq*xp5xc~aBrf4FFlL% zh|_iPKOaBbU1gV9ag}iK3XvS=k)Iov*USD%1>kTe#lOSp9bJ`cS5}z)1HYLRi_+d0 z8cN3!ns(LdFUNgJX5iH?(g_e-Dphbq#64&EF&9&1ABAmA!GKTRU}6IEM^;LJdPyh+ zx{7Z2uB~~3)`~P(B!PifXZc9eRy>+|zKs;=lL4RdRM^-=G)h!+!_b{|jf2G4@8yTZcG<3`&sWiX^6i{*Lt1_M1YK zf%XK3vKKOH|8b1EW?9>8Sn;N*U{E40Clg3zFW5M9n2tdW?1-isMrU?1&o>CkZjprF zB1XPJcswE-LvZdWIGDz%(MKctU=pr@R2P4IN`(Rhd((+Wjr0c&wGUo@wtVcfp+xjvA7F1N}y@d!xT}JL_BoT^uG@dCKao^(B8e0V~9{VZg1A*bXNIRfWhj-s5GM+#(L5}kyqicbjx_;g}WV-jBtPpB0RM)pY zUoit84f84?_o+Un$7byAga3uW-owjo!ua7NK1 z4$UqLCr%0-Bcm?oV~K0@6O|2V?g1ucsG3Nu;nVGN(K+MJsmWxy%BFY??`=47?_Q%} zP({;`0-@W+qd@@ymXQX#L!Q+k!_$A`aLCoo&{ zwz2l&$kUp53h5(S_?s8^HI(EIef3MIuKTl~0WAR5${gcT)4x%nnPofWAH4GDbhO-V zv0vU;fmqu8t{E!~iZja?$X^s<^1iXr?z-`d;B&+$H1GDn^1WY&$&>#W;uQEr&yG7v zRyJ^9`Z?p#1KWM`9XaYPDR%IA2w66LlWPyS3cPLY23bYT(B50p{|m6`!SaBFq5Zeh zmI=h=mgKZq9~KkdN&fO`{U1`(2i<>8^~;LGGoqEYIoDe6M6AS|W~Mn61=N#wP`gYm z&qZ&NP$0j4D{q`CMVw`)=kn3h%ZyZP3dP0l(gZjX)2zWw@{oO+6^D^eerciAzRX7B$L1weY@9DxDdcaj6{Bs=`ZwrG(Y5B*Kg9!o!&s+t z|HS_FdHws!ySj+A7wuhLDd;zq__Jy*75-$xkqr z0Y2EcF#u@FnuR~pbaf8K#$gzXqwoFT{UMy!h=G;0^3~bq`h}AZifix5+QK!6ZvI!< zcUngJ_h|-3^eihXX{IW&oZF6h{b7Yo@1t+odnLku&)Swx_R4m>=ic?#J*;Mqt`}kK zhTCESfL;o+$h5z52G>NehaS%uQD&49`~Avm>Fgq8lly@0PmUK9O*u(pf~EBH=eq-F zAX!&8+>`!7r>iUaXPNa!N50lqYhDy88LkuS<2-G*<8)}|q)4L4WLqdoxa9 z{=L6ecg>SaD!LTFRRNu3qJff}n5ld1<>0_jvg(8Ai^-(mGAZ>&f*zmdw*54nq6k6T zo30z_2NdnNZ^raPe>E%1@A^vO)s*ikcSHk?`<5TZrKdXI=x?FDFDyp?BS>6?W@RpHJ zTjSzy*6At^Z;GVz*&?nTKR%$Y-*^n3Cfj*XUuskEn}*KOt#;X{f>?@MSm z0p$R~l7c;Saj;o_cjQXTdt+up!)(k!@Hr09nswya4mc63TND&+1iSX$KJGNr3a@{* z$FkXfJ-6)^SX8~bHU*enMORl>OlOv*7W8sd7j590^Fi<;dG+PIO?3B-UO-8)hXR)> zE*Z{F0B`8pEt5oSd9O8brxY8d3;4 zd5N`q{O^qSZwYS|Hubhccjgsnwk_q;EhmKc)WvNmZAX6I7PC7@**@NabGvDOZI?>A zp(*vI=&C2jwe)hwImm4HOl>Xsv$s}*^=QdG$4|ES@HxD6HsLs>-+uCm>zz@O6Ks8< z;)#BUuJnb9RV>rpzdm)xI7|ZzLW7K5zzTi_JB4eVd9;T5B>AY`JO%0a*B@R1!kJc# z_S3Vc7iu`TP7y2D_L%S%9Ftfdax%ZOdQ|$d^#eY|a&A z_TGQ5{`b+twS=bQv(E}&y_eZbUw!gQ>$*mPJEB^enw?HAOu2hmSs%-wf-r6fmZuT@ zn~KDRD*F^{KuSGlu|ab?`m^*{<{Ot($KLv$OC`S0+%^tBLffmpuexor;}HMOph2V6 zxiv`7ZJNk$yKldXlirFZD^e>F;tG#7X0>m%l6A1Fd4VE1yZqqi$1ySG(C~eTlio|d zdd6RW{Yt9wrPPA;Dm$XL==F}3qpjtcmnvgXRcRxFw(|J=F&mi0DgNPFPAKzZJf7@7=Udsc?0v4kL{fgo_3h3cg@mC8FDY9(@^U&CUc%>(aH>v3*~cPIXm4< z(e)Zad~$5(K^pVXuZ^$IJ9>3K`s-Z${YnTpbN0!(&3nOKwjxR6Y2MpNk;K-NHs41_ z_x8Pwt9`KA*FoeP37=Z+38KsQkq*&#zH%$*o!hpC-PX2fnO-lfxko-Hx_9KqKq5{M!Q8SGy&aI!ZdScZ@?WGcz5P?E|}$WRJVG)c(Ulp!k7 zpfpf4B1x1aQ&OprBn>EuLPCYm{@kqLx8HZ~_t?k(zmDU#-gnXPJkR&Oui?DT>pUl1 z9@Vn_bc@dcb@?qjyc<*H)bMtdn$w3#1!q;0Pd{oN>DUX;o?~sh|8ecr{Xi7^RY+-` z2>WkGy!3fReu*-3d*Gl<$ET9j=Yan?~s3o-vzo#5Ny2)GJp+|*L<3+3JO|^M`?27nl zdq;KZGB!QH_s|BHiOxIyCTwAXze9QPScS~ZC_Ua>8zW^wpv;2Z$k(Z)IfQ!`U;a@W zQ!`+|tx-<4YW-&ndl??Q(f{Lrj6Y6HBphGLGwJ&CNdElSr6Z=Q7w$j1-eka_WvWk8 zzECYTQ!yPknT<;M=%UU$Ce-A`_qTT~n^BUCIbvNo5;RE!cI+D!lz1nJ%NFWVcl zgR85pZ5vk0fmedm*J}Gkt6&az{<(s6-LH0T*i5ZpV>B_^!6dmyPR^w2(leKQj@H7t z4>pcl+WgO9?|Mlx@7{UKSK2wo%;tmT%p`v8J^kb};mWH3exg6F;&49$S?U!pex#%N z`1YMTjc=&#C!(5SfNQaFA!4#y?uc@rdvE1{l0e4=H%()0zkX6d=tSNz`G$oqKqWvE|7U5jHLw+g0`nH3h1_HinjL11#1-_o%Xx= zuDFUbFj3pbzWIh%y=F#FbxFzT2?n2iI&E0^-l41F&jNwB*&e=X=-#hyApul*E0|38 zvHz%f^4vJUnVf4Ah=bEVX#I?_^pE`G+3)0IhXF-{RWI2VIWAL@#;*S{y0G*2@(d># zxQsjY?p=7cYPeyTMtxDPa@yB8yC2`yuPf@7QMg{31GvoI&d8@h;|>*HW>(zhTIzv{ zyZJUYu`kcpCCLsm`6{n^DP?@OAKyMUwYDygd$X>S=hWxD;ntYi4H?|eB{J0oqhGp$yGU?SL#dJFv$r>>r<^4 zZ|N{|_H1^)pdzz-fNz+_fdw>2~CFk^A?uDhv z{HTW~H`iWfytH@Q_mY>Dd7H*sEi%n^{0Caw=ey9#?rZvymNj+OUh7rH8~X3H62!TF zPI$5mteDUqR8>` z8bB7AhI>-hW`IPPMhn?D%aC{UBIiiW=AO!!^n3fIT7+UQk_)(S_`JvmaHxYzq~x@h z-GX^5PcAz9IDOn1VHDfis&HZ^m*wh->i(B(7v*nnQr)SCdeHIX?dQd8>w0FfWBIN9 z&0fZJ`1XB#N4aLUAS2X#=|BN{@MgpH zUB|kvRBJv|W8grG*tlK4Zj!i7iQP57?^t#b{C-foo10oa-f{bc?~Ssi=WdQT=nDdo zB(o>3+0T|&e6jdy1jAAu|L8GN>*npBIa2!h?|m;~L$=Kj_|xD2yb$>RzyHkip&!V zkUun2#iohc>R6*2At51MnqAb0S~zZKX|FHdxrcKoN84>@pZ$A_r0;9`C>E$}FT2*E z%YpK@5fKr=f%0<)KHQ@)T%lQGypi(Lf^`>dByQ)RHmj2@(+>Lvab@j__E^RB-=P;?gm_MiRb<0b#^Ial$={C$v?3B*SVuEJ;{LSPC zb9?%1HH$U+L%EAi8bT1;woHuCerxnWx5>q_sWW(^hpScXR z;ZR4i+v}V(N`MT3#aiD119txh6lQ&DPWgp>A79Lv)2KNqp?srfzWh4X=?yc}SsUw* z7(O$_8iZQR;tGbr^cf@Q+S%jAkL9IY@r8xWaEgWRRSf!MW|)|mRHLXr`%a8Ql)TPT zJFW4<4oc!&eaZe$UZ~pmUY@3kDDgqiX!d5GyL^8r_xijB&WH7wu^41e^3kmvR17>hEFaY9vhzpEw6J`@!G~EuOj+SncjM;!sI~; z(*q&^_3C_`R9+_j^&6(}0ggQ+KEPRgfMd3dS7|GSo z0wN?>0O-4CK<-gV`^q+$FnyAQL^(6faA0(?-1()8w5`vi*Pl;sxD(aRB7f}1vd6t2 z-d0#I-sp?5oqP9gKbfhe5Rrw4BX}5$_RI(8G9y#25)ds1+Y}Zg+h{vi1v_^^j4i`= zxE{=bEL*f%h4VQI$Ms)6?VoNf8JDlLkaFWP6Bnn?D8C}x^0nSRp%cJbcP!HW8qtL% zHIAW!=EJ~s@n&J1fng|y6Gmqu!#f5!*BJvc&p-rj(@VcqG6_(IUgyX3(VI}~HR`6f zX(@ZLEFcVHfGpVDVRHgE-*mVeFzyJ{20IvwOe?2BJb7Oy)nPmt=hVCUFC-oFu~k9& zCLL>aex$!!?-(sDjJHW&`wIvCkj;0G!@?U90y9N!I+*59x%{lGq0_ocQse9@Vc9OU z^#N^GF((0azfJXh=-Iie_wLO_Hi|{j%%>d&*&aJzfk#pJG--~-*a=AiN#0xMW$V~@ zUW`lT&|+EZMuRL=BXIosc+z@H+EcfHc_w_m;%Bqd7y`_|e!C4$DXf`{w0{9Cet=iE zDzQV?^tDK4G+8dlAI9f*vq#raR@Uo8QF47rCjVP}@A)j1JZM3ljM+4VaK@p?N%Xj)3 zxKDQDnvajRomyofj#R+Qa<8=+PKxaxI9}|~bL>F~BE4lgbnwuK9B-BOl+Qtjqti>h zsWkG))&9dFn5&j-+9ix3_}`rs@^DlD7o+>@1qiK%$uAu*Jh@a;H+HRr*4#?y2vZq~ zu7=4s%Hr-buAEq)6jK^lQukIYYnu2pnDg*j)5waE&mJd;7bk$2 zN3Sml@LeWt^JD&c?4uY3%Is;|y>!D)xofEkwf>85jxb`d%%eQP71OADY$hm8US6_s zr@yzq#5SWpXxKzliv=F zLy#OtC0Jh(0yw2daAsH83b-Xh;N3YW#MDJ8;aQ*dgCUhTk8E05Y${ z?A@)m@2-5D^U*=bT;#_{eTYrJ8KkY?77h|-Z}(^P5mubN)HZA(Z!XVN> zfwefJ%l2y~I#288qW5Hp8r)!Fib|u^<8w_mT)A8@%sJb9%iHNKQwHe7l;D~~v#!n&APf)Q31hI$jsgFYF| z2$F4%88s!y;&KckjnpT}BBMv4Qgs|Tx6UUlt zViwU2o$AClyi`G=M@52!(v;;LPFIr+teNbCb`T|!+`Q99LbjE7@Vw*RJ8y?sm4r?3 zG<77$pmnGFXH`I^bqOG%oj1WCvG`Q=x)h91+H_G~4ijBa2c#2*>xd;{pL9kaXD^s@ z*ERO9X|%a&k=27O_}TnICj?1$+>8xT5D#+U6CDcZ6wRhwA%IVP#DAIP>J zESR>1Rt;lIuI8Mt-Xd0{g?J70Sfg{xn+OUrxx@_NAHlynka~j!&qN-_j-G|VV&4%9 z!o^pQ&2&A>Gx3Yz$Yim`H_8iC>Jla*709J%fM>Af$*Vkg;J|?bzhiaQ_dc=v)Lgo@ zyH)4aC07!SbKpR9Q@4+}d0yx}P*O7L|I&ecUJ8Zg)9_ZCZMx^~J^1jTX6L4xziGOR zj=QzEx6<%{4}Xq4ZfgAJ;YFUCuN-@6(zjj5=FR;IQYL-Nn%K6js;2(8D?xiJ{F3W} zau#iQyk+d0__|d))LX}G7{5CrV|K+2D5!buCvOM+K78oVWa^|m>3nH+;^9Z$6-$oT zEuClXqh1L4Fh{U$;iN_Kg#4^c8G#&T&0c6RUj16B1O;V#a`FTgM2FdS&bzP`iY^4% zS=<~abasjQiN=K|`|D}6Z=KV`bWPxEfAd9kwY6CKbV)d7+TvNDV~Kw71r?K)v-Q-x zDx}Rqx>9G*jn3A04)sNK{lR&29UR%Up}QSem&d`$glv}YtkMG)`>S29-PFtO~Bfj^YpCCU~qm|S;=n@=K1s`AgY`IHkl`H%MK94v~duy*#}leE6Ad%^|H9xnOJUCep|xc9O9VGETrm5_OCi3H(w5; zEu42Wxgcf`!ObA}PA9#y=MQRMC0_9ZPWzvYg;iSVrjOb(O%S8#-+1C>eyf7?pZXp< zb^sG0W;Q~=A^-(>0yen_NpdQtHbUuuRT1-=2|}@jwwCvJHL2YVzXZHeu=HFvCx zpz>Ni+y|daR3T8AB8=k(3L!U>%d!YSBabg^^JtGBKYrY?SFhPbu^mZWHyfUYCDn~D zxa=TRwLsZh4z47^Fev^{-nenBShrv-#)FI7i23tV)A74E{c$wAIWI5n{zRV)9rX&= z#S4AxbhDjm1`IALfOO(OHT#8a_5#`;JdkCyQ}(YJ-Ge09RFH1375DtWTtm}3#NWqF ztACEh$F|%uVWXWW*;O#%&Y^WW#g!R5J5V1!qw>h97v7IqOWCSFRugQO&LoQGbiPDi(~x+IIOiJH1Pq4M((dNt|!KD(i!tNlgv z7yK8&5aO4}MChIO-YYGLR=^q@b=i9i4CAL2WM(o4{$1E>aCT$QpD*R5eX4#MOtmL` zC@BC`dbo*B2InfF&IsItyYxpX8Ue|ST5PZ@%6QSK$0B%wT$>4^(?oBp`{W7I&!Bfut3L;Da{3y#*;TEF zFWxLIUD_Kzjme?+k2(zSs@-hx;AzrS*@s96lS6jjmsYPt=Y(Y}Et7>fUJz+gQ-0JA zzl!#RzBQPdjP_4GNQ_UpQZZr9@;b|FjnHPXz*Fw7N*K6(j;#5+{~w& ztA?IFeVUHG-;g1#Q5hAxd}B@(V;C$P*x){#;ym)(J*YHxFv*b0-n~1U9d5W}Yt1XA z*NzLHRL3I&!UVR-uIsR7c6Fby`NHKn?lv`ldc|y+^?ToUJ9h2b)wFTTr)3(6%qOms zCGK0sllOeoYK}x~3cDX6POGy?V|l=~PZNezJPCc^9u*amy(Z#GMZAYc2U;uEEOw%+ zAk&d^k7i}=ISv`;7)7N;g8V25j3jR}E2}dlx9lAq7n%(6CXUqoAkmeFsY-wQj4@5d`6E7M|MFIu>%eT$k68mG%X>krVwXLR?b)vYJq9;Pbm zvS~waZ@ahpYxZtBZ&j2KJJWCP-o5@Q#kUFy5@$+3>Tht_GHK%S%DKG0Mhm&xRH5(wr9W?;SE_?T{1@ot0UAz!oLP{`{c%DTO8bn?PrIdkrFSHIEQNZz ze*3nSKDzy#{;#KM%=E}etc$oXZQmeo`xnCf5h3`$Ufp0(hI+NT|#Wrb)~+&O>Es{%}?Z4QTT5( zSH0P9+@923=g>l9YvRkj$|Ge)8ozgFP}}AuVU6_bH8njXC8U<=sXzYtqIw zyPK+U9?Rc|*kK1j`9v?2q7wy?T|ah|77yOtv2+T`_^aq9*gEbKdVimmU1Pec5NxouvlBbC zE@VgY zJH<_eLpBhISl8!mfD*bajlsz@?(x_zB_#%aT&*q4gcKRO*x1{z1ZxNw@92(|j&R8g z(LC@@F)U=T@Cqh!JufQ@_1U|7_l9rMXB(D&gvbC+1YkciO(r4vi^1{~WE9Ba=A@X) z%fv&tdef>Nol#mB3{e<1l{$k1&C1SG_ocbmi)bSbaqpw_A+|ucV$}d?PrS)Zsdqv2 zTtvYG7|LEj_#b8zVt;wo&yw&BQC4PexKf4@{KYL(>^kZ3?>%J96dm)ZcBYTXg7s5RVIbw0;vN$Wwuc5dHo=i;8W!wcTTzidtA3 zp6$)Fw3hsCWNzAtikd3iyz*h>>i9*V0a7MXStQ*-GB_xJ?uZ2;%wF^;W}yl%EGxl` zVSA4;+Bq$j`Z#Or>8&FC3%NjGMcGK=RPU}M|9!~!t7=7oz@B~R{nc}2T|_!yyJU2a zc8Q6J!c|7$_DpWo>d|6jk_3|l{~$T+EKFGxemfyj8mGQ&)AQzhn+xTv3?F~$>d_By z8gNKrAu}pcQQ2x)!fXtY8@sh_fvWNGVUDlv?O+IHdWqg*m<(ngGBlhrlfS!<>DCqm zBKdCZAO!_aJUU2lGg-+5S58G59^+|QO_eQEufL%G7|-Kqa_{7<&hg(sOcpGgMDrrB z6;>9BGurE>8P`Q#_{uG{>H9OUh$o$4MQnLxuWqj{@9yr-mDu^K?qJEhmz&nE`>R)i zFTnHiA6ievcI$FLV4#pH0XPtb#0$$^K`INIh~E%!)eFi5(J+8N=w-U-gv zM{=TGf*D%Z1uilebYqw4s#P=C{o^57e&-T4!{j~tv@<&yAQzo6=#gP3H0<^l>ad6T z4wVm22soczO}PhJhT&7LtIrN>Gga2YU%L6J42uLZ96v~|N6P!HQva|IB0NT>8N6W= zT{bC;ilUFv_mI+mqF6`~m{my0(E{Yi{VgMov(rTFw~II-1C-g9wvgW)m5I%87)MLt zQ69^2W7j?24rJd`)q3;+siDql%+l-X#YIJ~UJk(0kH;>0a&@`5urrQGxNm`mnNdt5 zcG&jJf&;sDHDh^@XR=~MH#jvX{k38!OGTMS0dbgk%Ks5i%w1EKXg0jK(@AWHQS*-I z))vY98miF^+TM}~N8l}-3SQIt zP1l7>yI>U}B#V>`f_@Fp-X)$XeeU^pZoSovl!ox4;f&g<+RgMF6zB%NEEPU9*8zu5 zKr9zOJXo+zFj8|cK_VR&a~YhY7oUz>Zj7Fyww0NUxrh`dU|O(lMo6o&d(l+_RZyW4 z0dzSx0xWY4i6~^fC>ShJ7_b2=3ZZ)AD>S*nialuC*EOO9?IkZ?L&ycAHuoQY;v5a9 z9VHhb9<{@3nVD5r>y0(K(}s0NZjEkllvZfXiM%Z=_aJPx?B$2^_oUbM;6hy##l8-e zX>$0{(&vZA{arZ7y`u{jGZZux9mauEvHE1Zg%vuD=> z6%rGLNUogHg5!CXS(VC<-AO70ymw7$5MbW~?SXiSWa(zn;zRwCkOtF~A$0>9?QLW9vsnAW!Ome#7NXyBB z*@FCv^70xXP=h)IhKRe^HRfa*=B@_2_=6u!2%pIZEyhfY_qf*KcN?lIDq3zoa-{S7 zl(J%j#Mg1?iRiH#q{56WgjfUKGb3=uZ_8=*#ojhHg`%-)DwadILTK3yl*WxSE*{Im z9Ckt*@Go!Cwj2D)Bd%{26M=) zJ-ZQ&-^py(tX{A{&0*>28bJ?HfDEy`wieMutfI#9>zurWfqa{iRWo z81x)vGT{BhQ#eWs-*9pzfZ#Yrbs|l?sHhkv1PZL=G{t%g`^0zGeRzN9P{(0QugqMz zasY4!o%;u1`%bd5zg^==35Ra!%kG? z%8G5oy|@rp`A zW_z-fe8S3qVdP2o-8`1#gIhBSR#jThqBRM^!tPHj$o{A7`73!ws_4#E^k_|VHK=d$ z!}s#*ZKKycp6hHy%9=B0j;E9SQt7(`swEjnU5f&QPgNr(xz68svkIQ!I{0Zai&#Mh z;2=F+8n`y_0|yHK(~B>;lcG=LNtVcg3k9tU7e@1i zwC68C{+k~EZz<1p_9v4rWcDTkHYCw`FKYjua^*0!4SlmP(?*djw%Zo|S`1bgE|33+3V$OXIsq`PUsy#KjD9sU%7xi0k za398piggqI%5(p>G$;L^^4zk-!UE7R>Jpxw26*1!_+Ia_>+i)Zn^dy(qb@B1@}VbF zABVMMdjW@X*Qe9j3)+ zSa0tbQfY^q>)go$v++WCZLcnaCrp0B2 zxE@?wgx2+0bh!K6W3`EdJfKl#;UQOz;qOw9*bm#^5T0M9t16T7I7LcXW`3z<)NPe@ z8MW3KO(dc|t>%78Tad-1YJ%dIO_Gxn8;%)_Se)Q7B;XhY`WeQiy4<;z^Ya-j8Da8y zZLa`z-n5*rH($+N>G11-$8oDV&<}P{-uB$7Yws4xOZ9+*DZZRnJ52l+01=P0_gV~4 zQN>!!_Un~fZ$)FnhvPZp?E$6XO1TXUQr+9dVVm)_`{~?!sPD9+>JCx%{iv~jmELkq@n7YZ7<82G5H{8?rdX=AuCv5T3((U05KD=`C;9F zlQlC?kX1fAd@!brT=?kDDKE>*V>djXiut6?#*IQY784Rfsd(b3QAZfal)iYe8@VG2 zgauWbA>~BOkM0t@^~0>#+G4$}HJ^26%~JgEk!i!ijd4-X=(B4Wn}}8x2;x;f6ohXPwShuBY2%EY0#`jxYLqg)gRM<9ZdH7Mp@^5=RAFXCj9nG{r>M4GA z8d!4V(&VqrVip!D{2jXXwG=EKK}TYUDa05E9m2YgMEabTNMu*0l##c((MBKaF{U+9 zgK`~1+E9s?FT>(34XgMD8mhMP>hsQw;?w{=;tnzmKRIIm&fRVrA8AuKY)?{D zVyeCWz=4}MawCoA^SFhf2?+3V%y~qeNn-5Wwd<;jjjdX>q8->l!UCAddP2o>o7v3o z2x%e|%l(mrotn()GiS04`oJ4zEVz7Z&2c{X_WyHp-}+1pv;~IG?dj}U2Nh#-8F~-# z%L#QMd6toM#JFS`rc~$V=3?t;abtHYF|HMzd-oa)FND(Rd#SGOCGys{P<=t@Zh_HO z04&*ABj@}|2)CMr=2xvj!I!_&3cWAt15kKyFJ?1fQ$j^w=b)#T*Y5kPUzGbUyZz+J zlXasdYCMh$&KQ1qUr>k}0iePgY+}T!KkyymW?lDJ<6p*$+mR$WxnYFbK{yg4`k!}E zE?-15BNR++qO9i2x=t1Syv5pDyJtb3#&FV9Hg{-Y{g_7 z&JjY=3KJ%nSSi3nG{whB9*)HahZ8lp8KG0>x9qgoh0|I&xtDA^i2Yf^~Fy92#*|9u6rP#A%2QkYO zU}l0%sPQ*Y+31?qk6u&roIH&zgXR-QUz~YWM!3OVK^jri4s)ubO9Eq3joQy1#yLI!(uk1Xf3t{^4e|7Moofpw1^JN9+ub^y@gViS4e zsQfCnF$ddxNC8{Mo_!WoHqq!Be`$g`%0B1O58$% z!$1srOcpNbshm2rTimm+Tr8okLfoefkg=Tj9#QfSA3vtwxwD+gkFyb7!<0*nk5*K+ z#Dns0BY#9vYA{mc>Ac3jWT7gk#sy8rM958CVs3B}=9!~WsBJ9-T9V=P^B z@0I=K4C|d8Lz+0C#>5aGMF4A+^8=s8VU>tP`AY7 z>3MTW2TtBAxMxZWPESTAB+vMqyo;gr(F~|xt*$-&QK!liW}<5WJA!)Q$VBI_vV|>l zY}OYgTqjsuqsAF}wvW2~U^-s0Cd-IZ;2a}2yyveTVFjmh^Y|91J%_%u!yJmr`tFVrnXy$#eZwTn}{pPhjNYS@dg;O_;I~ z25W6Rn&3&?|SpuxxU3ND6CjvE{B)2`-tfV~}c}?|E#okj`dvRkO6&lH|?a;O-rJ^l*jDo#4jgNx{eZ z+r`!QZ>-zaixlCFAsjG+uCbOMrDlL@Z_obBkG!L!4xVq*p@ZtN`fDrUu`#xY{bSL* zFXCX5O}UMt7Z#P;#v{`7ap_|Wynymo5yZTU56*B67$4Fvy=Fk~-M~l@dKV_H`AI$y z4T}C6QEewSwxK)0e@KQ93_i7RfEyjyzP&N0U;FmXBmU}l`s+J9+~|+rQ?z{~#fex% ztALq?68vd}tHW7v?%7l7Nl9+}rpk5RB4WuB#ynGi|I)Lv%z*1vo&VTja-hR(bi&8Uu&ziYXBwfm=%vEgHX+g$RV6Bpnob|kSBZxwI%&2 zfPE4lxVa(~Q|bV~NfCMth}eGhe9O*b2K#X(rOfdy%59TnsX@?j=LYuurRStVd6;4S zpqV#yCvL46@P?x&D8w4M=go-e1-jiGQ)K?;5_ImclNt=wwhq>_pdr{ArX(`)=WrK>Q%q($w1dt|29C;pLA<^1|Nkj3Nkh_ z_%;zW+w%(}Uij?aJLMPjq_4=2fCcXmS;)Hg^3grS{U2j#l$~+(KWlaWQ|b?@YAX%f zxGZyI|J#MIc#nyk=&NB{`yEOOtzO;;QLd3ZsDS zxGk0ew0%vMo14$CT*c5>O)QZ7^bp&U%XFTxgOYNV@$%(=f~ZCxGAd1<_h{|PDa_S5 zJYL;0^YbOh=^e$!0mC~SnqD&BM!~p@@@)C?;{{t%V3>$%B{Z}@@WOKl2^Lj|?PMKQ zG21{)Pfy=3P%*HJxXUE1kNw^XJ*N$h=WB`5*?5)A2{$9b$Z(qK%cppBY=pY4hMc?S z2Sy!Oe9c`U)dDdI&>l&>(AIi46nvprxt zgWNyx!ixoj2Y3wmF~vHa!W9sHz_4NMY55R3G(H)0Bc=ns79e^xID=AhJuWPSH+|c{ ztSLMzSEdpwNa6^#y+n>>pfWbPKMOZxCx(m6-70tkg1k>9Z@_UH^9a7NJ7oxW<8r=- z;l4pjZ6fuR6NBfiG1njA;@`18w&|V3kVJwKG>P2^`RTaCnqQ0Up-oMVmv&`UhM@7e3lH? zblEhw|1Ae~d73`W4BEVzyj`W!y|)8n`QtT*rbFzI2^rsQ!UwnJjG2-T(4LPd7v7c# za!H^yWLPSTxfK@@f$-4X!;SNtUBw*k*qScbz3rh+rNQjN%ti~^_GN3pc`=h%eZLOE@J1|ZzaiCKL3EaTKg)GS zpuO~Sc3XPlGr)gd+J%;FyKTR}`Xoo3qQZ2BiPnr6g2taq?eK?LmA$&)hl3vrS!$F@ zhHP-Ow-DY2fFYjwRBU3;3RXR0ojt`@LbW%vyvtZ6mY2_3wFJdw6|E4A9pNtVs&?}e z3HKW26-Y$&WZbD4p4&v^E5W-2pfGz>$Juy5%^G)1Vd}mQv0gn=4>#y~FX-a2c5`?A z#CPRAj1Mi15xYAeuIFYtU0=J5a_6Xq(@2qbTtkF9Dk<|89g8leb(_zi@@_$5qY=7? zo+Z$-lxmkVVJ|~u8G2=eEImc1n)^Be9-klv($o!9Rb6-{iE?gr{1el{?wvdDA{YR% zR*tnzKD~K%XlU)`2~fL<>ObALxEBIp!Tg&^@u}O2RW_CI{11cf9?_m%5@`ZIS0y z9!HKc{qQ1PESw)giuBpej9$=)y~#-YItsB=fRGk^?`HJ zm~(4yUB+NWI8m@z#wkR3Sz7({4L* z*v;*>Y1ObZSzymCw--2GTqBKxcK{>r!?ri2DrVy*Z5Qc(dh;s8cuCMSaP(rS2{vV} z2LqIq$=@m8->z4%7TzqZS7{N@Rx?(HU@07INB01bFk^8h_3GGDGw$T%tm07vSf6@$ z6!b$8GF}L1hsE0wsZ7V0T(*amX(ZM5nW(AHK0w@850_cY9%!V7AJ(3_FX}eP2K7da zC<19X{SbH+I$lg-u=x0$JPsgt;a0aak3JR1?yhM3@tuzEJcjF3{L>Jx{yN%|=#OkR zfQl@E5!$I3-KwFXDHsSpnIsoVr6AiOdW)GY(WF|SJg-bsMc> zB2E1Ktl5F`<}pI$nmH-&v;fKic-rX2KcK0L;vVsA(hxj~m0j3b|5Eu?BqsU=ZIe~Z zkzp%S*6LUFoV0Cg?Ay^j+yqVn>K7#y-l;urIUch^lv0HB9KpN{$vH4b2-X(N8^!Mb z=O19~1b_W5h|iNsg%Bw804Q%McT_?NY+juokemC3;kWB- zpQS!I{fOXDMCf2-9y&X*@?+!RU{3RC|E{R}b_z(L>CS{$=bQXI^RPKylz=P%%`h-H z{KRR|VQBH^L<^KIp_wJhP<(2M;SJ zECb0D#9N8Zdx1$kH}wc_9gvl8e+l$N1#VqogYefpwd^_sxjX$iK6*IuYmsSNhc3 zBa_EkQA+d%KVyU$J#E)eVFvZhZ#Y9i{qYNnyDAu*;&o1U>|1em{ zJ~DJe6J%KEl3cg?0!nvI7_T4ude)g6fwP(id!ftLi!N(===})2W(@eU*EMh%!8CPd zw_JSa)McHucDx)J*KeT5PsR{$Lf`pDToz5AzCy9ykFuSKZbOrYeM_kPK4d7ba~SK? zw}G|<;s9yrN14K{GBH1R`BLkQ1Sg~TB6k_*J%PChnio+}&_KAfE#JNT{?Ia><1_`{m>%uKz8VLTJ1?v;=<7YSLKRgk1f9-VOu_SGmQ~>WrcxLKLGa!w zovsW)qb3Gz;~WaUe4PFiKgrLJj{~(>qnDFDyKL&^Jt$DyhPI0iSJW$bX@6u05?Q5u zN(*+Cn2{4t+5rBov$D$c8gfR%s#rKCk)D7&TLCLzp1cQ?VDaqX{ihpySM)G`KOwAY zo=xoR7^bO1ug>6oKYzZ%+uM8dC{^TsCAfSz_JDmS`JWejM`{8%E;PUj%te6fKTwXa z_6uJ599%4%Gg#@C!=V@{{n#3(4nPidvE{q4;3kPnCt9~{vCIFkPI#`I(~#rmIMYod zLa*qmD(I$}BT!J@-G$2W7D-#w;#nZ9iS?t0Mus3vwr#oPmg zpGwHqZ_@kE%utr-)htZbqGYh0QWavw*ZU5@RzSWE$uVK~GIhts}Ok+(j+} z$ahq;Kh7>myn)K87%GbCHSLH+|F2A*40+k&w4!aSAG~oLBl*V9X_JmZSLXyB`#SnD z`j*1l&hN>hCB>)W3$nA_#cBqbByN+KDjb&lXU)^KmFqHf-njZ>jcWf6!>(A1qHV>mxI;2WFpDr}!ayitNMqqkHR#JdHn|c+kZ0QBteCB-wN z_Mg{y*1M{_>=eh|qQ8%B|JXjeKe*qx>f5O_?AR9yT*I=Xwl}(3n47!!`gRrhoSiw3 z)OjP8J3YMgde64&kHdO4xO)NXRc16uFkGcKp|w_uZi1#Yjdll*ktY z0S_$n&cK&xN%^~_I(aXXNA{J_-t_1@ZI4%EphBLBWN@sB*quTB{-L?1{%7|7kd7Q) zU!eftCj0!^v*X-`!S!pI778yy?c#S7d-`JV!C*x;Nmom2&q(Gwr#Q^o0B0EUy)-%< z{=Q03X+>!vM@3%Vr}6XN^MLXrQ=wf###`n^FRVxjY1m7Wm-hw@kF+6eof+&Jf+5#* zN-z&$Y7F^v2+)rKAKYnyIoqH-c3Qj~RtS`9<2r`?nVa}IA|O}qFuRz8dAF7I4=BOJ z{A&oG^5Mk;d#{pnm^10lM|D_MY!n@^CDm%`%bHUTXmjqo(2QQ=au)4VJ>s93|O9D7$N^}GI?Ce3Vk}8y7_88!A zC%nJ!n@*toqMX;J3h*Lr4dFi-Eoaa=c|%&I;9SN)WB2*>PbWGw5Ris6eyf<|O}V7R zXEG1;i3ys!gdBV7{rmcYsK^l9^-U%~gg5faN-*K{g;y1lki#ltHGqXBbH;*}6F4qm zshbaKg^JA{@kbep*C)?^nKM;iHnp%Y>9M-3rancESn3jD%TRqXCVaEVTo1?|R{grb zqtNEtIU{xVJ__Paus#?C(OO$yRugN=g^=xHf|}{=-0RS5lB=lwZkb*8vP-ydVLH=T z>D0?fEyN}_Gium+&#S7UU#2P>={?UE$(88IojngB4-R8qy1b5St8*L-@*6U2yTrhj zs{PZu;=&nN{aI*4lQs=Zn|X%1Tpj}Rsh8WMF9 zj#ryDsX@iSo_OJhd#*!bG+xhh74qFw1Jqf9Ol{G>g#4?xBz)GI$Ojp@kNEL28(6!f zgkx3UB{w4W=u)lU^*4fZDJ1&pKQ8ayx4q*a*_6$-Luic!X9&iGT+c!;Qvj+$Q|r$U zw48+xbV8DpB*tufL>-P#{L-xPSk(&5P*5{7-0kAxT@`l}bc2udCbb~o5T(@JC#%-3 zJpeLDYQBB_`iaNiWkZfLt{H_ZAOnX)ek0M#42Wdpq#K<>v|b;hoh%pVM)&a>Pa7LE zUfYqhW}33@*7-i)(l)3{gI+M6yZ#H)xZq!H6qBC|-XKVqKqgsB$rr-hFH-e)lB}iA zfw+XD;5%mg4giNKf!z$E^Qg;y?leESAjIRv_93q=YTODqD{B`ag}c%q|8Eo{yE)jn zE?9ckt}TncpY2#WOIofSe*S-Em&Scd5#}%eLA0Rf*&(^G=!|h-b2oIRf%hT;8~Al$ zXe5BQdncU%Z8=4wBR1qfJ_L=P&!Ybtx7`VP;1&L}BTX|dFA1O-70mR3x|09Au+8|s zPkorri#J2>AobCGR24*yaQFgFq;Pw9x1{%D87d7M+3CV8;^*B;DlyANA?LUDKIPm+ z1}nL_x$FF8-*pq(AA+nDJFIVO;CLQdyM!$rv^2(v%CzPVb?X1#)59BCDDAIS9NMT- z6nLSM3=qn=n*s3ATeqf*msE6zj##NKKC*LP2$MiCkxs*OT)#m38p(=zeCO-8Wb?tp z$?l*KdEtZENufE_9H`8^NeDB*r}L?blLb-eJ(gLCKt^7g>k!5*Hin)$NB9`IL3%bh z?tH1~BiKB|WEBdr167&13&`I660hm>h#$h3t>^1IDitqYe41Msc`#>!vhv+GYg+0j zW;}@7Pg!qa#)Sl2j%j|dn$1o&8wKeB08ij90+yl+vZ$5a z=1i0l`=4RA3*a8};fM0w3b!>#yv*G-gN6+Lr%C(I9&OQ!4v*-#D1Wn_FyEi5kq!2n zH|toNA8!gT#hgDjPz;Lt?*82PGt%_W@rp}evF(&~AzU3Ajhn;{VZhA%STF~3rEopr ztJYag&IR;Gn9mFv)VWKSu$QBTi6F$>mjXr@uZo2iDq5k*(Zen5)=>`Kg8J*k8m2~q z|v(J~N40B7ge#OiaiJAX&-6X+lzx@Np8Z>S*A){9)D}wR7f~QWsigL;?nl z8kKvxeZu&PX_l5z{x?U$69mL`NRid-OH~Ed_Jkryzt5SD9&Qlgh%`fea*nB1@5o!z zqkA#46G9L!@i&@9W)5H>fE*VG67(o$*|K%5yD%u6zYxU)w(4XU6@@;wH*pO)o-b50 zFA4qIHjJs#7!O~#aG~{@y--PzXNf*HPYIT_*kP!bUqvGB5^Vb5wLR_*3s;-?+=>7C zLtfqf&YSv||Ex237hh=kjy`@Br3ws(2fSWVtZ{+vAN8~JWyKV?zCI17U-}O^zuQc? zRJygTmwM&f2Z>E6<($mSq^DtB7gt~x?^-N2)a4eW1a#mqxo&_YNcNZ2--vkp+<<@p z*-wWG4g{iXF9rvQlfdOjfHyGK9}s&m6UX-fahVRxIXqNfiiU&5%9VpVOX%QITsj1AG2FVv`svj(I4DmgsVpGjiV6V@oTZ6y<(=~t zqrzO(!^;;9jXDh1a7fvbk=@z~-k>+F@eol%h*9X9jXw(laz#0$lnhEmFXl3aR1}JK zCZ|mLEaWO+f_15=!EShd@fQ8hQuX$|CqiQK{#*4j;%rONRm!z&*-~Nr_{HRM7FZ8| zz&%lsx@6n&+65O69&DSMmp31yq6FUIA3b|Ux9z1i_j_>lEU_<;TmR`z+IKbHNP14r z!hv%FB0yXRv(CSOp2?eALslxwkVL^oUK^Q88x`~R`&yk>wsqpa>z=pzNANLeG`xuu<{B{R;4ApihcWMoeiPQJ z<{oGX6s!Yx$FT410U-izO^fAtrpPZrI{aD4N z6nxj}$%W@`swFbl{%OD7B?*$02kM61xch0#ethUU@yBPgfwPY;w@IR?ckeY$m=+1Y zKY(H3j8K_-)?EKVtqL%%cuEk_qKc%34Zj{l_4by!XY)F^^U4@nzd-8Z_Uh3KHrn+* z`hTB0Zi%-o*Jg}%jjtBgn!rETi8;hO71p=}FE&g~U=ZkV)IZ!)TC!!5%6}QuR7;8$ zD{Lof1pS%t0#rMA+|&qM;0#pwKmGRz(3 za+j0_4}Jy!c>?h@By)CflAl*s$IMyY?JKj~ZZzJv(FmZLU{IDaPi^CTQ=E-XlFTdZ z*pZ;k$|y?H%!;h6NH}-ykeD{R%SS)bQr0sMc6)wP+S}~evuAVPR{vIRwfND8s5kv( z1aMBA{j*sgUyHxV1n|pD>Im+FkO@2e;1i!qlUTbLff^Tq`BmYi;UpI=MbDMerjhB! zid@X88n(=O`l+bI-fgur>cS}HYFN55pn*^qSqtE>U`m!yB$bP=0@nrK;~0uXei{X4 zmZ1-iy12LmOmyzip+g5?!^!;G&%DDgR0}_SZP_MdhPecv7*cMtL;n65FjHx{802D8 z(}yL>W!4&getrVSAR~VSeu|7yQB-VYXlTf>?tS;zu-$_fN>ipZVTVM3D9r20tAy(3 ztR|iLR6TFL*J-zv!VZ)=u{xlF*t2!_ewhAy+qPo)L*z(Wry3T8y+y;x zS|U^e{U~R^F&^d$m`kEG&Y1rez6$VyFjk^%5|SLUveL=3!>bb_e=cfYRQC%Td@63b zSbWELi#-ETuh1*gVI>u9JJ4rgJIDMUmA=L7FgI`0MQp2)n3dcLkH4tjemJw?)9cXQ z)zl_#fD{yY;F{;axrI+2&6;RIqCW+t$9{dTFYCfW?MIXpMy!@_<4XiPS(P0igfFRa zS)0kG?`wjS>k!6tIL&sFlhb4k*X?UjlH>5dWFN$wh$Y^$Y&Uh{%?Rm2%w0s&C)l5t ztOND?&_W9t0+hsLl$4a>7cA5?TPXXHu%Cb?3GrN|N4Qm?bdgTAvdNVrrTR(eZwj(} z&F!Av7tg$kYf|stM23)>H_U!%$wj$ZJ=hZ4J0yLR(^t(b(v-dnzAP2{lX z)skTA&|go9&ijK$M!0(DaDI|TB4)udq9ENg7Db~DLb6ILv+bbX>>IJeurgAzf{(U2 zd8yUH0H#6=t((4c?tbyor8L0i2wmgM_(el$Ffe6YoV@cKO-^O{;ZieyKR-ca#Pn_l zkPeg;TotKb9YONBfNy_B-|WknWZK-UVT zSGyr%URP39UI~rQ$jFE@G5yS<#`@t8$Y{PTM`{@>8nulyoA~8L=a<=m4U=y>1!zQl z8mo5zIHG-@iL;q>G36q(gZl5XExd%CIrRmLOql?kf(*_)>gKImyYa-bd-GU@6Q0+( zj&4efm5Z-lk;*LCqJpn~kk$9&^G@6YrV!JSi`Kp0WX* zV4GOf-h+msmbWu7(8RlRSjj5tpNG{=%-!OV?LuoSi>0j{TA@?)Wm(;LINtf=hY!Ma zu*nel#qN1>cEn}>yP_SFVgI;pkzeO23WdnUAm*`$rPU49XOEKN#4Ff#kzaCyG$kG` zzx6h61o8v5upV8o+t=Fn@8{MBlTMaN&Km)M6(tGU4R(D(MaB0E13fJoH{!|c}`3|ea{|py#Dx`b$gVyFxF=em$k*j zhjj|RJ--HwSg})m|6C)ZJD}ujiC89@-AGn?NsXT)#4~37mf(0;fj|oS)>NcJe$w}C!d!Fh3zN6VFe^$e0A$M2t3ySa_JYv>N-zB52ga|XtZStLGbvuvM{Fa z)UrZ`2vS3nZq5^VqGB15@_pFH`UM3xU9a7`B~728FIZWo*4Ej)z=vk*G;Ms}d<2V7 zvP*ovTTq3-QKrKE&#z)kEXWsHOhCSU)K2p;b10{++OcJgAr!G}BHoBmqoVDTC|@_! zzccBgY;=vsCqz4-e)m;SXs)S$@ZH=Ig9i`3_Ta%Ew3O7Tni6X#jB!U~6fWJmGyTSm zozz7Gni+Ivd=ha&)lnpMa?=!z-qCpYGL=JaT5{UOK zJ-u+4{UY*fIkug5VbZnaHfP)3&C1G46BlP?Kj53eC!4ebJX>r3LHGBXSF)Mc{T3Xt zxt83yKU3h!S+12A(bem(s@j#_B(#Dh9K|@3nsOc*W9(gg3{japb1gZgU3U+HpMb;ZdLn5_`!vCT8gD6*K5D@)^Qn4=IYzXcyh3C2ZBYwSnH&hK7dD zxXIBrNZBAWRC&*6i|Lxho-;2rBXD|z^*56AAV!4HaLI9ky(3haa$9)KmN&dIQKzOx z_%1W_%t3cckCP_W)+u^cLC-W=wCICaPK0_M6BFZ~Z4T>8>wCA`XWp%ic=dhsr#uvt zgucndM(LZK*oj*ZJ%7+2g3QkyBkEMf z_=>o5D(y}w7UgsJWCat3&#SVyUAc4hu&Pa76cX^Z#3IT4`Sk+9pR^E^9KPYHhx5P4 z)h>Qy6F)}^RR0E61+E|fi|s>FO>BGJw7GOTwyj4yD^529Aw#nR$b=JKh=2W4bqd>^n*-6RJbV@C| zSya@EJ_6aQc`HnIMQsi06aS&kvy?n0v>2EJ3Vu2WmUqEIrnTzr`vw`O)}`c*=`OV_O8P2hGaB4!DI#z|bZTm*Le3xK za&SR(IYNml8H<-_)ap1aon~Gg&L6?aIvVxIr77vvO;UY(-X4jT-9(=vt>`>43g(O$zJ6?bdfR7$8hGMBN;cR9!;Kb6 zi%gj((B*@<%F4;vu!a;Lrpem6Xk4jwz|j8}XKws%iD}p;Dl8iO!foPPt zUpkhHUae`YHSop4v*V|;u)*tf{m^3N)(xpqIh#&M(?63D{w}5Xo@tsn~9g3#BWNi!K~tF%DUpnz`Uxxm2py2b!&syK|ZS8)luxVVrtJs6)7 zM=lyVWX7=*4YhQ2?+K^|eMA5LO+>NTVK57LqA=D07{g7EfS9q);(J07OOX*I{u9ea z6i8IJl}ZVQDK9%Fw8G}1vEBddg&FQEBVDQ7 zYh$MlRj`PwW!$5{PQ?K@Qbh%O>ptPB?b>w??qBCW|6C40Dbb#vPfg>{Nl4%coTq1U8j4%{*J z1Q)(r)S!4>?}1`HoYw2e&aOH)ImQc{S==^kTG$KcvdLWh$rgLL6ct_Tbj5#Q@OU;k|h~4 zrxmg>cFAAgd13H=+biF`d>QqrWk0r?#=X>TReqdPb51@jB;~UovaMqn$fUzGK3~w( zY=)tE5p0}99p-gPcQ{?Mn2;`O$xjwey&`;;8cHs?IM(LUrfR?frA|73Zk0-u8;2$Z z2reS|bGK?P`1v^_gRQWZ70#bO-_*_3^|Aklmpupg6C7jH;VWBU4H1<=?bs7%?z}PW z?rEuwrt=y##f?VV?!%Ds)*Y`tt7da_UAR%zSKC_!qccFZ=PJiA%QNu@g|Zy3?<4d2 zI$zeXes)0jqS6N~AzXxI5I~?9O+XgSD9waXo8QewctG$@cc{Cu8$+7ypvmV-3Dyeg zbrVSl0=A|GmrR4cbJH4U&g~=o>f$bN4BPUk`|AZB*DgAl>*94)c|y`G+D3xDs;+KmbjxD(SyeayzZ&PX1yuJkAkdD*q_}{-j+;HI?t%b?) z+1a()`^v=xq?I2ls*XH)-M`UaL=fhtr1V0G-0y0r#6ve@vVAM{S*-`3>)DwniXHa+ z*AwP_&TZZIwJ(R7o)`AQW(=1YAhkA|OOMgP=AID}(T>co6>4H?V z={t58-O$o@-$Y-0tM>fF4sHs(h7CXbZH&Z`UR7J5|` zOKTF9IbUR#q!k;b=JR|GDz9Mm##oyX`-xu-^s)y9AUEF*j_G6Jnlw`*U ze>qK)WJb+O4Vu1nZT+|BcJ23jy(QY_6DF(1qeqYC`Y$pE$C$t^+p*Tn6u?T>&mYj#SMaU5)?@aNSV4n ze^>}FpGYfvhyXec2_2qB5(3Q~zMOZ5Kz`GN(3lz+=pJhN)X)vBBXup0KC6WfXtxs_J56AuZ>g6#l3Ca zPA|9|_@V8dNaf82u5DEk50?}YJA-4=BO|zhF#=>T{g5SxNK4o}K!_J+)!NE;v!`GpImC!6C8x~}jA%evJx?yXQBS8;Twm{%|HLgO?v&ZHRlalw+$yD4q-~ve^@JKX6FB!o? zH_6NoX{m9qE?t@tUUX$**IFngg1myV!;OvrtB8)5eOnxA#0_EoKL|wi1!_H@m_03# zM?xHeG&#h$1bpNXNT5ny`}~rtb`JU~AuaH@k8WjQizt*$;?cfGDHD(5&;0ro7+M)- zFp~Y&?9vZZ4@nO!b^&oek5^d6dC(ap>`;E;>v~k-87?%G$!US9U~&7r2g$g1`$0Im z_;#+M5qZFqedSO!ZFeZ|m}{rOism}Vgh&dws6}GV-;74UFGiy}wq?r}WfvznNL6Em zADB1Q7oP{~Gw;)oM&110z_y6uZ*prtrHI&E^{y0Mg$^GIyp0k z4IN6P@#U*muWE*htfK|BM@7*|%2An53n_n7=j^$2XNEWnxN&l#eHH0z+NOds%~Nu@0|7B-{(N6( z0S(I&Lk)FGzH>O>`QN{Pf67=MI!fyic)wk8)cTz3OI1V>wDHTkIdqM;f0@uCF>Zp< z25ij8RezaecZ=s-UQ)DJRijz9wa>kHrFN>UgkqQ^;sX_C6k_2-@vL&?q<(0RXOZp5DO)|W7_SLA9SNYd?``RPAFempVT;L& zUv!>hwCEb^S_HRQpXE})!^};u0;8H8%SL zoX*-yXH0(FqGiidW7f3?R-%8Cx*GfcI1%^L_gV3Q&dYP6wp>mzLsqBco8)&0z;$!A zNA3r*3Z@hk6p(=&3MSHG;n#7b*E(_U&3zq7lTfAp^1EIaFlwG6tb?(``jbnY>dx#C zF{Auc(HAOxK{J=^!3;ln-9?T1lB4Hf04_*dX-JG_#I0>4bRxm7IJ65<Me$2U`#(Fl>Z{$XLiQR__tv=hw`MYi*s& zMz;$HzD1%)-IJ=Gd)ZJUr)`4+gQO5a0h$)lsAgr6Tefc(;w_IG<>lowL()e>kZyiH zMKJe-G$DIO?h>ON;KQn!t6BmfNBrkSg!;nS4xOv(zcA9^;ubuF<0nnZLyXY5>iNqU=Q9>zM$e%G#)vbL=j<9;J1h|FXI0N zkq@1%|8cy4KSjbcCmh(JV41phmAYySR7-LhQja!C4QSIZut|E8)qxowX?=@8{m{%H zK~8Nk@Chy1G&C{9Vu=Y}49wKYbKh|mB{X%^2Hn{Xd=!sw&3`o&bl75$0ITUa0nI)m z#qbNtM?sVd{Zg{0G)ZT&akDHQI(!c-cP^YkA}Q@~8sGsBFu10c z>T=`N3BfHW*oVWIYd%i1V=4w_z@$^-H}%B>bm8VL9uwa1BHUwwk>(5&%pEQi$J{@#(gBamyAO~6*pf1Z-|Hcw z!GKf=?W9;nN+L>25(I}Z8&IJOC zM`<*Ki>C{>;f~sE23dKFgXbjTGY;zJAOH{p)PfYA z9tx`8r_qdsy#1@Evc$Hg^3(I2>2ojZK57+nTpJ41-(4>M;Xhb(FH9v9A}leFQ3i(W zr@KkD&$$k1!@S#6K9lzty4K_wXA=XFLqyVO$gM9eK+x`>EcRPwXX}Y=U8;sDDdlp^LV_=hIWXghB(^thKalt-@G4xrm;n^TbT9;q$E%vnaK7v^ufcuq zzh?IQYlNyQy3Rx!-l@}XpQpLhH7|?wGjC@dX)yH-Yd$}?4kMbNn1HTn2=)kshq4+9 z*7g42ACcZOiaJ)oLG2w(8!3wN0cWFP;U(?$-P(8 z*YAX6Mn&m{$%{Nhx1y^T2sPf=GtyW~ZBKjZe27zw4cHxL0ufKh zaxr?zU8E4IuPSfMo?#;1i0*+4&c329*V_iAJ_*&1!b*zcVBM96@xqZcBp0ZB%7xwzvA5WysRNFq==vyD2#0;W# z`S~VN|Iwry0)W-To!@`uR=RZN#0e@Wd4zW8=|sLvLkSXeVfJ2|Yh7I~XS{kPX7|B@ z^tEmWP(X3$LJzL|l7O>*8V7r`yK>fwiC<7!Pd`@Z6&0e`$pgyO`j{^3(5aI!0Wb_K z9ymt~Fc>l7Fk9`EtKUbD8pY%RX$6fW4Z}!=Dsc6T^CP)ZbNc6O-7D7mVg>=1)^xIr0Q-$qyyujPcUESdm%VDa+kicv<^S_dF@L(&3ZX854#;51~w|@ER zmH&2C%*fB!-oj%f4WA2;W&B(JyB!V0oL}v}%{#Giz{s8Yy@}qsHH)(ug(Rbwdb)ND zh5q^HAG*}H`pSIMj2Vp(VqhN#ljHMWXu)^F@seQ8)*<2XkLkvmQ0E!1B01+1KmhVM z(&UA}Qh<4AJWz4$v^R+DYTkO!zI{Z2#si6_27co47ZDW|E?CYyoh#D#_SjA1^Eka^eZ@sX0=LO5M zo1-{EmZSP8Do6GXv^f_;-I54tgPvPU5l%_f%IMmVJ0~ekMVKSin#g&lx44FU9Xdc= zL`&CX-?To!ekY^_=!jdzi|AoAbFxTgtxWST^`x>U*I?1&#j}HqHs3^q^YuvF`ueT8 z|HN&doEKcCzj)4CMu30bWT$B-iB6eA0nlEfxK&2CXNn&x?C#vXdkRaqfAQSm-Q$IZVj!S9&9kCw1V=j!H-1cwh$84!q3}l) zlU55?zF6s!n9}#G_HFjm``FU%rAcWXXo^Nsq--|D0m2>4efxHKssTzpj4i?klUZr% zynjwedO7iLTId1993uQ>v>uq08@4TsNajLQ&AeH_HLi*FeZ6HX^q`PTVB8Lgc41%}G=+R-DGiZhW z)tImSZ;iQ9kk2wiVw{*l(+{b*24os)FzlHlP-a75d*)~3wpz-nRL3JCDK_mZ`pED zsSEY*&WY0(cU;x6lkz`xvMaZ$zpod`wzn^vQR>lgR77Z~ZEpdsKEH>v_?KAc;)4F} zzI+lHj?OFtrTfCH$qj@;> zbOO3}O>Mw5`uV1re_+S;H7Gis4pK`+;hn9$kvK>Zup4P#8T>lf{$BzV#GTDDqjkFWM#hQU1RgxMhq@s@xG^Wj24j>>WXiA=GQIiM-48kiUN1b0O-H4^}U zwJAWsqLcq_^nKFZK_pnOyiT>Dtheh_%<|3zLx zBP=HNp}OJ_6~jsLXrs`4gRu1J*Kg*O!t7hErmm2mgl5xOArye`(I3a3}9U!iYd3I2j6_N_ENi@)*RG z^m#)y1h_SjRPj6khllyNRVe`NTtrA|)drJS&25@kJ z)?=@!USupo%uQ#ip7!#sgICf9-KTZjPs1@H=m_Z~9YvQ1jspJMo$y!e@$;EALdmfx zZVBTEJCK9`n?>%Im=8M0T8nLyvCykPio|5;_?QPxJ*5Tvk>HXJDaHuD+1uG<8MsT) zHjj#EwsE$Zok$}9d{@Og$!U~DM@DW0^QwsQ9<&2wMWeHALp#>z-ss0KUVJ+YNPulD zWb&MP_dHm>^v>+&&6}#vMjn{-U+@A6z~I1~$z8~w3=I$0qdD;D_0%zl<)}aD5C`4N z?!J!T2SYMN-mrmCdN86_zpIM5dsS2``~@gGW(qHy<;UXUAYo2-h=mTw$80{b$m$p5 zpwu0TGwmVD`&0+Py*!*+Tc+SsNUg2Y0mz4X+ZRS-+Jofeu`WW50-oU#7bl&{`%zV; zL`%AE?0j3vH`8|5+%O9)AmQGRBY!P76$X0tF z%{7Sv#moI5CYX(;6CznMYE#~C^{_^#VMps>jnf^Lbehpj>Z2A;h#(=g8MSOrrK1Tv zJ>BNA5s)imi|j9&k-^FC`eO34k#iN9@>ylW7NXMSPn-DbAest(A9|lJQcq2Kh8dkE zZyIYqPHlNs-iO$g7&8p<6dgOD!_NOTl&oH zvxO%12IIFp0YDmG1te2Eqybl|`isIz^L0b`6Ef)G>^E)Qv=43Tb53qud>?gf4Z`yk zWIYdb7_K+WI~H>tor8dk2}=h75DXPr-|DQ#A;ZSE$?v?lv?1GP&pv4%sTcF9b|C8d z_3e9SkOpM}5Y3%p#EjrEbVfb6(gJIRv!)29sW-AzuHVyNt?;W`HXa-Fy|CFO4M$(E z3sW6`e%DxLHebH)9zWZJmW%cKqa%Z=I>t;eG%9zxj20N{O1douinn*7au$MyxVWj*=BYtTI^%a1(zZ^EOhtxi$i`|hV5+iG zx%>xN80un^Wy%RMi&bR&bIADlKfHK2ssS~5F?-k2dibW#xlQ|WuTS(0zBE&^?JJbU zAW$1|W8qpss^4ElMNBO5pRRKkL66MY&FzHgp0<)xcZGX*dE*lyb``GTeQK4mR{aO2w27B+lBJp&YQz`WmKk-JWHoGe5lKpwe>fwA2(Z}JIh9D{XJNk5Xh z3Hv#h&9|ttu-cnC{@9ytV*hw;)Pl=-I9e1wg^L2T-d8#hGIlw~s=}4hhyR6Y?^{6= zt8n(GUc35Uy8aKhM-)?0*tWo6?lD0SPzUvm7}E$1Bee%x2-cZOKym@N$&f9cED-z~ zzQ?UHZnz?Y`52q4%p?grabgHDEC{ds>+R~0@;@?iYA?M|EFu;hz6gcdn#R+`RdT?SCNBIOH^4!&^6Remfzdsl2?r$a2YtPr-oGm47mkV6GUa zwQfK9QV60EH-A5W{=5^V+&-dw(Wc0Q3+#=Hi%VH4$`mB2Qd9c5iy;0qm#v6C-U?U_ z%dMRu8b72~h}-3ye;GCH8jPC!^U7z3jx(I6&S*ec8rJl^eqyQuzgL7XD_MTb&rje{ zXx6;>a_|!Bzc7*C;eZiLxaE9XsAVuk{+6w@g))jdKEmoU=LZl{KvJ+tj0+er@#|P( zQrKy&QNfi*BFK;cP4rL+1h*Kh@E0obA2Fw_5L?@Q^zeO-+qL!dIpnlN0oJw-n< z{y<9~Z*LnckrKgVh5u;lUH@+D-&V7OOM4oyXYQmq+LJXLjtl1n?If)NTVs$aVE}mt zlw+5%cL{!E`JFwA2N$qgPPv;U+ZWxK7FcXIltF{TV&0n9<{FeR{!V?^()E$KQ>RSv zL^5*;W$z)x!6KWA@9U$a6!0)q7`Etpbs&?V7KLF8*PMnWMqngq&3Oz_J*0p>yvgn% zN1~FbX9P(M&W0XA&-@^$L<-;zDtkZExLCW0>6n&z!pta6uR{UROP5grk0xPxTe0-I zY33Oxk$fqjH~I>ZVh3aq;1C@Z)via6pzQn$Y33rP0+}~$ZsTPfuN~a>?rn;G0CZ9R zhXb@#z%OA7DKf6jn>Bkw&#BTw!$ZI)G-9mEmYHYNFu28Y#DYl+XB@05ptp+DT}<4a zxG!3+)pmLDqX*A4D^&`SNw^{-g>JM=prvJ1C5z?=paqMH#2hoH?PGM`0kw-AZE8 zCiCt@cP%@7_Uu9LL5!@qaH)yFazaxeWK1}Lg~nB6@=y@V z>d9rR269>97h@q-%^tQffgy4V27k~B=s#*ydtg*i#d;4CnV*tKiJIbXwtV??ZS8j8 zXe#v=Bo^#Z>0tbbUX*=ebQF<{S@c%%V3~N;E9d2e|n9N(1O$G|j-C+adHq z$c^);n|MxSpzBQpr=n2|hr~j1^vLN)cDPalan8m+xh;(t`92pkEtwr7jy>2o5mG-i zECg9y82ad6v_v9!dEl*{Z&17to7)O)2TLM}s(t-+sYaL~I2ddZhHj);d}v5u9NWPf zDsDj2tK>J6ME`!WF7AcE^8SK=AT${VPwD7flgMZNz`>i)m*xYECf+@V{p z5J%?x{ksiHmQ}hZU-rh;#No7T#flj$DHv|12%a9Z?hZpd9#WD4z`Wx=0#Zdi8rebp zEq{m4#~fzGVJJMDqJ0u!**pbwj7I?K_?E65q_SVEaK}R_(b9G?V@LpA^~`1FKl~5? zB--pn+%mg6jHT4{Df;fC2jq5t8njaU$&JKZ%M0 zuJ;t^5O}#5e}sxPR0<8%e!jlTjX2!SxTE?G9V%`QSNvuT47u>~;jLmH_74dmdyOp5 zUuvUdp{ErI!+WsS0GU))n6&7l)>^a4c>8bb!^f>B#fJhM+(BV;b61vHJP)8|?d>|HZF$J=9fXRHmhg@LfgEmCDH*XXFUMdf?Xs zDDERS_MxW(vOsL-dr9611wLU5nA`UX2R(^3GQYChF^bKFs+dg`HHHbFdX{knE0Tgr z%-RCVp;9-%!{O}Dgd|}X{3s|hav-XfYIlew z@LpgUwM|po-YwMhNjV$*R%=hVWv6+6YYFNrKK{@){>2{a7z1~?EYCPP6%-+VLm}A+ zBE&_)%1hjxHuu8yJ_fJw++Xx>5^PKmJ4B%7$C)!{dZ3)rB5qSJox3xHhFmYA?!AxO z!5bWXN4~sT<0VLFtn4~y)6T;jbhD%mw(>}AJ z-M|D$Vv)vcrF_`>kmJVOF2eB&=$65US7C+ydR4+u>w_%oo7;jrafdqRb7%N1nk!dc z{K7>ZgyzR>;yqF+J1KD{(J{uowfZ_iUVi^gl^Vm&GjvUZ9ug~7Um>ckqsoB)LWfHF zd}ns=^Q@+h8+@Ki9~MV!=s^u8rUDDPDgek-sFfHWJ$-&V-G3X2eLtdlq&LHVC_)f? zLWKya;2Rq>nw616JT!o=*dGpo9R_?9(fX)IIWllQf}?oeEf_71Ef~9$2ZSxj5n{M!Y6UmB@4$ib7)Tq&Zh(2+ z_RFDR#+$pL!wI(Ko&ON<*SW>myB)aps4lT?h1#?Z^IL0AoMYoMW7BVD8`W<&-s4gt z9bXp}xbM}IyTP?ai;~PLM&4a?O8?o0;3dZo@9((6+sUKiik71Jiajli6y;TtAGI1$ zUR$fZCGoH2;|H$}Da?DD_jYNzbzDfL_2Vh!PpeN?-z@%gNoLa}J}bzxL(L^UxxcQ5 zg-zNp_KX2h493@No3zV19(i~g6q1_SWAEo5Mo6%e2%FMg-T$(54|$WRkro%Kno6Dv zl3h|MazWzRJJA#9CijIzcY$tn<`~;8O9^|-UKaH%btuEt4zMGvbnM_a}HA?40 z^u>!!(A^5NgMzFJ*7lwbU$q5hL2)xxFnG)@0~(h5ZrjqQNTGelb_fpJ!$>56>YP^n zv;KlM{1Fj&Np*dcFTM6$a#nNRRV@*b^0}p@%6}ZgQI7__H|P7&laCAZA0Kd(?(ZLl z$+ulr_gnqMjC4M3OEzv^*>ZDD5LK=#V%UC6rlNpsEW?)Af*L63bZ35~UCQHodz*+0 z)7RV@O!uD9@zUo1k(Q&RiT-P(R z(q1>O|1R;Jym0Y>md*e1k#q+R?_d8vrwkh?GFfa0F>lwltrN8%qO>MFC68UAk)?4> zuD$q@ZYsqjyyN_v|@5$FVv{M}O(kp+-UaoE%qP z8$G0r%DT8?SfbZt+iTf&bK?MI-PwHTr`G96<4EEXKS%e|w=dH6!5p{kVrXY@2!9bsK$#5yFbf~IOoktPI_BZsW@ruZ@HAsLT=Wq6*xQ1F z-w+4~-rzTZPwmHNb?nRhdGplh`bRE{cbfQdSPcM;8Dt`_>=))>=G+X%vjsmR9qcu= zr|eBpQHx^__ApBgAYx3kLj6r$5*glbJHnmOQ;EiVY=-BAEx)Z=wF)KY3QtSHVZu7i z2uu@Y19Xv25zvI2vvV`tyX`Q=q5hW1JQwVz=Y8Sq`}~8ux(m8VkyzbD#k>PWM%p$> zjqdd%wId)W&Xj{pwaWialgE`1Va4C?_NsLvqd#4#7D`D>)64_fyiLPqF|=>0$~lV1 zuAl;;=FyCKgFjU0yNgm-6u93%n$`uRa+t_OH=T8C-rqM_O)XUiJF5>I69xpygAaYi z^2yl={UCk`sT7)PlG=ib!0?A)GT)2^6)pZKSZc$o%ci@=f@xZQvl>kA_j|h|RHI3} zhrt>?+EAoJ0oVDDkRmieA`6`CxQAmkX#c&j1tS?afpXC~Wvx5zaLu)A{q$`2#^bYlNw@ktrG2dX6uS;o2jNQHQ1{7}!Obv(Ms9Q5KxePWZ9@w z04i!&-?}+`V&7Ee;iW3>zdFniOu@s{!=R4(bY#k4mi>xG?QOD45 zFswjF1|N49<394k_i|Thn>Dd;`xI7HI&H7tu>QTPo@T68)BOB=yf8LCZpC0m^hUdS zPLb?-4;j*u9gNblw@6Q1WBS`}Io46wt2kdvWRRlNL~_&$K58UDoGOogAL5~4K4eaG z!2KA|C+yACj}T(_jPj6wdEdI#o&R0Gf+zJmaYCW+nBeBkWQt>YLX-IED#4Jp6Pw4m zGU>2~$6gjJpM6QV+kpdOo;?|4-NvQVKQE8t8;*x|+F^8`Vkjy?rHUI!%_>E35Rkb0 zi4%Do!^~Ig$Clzk#dv4sC^-Sr>I|;#sIkLo`?@DK3u!3=?g9JTT@bRe#HqFIIJ*#~#eDf#1#GrVL>?hv6G=jJx!#lu-@e6V&(2~IQ}Hq2 zqyvk0(BQ$hQKHdBuTD#M%~zQ6wXFy+se4?JrK7R>)=LwD;k;87DD##kPjf9n>{&fBeW6L{4o$bS3?FfAS zQ=0#FbbE0%HndUwRRFy2q{O?$MMcfJk77AKJv~CB8-TPJ7(!tr3=6Vl=x>;C2D29f&Em^F>F7#p> zSqEc?ky$qSobwwJX=YM4L)b1K(M8NsSf6pDHHES3!(kvVV1GCZjeHIT5=wCo9Tl+> z@;)iTDL{UVzxoO)AxXSFzEVohvilLN6``Z9zE|uO6mb0+)T^VV)tjPEoZx&tK4jWZ z0uT+6b$;`bd4C*(Y&135QKmjYs=cMFum6x_#sR~JFIa~-ygvtuh@==hsqQ{$?f<(R zFp;FW<&ixWC+EMYohRN#);gP6jl^HtW$Ew~*)Wrx=^phmZWdu}gy^M*J(~p*fYR-4 zaj|$0LiGXQ+K({-#CplqZ@zR?nr9e$01|NlEa!2rX^sE|&X3@5ahTuE-c=eUVp^cREirp-!P84BZU0JBPz#}u(t{p^_r!%6! zK2xR$(Il@W`&^#zl|HjC$#?8`T_$Z4w<&1&mnHCDMoF}52jP3qN>mtJ-Yz4S$poRk zD#@D%`}~zFt*9{fV9asN!^I&$1&TL@YL~wO=P@%Qt=uyyt_Fkx`;G}QefDb)a=RR@>U`;fSth%7(#P6PoPZ1 z50+LpG%oc|TWarNq2b!ww%s7Yg!F<1F~xWT6P`R-2m)T@>Z>0p6x}*TMoM%G$LIz` z#1|nwYGXHK4x-aDcpb7hbOxf+2^ivzUb{Bx)JU^^;-w^T@1P0Ui)#Ur{#vwf$^dA- z6Fm2b|Gg)FduetJa?STK`%wShy{Az^fj=1@)G!MQ#XDhfP?(#KdU+|Mo6#>{>Ce2> zShlDGx0fA;nN6Y=aPI=l9yXTie}DXDlBQN3=Su;cqx4uQE6t1PB8!PB{+rufGR z_72M?Gb@7iL`@-+ah-!JlJ`(OAjU|G&Od~<3Zy?2kcAV*Sx8r|F1y``zcZhz-lm9( zWH*W+ab(e*G?q>N{$@8NhZ<16`Mrwr@-U^kug|50n-FY+I9*@|lsU=ww2^T|sN6N9 zX^UAkpW>;+#i%q9?k6%M1V<*L`(`Ew?0$Gr2ue^9l8g;pP9|nlP(BDeT&UX!)8Ovy z*<3k|@-77l!>V9xJmB1|iIR{x0T@d_KH>?ZN)i|a1It4d*ZitMhMg1-#jmf^zERuz zpPcsxTfz}Q!!wp%+{{4Js`QQ@;RLSQF~wwM#Y8YXwgjMBmj#!@Cf3q_9s{^C%F|aK zcjoaZN{2xCKMpwS>gmm4XfxZ=Rw1Uk@EP&NIm?Wy;L(-FM&P-yU#e- zu|tO@GU38MsL_ve;L@$JU0UG}1&sR*BA?yxa`!l5^>|M5zZY$4{o8K6A4tbgU@ghJ zodohQ15cFsy{qs4Rle9QPPp-Iu|s#jy#Vd8JHCH5Dtt6bKj|f9(D^j;p!=((uJ)+U zc{B(Ewena3>JM%v2H6yL7j0)dl}?@Oj~{10L#TP-r(7Sm$cyKt--HQWLCgjzE6-9_ zZ%PxopP{6Ii;o96@qN?-9zha+!OO^#Df(|&Jo^9#XFDcn(diq8hn&lE%}q}=1~q!3rY8(Xo7pYPXQXb^@GlJ%ZyI->HQE)jb#k; zG4GM+TCSUMHf`(;=(OK7eA@Bu^v#lzlQrmk#AMF=?=&}DN)ZHWt$Oct{W_8A|MW{S z%3Yq+T92|35>oVpv>qO`Ij6{l{#h#39i#7I-rr^nbM4yYR|weXqlHh(AQUU@$u+XY z{KxIq;CUzS?sdSzq)p?FA8;0vXDL$q(n%gg;_v|b<}m6^%^CtR5PEB|@FkJ?@`Z`i z413auE<#Qke^o9lQ2bDJl%RtwWoj)ywAkP{(8;6-lm>R0OumZx(jKi5MEGk z3V+!;Qb+$MC{eUM#<_lP9;fhYIXQ3WC&K=srP9~~?5qT;^s`p?W_t!B>EDJ>X{CC1b$luRls%+z+kXPKo*8kjTkB^ zf@6e>mFR>XbfOm9=850xef-XvzY&l%W6!kf+VxN26o$sODLA!8L}CcH2X@0qNC(9Z zmm?#)z^+VY4xO|IluAyrQ{nA1CyQ3Oh3Oe z$6+zsH3Q4K(b^`$nr*^BFWLpV5>C0tF7w6ZN^So)h!^4hYW;n0^#k;L&R{BcZ{MEo zFQyu}4_>6$eaPU!Qgh&vhkYB#5Ubt+!hdj}+3_w@?7liZSQ!v{n7F_)AkTQBd^iyp zTeJOGb-Q*9J?}t1jKJw-@eCLG<5+VFZ0N(ao7exk8vnT}!4Igw)KxLJST~?*`x1yD zNtupBc<;!P5CennI>|Mb$$0u_!8OU45-2oZ@zmUy&qi$f^m5`BoJs$)jxcxsK=adc ziXwU#bwk5bdIt?mY@14e6B|(n=5N@8%ANwUIggU4PHD?T4_jYhNdKY4_+J&qju;aK znSRTm;?n;53epJDKLFodKp2Qz^4 zc?~aM`PL8nVMGGR8fqwAQ=`1`jf{*;9yJ#g-|D~E;5@pZ#XiCY@DL#4UV6F#n?Hg4 z1|)A_E;MAujY!#A7sozmi=vY$O>@}+S2A5nGg+WtZQJ%1yM}Ggf4~vlkEkR?Wxny1Us=(i}y8HRKs?f4Wbf<#axnG9aUrqFXc z{=t{KM7}XC74rK}Rj>POKmG4}#rE-hOy!{_#~$=YIf4Sv zDy*35b0tgKTDngjUfZx&VglF0kfgx6IhvZ+%&lxKhbYViJ+RO^HQ+w`;!u^l0UB^E6QMURg1fDv->?LEs!jqFAOyI15Or0erajIO^i* zzYBodliEAipFe(vf;%@c5s~H@>zC;1?PY+lVm{$uyG=r%+uKsJ9>#}+7RZ~b%lg~8 z5nV>W?EaR&+`pTOx$f?VLq`&B?Zi&8jM*nbH$fw_6HfcivJuqG&16rhuc+DH;=xzv z!4kzk&ji*MX?I74MZH1q3}XW-$n z%}f6QD>!uS)TuB1Bo@URKiZm$J*nZ#d%J#S=cacgiWh$+RAn1E46wv_8hdnmXYljZ#O;9dn$s!K;R=rf0Qtk|7y#q&Dtlzu!=l4y%!BsO4 ze;g86`#Fz(_yKKC@6} zP*ERup~5(N%*jH#PHh{Y@yiYEj&z{%rHJHXMHI9I~;?~XgO<60Gak8sO1CQ`h zeof1|{sQ!ey=MoYoENF2bjGB1@1+1+2Ku7;pD>1{YT^*~&S?Mlxo-3nnxPCN%$uH& zM?4YmsSL*KPC|nEIv*YXw!L~Oyh%4rsjn`xf~CI?s?=RrKAeDKu5VMUUtz;E{=Z0v z4VDc{F{l9g^JTb2XB3k{>;%+J>$d$AswqlJ8nr}-%%>=G5CRCR%i}{a z5=c3l?=t!_ndm}^J3S$qkIC}0}_WAMu;34#E{~Jaj)q@ntagmXW8;VCGH8u8kBv5m4G&wuN=nG-6T1fi1 zXl3*9|J=eO#@?J6*(hsVjwHQP$j+Xs>zCFs$KGCwdmCFlcI$5wRmSW?18GFf(((P% zm$kzZ4}D5qcX92QM)Cee;dk$B$m(?V>lbOFq3XtEQQARcSF3)R%@iNQth1`6>(WcI zx42E-+@|$-H_Lb17cH*35*TtR`PJj(>eXvqe?)l9U6dU4dQ8FOTRpC8#NJSC8I)G) z`8mcqF#8F^-WMd0LcBgt;P z`MbM+s{*#_t}bwHw+#^;aiy`&4kjk@JCe`UGKhs4VFTAyB0D^-4qs`&8f)8*~8 zUl$FPRj)D#9vXAXp?!)~I-*TR2{=(Fq-K;bU}xsKb%POMxAX|8wSFK-W>z1dX>pRO@$T_K9!S|E?iRgEW1o%I(C#iRAJx2BSmNkx{ehP z31pZsF<3m|GFjhU{|g)fO#MwJv@s%CN)QQUXai*N3^bD)Jxe^BTkWgo|J-%FC?ffB z@nfsj`>ebkCOb~NyY^8Nts^Gdo3{PjE46ex;CyS_lPPmIY-_X4>7?4c)yE#JZ$DD5 zt?cE-(wLBlMgFf_C;!##wSf^#(OX5Wqq@3Zb4)kK3@CaSq7we?Y*c`*vHhfpH{Lh> z>*k8=L*-I*lt<|-i-N6_))hZlwx~Qh?WWumT_^Lxg;Nvc{A60$ineW2ic7(KQNj$p z&*YdTR?%3K1rRCd#K-D;1S|#yLWI^RlI#$rEA04LmZ(`n2+MgLe}-Q;3E2_pEJ&X3 zqG$_Uo8mn&55amn%%sz~146!+2LsJ4rA8t@;8w+pB{s|qD8nGDR4lwGctOk{5oRxt z7UGdVkS?ra8~WJ6xoQykCvC#f>9-g;Bfwmzj=zjiYeJGXbNKHIA_^GDgZFXibz z1?qo1uTBq%tJ5t^KIUW^vAbDG>N?%mGZlQhrkUrBGcn2W^Ym2Fm-9Tk)YUw^Ec8&1 zSq93(zmyHOyJD7TfsTB-@JsneWWw+3teH`zhiYicToZ_sXa)%n(*J0@By zu75DN=I#FHHLt^;8x?7UTTSg-d}04HrPLyq^;V_p=e9|Uh`6e9W8#;<+xn&zkBw)Y zPuj5XNu9ew_r;E(Qr&@mr8g~BI+$%JI6L9WrSV_Tutpp4~RZ$T>fw24cdHE32+_WE~htBo#va@rv zvx?qUxwX+mso=wpSJH?5y;LpNm47$hUh_eF%^FkLf^H?zRx@Apb6c5pG9)Ne#XTst z=s>UZK!eQK?3l`|)?XvLz2D?#l{3>dt8;9hEx*a?N?w)c% zoxN3Qsj2n$y10D>OVVr0GJ4u9%Xk~>ppf%CI4hfnXvD;d;K6T)ARnW)k^A`#?8keV> zwC!6pWnIbLH-MdvQp&vO+OJQa>5o>2fnO>qBnQ=!$@?H6COIZ0d(^!&a~o{Sc!}3ifQj0@wPPMa>PxHk7u=xlug_0rTReGy0mtM+2(F*rOyUTwu*O}9RJPUIA(su&zp^dSG(T-nbc_Ss_s4q zcI16Dq8pAePzLY z+3{}<%!VB}YC%!v5C5Y(8_!joC?B3+@t5uU?BFa1Ub=g>?_3+>O)zHir><%C71MO~AC5KE*t%^-glP@xyy*9o}k>`PK)mKXYzMXrxoyKcR zW{62x0^66q+A&Ci_>98L*8)TU%laat5Qh_Y$;hKFUs*bWSSn?8 zFdBdl;{Xx=+(Kx@fDM=s{L;+DkhViP;T9)!tR9pjXVrHQGnZ?>UY`2rgO%4N^h&+f zP;YPDveCjPC-T55X;tUPPCqWR>2b?~Ops4Mwf5aUtQ~XZ<=RK#KVz<5yY{rCu;p$2 z&WFEw9Pb&atG#L6*7RUE`|%T|j35H-cCU8@FRBiFy1DYi3Dfq|##q18&$9L@n>8TL z#U;0BLQF|X&m-fOP5btx=al1>n>H6!&kZwDzNWHreMM-RfpJ;srfsp3f-KqNgjZN< zXI=dlc5kCpTb~LmNlsRHDYq@h-Tk70@>Byu+u$=X4&!vImyX+}n(66T8qxSuY)N`4 zrEFhC*BvR*+jk_6wVc>!qs-~6i%VE^%NE;|w{BT-{pXkb{IwSxGNyQX_PQPs7B*Ge z=(PS4hebJOZdMKP^NU>n!+wJv{_ZwPdh#+$#ZmG%QazH~>}nLG2MmM)NGPPyg-Y4f1ioe9HN;z2`7+M-VtitueV24*#s(!)2 z->o0dTBN#PZRME-m+$&%`@DI#(!O}?h!Jf$$rC`HgdPsM2ro?=F(ON%(`8DR?AQZ< ze;Qw^UF7vn|Fcb=2CAi_N_Z57L%%=%_zk6k=1&2*FYVEzhe!Hx$6+Be{~OdkxLDY; z>*aJ+AFH+ivWs*}`kpgSoO%m=SKq3jH*xEwycRp}-1GLnXS-p;lXM-+sUJ(C*K8mO zB;feOGkwd;bI!a7|L7i|_n}t@*EF7LH9a(P zmfMMaQnlq%ui;C-=iRVKS6yp<<;*LCBM)?&$V%F@sa*W9#ESmne!0fOt*S#A0At}`~JNU3suGP z3H`nIu@%Mx0Ch{Us&Ge;1E zja>FlcDBi%BGBO9N(9Xx?9ThilC2N%OytoXfe}nZ0tvEh&Yb2Hoo>(t^{B8&M?zPa zR%Q1ley7dXlv$zJbO=UkD$_DF^iRdewRlR{bhWEwFo@YpyMNnMI7Zykn_D*adD~`0 z<^Em;txKLR|NP?Iln7M@`pBIx?pK^qS$E4IWIURo#ggZV%GHZ<9k*5#-I$dTQE7i; zNyLXznxIk zRQ>S@Z#4$YI3Gj-nuz<+;-!c)q}^JX(?N#Tm4OX-)yU# zW36w=?Q7R|f6U6t+05cmm3?cR5z%WzfJ1e=xb81jpV^-{t8{5hRY~0m4;6=L(c2nd zioU73#Prd=o8IZBFFJPokZu2Fr}Xoh<)N5`)!$Sd&Ytagt83xyk6F@9^{sLfzgchn z6n)*kGNJCKqk~1`!t)O%^BrAsb4N9CIyaX$_`NO&j>@}H;rP-e$9>Di z30`}19_E+O?mo%bE5OZ3i7>fbq#u$NWDnTleAirF(ykQhf4b)oOE>`Io$#EPS#@ zDg5>47`G=0anVsVS-OUXt3G~zYk!t_rH9mw4qoW>B)N5$&q1S@UijzgcIDe^sytN!(pD`giL1+5VPz#R zn``&g#y>hHCL@V4@Kw^nX=UC+3_4jx7M>d?MV()|=Fz^>j~WOmXz)f`aXZcET^ieEjLVyXc2z%PV4v5inXR%N1NAXI6EueMsQ6du3==PD8zzjxEe&iOT7s4 z1KDp`w|8#E#EPdgFV|WZzdkHbyV9FtDK9;o?fTf!&6fVpSW!(MnLl3;JCE^ zc>{ph$DFu{#xju<_d_xkFV$(KRdn;3o>4O6%XsATU)%^<@-;26ZbR`7bSEROnI*Q% z2r;dT3@@*z;lxku%bU?Ctti0AA-~$ir8LoNQe|S`v~?DRGu5{5D;#cY7aYFjU*CKG zE#Ld_gY+@PbA1Fq{OikmQjfn0=QJ@7S&-Uh4{L{<@t3Tw_yg$fI*uIjHY6yx4A~NvcDr4^O0NR~H%FXUvR628PY$A6s z`dHI&)22{bvpI9;hM8Lop2`IGW1DYumpCo#koBH}sG;hH9KJNCChMM!xadO zGBb&AGD{mfa-;~l0>Is%la1n7cxKSpXswCy&4FY=kkS=m-};7Ct9G}aNxpNLz_^>J ztHv@S8kdhcb@b_=}&@zBq9NV)933fSfz$345qeh6!cTzCtDoEcOi$N@b--WauI^A`l>W?NkQ-VUB zwbE_GC?D2GVn8wC36V|e-0YW7C@(~+3DgNm|0ow<(7j#}C&UfN8>G2&*IT-KdJe;M ze)QO}MOM*>Ed}fk{&HsIf6*ALqUEtHSpHu%#*4nR{QsdZ)^J^eXio^4eTz{lt|IaS zi)sG`LQl&q@DgDD0bD%rC$G*X<+N;ozeOc4RK|$>k3#sYAQel06~Q8MGC>m4Som%F z8*T^;vJn}WKS$*t6E3|~-Ug~$A`{m|5W^U+WltG0)TPyL{}O zGTd{_5^_Uc3)R%}eNNm|nHV$m529|?PUIX&b@15uWXcJZAH%8*{0#!rAWO+qj&VOb zY`tqD$COf<^km#YrqZ)5G!hYf9A-i~^c5)bzW)E|>`Z{FyskL@ftu6=OEG9zA{miJ zDNhzbOpv-Dm_!gGBBCG=St2453b^ZYi%KE}P|-+T24W!M!-7I0fl!GYYK7^CfpmVNfdjZ<7)*3Ik` z%S!4tc?HALhF+(eNjE!)*dUb3KI)k5!N_|xh53|Aq*(?{hVtKODjwdW)VR@DH>9Vg zQmf)d0A-{U5}B_#c+dsB3KO$gO8maI~eJq=-*Z4tcqN} zZk@E2`4C@_2S`$)aXA#e7?uF)Qdbdh0*rqtc>$DE0_V2pOvVu3*dXxlxd&~mtjd5Y zHWRu&q{Wl+!~VM*E4m!;5_3Pg`l=#q6)>fJl>sK7qZJYW`>#NW#QF~^pY3$CGc6JHyJ9zkXVRgCo4PASvG#WZ_jG?% zt77u|o>;j|yB`YB64)%~8PYC!7jDq>zl}%!Bz-hSs8kz@ zIuDQWzTbTIRkQ>6in_7p^dcN2>$BUJ1V5al|K|--k7X4>jbT2;-Ca)04MZAY$ER>0 z7Yuv53!E}c6YUQE5p{K(B>HoJi9$d;sIM25UPt}DiJl5f2N9GXDC99y^sbCBC?)jT zp%B+Iambt)l^MVQvJNGWF z?=Mv~6g);wvGX;zK#0)+=qM%$jXqz!CqMCJor`d@;Jh^>zv5JV*RBiN@NN;&=-<7W z|C}1cHLkw>>-BG{=R1fI4-PMarICM7 zpsLk|_gsMdJw2Gimj5GL@M#z{Xpl&BMbE&zc#qD}Yk`obkPPto4r8D6f@Y6?4$o>s zTNVfovgG@wdp?;F**E9e8d*3y`xCa^TrKSFXMiSIEHCQ%s$u>+JN+KpyYX0KGp_YU zomlrTs=`E80Hq;@TcrEjotSED9uJh}AWr}0?(BA@=GXz{7wXwY1l_^_B z=VD7NlKRsA>q%V{mY(=N)tGl)_FRy9v*4Ln=~tj~52xG`D?uc)-%}=Qj`)r|SHe>N zs)ltc{}%RbR8YkIf^pcAO^6#la?=cLNLMaOsB5tvw8-ED*yyQ4K$H&NSHbdZInm+EY4ie#9xQA zn#j}8bhgts10$kRDP|)KlI2m*R$zd2cyJ+N{XM!U;oj~4;k;h*A||uQ-Zb^zhof#7 zEv#zbK5c_a6s9s~HduNI4aWIFSW(QLq$CkL4n^c+t+uwlP_$x<7^U?JqkNBqCHz<% zaQI~<>yw^^U4pOqqXF~dH3?{QD&T=&Pnsilrlwkf1L80mYTw9N-yazoSnb>3App-~ zMLf)``G_~9*Hb|dR*53DJ8iBH`F-R0lD?UWei}*1>g0hJzl}~8s3E*~Ud1cV^F)*^ z@LaPZ5}3E$bDWBfD+%`*3VZBhLq_jQ=}J+}HV0?&g0MF?7jF5}v82DD&M7$!jg9Aw zq5y-_PknIJf6hGI!UE;mgKR}V9?F497ljZ756Y2?VN1y{h$ zRX`}h)Hkg-73*~CHVZcxVK`14^Zt!~WreWi+LvZEbRX5>YpC3ADHUDMN+COhjUfE!C}to{Qmhk+^cuwbF8sDheiTmi zywi)3&xokB(wBKlTlB54@hEC_6ilbCwWwS4C{hb3O7Da}kis9TXOkk9uS?lnm^#1Y z7xQ(GPz%{!!=kmQX3|)a+N;}3_a@p~1FNSo%HmFp zk#O7diZxx(I;6i80$f}y#*MoYc1ew?_h|;t%`F%pz^wF0vv&4Mv#zvh>yI_<%^Ka6 z&?yb-^G@azdU|*mLQ=s?kpUgrs&2_S1y;po0YQj0S+oFANP2g0eU+%Wc04>(!cLhi(_UbrK&#j!Jy^kO!lyuPC zd>(`=qBo!GgADARC0g0{io*N+Rl4#95#v)6IiEK16H2m`C2nUlLZ}Zvf zOM@m*{>G>U`s!sk--ZWF(h?|s*a z-szJq+ldjlY_0Xl%KJ3pUt^f>d>O4RC zynt~y?TWmDx_vO{E@o;jbV%ZnXtX_4R>gwuCGzJ>)1Pb;(I~HlC~ry6w&%J{TSdgn zVN=#}rN|VPf+heZ(1dqLWrx1~wh`tEmFFf%E8W3nY7x{J>yY*xu=cDtJ^Xj z^?PjkxX=FbPVlNA2=LclOD~W6_~QwH=UVmA@IGwp>zZDD2*T&nY--PlpM7b?tj-4o zjmw{Ob&4{J&Z4UyTzeV1zK5pteBudH}6i zYqVc?SgW7I#(vO#?QjXy(-w=6H^!|rLL@HEV62sQI29_ZZf$&We@1$Z^5k&Q*vau$ zuk5#7**aSjM4F!i;(V-`kT RIb4zQzMH3OwoA~u{{Tk~LGJ(n delta 102710 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 AH2?qr literal 18677 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�$ zN?KYJ+dDe0d@FEu;W&M|xS~S*_Q_WY3JRV;ya`@z?pu?+U2_h3!&X9FLjwaW!PUAV zA|l`V`n1TmJ@oV>RxT%JzL}YnVL{g_jFI9J6Y<(J zI{3SBT(|a=|D-41HZ?oDZ((7f-iW^sljzQ_F1$5!XJ_ZT=8^rPQvyi2aSIF6H#9VG z^YT(#T3X)1Qd?W|(HuAssXV`OkmOe2F}VeSxmWl0#kqW}N^ From 61a7a87403894354894ea2c30c5f8445371f7dca Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 30 Jun 2019 17:09:36 +0800 Subject: [PATCH 044/176] release v0.9 --- LICENSE | 222 ++++++++++++++++++++++++++++++++++++++++++++++----- README.md | 8 +- README_cn.md | 8 +- 3 files changed, 215 insertions(+), 23 deletions(-) diff --git a/LICENSE b/LICENSE index e94a6c89..3a64707e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,201 @@ -MIT License - -Copyright (c) 2018 Bwar - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (C) 2018, Bwar. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index fa5a4213..dd59b55f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ English | [中文](/README_cn.md)         ## Overview -Nebula is a business-oriented IoC distributed network framework developed in C++ language. It supports multiple application layer communication protocols including proto3, http, https, and websocket. The purpose of developing the Nebula framework is to provide a fast and high-performance distributed service cluster based on C++. +Nebula is a flexible, high-performance business-oriented IoC distributed network framework developed in C++ language, designed for production environments. It supports multiple application layer communication protocols including proto3, http, https, and websocket. Nebula makes it easy to deploy a fast and high-performance distributed service with C++, while keeping the same server architecture and APIs. NebulaBootstrap provides out-of-the-box integration with Nebula service, but can be easily extended to serve other types of application. Nebula is a production level framework and distributed solution project for instant messaging, data collection, real-time computing, message push and other applications, as well as web api services. There were production applications for instant messaging, data acquisition and real-time analysis on line now, and a recommendation engine application for a large user base will be born soon. By the way, using Nebula for toy-level projects is also good for learning network communication. Bwar welcomes more developers to join the Nebula project. Nebula is a proactor development framework(a framework-implemented proactor, not an operating system support). The IO-intensive application on nebula with be good performance. @@ -126,6 +126,12 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v0.9 + - add Model + - add Chain + - simplify Context + - optimize Actor + - change license from MIT to Apache-2.0 #### v0.8 - compatible with gcc4.8 compiler. - add cpu affinity inorder to support cpu binding. diff --git a/README_cn.md b/README_cn.md index beac3004..774d7fea 100644 --- a/README_cn.md +++ b/README_cn.md @@ -24,7 +24,7 @@ ## 概述 -  Nebula是一个面向业务的IoC分布式网络框架,以C\+\+语言开发基于事件驱动型的TCP协议,支持包括proto3、http、https、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建高性能的分布式服务。Nebula自身核心代码只有2万行左右(不计算proto文件生成的代码)。 +  Nebula是一个灵活,高性能的面向业务的IoC分布式网络框架,专为生产环境而设计。Nebula以C\+\+语言开发基于事件驱动型的TCP协议,支持包括proto3、http、https、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建高性能的分布式服务。Nebula自身核心代码只有2万行左右(不计算proto文件生成的代码)。   Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建分布式服务才能真正体现其价值。为了能快速搭建分布式服务,开发了包括各种类型服务的NebulaBootstrap解决方案。 @@ -138,6 +138,12 @@ Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula) ## 版本历史 +#### v0.9 + - 增加Model模型组件 + - 增加Chain调用链组件 + - 简化Context上下文组件 + - 减少Actor继承层次,优化Actor相关代码 + - 将开源许可证从MIT修改为Apache-2.0 #### v0.8 - 兼容gcc4.8编译器(从这个版本起无须另行安装5以上gcc版本,可以无障碍无等待地在个人机器上部署和测试,也为应用于生产铺平道路。之前Bwar的埋点数据采集和实时分析的生产项目Nebio是在服务器上安装了gcc6才部署的。) - 增加CPU亲和度设置以支持将Worker进程绑定CPU功能。(有人测试过繁忙的多核服务器,绑定CPU比不绑定CPU有20%左右的性能提升,现在Nebua可以让开发者自行选择是否绑定CPU) From 5f1b88186a4c3551b29fa1ff83638ba26c6656a7 Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 2 Jul 2019 23:12:46 +0800 Subject: [PATCH 045/176] change document address --- README.md | 2 +- README_cn.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dd59b55f..e9699a64 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Documentation - The complete documentation for Nebula is available: [Nebula documentation](https://bwar.github.io/Nebula) and [Nebula wiki](https://github.com/Bwar/Nebula/wiki) + The complete documentation for Nebula is available: [Nebula class reference](https://bwar.gitee.io/nebula) and [Nebula wiki](https://github.com/Bwar/Nebula/wiki) ## Depend on diff --git a/README_cn.md b/README_cn.md index 774d7fea..e66dae8f 100644 --- a/README_cn.md +++ b/README_cn.md @@ -106,7 +106,7 @@ curl -H "Content-Type:application/json" -X POST -d '{"name": "Nebula", "address" ## 文档 -Nebula 完成的文档在 [Nebula documentation](https://bwar.github.io/Nebula)和[Nebula wiki](https://github.com/Bwar/Nebula/wiki),持续更新中。 +Nebula 完成的文档在 [Nebula类参考](https://bwar.gitee.io/nebula)和[Nebula wiki](https://github.com/Bwar/Nebula/wiki),持续更新中。 ## 依赖 From ae622eaf5e93fe2a37b4da300731dc39b9a7f70d Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 5 Jul 2019 20:52:02 +0800 Subject: [PATCH 046/176] change Targs to Targs&& --- README.md | 5 ++--- README_cn.md | 5 ++--- src/actor/Actor.hpp | 36 ++++++++++++++++++------------------ src/codec/CodecHttp.cpp | 2 +- src/codec/CodecUtil.cpp | 3 --- src/labor/Worker.hpp | 36 ++++++++++++++++++------------------ src/labor/WorkerImpl.hpp | 16 ++++++++-------- src/labor/WorkerImpl.inl | 34 +++++++++++++++++----------------- 8 files changed, 66 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index e9699a64..d57da197 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ English | [中文](/README_cn.md)         ``` # Nebula : An event driven asynchronous C++ framework -[![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
+[![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)
1. [Overview](#Overview) 2. [Features](#Features) @@ -120,9 +120,9 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Todo list + - Complete writing user guide before September 2019. - NebulaMydis Data Agency Service. - Developing an IM with the Nebula. - - Complete writing user guide before September 2019. ## Change log @@ -131,7 +131,6 @@ A simple testing can be start with a NebulaInterface only, and also can be start - add Chain - simplify Context - optimize Actor - - change license from MIT to Apache-2.0 #### v0.8 - compatible with gcc4.8 compiler. - add cpu affinity inorder to support cpu binding. diff --git a/README_cn.md b/README_cn.md index e66dae8f..7befb6b4 100644 --- a/README_cn.md +++ b/README_cn.md @@ -9,7 +9,7 @@ ``` # Nebula : 事件驱动型网络框架 -[![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
+[![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)
1. [概述](#Overview) 2. [功能](#Features) @@ -132,9 +132,9 @@ Nebula 完成的文档在 [Nebula类参考](https://bwar.gitee.io/nebula)和[Neb ## 开发任务 + - 2019年8月底前完成开发指南 - NebulaMydis数据代理服务 - 应用Nebula开发IM项目 - - 2019年8月底前完成开发指南 ## 版本历史 @@ -143,7 +143,6 @@ Nebula 完成的文档在 [Nebula类参考](https://bwar.gitee.io/nebula)和[Neb - 增加Chain调用链组件 - 简化Context上下文组件 - 减少Actor继承层次,优化Actor相关代码 - - 将开源许可证从MIT修改为Apache-2.0 #### v0.8 - 兼容gcc4.8编译器(从这个版本起无须另行安装5以上gcc版本,可以无障碍无等待地在个人机器上部署和测试,也为应用于生产铺平道路。之前Bwar的埋点数据采集和实时分析的生产项目Nebio是在服务器上安装了gcc6才部署的。) - 增加CPU亲和度设置以支持将Worker进程绑定CPU功能。(有人测试过繁忙的多核服务器,绑定CPU比不绑定CPU有20%左右的性能提升,现在Nebua可以让开发者自行选择是否绑定CPU) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index b8c6af7d..712defe6 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -72,12 +72,12 @@ class Actor: public std::enable_shared_from_this Actor& operator=(const Actor&) = delete; virtual ~Actor(); - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); - template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs... args); - template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs... args); - template std::shared_ptr MakeSharedContext(const std::string& strContextName, Targs... args); - template std::shared_ptr MakeSharedChain(const std::string& strChainName, Targs... args); - template std::shared_ptr MakeSharedActor(const std::string& strActorName, Targs... args); + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); + template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs&&... args); + template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs&&... args); + template std::shared_ptr MakeSharedContext(const std::string& strContextName, Targs&&... args); + template std::shared_ptr MakeSharedChain(const std::string& strChainName, Targs&&... args); + template std::shared_ptr MakeSharedActor(const std::string& strActorName, Targs&&... args); ACTOR_TYPE GetActorType() const { @@ -264,39 +264,39 @@ class Actor: public std::enable_shared_from_this }; template -void Actor::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) +void Actor::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { - m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); + m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } template -std::shared_ptr Actor::MakeSharedStep(const std::string& strStepName, Targs... args) +std::shared_ptr Actor::MakeSharedStep(const std::string& strStepName, Targs&&... args) { - return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); + return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); } template -std::shared_ptr Actor::MakeSharedSession(const std::string& strSessionName, Targs... args) +std::shared_ptr Actor::MakeSharedSession(const std::string& strSessionName, Targs&&... args) { - return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); + return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); } template -std::shared_ptr Actor::MakeSharedContext(const std::string& strContextName, Targs... args) +std::shared_ptr Actor::MakeSharedContext(const std::string& strContextName, Targs&&... args) { - return(m_pWorker->MakeSharedContext(this, strContextName, std::forward(args)...)); + return(m_pWorker->MakeSharedContext(this, strContextName, std::forward(args)...)); } template -std::shared_ptr Actor::MakeSharedActor(const std::string& strActorName, Targs... args) +std::shared_ptr Actor::MakeSharedActor(const std::string& strActorName, Targs&&... args) { - return(m_pWorker->MakeSharedActor(this, strActorName, std::forward(args)...)); + return(m_pWorker->MakeSharedActor(this, strActorName, std::forward(args)...)); } template -std::shared_ptr Actor::MakeSharedChain(const std::string& strChainName, Targs... args) +std::shared_ptr Actor::MakeSharedChain(const std::string& strChainName, Targs&&... args) { - return(m_pWorker->MakeSharedChain(this, strChainName, std::forward(args)...)); + return(m_pWorker->MakeSharedChain(this, strChainName, std::forward(args)...)); } } /* namespace neb */ diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 25a3be22..47590188 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -779,7 +779,7 @@ int CodecHttp::OnUrl(http_parser *parser, const char *at, size_t len) std::string strQuery; strQuery.assign(at+stUrl.field_data[UF_QUERY].off, stUrl.field_data[UF_QUERY].len); std::map mapParam; - EncodeParameter(mapParam, strQuery); + DecodeParameter(strQuery, mapParam); for (auto it = mapParam.begin(); it != mapParam.end(); ++it) { (*pHttpMsg->mutable_params())[it->first] = it->second; diff --git a/src/codec/CodecUtil.cpp b/src/codec/CodecUtil.cpp index 72d53e34..73e6267d 100644 --- a/src/codec/CodecUtil.cpp +++ b/src/codec/CodecUtil.cpp @@ -7,9 +7,6 @@ * @note * Modify history: ******************************************************************************/ - - -#include "logger/NetLogger.hpp" #include "CodecUtil.hpp" namespace neb diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index a1a65241..18f179b1 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -30,22 +30,22 @@ class Worker: public Labor // actor操作相关方法 template - void Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); + void Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); template - std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args); + std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args); template - std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args); + std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args); template - std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args); + std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args); template - std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs... args); + std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args); template - std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs... args); + std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args); virtual uint32 GetSequence() const; virtual std::shared_ptr GetSession(uint32 uiSessionId); @@ -94,39 +94,39 @@ class Worker: public Labor }; template -void Worker::Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) +void Worker::Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { - m_pImpl->Logger(strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); + m_pImpl->Logger(strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } template -std::shared_ptr Worker::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args) +std::shared_ptr Worker::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args) { - return(m_pImpl->MakeSharedActor(pCreator, strActorName, std::forward(args)...)); + return(m_pImpl->MakeSharedActor(pCreator, strActorName, std::forward(args)...)); } template -std::shared_ptr Worker::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args) +std::shared_ptr Worker::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args) { - return(m_pImpl->MakeSharedStep(pCreator, strStepName, std::forward(args)...)); + return(m_pImpl->MakeSharedStep(pCreator, strStepName, std::forward(args)...)); } template -std::shared_ptr Worker::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args) +std::shared_ptr Worker::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args) { - return(m_pImpl->MakeSharedSession(pCreator, strSessionName, std::forward(args)...)); + return(m_pImpl->MakeSharedSession(pCreator, strSessionName, std::forward(args)...)); } template -std::shared_ptr Worker::MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs... args) +std::shared_ptr Worker::MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args) { - return(m_pImpl->MakeSharedContext(pCreator, strContextName, std::forward(args)...)); + return(m_pImpl->MakeSharedContext(pCreator, strContextName, std::forward(args)...)); } template -std::shared_ptr Worker::MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs... args) +std::shared_ptr Worker::MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args) { - return(m_pImpl->MakeSharedChain(pCreator, strChainName, std::forward(args)...)); + return(m_pImpl->MakeSharedChain(pCreator, strChainName, std::forward(args)...)); } } /* namespace neb */ diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 47c0147e..e2c9fc44 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -194,28 +194,28 @@ class WorkerImpl void Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); template - std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args); + std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args); template - std::shared_ptr MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args); + std::shared_ptr MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs&&... args); template - std::shared_ptr MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs... args); + std::shared_ptr MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs&&... args); template - std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args); + std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args); template - std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args); + std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args); template - std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs... args); + std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args); template - std::shared_ptr MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs... args); + std::shared_ptr MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs&&... args); template - std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs... args); + std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args); std::shared_ptr InitializeSharedActor(Actor* pCreator, std::shared_ptr pSharedActor, const std::string& strActorName); bool TransformToSharedCmd(Actor* pCreator, std::shared_ptr pSharedActor); diff --git a/src/labor/WorkerImpl.inl b/src/labor/WorkerImpl.inl index c0b8e67e..4e183096 100644 --- a/src/labor/WorkerImpl.inl +++ b/src/labor/WorkerImpl.inl @@ -22,13 +22,13 @@ void WorkerImpl::Logger(int iLogLevel, const char* szFileName, unsigned int uiFi template void WorkerImpl::Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { - m_pLogger->WriteLog(strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); + m_pLogger->WriteLog(strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } template -std::shared_ptr WorkerImpl::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args) { - Actor* pActor = ActorFactory::Instance()->Create(strActorName, std::forward(args)...); + Actor* pActor = ActorFactory::Instance()->Create(strActorName, std::forward(args)...); if (nullptr == pActor) { LOG4_ERROR("failed to make shared actor \"%s\"", strActorName.c_str()); @@ -41,45 +41,45 @@ std::shared_ptr WorkerImpl::MakeSharedActor(Actor* pCreator, const std::s } template -std::shared_ptr WorkerImpl::MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strCmdName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strCmdName, std::forward(args)...))); } template -std::shared_ptr WorkerImpl::MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModuleName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModuleName, std::forward(args)...))); } template -std::shared_ptr WorkerImpl::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strStepName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strStepName, std::forward(args)...))); } template -std::shared_ptr WorkerImpl::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strSessionName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strSessionName, std::forward(args)...))); } template -std::shared_ptr WorkerImpl::MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strContextName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strContextName, std::forward(args)...))); } template -std::shared_ptr WorkerImpl::MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModelName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModelName, std::forward(args)...))); } template -std::shared_ptr WorkerImpl::MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs... args) +std::shared_ptr WorkerImpl::MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strChainName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strChainName, std::forward(args)...))); } } From 674e3f086302455227e951f6cba7c5e3a89a2823 Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 5 Jul 2019 22:48:15 +0800 Subject: [PATCH 047/176] change project description. --- README.md | 4 ++-- README_cn.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d57da197..fa993b3a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ English | [中文](/README_cn.md)         /_/ |_/\___/_.___/\__,_/_/\__,_/ ``` -# Nebula : An event driven asynchronous C++ framework +# Nebula : a powerful framwork for building highly concurrent, distributed, and resilient message-driven applications for C++. [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)
1. [Overview](#Overview) @@ -94,7 +94,7 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Documentation - The complete documentation for Nebula is available: [Nebula class reference](https://bwar.gitee.io/nebula) and [Nebula wiki](https://github.com/Bwar/Nebula/wiki) + The complete documentation for Nebula is available: [Nebula class reference](https://bwar.gitee.io/nebula) ## Depend on diff --git a/README_cn.md b/README_cn.md index 7befb6b4..84fece69 100644 --- a/README_cn.md +++ b/README_cn.md @@ -8,7 +8,7 @@ /_/ |_/\___/_.___/\__,_/_/\__,_/ ``` -# Nebula : 事件驱动型网络框架 +# Nebula : 一个强大的IoC网络框架,用于以C++快速构建高度并发,分布式和弹性的消息驱动应用程序。 [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)
1. [概述](#Overview) @@ -106,7 +106,7 @@ curl -H "Content-Type:application/json" -X POST -d '{"name": "Nebula", "address" ## 文档 -Nebula 完成的文档在 [Nebula类参考](https://bwar.gitee.io/nebula)和[Nebula wiki](https://github.com/Bwar/Nebula/wiki),持续更新中。 +Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 依赖 From bcb2cdde72e21cf472cc824eb5710fd4663f54e5 Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 8 Jul 2019 22:03:42 +0800 Subject: [PATCH 048/176] add nebula_server_demo --- README.md | 2 +- README_cn.md | 7 +- docs/cn/nebula_server_demo.md | 133 ++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 docs/cn/nebula_server_demo.md diff --git a/README.md b/README.md index fa993b3a..b61f3ab0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ English | [中文](/README_cn.md)         / |/ / _ \/ __ \/ / / / / __ `/ / /| / __/ /_/ / /_/ / / /_/ / /_/ |_/\___/_.___/\__,_/_/\__,_/ - + one-click installation ``` # Nebula : a powerful framwork for building highly concurrent, distributed, and resilient message-driven applications for C++. [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)
diff --git a/README_cn.md b/README_cn.md index 84fece69..4d593bb1 100644 --- a/README_cn.md +++ b/README_cn.md @@ -6,6 +6,7 @@ / |/ / _ \/ __ \/ / / / / __ `/ / /| / __/ /_/ / /_/ / / /_/ / /_/ |_/\___/_.___/\__,_/_/\__,_/ + 真正一键安装部署 ``` # Nebula : 一个强大的IoC网络框架,用于以C++快速构建高度并发,分布式和弹性的消息驱动应用程序。 @@ -57,11 +58,9 @@ ## 开始 -  Nebula以C++11/C++14标准开发,编译器必须完全支持C++11(部分C++14的特性在遇到较低版本的编译器时有预编译开关控制使用C++11标准替代),最低要求gcc4.8.2,建议使用5以上gcc版本。Nebula目前只有Linux版本,暂无支持Linux之外的其他类UNIX系统的时间表。 +  Nebula是个较大型项目,也是一个你难得一见的依赖很少的项目,并且提供了一键安装脚本,[NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap),让开发者可以快速部署和体验Nebula。相信部署和体验之后,你会对Nebula产生兴趣,这将会是一个可以广泛应用的框架,基于NebulaBootstrap提供的分布式解决方案可以很方便地用C++开发微服务应用。 -  Nebula是个较大型项目(因为要构建一个生产用的分布式集群),有一些外部[依赖](#DependOn),鉴于外部依赖的存在和框架本身较难测试,提供了[NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap),让开发者可以快速部署和体验Nebula。相信部署和体验之后,你会对Nebula产生兴趣,这将会是一个可以广泛应用的框架,基于NebulaBootstrap提供的分布式解决方案可以很方便地用C++开发微服务应用。 - -  构建前必须保证你的系统已安装好完全支持C++11的编译器,除此之外的所有依赖都会在以下构建步骤中自动解决。 +[高并发单机Server示例](docs/cn/nebula_server_demo.md) 构建步骤: 1. wget https://github.com/Bwar/NebulaBootstrap/archive/master.zip diff --git a/docs/cn/nebula_server_demo.md b/docs/cn/nebula_server_demo.md new file mode 100644 index 00000000..813fbaf2 --- /dev/null +++ b/docs/cn/nebula_server_demo.md @@ -0,0 +1,133 @@ +### 安装单机Server Demo +  Nebula提供一键安装部署,不管是单机Server还是分布式服务均一键完成,单机Server安装如下: + +```bash +# 获取安装源 +wget https://github.com/Bwar/NebulaBootstrap/archive/master.zip + +# 解压安装包,给脚本加可执行权限 +unzip master.zip; rm master.zip; mv NebulaBootstrap-master NebulaBootstrap; cd NebulaBootstrap; chmod u+x deploy.sh + +# 一键安装 +./deploy.sh --demo +``` + +### 运行单机Server Demo +  安装完成之后就可以启动Server并测试了,配置和启动过程同样是一键完成。 + +```bash +# 配置Server ip地址 +./configure.sh + +# 启动Server +./startup.sh + +# 测试一下 +curl -H "Content-Type:application/json" -X POST -d '{"name": "Nebula", "address":"https://github.com/Bwar/Nebula"}' http://${your_ip}:15003/hello +``` + +  这里给出的是用curl命令测试的方法,同样可以用postman等客户端测试。 + +### 单机Server简析 +  基于Nebula框架开发高并发单机Server是一件非常简单的事情。这个单机Server示例只有三个文件,当然,写成一个文件也是可以的,但为了清晰起见应独立成不同的文件。Nebula是个框架,编译成libnebula.so给业务开发使用,所以Nebula自身是没有main()函数的,各个基于Nebula的Server写个main()函数就完成了一个可运行的Server开发。先来看看这个不足10行的main()函数有多简单: + +```C++ +#include "util/proctitle_helper.h" +#include "labor/Manager.hpp" + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + std::cerr << "para num error!" << std::endl; + exit(-1); + } + ngx_init_setproctitle(argc, argv); + neb::Manager oManager(argv[1]); + oManager.Run(); + return(0); +} +``` + +  别看Nebula提供了NebulaBeacon、NebulaInterface、NebulaLogic等许多Server,实际上这些Server的main()函数及main()函数所在文件都是一样的。严格来说,这个main只需要三行: + +```C++ +// 设置程序名 +ngx_init_setproctitle(argc, argv); +// 定义Manager类实例 +neb::Manager oManager(argv[1]); +//开始运行 +oManager.Run(); +``` + +  ModuleHello.hpp和ModuleHello.cpp是测试的业务逻辑代码,这个HelloWorld代码量也非常少。 + +ModuleHello.hpp: + +```C++ +#ifndef MODULEHELLO_HPP_ +#define MODULEHELLO_HPP_ + +#include +#include + +namespace demo +{ +class ModuleHello: public neb::Module, public neb::DynamicCreator +{ +public: + ModuleHello(const std::string& strModulePath); + virtual ~ModuleHello(); + virtual bool Init(); + virtual bool AnyMessage( + std::shared_ptr pUpstreamChannel, + const HttpMsg& oHttpMsg); +}; +} /* namespace demo */ + +#endif /* MODULEHELLO_HPP_ */ +``` + +ModuleHello.cpp: + +```C++ +#include "ModuleHello.hpp" +#include + +namespace demo +{ + +ModuleHello::ModuleHello(const std::string& strModulePath) + : neb::Module(strModulePath) +{ +} + +ModuleHello::~ModuleHello() +{ +} + +bool ModuleHello::Init() +{ + return(true); +} + +bool ModuleHello::AnyMessage( + std::shared_ptr pUpstreamChannel, + const HttpMsg& oInHttpMsg) +{ + HttpMsg oHttpMsg; + neb::CJsonObject oResponseData; + oHttpMsg.set_type(HTTP_RESPONSE); + oHttpMsg.set_status_code(200); + oHttpMsg.set_http_major(oInHttpMsg.http_major()); + oHttpMsg.set_http_minor(oInHttpMsg.http_minor()); + oResponseData.Add("code", 0); + oResponseData.Add("msg", "hello!"); + oHttpMsg.set_body(oResponseData.ToFormattedString()); + SendTo(pUpstreamChannel, oHttpMsg); + return(true); +} + +} /* namespace demo */ +``` + From cfc48cc07a2a3a74f8db71a438d622f7248f1225 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 13 Jul 2019 17:16:58 +0800 Subject: [PATCH 049/176] add documents --- .gitignore | 1 - README_cn.md | 44 +------ docs/cn/FAQ.md | 0 docs/cn/SUMMARY.md | 31 +++++ docs/cn/configuration.md | 204 +++++++++++++++++++++++++++++ docs/cn/dynamic_hello_demo.md | 186 ++++++++++++++++++++++++++ docs/cn/install.md | 38 ++++++ docs/cn/nebula_distributed_demo.md | 41 ++++++ 8 files changed, 505 insertions(+), 40 deletions(-) create mode 100644 docs/cn/FAQ.md create mode 100644 docs/cn/SUMMARY.md create mode 100644 docs/cn/configuration.md create mode 100644 docs/cn/dynamic_hello_demo.md create mode 100644 docs/cn/install.md create mode 100644 docs/cn/nebula_distributed_demo.md diff --git a/.gitignore b/.gitignore index 345f6b40..29188631 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ docs/html *.o *.so -config* include/ lib/ *.cache diff --git a/README_cn.md b/README_cn.md index 4d593bb1..de555557 100644 --- a/README_cn.md +++ b/README_cn.md @@ -60,46 +60,12 @@ ## 开始   Nebula是个较大型项目,也是一个你难得一见的依赖很少的项目,并且提供了一键安装脚本,[NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap),让开发者可以快速部署和体验Nebula。相信部署和体验之后,你会对Nebula产生兴趣,这将会是一个可以广泛应用的框架,基于NebulaBootstrap提供的分布式解决方案可以很方便地用C++开发微服务应用。 -[高并发单机Server示例](docs/cn/nebula_server_demo.md) - -构建步骤: -1. wget https://github.com/Bwar/NebulaBootstrap/archive/master.zip -2. unzip master.zip; rm master.zip; mv NebulaBootstrap-master NebulaBootstrap; cd NebulaBootstrap; chmod u+x deploy.sh -3. ./deploy.sh - -  执行deploy脚本后即完成了Nebula及NebulaBootstrap分布式服务的编译和部署,Nebula的依赖也由deploy在构建Nebula前自动从网上下载并编译部署。虽然不像autoconf、automake那样众所周知,但deploy脚本完成的不止是autoconf、automake的工作。deploy之后的目录结构如下: -* NebulaBootstrap - + bin         server的bin文件存放路径。 - + build        构建路径,由deploy.sh生成,如果部署完后不需要再构建,可以直接删掉(可选)。 - + conf        配置文件存放路径。 - + data        数据文件存放路径,比如基于Nebula开发的页面埋点数据采集和实时分析[Nebio](https://github.com/Bwar/Nebio)项目,将采集的数据落地到这个路径(可选)。 - + lib         运行所需的库文件存放路径。 - + log         程序日志存放路径。 - + plugins       插件(动态加载的业务逻辑so)存放路径。 - - logic       逻辑Server插件存放路径,插件按server存放只是为了好区分,也可直接存放在plugins下,具体规则可自定义(可选)。 - + script       脚本库存放路径,deploy.sh startup.sh shutdown.sh等脚本都需要依赖这个路径。 - + temp        临时文件存放路径(可选)。 - - configure.sh     配置脚本,deploy之后第一次启动server之前先执行该脚本做简单的配置修改,也可以逐个配置文件打开直接修改。 - - deploy.sh      自动构建和部署脚本,自动下载并安装依赖,自动构建和部署,执行./deploy.sh --help查看帮助。 - - shutdown.sh      关闭server,可以指定关闭一个或多个server,也可关闭所有server,不带参数时关闭所有server(需用户确认)。 - - startup.sh      启动server,可以指定启动一个或多个server,也可启动所有server。 - - README_cn.md - - README.md - -  构建完成后,可以开始启动server了: -``` -./configure.sh -./startup.sh -``` -  server应该已经启动成功了,startup.sh会打印已启动的server。如果没有启动成功,可以到log目录查看原因。执行grep "ERROR" log/*和grep "FATAL" log/* 先看看是否有错误,再到具体日志文件查看错误详情。注意,Nebula的默认配置文件对IP单位时间连接次数做了限制,如果在测试量较大发生莫名奇妙的问题,可以修改配置限制,通过查看日志中的WARNING信息通常有助于定位这种不是错误的“错误”。如果server已启动成功,那么可以用postman、curl等做测试,看看结果。 -``` -# 只启动NebulaInterface即可完成http的hello测试 -curl -H "Content-Type:application/json" -X POST -d '{"name": "Nebula", "address":"https://github.com/Bwar/Nebula"}' http://${your_ip}:16003/hello +* [高并发单机Server示例](docs/cn/nebula_server_demo.md) +* [分布式服务示例](docs/cn/nebula_distributed_demo.md) + +* [安装部署说明](docs/cn/install.md) +* [配置说明](docs/cn/configuration.md) -# 启动NebulaInterface、NebulaLogic和NebulaBeacon完成分布式服务http的hello测试。 -curl -H "Content-Type:application/json" -X POST -d '{"name": "Nebula", "address":"https://github.com/Bwar/Nebula"}' http://${your_ip}:16003/hello_nebula -``` -  这个简单的测试可以只启动一个NebulaInterface即可完成,也可以启动分布式服务完成。NebulaBootstrap提供基于集群和单个Server的HelloWorld,基于集群的HelloWorld启动了NebulaBeacon、NebulaInterface、NebulaLogic三个server。下面是一张集群架构图: ![nebula_cluster](https://github.com/Bwar/NebulaBootstrap/blob/master/image/nebula_cluster.png?raw=true) diff --git a/docs/cn/FAQ.md b/docs/cn/FAQ.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/cn/SUMMARY.md b/docs/cn/SUMMARY.md new file mode 100644 index 00000000..e815d0b1 --- /dev/null +++ b/docs/cn/SUMMARY.md @@ -0,0 +1,31 @@ + +* [开始](getting_start.md) +* [高并发单机Server示例](independent_service.md) +* [分布式服务示例](distributed_service.md) +* [Nebula工作原理](how_nebula_works.md) +* [Nebula分布式服务简析](nebula_cluster.md) +* [快速开发组件](actor_components.md) + * [Actor模型](actor_model.md) + * [Actor概览](actor_overview.md) + * [Actor](actor.md) + * [Cmd和Module](cmd_and_module.md) + * [Step](step.md) + * [PbStep](pb_step.md) + * [HttpStep](http_step.md) + * [RedisStep](redis_step.md) + * [Session和Timer](session_and_timer.md) + * [数据](data.md) + * [会话](session.md) + * [Model计算模型](model.md) + * [Chain调用链](chain.md) +* [通信方式](way_of_communication.md) +* [配置说明](configuration.md) +* [通信协议](protocol.md) +* [高效复用与组合](fficient_reuse_and_combination.md) +* [动态加载,反射创建](dynamic_loading_and_reflection_creation.md) +* [相关项目](main_projects.md) +* [分布式、微服务](distributed.md) +* [应用案例](applications.md) +* [开发规范](specification.md) + + diff --git a/docs/cn/configuration.md b/docs/cn/configuration.md new file mode 100644 index 00000000..288bfc88 --- /dev/null +++ b/docs/cn/configuration.md @@ -0,0 +1,204 @@ +  Nebula的配置文件为json格式,可读性、可扩展性和易维护性都很好,同时也非常方便通过配置中心远程管理。选择json格式配置文件还有很重要的一点,因为[CJsonObject](https://github.com/Bwar/CJsonObject)让使用json如使用C++自己的结构体那般方便,随心所欲(关注一下[CJsonObject](https://github.com/Bwar/CJsonObject),这也许会是你见过的最为简单易用的C++json库)。 + +  讲解配置文件前先明确几个概念:分布式服务集群(distributed service cluster)、节点(node)、接入(access)。每个启动的Server(可以理解为一个Manager进程)称为一个节点(node);每个节点都会向一个Beacon或一组存在主备关系的多个Beacon节点发起注册,由同一个或一组Beacon管理的所有节点称为Nebula cluster。从Nebula cluster外部访问Nebula cluster需经由某一个或一组具有相同节点类型的节点,这些接收外部访问的动作称为接入,接收外部访问的节点称为接入节点。 + +  Nebula项目提供的是配置文件的模板,每个基于Nebula的项目(如NebulaBeacon、NebulaInterface等)都有各自具体的配置文件,可以理解为Nebula提供的配置文件是类,基于Nebula个项目的配置文件是类的对象。如果基于Nebula开发了自己全新的Server服务,也可以给予Nebula的配置文件改一个属于Server自己的配置文件。先来看看配置文件的内容再来逐项配置说明: + +```json +{ + "//node_type": "节点类型:ACCESS,LOGIC,PROXY,CENTER等,由业务层定义", + "node_type": "ACCESS", + "//host": "系统内各Server之间通信绑定的IP(Server to Server)", + "host": "192.168.18.81", + "//port": "系统内各Server之间通信监听的端口", + "port": 9987, + "//access_host": "对系统外提供服务绑定的IP(Client to Server),若不提供对外服务,则无需配置", + "access_host": "192.168.18.81", + "//access_port": "对系统外提供服务监听的端口", + "access_port": 9988, + "//access_codec": "接入端编解码器,目前支持CODEC_PRIVATE(4),CODEC_HTTP(3),CODEC_PROTOBUF(2)", + "access_codec": 4, + "gateway": "113.102.157.188", + "gateway_port": 9988, + "//server_name": "异步事件驱动Server", + "server_name": "AsyncServer", + "//worker_num": "进程数量", + "worker_num": 10, + "//worker_capacity": "子进程最大工作负荷", + "worker_capacity": 1000000, + "//cpu_affinity":"是否设置进程CPU亲和度(绑定CPU)", + "cpu_affinity":false, + "//config_path": "配置文件路径(相对路径)", + "config_path": "conf/", + "//log_path": "日志文件路径(相对路径)", + "log_path": "log/", + "//max_log_file_num": "最大日志文件数量,用于日志文件滚动", + "max_log_file_num": 5, + "//max_log_file_size": "单个日志文件大小限制", + "max_log_file_size": 20480000, + "log_levels": { "FATAL": 0, "CRITICAL": 1, "ERROR": 2, "NOTICE": 3, "WARNING": 4, "INFO": 5, "DEBUG": 6, "TRACE": 7 }, + "log_level": 7, + "net_log_level": 6, + "//permission": "限制。addr_permit为连接限制,限制每个IP在统计时间内连接次数;uin_permit为消息数量限制,限制每个用户在单位统计时间内发送消息数量。", + "permission": { + "addr_permit": { "stat_interval": 60.0, "permit_num": 1000000000 }, + "uin_permit": { "stat_interval": 60.0, "permit_num": 600000000 } + }, + "//io_timeout": "网络IO(连接)超时设置(单位:秒)小数点后面至少保留一位", + "io_timeout": 300.0, + "//step_timeout": "步骤超时设置(单位:秒)小数点后面至少保留一位", + "step_timeout": 1.5, + "boot_load": { + "cmd": [ + { "cmd": 1001, "class": "neb::CmdHello" }, + { "cmd": 1003, "class": "neb::CmdDbAgent" } + ], + "module": [ + { "path": "neb/switch", "class": "neb::ModuleSwitch" }, + { "path": "neb/hello", "class": "neb::ModuleHello" } + ] + }, + "//with_ssl": "SSL配置(可为空),路径为相对${WorkPath}的相对路径,公钥文件和私钥文件均为PEM格式", + "with_ssl": { + "config_path": "conf/ssl", + "cert_file": "20180623143147.pem", + "key_file": "20180623143147.key" + }, + "//refresh_interval": "刷新Server配置,检查、加载插件动态库时间周期", + "refresh_interval": 60, + "//beacon": "控制中心", + "beacon": [ + { "host": "192.168.1.11", "port": 16000 }, + { "host": "192.168.1.12", "port": 16000 } + ], + "dynamic_loading": [{ + "so_path": "plugins/User.so", + "load": false, + "version": 1, + "cmd": [ + { "cmd": 2001, "class": "neb::CmdUserLogin" }, + { "cmd": 2003, "class": "neb::CmdUserLogout" } + ], + "module": [ + { "path": "im/user/login", "class": "neb::ModuleLogin" }, + { "path": "im/user/logout", "class": "neb::ModuleLogout" } + ], + "session":["im::SessionUser", "im::SessionGroup"], + "step":["im::StepLogin", "im::StepLogout", "im::StepP2pChat"], + "model":[] + }, + { + "so_path": "plugins/ChatMsg.so", + "load": false, + "version": 1, + "cmd": [ + { "cmd": 2001, "class": "neb::CmdChat" } + ], + "module": [], + "session":[], + "step":[], + "model":[] + } + ], + "runtime":{ + "chains":{ + "chain_1":["step1", "matrix1", ["step2A", "step2B", "step2C"], "step3", "matrix2"], + "chain_2":[] + } + }, + "//custom": "自定义配置,用于通过框架层带给业务", + "custom": {} +} +``` + +  配置文件自带了比较详细的注释,因json自身不支持注释,所以注释都是在需注释的key之前增加一个以json_key前加上“//”表示的key。配置文件里的必选配置项和可选配置项之间差异比较模糊,因为配置项本就很少,理解每项配置也很容易,给出必选可选提示反而会让人偷懒。如果不想先理解配置那么麻烦,想先以最快速度搭建服务体验一下,也没问题,[NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap)提供了一键部署,可以很负责任地说Nebula绝对是极为少有依赖那么少部署那么容易的开源项目。配置文件模板所列的所有配置项都会在框架启动时自动读取,如果某个Server不需要这项配置,删掉并不会有任何副作用,如果是必需的配置项,Server启动时就会报错。 + +  配置文件大体可以分为Server参数配置、控制中心配置、动态加载配置、运行时配置和自定义配置五部分。日志级别和动态加载配置、运行时配置、自定义配置都支持运行时修改,一个刷新周期refresh_interval后生效,如果是通过Beacon配置中心修改则修改成功后立即生效。 + +#### Server参数配置 +* node_type 节点类型,由业务层定义,框架把节点类型当字符串处理,用于标识若干(数量不限)服务节点,节点类型字符串建议全大写字母。在Beacon配置节点订阅关系就是通过此项节点类型配置来实现。比如,有3个Server的节点类型配置为“ACCESS”,另有6个Server的节点类型配置为“LOGIC”,在Beacon配置了ACCESS类型的节点订阅LOGIC类型节点信息,那么当有任意LOGIC类型上线、下线时,所有ACCESS类型的节点都会收到通知,ACCESS收到通知后会自动更新内存里存储的LOGIC节点信息。 +* access_host 对系统外提供接入服务绑定的IP(Client to Server),若当前节点不提供对外接入服务,则可以直接删除这项配置。 +* access_port 对系统外提供接入服务监听的端口,若当前节点不提供对外接入服务,则可以直接删除这项配置。 +* access_codec 对外接入服务的编解码器。目前支持CODEC_PRIVATE(4),CODEC_HTTP(3),CODEC_PROTOBUF(2),且只能配置一种编解码器,后续有可能在同一个端口提供多种协议编解码服务。若当前节点不提供对外接入服务,则可以直接删除这项配置。 +* gateway 网关服务地址,用于客户端路由和负载均衡。gateway和gateway_port提供的是预先通过请求某个接口获取接入服务地址的负载均衡方式,因每个节点的负载都可以从Beacon获取到,客户端发业务请求前先通过一个固定接口查询需将正式请求发往哪个(些)接入节点,得到接入节点地址后再发业务请求。为什么不直接返回access_host、access_port?因为这两个是节点实际绑定的地址和监听的端口,外部不一定能直接访问(很可能也是内网IP和端口),比如需要通过交换机或防火墙,而交换机或防火墙是通过端口映射的方式到access_host、access_port的,那么客户端需要获取的是映射前暴露在外网的地址和端口,此时gateway和gateway_port的作用就体现出来了。假设接入服务前端还有一层类似LVS的外部负载均衡服务,只需把access_host、access_port配置到负载均衡服务即可,gateway和gateway_port不会用到。如果access_host和access_port是外部客户端可以直接访问的,则gateway配置的地址与access_host相同,gateway_port与access_port相同。 +* gateway_port 网关服务端口。 +* host 集群内部通信地址。 +* port 集群内部通信端口。host:port组成的字符串在集群内唯一标识一个节点,用作管理层面(与Beacon通信)名字服务的节点名字。host:port.workerindex组成的字符串在集群内唯一标识一个节点的一个Worker进程,用作数据层面(也即业务层面,除Beacon之外的节点间通信)名字服务的节点Worker名字。 +* server_name 节点Server进程名,方便在服务器中标识和管理,在集群管理和节点通信中都不会用到。通俗一点讲,这是给人用的不是给机器用的。 +* worker_num Worker进程数量,每个节点由一个Manager进程和若干个Worker进程构成。通常,如果某台机器只部署了一个Nebula服务并且主要是给这个服务使用的,为了更充分使用机器资源,将worker_num配置成与cpu核数相同。 +* worker_capacity 进程容量,用于过载保护。进程负载 = Channel数量 * 系数 + Step数量 * 系数。这个计算公式会根据需要和合理性做调整。当进程负载达到进程容量限制时会拒绝新的连接。 +* cpu_affinity CPU亲和度,为true时,Worker进程会均匀地绑定到CPU核。默认为false,不绑定。 +* config_path 配置文件存储路径,相对于NEBULA_HOME的路径。 +* log_path 日志文件存储路径,相对于NEBULA_HOME的路径。 +* max_log_file_num 最大日志文件数量,用于日志文件滚动,超出这个数量的日志文件将会被直接删除。 +* max_log_file_size 单个日志文件大小限制,用于日志文件滚动,超出这个限制将滚动日志,生成一个新的日志文件。 +* log_levels 日志级别枚举,仅用于给log_level和net_log_level配置时参考。 +* log_level 本地文件日志级别。框架级别的调试日志用的是LOG4_TRACE(日志量很大,部署生产时请不要打开TRACE级别日志),建议业务的调试日志用LOG4_DEBUG。 +* net_log_level 发到LOGTRACE日志跟踪服务的网络日志级别。 +* permission 连接或消息发送频率限制。addr_permit为连接限制,限制每个IP在统计时间内连接次数,超出permit_num次数限制的连接会被直接拒绝;uin_permit为消息数量限制,限制每个用户在单位统计时间内permit_num发送消息数量,超出限制数量的消息会被直接丢弃。stat_interval到了之后重新计算。 +* io_timeout 网络IO(连接)超时设置(单位:秒)小数点后面至少保留一位,用于触发连接的有效性检查。如果在到达超时时间前连接有数据收或发过,则从最后一次收发数据时间开始重新计算超时时间;如果超时时间到达却一直没有数据收发过,有三种处理情况:(1) 需要做应用层心跳检查,自动发送心跳包,心跳包得到响应,连接得以保持,重新计算超时时间;(2) 需要做应用层心跳检查,自动发送心跳包,心跳包未得到响应,立即断开连接,回收连接所分配资源;(3) 无须做应用层心跳检查,立即断开连接,回收连接所分配资源。 +* step_timeout 步骤超时设置(单位:秒)小数点后面至少保留一位,用于请求发出后等待响应的默认超时等待。在代码层可以为每一个发出的请求设置等待超时,通常为Step类的最后一个参数,如果这个参数为缺省值,则配置文件里的step_timeout会作为这个Step发出请求后等待响应的超时时间。 +* boot_load 服务启动加载的处理模块入口。Nebula框架会被编译成动态库 libnebula.so,基于Nebula框架的Server需要写个main函数并编译链接成一个二进制文件,比如NebulaBeacon,还有一些不需要做动态加载的模块会一起编译链接到这个二进制文件里,而这些模块是Nebula框架未知的(libnebula.so早于任何一个server二进制文件存在),boot_load就是为这些模块加载使用的。boot_load里的配置说明见下文描述动态加载配置说明。 +* with_ssl 配置接入服务的对外连接是否需要SSL传输加密,如果需要则配置ssl配置文件路径和相关文件名。 +* refresh_interval 刷新Server配置,检查、加载配置或加载卸载插件动态库时间周期。 + +#### 控制中心配置 +* beacon 控制中心地址和端口,有几个控制中心节点就配置几条。如果无须控制中心,则配置为空。 + +#### 动态加载配置 +  动态加载配置dynamic_loading是一个json数组,每个数组元素都是一个SO动态库插件。每个插件的配置项说明如下: + +```json + "dynamic_loading": [{ + "so_path": "plugins/User.so", + "load": false, + "version": 1, + "cmd": [ + { "cmd": 2001, "class": "neb::CmdUserLogin" }, + { "cmd": 2003, "class": "neb::CmdUserLogout" } + ], + "module": [ + { "path": "im/user/login", "class": "neb::ModuleLogin" }, + { "path": "im/user/logout", "class": "neb::ModuleLogout" } + ], + "session":["im::SessionUser", "im::SessionGroup"], + "step":["im::StepLogin", "im::StepLogout", "im::StepP2pChat"], + "model":[] + } + ] +``` + +* so_path 动态库路径,包含文件名,是相对于NEBULA_HOME的路径。 +* load 表明该插件是否需要被服务加载。服务运行过程中,把load从true改为false,在下一个refresh_interval到来时,该插件会被卸载;把load从false改成true,在下一个refresh_interval到来时,该插件会被加载。 +* version 插件版本。如果load为true,当版本发生变更时,在下一个refresh_interval到来时,该插件会被卸载然后重新加载。因插件在被正在运行的服务加载时,直接替换so文件会导致服务coredump,所以虽然version这个功能存在,但并无有意义的作用。如果要升级插件,通常是先把load改为false,等待一个refresh_interval,再替换so文件,把load改为true。这样做插件升级,虽然不用重启服务,但实际上服务对应插件的功能是会有一到两个refresh_interval的中断,其他插件服务不受影响。 +* cmd 以命令字作为入口的模块定义。这是一个json数组,意味着一个动态库里可以有多个功能模块,通常相关联的功能模块会被编译链接到同一个插件动态库里。当然,每个功能模块自成一个插件动态库也是可以的。 + + cmd 命令字。 + + class 类名,如果类被定义在某个名字空间里,则类名必须带上名字空间。插件被动态加载时,框架会通过类名反射机制创建对应Cmd类实例。 +* module 以url location路径作为入口的模块定义,用于http模块接入。这也是一个json数组,一个动态库里可以有多个功能模块,通常相关联的功能模块会被编译链接到同一个插件动态库里。当然,每个功能模块自成一个插件动态库也是可以的。 + + path url路径,不含schema和host、port部分。 + + class 类名,如果类被定义在某个名字空间里,则类名必须带上名字空间。插件被动态加载时,框架会通过类名反射机制创建对应Module类实例。 +* session Session类名数组,配置该插件内所有Session类名,类名需带上名字空间。插件卸载时,所有插件内的符号(在这里体现为各动态创建的类对象)均需要先释放再调用unload_so,所以类名如果配置不全将可能会导致卸载不完整,服务有可能会在后续执行中coredump。 +* step Step类名数组,配置该插件内所有Step类名,类名需带上名字空间。作用同上。 +* model Model类名数组,配置该插件内所有Model类名,类名需带上名字空间。作用同上。 + +#### 运行时配置 +  运行时配置是为了提供随时调整服务的功能,在许多业务场景里边不会用到。当前的运行时配置只有调用链配置。 + +```json +"runtime":{ + "chains":{ + "chain_1":["step1", "matrix1", ["step2A", "step2B", "step2C"], "step3", "matrix2"], + "chain_2":[] + } + } +``` + +* chains 调用链(调用链的概念和使用方法详见Actor组件说明),调用链的配置是一个类似于数组的配置,可以理解为chains里存储的是一个map,map的key为一个调用链标识,map的value为调用链的链块配置。“chain_1”、“chain_2”只是用于标识一个调用链,名字由开发者自行定义,无特殊含义也无任何限制。chain map的value部分配置的是调用链的实际调用顺序,这是一个一维或二维的json数组,每个数组元素只能是Step类或Model类,第一维的元素是串行关系,第二维的元素是并行关系,并行关系的元素只能是Step类。如示例“chain_1”所示,调用链首先调用step1,在step1完成后调用matrix1,matrix1完成后同时调用step2A、step2B、step2C,在这三个Step都完成后才调用step3,step3完成后整个调用链完成。如果调用链只有串行关系,只配置一维即可。 + +#### 自定义配置 +  自定义配置是业务层所需的配置,通常会独立定义配置文件存放在conf路径下。这里的自定义配置指的不是那种业务独立定义的配置文件,而是以“custom”为key的json嵌入到Nebula框架配置文件中的自定义配置。这样配置有什么好处?因为所有的业务代码类都是Actor的派生类,把自定义配置嵌入到框架的配置文件中,只需调用GetCustomConf()方法即可获得配置内容,这个方法是Actor类的成员方法,读取配置就跟访问类自己的成员一样随时随地随心所欲。虽然把自定义配置嵌入到Nebula框架配置文件里非常方便,但也不要滥用,如果什么配置都往这里放会使得框架配置文件变得非常大,可读性和可维护性都变差。建议把许多业务功能模块都会用到或使用频率非常高,配置量不是太大的配置嵌入到框架配置文件里,而只有少数模块会用到,又或使用频率低,或配置内容非常庞大的应独立成其他自定义配置文件。非json格式的配置也应独立成自定义配置文件。 + +```json +"custom": {} +``` + diff --git a/docs/cn/dynamic_hello_demo.md b/docs/cn/dynamic_hello_demo.md new file mode 100644 index 00000000..ccb3d92e --- /dev/null +++ b/docs/cn/dynamic_hello_demo.md @@ -0,0 +1,186 @@ +基于Nebula框架开发业务逻辑主要可能使用到的类包括Cmd、Module、Step、Session,这里给出一个从Cmd派生的最简单例子。
+CmdHello.hpp:
+```c++ +#ifndef SRC_CMDHELLO_CMDHELLO_HPP_ +#define SRC_CMDHELLO_CMDHELLO_HPP_ + +#include + +namespace logic +{ + +class CmdHello: public neb::Cmd, public neb::DynamicCreator +{ +public: + CmdHello(int32 iCmd); + virtual ~CmdHello(); + + virtual bool Init(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); +}; + +} /* namespace logic */ + +#endif /* SRC_CMDHELLO_CMDHELLO_HPP_ */ + +``` +
+CmdHello.cpp:
+ +```c++ + +#include "CmdHello.hpp" + +namespace logic +{ + +CmdHello::CmdHello(int32 iCmd) + : neb::Cmd(iCmd) +{ +} + +CmdHello::~CmdHello() +{ +} + +bool CmdHello::Init() +{ + return(true); +} + +bool CmdHello::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, const MsgBody& oMsgBody) +{ + MsgBody oOutMsgBody; + oOutMsgBody.set_data("Nebula: hello!\n"); + SendTo(pChannel, oMsgHead.cmd() + 1, oMsgHead.seq(), oOutMsgBody); + return(true); +} + +} /* namespace logic */ +``` +
+将CmdHello编译成Hello.so后,用在[NebulaLogic](https://github.com/Bwar/NebulaLogic)动态加载。编译CmdHello的Makefile如下: +
+ +```makefile + +CC = gcc +CXX = g++ +CFLAGS = -g -O2 -fPIC +CXXFLAG = -std=c++14 -O2 -Wall -ggdb -m64 -D_GNU_SOURCE=1 -D_REENTRANT -D__GUNC__ -fPIC -DNODE_BEAT=10.0 + +ARCH:=$(shell uname -m) + +ARCH32:=i686 +ARCH64:=x86_64 + +ifeq ($(ARCH),$(ARCH64)) +SYSTEM_LIB_PATH:=/usr/lib64 +else +SYSTEM_LIB_PATH:=/usr/lib +endif +LIB3RD_PATH = ../../../NebulaDepend +NEBULA_PATH = ../../../Nebula +PLUGIN_PATH = ../../ + +VPATH = . +SUB_DIRS := $(foreach dir, $(VPATH), $(shell find $(dir) -maxdepth 5 -type d)) +DIRS := $(SUB_DIRS) + + +INC := $(INC) \ + -I $(LIB3RD_PATH)/include \ + -I $(NEBULA_PATH)/include \ + -I $(PLUGIN_PATH)/src + + +LDFLAGS := $(LDFLAGS) -D_LINUX_OS_ \ + -L$(NEBULA_PATH)/lib -lnebula \ + -L$(LIB3RD_PATH)/lib -lhiredis \ + -L$(LIB3RD_PATH)/lib -lcryptopp \ + -L$(LIB3RD_PATH)/lib -lprotobuf \ + -L$(LIB3RD_PATH)/lib -lev \ + -L$(SYSTEM_LIB_PATH) -lc -lrt -ldl + +CPP_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cpp)) +CC_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cc)) +C_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c)) +OBJS = $(patsubst %.cpp,%.o,$(CPP_SRCS)) $(patsubst %.c,%.o,$(C_SRCS)) $(patsubst %.cc,%.o,$(CC_SRCS)) + + +TARGET = Hello.so + +all: $(TARGET) + +Hello.so:$(OBJS) + $(CXX) -fPIE -rdynamic -shared -g -o $@ $^ $(LDFLAGS) + +%.o:%.cpp + $(CXX) $(INC) $(CXXFLAG) -c -o $@ $< $(LDFLAGS) +%.o:%.cc + $(CXX) $(INC) $(CXXFLAG) -c -o $@ $< $(LDFLAGS) +%.o:%.c + $(CC) $(INC) $(CXXFLAG) -c -o $@ $< $(LDFLAGS) +clean: + rm -f $(PB_OBJS) $(CMD_DEP_OBJS) + rm -f $(TARGET) +``` +编译完成后,部署到Logic,配置文件如下:
+```json +{ + "node_type":"LOGIC", + "//host":"系统内各Server之间通信绑定的IP(Server to Server)", + "host":"192.168.157.138", + "//port":"系统内各Server之间通信监听的端口", + "port":16005, + "//server_name":"异步事件驱动Server", + "server_name":"neb_Logic", + "//process_num":"进程数量", + "process_num":1, + "//worker_capacity":"子进程最大工作负荷", + "worker_capacity":1000000, + "//config_path":"配置文件路径(相对路径)", + "config_path":"conf/", + "//log_path":"日志文件路径(相对路径)", + "log_path":"log/", + "//max_log_file_num":"最大日志文件数量,用于日志文件滚动", + "max_log_file_num":5, + "//max_log_file_size":"单个日志文件大小限制", + "max_log_file_size":20480000, + "//io_timeout":"网络IO(连接)超时设置(单位:秒)小数点后面至少保留一位", + "io_timeout":300.0, + "//step_timeout":"步骤超时设置(单位:秒)小数点后面至少保留一位", + "step_timeout":1.5, + "log_levels":{"FATAL":50000, "ERROR":40000, "WARN":30000, "INFO":20000, "DEBUG":10000, "TRACE":0}, + "log_level":10000, + "net_log_level":20000, + "//beacon":"控制中心", + "beacon":[ + {"host":"192.168.157.138","port":"16000"} + ], + "boot_load":{ + "cmd":[], + "module":[] + }, + "//refresh_interval":"刷新Server配置,检查、加载插件动态库时间周期(周期时间长短视服务器忙闲而定)", + "refresh_interval":60, + "dynamic_loading":[ + { + "so_path":"plugins/logic/Hello.so", "load":true, "version":1, + "cmd":[ + {"cmd":65531, "class":"logic::CmdHello"} + ], + "module":[ + ] + } + ], + "//custom":"自定义配置,用于通过框架层带给业务", + "custom":{ + } +} +``` diff --git a/docs/cn/install.md b/docs/cn/install.md new file mode 100644 index 00000000..e40c13e4 --- /dev/null +++ b/docs/cn/install.md @@ -0,0 +1,38 @@ +### NebulaBootstrap +  NebulaBootstrap是提供Nebula一键安装部署的一站式解决方案,不管是单机Server还是分布式服务均可通过NebulaBootstrap一键完成。NebulaBootstrap的核心是几个shell脚本,其目录结构就是Nebula项目的运行时目录结构。NebulaBootstrap项目会根据需要随Nebula项目的更新而更新,也可能保持不变。 +* NebulaBootstrap + + bin         server的bin文件存放路径。 + + build        构建路径,由deploy.sh生成,如果部署完后不需要再构建,可以直接删掉(可选)。 + + conf        配置文件存放路径。 + + data        数据文件存放路径,比如基于Nebula开发的页面埋点数据采集和实时分析[Nebio](https://github.com/Bwar/Nebio)项目,将采集的数据落地到这个路径(可选)。 + + lib         运行所需的库文件存放路径。 + + log         程序日志存放路径。 + + plugins       插件(动态加载的业务逻辑so)存放路径。 + - logic       逻辑Server插件存放路径,插件按server存放只是为了好区分,也可直接存放在plugins下,具体规则可自定义(可选)。 + + script       脚本库存放路径,deploy.sh startup.sh shutdown.sh等脚本都需要依赖这个路径。 + + temp        临时文件存放路径(可选)。 + - configure.sh     配置脚本,deploy之后第一次启动server之前先执行该脚本做简单的配置修改,也可以逐个配置文件打开直接修改。 + - deploy.sh      自动构建和部署脚本,自动下载并安装依赖,自动构建和部署,执行./deploy.sh --help查看帮助。 + - shutdown.sh      关闭server,可以指定关闭一个或多个server,也可关闭所有server,不带参数时关闭所有server(需用户确认)。 + - startup.sh      启动server,可以指定启动一个或多个server,也可启动所有server。 + - README_cn.md + - README.md + +  conf路径上存放配置文件,通常有Server启动配置和业务自定义配置。Server启动配置文件名必须是Server在bin/路径下的二进制文件名加上.json后缀,否则服务不能正常启动。比如:bin/NebulaBeacon 对应的配置文件是 conf/NebulaBeacon.json。之所以规定得这么严格,是为了直观且方便管理,可以认为这就是Nebula部署规范之一。自定义的配置文件名和文件格式不作强制要求,不过建议用户自定义配置文件也使用json格式。 + +### 安装说明 +  NebulaBootstrap使原本依赖很少的Nebula部署起来更轻而易举,获取安装源->解压安装包,给脚本加可执行权限->一键安装。 + +```bash +# 获取安装源 +wget https://github.com/Bwar/NebulaBootstrap/archive/master.zip + +# 解压安装包,给脚本加可执行权限 +unzip master.zip; rm master.zip; mv NebulaBootstrap-master NebulaBootstrap; cd NebulaBootstrap; chmod u+x deploy.sh + +# 一键安装 +./deploy.sh +``` + +  这里是为了方便安装和测试,把分布式的各个服务都部署在同一路径下,并且每个服务只启动了一个Worker进程。实际上,在生产环境部署时如果硬件资源允许,一般都是每台机器只部署一个服务,Worker数量与机器CPU核数相同。生产部署时,每个服务的目录结构都与NebulaBootstrap相同,build目录不应存在,plugins目录下没必要再创建子目录,deploy.sh脚本也可以删除。 + diff --git a/docs/cn/nebula_distributed_demo.md b/docs/cn/nebula_distributed_demo.md new file mode 100644 index 00000000..255ca8a9 --- /dev/null +++ b/docs/cn/nebula_distributed_demo.md @@ -0,0 +1,41 @@ +### 安装Nebula分布式服务 +  Nebula提供一键安装部署,不管是单机Server还是分布式服务均一键完成,分布式服务安装如下: + +```bash +# 获取安装源 +wget https://github.com/Bwar/NebulaBootstrap/archive/master.zip + +# 解压安装包,给脚本加可执行权限 +unzip master.zip; rm master.zip; mv NebulaBootstrap-master NebulaBootstrap; cd NebulaBootstrap; chmod u+x deploy.sh + +# 一键安装 +./deploy.sh +``` + +### 运行分布式服务Demo +  安装完成之后就可以启动服务并测试了,配置和启动过程同样是一键完成。 + +```bash +# 配置Server ip地址 +./configure.sh + +# 启动Server +./startup.sh +``` + +  server应该已经启动成功了,startup.sh会打印已启动的server。如果没有启动成功,可以到log目录查看原因。执行grep "ERROR" log/*和grep "FATAL" log/* 先看看是否有错误,再到具体日志文件查看错误详情。注意,Nebula的默认配置文件对IP单位时间连接次数做了限制,如果在测试量较大发生莫名奇妙的问题,可以修改配置限制,通过查看日志中的WARNING信息通常有助于定位这种不是错误的“错误”。如果server已启动成功,那么可以用postman、curl等做测试,看看结果。 + +```bash +# 测试一下 +curl -H "Content-Type:application/json" -X POST -d '{"name": "Nebula", "address":"https://github.com/Bwar/Nebula"}' http://${your_ip}:16003/hello +``` + +  这里给出的是用curl命令测试的方法,同样可以用postman等客户端测试。 + +### Nebula分布式服务简析 +  基于Nebula框架开发分布式服务是一件非常简单的事情。Nebula提供了三个业务无关的Server:NebulaBeacon、NebulaInterface、NebulaLogic,通过这几个Server就可以构建分布式服务。Nebula是个框架,编译成libnebula.so给业务开发使用,所以Nebula自身是没有main()函数的,各个基于Nebula的Server写个main()函数就完成了一个可运行的Server开发。 + +  基于Nebula框架的Server除负责Nebula集群管理、注册中心、配置管理的[NebulaBeacon](https://github.com/Bwar/NebulaBeacon)与Nebula框架关系非常紧密之外,其他如Nebula集群http接入服务[NebulaInterface](https://github.com/Bwar/NebulaInterface)、Nebula集群逻辑服务[NebulaLogic](https://github.com/Bwar/NebulaLogic)、Nebula集群存储代理(redis)[NebulaMydis](https://github.com/Bwar/NebulaMydis)、Nebula集群数据库代理(mysql)[NebulaDbAgent](https://github.com/Bwar/NebulaDbAgent) 、Nebula集群分布式日志服务[NebulaLogger](https://github.com/Bwar/NebulaLogger)、Nebula集群私有应用协议接入服务[NebulaAccess](https://github.com/Bwar/NebulaAccess)等都是为帮助开发者快速搭建分布式服务而提供的解决方案,同时也是Nebula框架的应用案例和教程。基于Nebula开发一个Server也就不到10行代码,如果觉得Nebula框架提供的Server不满足需求,可以不采用这些解决方案Server。比如,2018年7月起就应用于生产的埋点数据采集与实时分析项目[Nebio](https://github.com/Bwar/Nebio)就只用了NebulaBeacon和NebulaDbAgent,其他三个Server(Collect/Analyse/Aggregate)都是全新开发。 + +  [NebulaDynamic](https://github.com/Bwar/NebulaDynamic) Nebula集群动态加载插件,Nebula的框架测试代码和各业务逻辑示例代码都会存放在这里。 + From 3b3087c4254b389ae056760617eb550172ba40cc Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 27 Jul 2019 18:30:17 +0800 Subject: [PATCH 050/176] add docment for cmd and module. --- README_cn.md | 14 +- docs/cn/actor_overview.md | 49 ++++++ docs/cn/cmd_and_module.md | 295 ++++++++++++++++++++++++++++++++++ docs/cn/protocol.md | 94 +++++++++++ docs/image/actor_overview.png | Bin 0 -> 12436 bytes src/Definition.hpp | 29 +++- src/actor/cmd/Cmd.hpp | 6 +- src/labor/WorkerImpl.cpp | 2 + 8 files changed, 483 insertions(+), 6 deletions(-) create mode 100644 docs/cn/actor_overview.md create mode 100644 docs/cn/cmd_and_module.md create mode 100644 docs/cn/protocol.md create mode 100644 docs/image/actor_overview.png diff --git a/README_cn.md b/README_cn.md index de555557..12366e58 100644 --- a/README_cn.md +++ b/README_cn.md @@ -6,10 +6,10 @@ / |/ / _ \/ __ \/ / / / / __ `/ / /| / __/ /_/ / /_/ / / /_/ / /_/ |_/\___/_.___/\__,_/_/\__,_/ - 真正一键安装部署 + 一键安装部署 ``` -# Nebula : 一个强大的IoC网络框架,用于以C++快速构建高度并发,分布式和弹性的消息驱动应用程序。 +# Nebula : 一个强大的IoC网络框架,用于以C++快速构建高并发、分布式和弹性的消息驱动应用程序。 [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)
1. [概述](#Overview) @@ -65,6 +65,16 @@ * [安装部署说明](docs/cn/install.md) * [配置说明](docs/cn/configuration.md) +* [协议说明](docs/cn/protocol.md) +* [开发组件说明] + * [Actor组件概述](docs/cn/actor_overview.md) + * [Cmd和Module组件](docs/cn/cmd_and_module.md) + * Step组件 + * Session组件 + * Context组件 + * Model组件 + * Chain组件 + * Actor类 ![nebula_cluster](https://github.com/Bwar/NebulaBootstrap/blob/master/image/nebula_cluster.png?raw=true) diff --git a/docs/cn/actor_overview.md b/docs/cn/actor_overview.md new file mode 100644 index 00000000..38b2f1ae --- /dev/null +++ b/docs/cn/actor_overview.md @@ -0,0 +1,49 @@ +### Nebula中的Actor组件 + +  Nebula是一个面向业务的IoC网络框架,面向业务是Nebula区别于常见网络框架的最大特点之一,之所以说是面向业务是因为Nebula提供了快速开发的组件,有了这些组件,业务开发效率非常高,同时这些组件也规范和统一了业务开发代码,提高代码可读性,加强团队交流。只需掌握Actor的应用就足以利用Nebula框架开发出高性能的分布式服务,而掌握Actor的应用又是非常简单的事情。 + + +### Actor由来 + +  也许有人听说过Actor模型,甚至可能有人非常熟悉Actor模型。Actor模型是处理并行计算的概念模型,它定义了系统部件行为和交互的一些规则。跟共享数据方式的并发编程相比,基于消息传递机制的Actor模型最大的优点就是不会产生数据竞争状态,不用处理让人十分头痛的各种锁问题。使用这个模型的最著名的编程语言是Erlang和Elixir,Scala语言鼎鼎有名的Akka库也是使用Actor模型。 + + +  Nebula的Actor组件不是从Erlang或Akka等借鉴过来的Actor模型。Nebula的前身Starship框架先有了Cmd、Step和Session三个组件,开始编写Nebula时在三个组件之上再抽象了一层就有了Actor。Nebula的Actor跟Actor模型的Actor可能有一定相似之处,那纯属偶然,作者在写Nebula之前对Actor没有了解。 + +### Actor概览 + +  Actor组件的讲解是将Actor应用与Actor组件定义穿插着进行的,这样比先逐个定义讲完再讲应用之理解起来要容易,并且不会枯燥。先来简单了解Actor组件全貌: + +![Actor概览](../image/actor_overview.png) + + +  Nebula是一个IoC网络框架,开发者只需熟悉Actor相关的类无须对了解整个框架即可开发出分布式服务,对应用开发者而言开发更高效,这是Proactor模式的Nebula框架跟许多其他网络框架不太一样的地方。 + +  所有业务逻辑均抽象成事件和事件处理,Actor为事件处理者。Actor是所有Actor派生类与Nebula框架的媒介,在框架应用和业务逻辑开发中有至关重要的作用,这在后续会详细讲解,暂且先理解为Actor是所有功能组件的基类。 + + +  Actor为事件(消息)处理者,所有业务逻辑均抽象成事件和事件处理,Actor分为Cmd、Module、Step、Session、Context、Model、Chain七种不同类型。 业务逻辑代码均通过从除Chain之外的六种不同类型事件处理者派生子类来实现,专注于业务逻辑实现,而无须关注业务逻辑之外的东西。Nebula的前身Starship只有Cmd、Module、Step、Session四种组件,却已成功应用于一个生产环境的IM,这些组件的理解和应用都很容易,Nebula虽然增加了几种组件,但只带来了方便而没增加复杂性。Actor类自身提供各组件与框架交互的成员函数,通过这些成员函数即可完成所有业务逻辑所需的功能,换句话说,基于Nebula框架的业务开发只需了解Actor类的30余成员函数的功能是什么就足够,门槛低到难以想象。 + + +  Cmd为业务逻辑入口,对应通信协议MsgHead中的cmd命令字,用于选择对应处理逻辑类。一些知名框架把Handler做成一个处理链,一个event过来顺着这个处理链顺序传递,相应的Handler处理完后调用下一个Handler处理,与event无关的Handler收到event时什么都不做就直接调用下一个Handler处理。与之不同的是,Nebula把所有Cmd(Handler)放在一个HashMap里,当收到Msg(event)时,根据cmd命令字(event type)直接交给对应的Cmd类处理,每个事件只会有一个处理者,如果需要多个处理者那就是对应Cmd内部的事情,可以通过Step、Model等组成一条处理链(后续章节会有说明)。与一些框架把编解码器作为Handler的一种加到Handler链里不同,Nebula的Codec编解码器不属于Cmd也不属于Actor,因此也不属于处理链的一部分。Codec会在编解码器章节说明,不在Actor组件这里说明。 + + +  Module跟Cmd的作用完全相同,差异只在于Cmd与MsgHead里的cmd命令字对应,Module与HttpMsg里的url_path对应。理解了Cmd也就等于理解了Module。 + + +  Step是最为核心的Actor组件,Nebula高性能的关键在于Step,IO密集型应用的逻辑都是围绕Step展开,暂且先把Step理解为一个操作步骤。Step起到C语言中异步回调函数的作用,却又比异步回调函数适用要简单得多,事实上Step底层就是通过异步回调函数实现的,只是复杂的事情由Nebula框架完成了,留给应用开发者的是较为简单的Step。Step与future、promise异步实现方式也完全不同,许多号称支持同步、异步、半同步的rpc框架采用的都是future、promise。 + + +  Session是仅次于Step的核心Actor组件,用于保存状态和数据。Cmd、Step等其他Actor组件也可以用Session来传递数据。在一些实时数据流应用中(比如[Nebio](https://github.com/Bwar/Nebio))也适合把Session作最主要的组件使用。 + + +  Context为上下文数据组件,用于有上下文关系的Actor之间传递数据。与Session在传递数据上的区别在于,Context仅用于有强依赖的上下文之间传递,且传递效率和方便性高于Session。 + + +  Model为模型和计算组件。 + + +  Chain为动态调用链管理组件,用于运行时动态管理Step和Model的调用。用Chain管理的调用链除了可动态变化之外,还更为方便,更直观。 + + +  Actor组件各有各的用途和适用场景,灵活应用这些组件可以满足绝大多数业务需求,这是Nebula号称面向业务的IoC网络框架的根本。同时也因Actor组件设计比较精妙,有些作用可能连Nebula框架设计者也没有发现,有待应用开发者们发掘。当然,发现新的用途并不意味着滥用,与官方文档相悖的用途建议不要使用。 \ No newline at end of file diff --git a/docs/cn/cmd_and_module.md b/docs/cn/cmd_and_module.md new file mode 100644 index 00000000..3346679b --- /dev/null +++ b/docs/cn/cmd_and_module.md @@ -0,0 +1,295 @@ +### 应用Cmd类处理全部业务逻辑 + +  Cmd和Module均为业务逻辑入口。在无须网络IO即可完成的场景里,Cmd和Module也可以完成全部处理逻辑,用于演示Nebula网络功能的demo就是一个Cmd或一个Module完成。先来看在[NebulaDynamic](https://github.com/Bwar/NebulaDynamic)中的CmdHello示例: + +CmdHello.hpp: + +```C++ +#include + +namespace logic +{ + +class CmdHello: public neb::Cmd, public neb::DynamicCreator +{ +public: + CmdHello(int32 iCmd); + virtual ~CmdHello(); + + virtual bool Init(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); +}; + +} /* namespace logic */ +``` + +  CmdHello的头文件很简单,直接从neb::Cmd派生,实现Init()和AnyMessage()两个纯虚函数。事实上,所有的Cmd类定义都与CmdHello极为相似,复杂一点的Cmd也只是多定义几个成员函数给Init()或AnyMessage()调用而已。public neb::DynamicCreator用于给框架通过类名动态创建CmdHello,后续我们讲解动态创建时会详细描述,这里可以先理解为neb::DynamicCreator是固定不变的动态创建类,的第一个参数固定为当前派生类的类名CmdHello,后续参数数量和参数类型与当前派生类构造函数的参数个数和类型相同,CmdHello的构造函数只有一个int32型的参数。 + +CmdHello.cpp: + +```C++ +#include +#include +#include "util/json/CJsonObject.hpp" +#include "CmdHello.hpp" + +namespace logic +{ + +CmdHello::CmdHello(int32 iCmd) + : neb::Cmd(iCmd) +{ +} + +CmdHello::~CmdHello() +{ +} + +bool CmdHello::Init() +{ + return(true); +} + +bool CmdHello::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, const MsgBody& oMsgBody) +{ + MsgBody oOutMsgBody; + neb::CJsonObject oJson; + oOutMsgBody.set_data("Nebula: hello!\n"); + SendTo(pChannel, oMsgHead.cmd() + 1, oMsgHead.seq(), oOutMsgBody); + return(true); +} + +} /* namespace logic */ +``` + +  CmdHello的源文件实现也很简单,在这个例子里只需要关注AnyMessage函数实现。这个函数是当从pChannel这个网络通信通道中收到一串字节流,Nebula框架将字节流解码成一个完整的消息(oMsgHead+oMsgBody)时,AnyMessage()被调用。CmdHello这个示例是在收到完整消息时直接回复一个消息,oOutMsgBody是将回复给客户端的消息体,oOutMsgBody.set_data("Nebula: hello!\n")是往消息体里填充内容。SendTo(pChannel, oMsgHead.cmd() + 1, oMsgHead.seq(), oOutMsgBody)是将消息通过请求来源通道pChannel原路返回给客户端。在Nebula框架里约定了整型cmd为奇数时是请求,为偶数时是响应,从AnyMessage()参数里进来的oMsgHead.cmd()必是奇数命令字,oMsgHead.cmd()+1是对应oMsgHead.cmd()的偶数响应命令字。oMsgHead.seq()为请求的seq,响应也以请求时的seq回复客户端。 + +  这样一个简单的CmdHello就完成了网络通信的整个服务端代码。实现这样一个服务端代码只需要理解通信协议MsgHead和MsgBody,这个在[协议说明](protocol.md)章节里有详细说明。SendTo()函数是Actor基类的成员函数,在[Actor](actor.md)里有详细说明。基类neb::Cmd也十分简单,接下来我们讲解neb::Cmd的定义。 + +### neb::Cmd类定义 + +  neb::Cmd类定义看起来跟上面的CmdHello很相似: + +Cmd.hpp: + +```C++ +#include "actor/Actor.hpp" +#include "actor/DynamicCreator.hpp" + +namespace neb +{ + +class Cmd: public Actor +{ +public: + Cmd(int32 iCmd) + : Actor(Actor::ACT_CMD, gc_dNoTimeout), + m_iCmd(iCmd) + { + } + Cmd(const Cmd&) = delete; + Cmd& operator=(const Cmd&) = delete; + virtual ~Cmd(){}; + + /** + * @brief 初始化Cmd;重新加载配置 + * @note Cmd类实例初始化函数,大部分Cmd不需要初始化,需要初始化的Cmd可派生后实现此函数, + * 在此函数里可以读取配置文件(配置文件必须为json格式)。配置文件由Cmd的设计者自行定义, + * 存放于conf/目录,配置文件名最好与Cmd名字保持一致,加上.json后缀。配置文件的更新同步 + * 会由框架自动完成。 + * Init()需设计成可重入方法,因各服务框架在收到Beacon的重新加载配置指令后会执行每个 + * Cmd的Init()方法,所以必须保证Init()方法执行后没有副作用。 + * @return 是否初始化成功 + */ + virtual bool Init() + { + return(true); + } + + /** + * @brief 命令处理入口 + * @note 框架层成功解析数据包后,根据MsgHead里的Cmd找到对应的Cmd类实例调用将数据包及 + * 数据包来源pChannel传给AnyMessage处理。若处理过程不涉及网络IO之类需异步处 + * 理的耗时调用,则无需新创建Step类实例来处理。若处理过程涉及耗时异步调用,则应创建Step + * 类实例,并向框架层注册Step类实例,调用Step.Emit()后即返回。 + * @param pChannel 消息来源通道 + * @param oMsgHead 数据包头 + * @param oMsgBody 数据包体 + * @return 命令是否处理成功 + */ + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody) = 0; + +protected: + int GetCmd() const + { + return(m_iCmd); + } + +private: + int32 m_iCmd; + friend class WorkerImpl; +}; + +} /* namespace neb */ +``` + +  neb::Cmd类是一个虚基类,且只有一个头文件。 + +### neb::Module类定义 + +  neb::Module类也是业务逻辑入口,定义与Cmd类定义相似: + +```C++ +#include "codec/CodecHttp.hpp" +#include "labor/Worker.hpp" +#include "actor/Actor.hpp" +#include "actor/DynamicCreator.hpp" + +namespace neb +{ +class Module: public Actor +{ +public: + Module(const std::string& strModulePath) + : Actor(Actor::ACT_MODULE, gc_dNoTimeout), + m_strModulePath(strModulePath) + { + } + Module(const Module&) = delete; + Module& operator=(const Module&) = delete; + virtual ~Module(){}; + + /** + * @brief 初始化Module + * @note Module类实例初始化函数,大部分Module不需要初始化,需要初始化的Module可派生后实现此函数, + * 在此函数里可以读取配置文件(配置文件必须为json格式)。配置文件由Module的设计者自行定义, + * 存放于conf/目录,配置文件名最好与Module名字保持一致,加上.json后缀。配置文件的更新同步 + * 会由框架自动完成。 + * @return 是否初始化成功 + */ + virtual bool Init() + { + return(true); + } + + /** + * @brief http服务模块处理入口 + * @param stCtx 消息来源上下文 + * @param oHttpMsg 接收到的http数据包 + * @return 是否处理成功 + */ + virtual bool AnyMessage( + std::shared_ptr pChannel, + const HttpMsg& oHttpMsg) = 0; + +protected: + const std::string& GetModulePath() const + { + return(m_strModulePath); + } + +private: + std::string m_strModulePath; + friend class WorkerImpl; +}; + +} /* namespace neb */ +``` + +### 应用Module类处理全部业务逻辑 + +  Module也可以完成全部处理逻辑,用于演示Nebula网络功能的demo就是一个Module完成。先来看在[NebulaDynamic](https://github.com/Bwar/NebulaDemo)中ModuleHello示例: + +ModuleHello.hpp: + +```C++ +#include +#include + +namespace demo +{ + +class ModuleHello: public neb::Module, public neb::DynamicCreator +{ +public: + ModuleHello(const std::string& strModulePath); + virtual ~ModuleHello(); + + virtual bool Init(); + + virtual bool AnyMessage( + std::shared_ptr pUpstreamChannel, + const HttpMsg& oHttpMsg); +}; + +} /* namespace demo */ +``` + +  ModuleHello的头文件很简单,直接从neb::Module派生,实现Init()和AnyMessage()两个纯虚函数。事实上,所有的Module类定义都与ModuleHello极为相似,复杂一点的Module也只是多定义几个成员函数给Init()或AnyMessage()调用而已。public neb::DynamicCreator用于给框架通过类名动态创建ModuleHello,后续我们讲解动态创建时会详细描述,这里可以先理解为neb::DynamicCreator是固定不变的动态创建类,的第一个参数固定为当前派生类的类名ModuleHello,后续参数数量和参数类型与当前派生类构造函数的参数个数和类型相同,ModuleHello的构造函数只有一个std::string型的参数。 + +ModuleHello.cpp: + +```C++ +#include "ModuleHello.hpp" +#include + +namespace demo +{ + +ModuleHello::ModuleHello(const std::string& strModulePath) + : neb::Module(strModulePath) +{ +} + +ModuleHello::~ModuleHello() +{ +} + +bool ModuleHello::Init() +{ + return(true); +} + +bool ModuleHello::AnyMessage( + std::shared_ptr pChannel, + const HttpMsg& oInHttpMsg) +{ + HttpMsg oHttpMsg; + neb::CJsonObject oResponseData; + oHttpMsg.set_type(HTTP_RESPONSE); + oHttpMsg.set_status_code(200); + oHttpMsg.set_http_major(oInHttpMsg.http_major()); + oHttpMsg.set_http_minor(oInHttpMsg.http_minor()); + oResponseData.Add("code", 0); + oResponseData.Add("msg", "hello!"); + oHttpMsg.set_body(oResponseData.ToFormattedString()); + SendTo(pChannel, oHttpMsg); + return(true); +} + +} /* namespace demo */ +``` + +  ModuleHello的源文件实现也很简单,在这个例子里只需要关注AnyMessage函数实现。这个函数是当从pChannel这个网络通信通道中收到一串字节流,Nebula框架将字节流解码成一个完整的消息(oMsgHead+oMsgBody)时,AnyMessage()被调用。CmdHello这个示例是在收到完整消息时直接回复一个消息,oHttpMsg是将回复给客户端的消息体,oHttpMsg.set_body("")是往消息体里填充内容。SendTo(pChannel, oHttpMsg)是将消息通过请求来源通道pChannel原路返回给客户端。 + +  这样一个简单的ModuleHello就完成了网络通信的整个服务端代码。 + +### 完整业务流程简析 + +  上述Cmd、Module定义和例子是为了说明Cmd和Module两种组件的用途及使用方法,测试Nebula的功能demo有Cmd和Module基本上就可以了。然而,实际的业务应用并不是demo这么简单的,不过再复杂的业务以Actor组件来实现也不难,熟悉了Actor组件甚至能更好地帮助开发者做业务逻辑抽象出来。Nebula的Actor组件有7种,其中Cmd、Module、Step、Session是与Nebula生而俱来的组件,其中Cmd和Module可视作同一种组件,有了这几个组件就可以满足绝大部分复杂业务。Context、Model、Chain是Nebula发展过程中产生的组件,我们暂且先不用关注。 + +  使用Actor组件的普遍业务流程如图: + +![Actor业务流程](../image/static_chain.png) + +  如图所示,客户端请求从Cmd/Module进来,Cmd/Module创建一个步骤Step1完成异步IO操作,在Step1的回调之前,服务节点可以接收并处理其他客户端的请求。Step1完成后可能还需要Step2、Step3才完成整个业务流程,完成后将由最后一个Step给客户端发响应。在整个处理流程中可能需要暂时存储一些数据或共享一些数据,共享数据主要通过Session来实现。Context用于传递各Step所需的上下文,上下文传递也可以通过创建Step时的构造函数参数传递。 + +  这是一个通过Actor组件实现业务逻辑的完整流程,再复杂的业务都可通过Actor组件的组合来实现,Step组件的章节还会有更多业务实现流程说明。无论何种业务流程都是通过上图的简单流程根据业务需要发展和衍生出来的,理解了这个简单的业务流程就容易理解后续讲解的业务流程。 \ No newline at end of file diff --git a/docs/cn/protocol.md b/docs/cn/protocol.md new file mode 100644 index 00000000..57332d49 --- /dev/null +++ b/docs/cn/protocol.md @@ -0,0 +1,94 @@ +  Nebula将网络消息分为请求(request)和响应(response)两类,事实上无论什么类型的网络消息也都可以归为请求或响应。 + +### Nebula通信协议 +  Nebula支持protobuf、http、websocket等通信协议,其中基于protobuf3设计的自定义通信协议是Nebula核心通信协议,我们称之为Nebula通信协议。Nebula通信协议用于Nebula分布式服务节点间的通信,也可用于与分布式服务外部的客户端通信。Nebula支持的http、websocket等协议都是在Nebula接入层解码后转成了Nebula通信协议在Nebula各节点间通信,并作为业务层可直接读写的数据结构。下面是Nebula协议的定义: + +```protobuf +syntax = "proto3"; + +/** + * @brief 消息头 + * @note MsgHead为固定15字节的头部,当MsgHead不等于15字节时,消息发送将出错。 + * 在proto2版本,MsgHead为15字节总是成立,cmd、seq、len都是required; + * 但proto3版本,MsgHead为15字节则必须要求cmd、seq、len均不等于0,否则无法正确进行收发编解码。 + */ +message MsgHead +{ + fixed32 cmd = 1; ///< 命令字(压缩加密算法占高位1字节) + fixed32 seq = 2; ///< 序列号 + sfixed32 len = 3; ///< 消息体长度 +} + +/** + * @brief 消息体 + * @note 消息体主体是data,所有业务逻辑内容均放在data里。req_target是请求目标,用于 + * 服务端接入路由,请求包必须填充。rsp_result是响应结果,响应包必须填充。 + */ +message MsgBody +{ + oneof msg_type + { + Request req_target = 1; ///< 请求目标(请求包必须填充) + Response rsp_result = 2; ///< 响应结果(响应包必须填充) + } + bytes data = 3; ///< 消息体主体 + bytes add_on = 4; ///< 服务端接入层附加在请求包的数据(客户端无须理会) + string trace_id = 5; ///< for log trace + + message Request + { + uint32 route_id = 1; ///< 路由ID + string route = 2; ///< 路由ID(当route_id用整型无法表达时使用) + } + + message Response + { + int32 code = 1; ///< 错误码 + bytes msg = 2; ///< 错误信息 + } +} +``` + +  Nebula协议由MsgHead和MsgBody构成,这是一种普遍使用的类TLV协议设计。固定为15字节的MsgHead。cmd命令字表示消息处理者对应的功能号,在Nebula里约定cmd为奇数时消息为request,cmd为偶数时消息为response,这样设计可以省去标识request和response的消息类型字段。cmd长度为32位4个字节,实际用作cmd功能的只有低位2个字节,高位2个字节用作压缩、加密算法标识和版本号等,这样做是为了尽量节省流量,因2字节的cmd已经可以表示32266(65535/2,0不能用作cmd,再排除Nebula保留的请求命令字500个)个功能,已足够业务使用。 + +  MsgBody的data字段存储消息主体,任何自定义数据均可以二进制数据流方式写入到data。 + +  msg_type用于标识该消息是请求还是响应(所有网络数据流都可归为请求或响应),如果是请求,则可以选择性填充Request里的route_id或route,如果填充了,则框架层无须解析应用层协议(也无法解析)就能自动根据路由ID转发,而无须应用层解开data里的内容再根据自定义逻辑转发。如果是响应,则定义了统一的错误标准,也为业务无关的错误处理提供方便。 + +  add_on是附在长连接上的业务数据,框架并不会解析但会在每次转发消息时带上,可以为应用提供极其方便且强大的功能。比如,IM用户登录时客户端只发送用户ID和密码到服务端,服务端在登录校验通过后,将该用户的昵称、头像等信息通过框架提供的方法SetClientData()将数据附在服务端接入层该用户对应的长连接Channel上,之后所有从该连接过来的请求都会由框架层自动填充add_on字段,服务端的其他逻辑服务器只从data中得到自定义业务逻辑(比如聊天消息)数据,却可以从add_on中得到这个发送用户的信息。add_on的设计简化了应用开发逻辑,并降低了客户端与服务端传输的数据量。 + +  trace_id用于分布式日志跟踪。分布式服务的错误定位是相当麻烦的,Nebula分布式服务解决方案提供了日志跟踪功能,协议里的trace_id字段的设计使得Nebula框架可以在完全不增加应用开发者额外工作的情况下(正常调用LOG4_INFO写日志而无须额外工作)实现所有标记着同一trace_id的日志发送到指定一台日志服务器,定义错误时跟单体服务那样登录一台服务器查看日志即可。比如,IM用户发送一条消息失败,在用户发送的消息到达服务端接入层时就被打上了trace_id标记,这个id会一直传递到逻辑层、存储层等,哪个环节发生了错误都可以从消息的发送、转发、处理路径上查到。 + +  MsgHead和MsgBody的编解码实现见Nebula框架的[https://github.com/Bwar/Nebula/blob/master/src/codec/CodecProto.cpp](https://github.com/Bwar/Nebula/blob/master/src/codec/CodecProto.cpp)。 + +### Http通信协议 +  http协议应该是对外提供服务的最佳选择,作为一个通用网络框架,http协议自然是Nebula对外提供服务的最重要的协议。Nebula把http协议protobuf化,解析http文本协议并转化为HttpMsg在服务内部处理,应用开发者填充HttpMsg,接入层将响应的HttpMsg转换成http文本协议发回给请求方,不管服务端内部经过多少次中转,始终只有一次http文本协议的decode和一次http协议的encode。下面是http协议protobuf化的定义: + +```protobuf +syntax = "proto3"; +message HttpMsg +{ + int32 type = 1; ///< http_parser_type 请求或响应 + int32 http_major = 2; ///< http大版本号 + int32 http_minor = 3; ///< http小版本号 + int32 content_length = 4; ///< 内容长度 + int32 method = 5; ///< 请求方法 + int32 status_code = 6; ///< 响应状态码 + int32 encoding = 7; ///< 传输编码(只在encode时使用,当 Transfer-Encoding: chunked 时,用于标识chunk序号,0表示第一个chunk,依次递增) + string url = 8; ///< 地址 + map headers = 9; ///< http头域 + bytes body = 10; ///< 消息体(当 Transfer-Encoding: chunked 时,只存储一个chunk) + map params = 11; ///< GET方法参数,POST方法表单提交的参数 + Upgrade upgrade = 12; ///< 升级协议 + float keep_alive = 13; ///< keep alive time + string path = 14; ///< Http Decode时从url中解析出来,不需要人为填充(encode时不需要填) + bool is_decoding = 15; ///< 是否正在解码(true 正在解码, false 未解码或已完成解码) + message Upgrade + { + bool is_upgrade = 1; + string protocol = 2; + } +} +``` + +  HttpMsg的编解码实现见Nebula框架的[https://github.com/Bwar/Nebula/blob/master/src/codec/CodecHttp.cpp](https://github.com/Bwar/Nebula/blob/master/src/codec/CodecHttp.cpp)。 \ No newline at end of file diff --git a/docs/image/actor_overview.png b/docs/image/actor_overview.png new file mode 100644 index 0000000000000000000000000000000000000000..6788232c4c5c7cfa09d455c172a7b9e957185aae GIT binary patch literal 12436 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 U2AAgpWVzATRj=h=x%=eb0G)T&fdBvi literal 0 HcmV?d00001 diff --git a/src/Definition.hpp b/src/Definition.hpp index 067519b9..883317cb 100644 --- a/src/Definition.hpp +++ b/src/Definition.hpp @@ -11,7 +11,6 @@ #define SRC_DEFINITION_HPP_ #include -#include #ifndef NODE_BEAT #define NODE_BEAT 1.0 @@ -115,6 +114,34 @@ typedef long long int int64; typedef unsigned long long int uint64; typedef double ev_tstamp; // ev.h +#ifdef __CYGWIN__ +/* TCP socket options */ +//#define TCP_NODELAY 1 /* Turn off Nagle's algorithm. */ +#define TCP_MAXSEG 2 /* Limit MSS */ +#define TCP_CORK 3 /* Never send partially complete segments */ +#define TCP_KEEPIDLE 4 /* Start keeplives after this period */ +#define TCP_KEEPINTVL 5 /* Interval between keepalives */ +#define TCP_KEEPCNT 6 /* Number of keepalives before death */ +#define TCP_SYNCNT 7 /* Number of SYN retransmits */ +#define TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */ +#define TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */ +#define TCP_WINDOW_CLAMP 10 /* Bound advertised window */ +#define TCP_INFO 11 /* Information about this connection. */ +#define TCP_QUICKACK 12 /* Block/reenable quick acks */ +#define TCP_CONGESTION 13 /* Congestion control algorithm */ +#define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */ +#define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/ +#define TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */ +#define TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */ +#define TCP_REPAIR 19 /* TCP sock is under repair right now */ +#define TCP_REPAIR_QUEUE 20 +#define TCP_QUEUE_SEQ 21 +#define TCP_REPAIR_OPTIONS 22 +#define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */ +#define TCP_TIMESTAMP 24 +#define TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes in write queue */ +#define TCP_REPAIR_WINDOW 29 /* Get/set window parameters */ +#endif namespace neb { diff --git a/src/actor/cmd/Cmd.hpp b/src/actor/cmd/Cmd.hpp index 1bc09ec7..f1883e85 100644 --- a/src/actor/cmd/Cmd.hpp +++ b/src/actor/cmd/Cmd.hpp @@ -47,10 +47,10 @@ class Cmd: public Actor /** * @brief 命令处理入口 * @note 框架层成功解析数据包后,根据MsgHead里的Cmd找到对应的Cmd类实例调用将数据包及 - * 数据包来源tagChannelContext传给AnyMessage处理。若处理过程不涉及网络IO之类需异步处 + * 数据包来源pChannel传给AnyMessage处理。若处理过程不涉及网络IO之类需异步处 * 理的耗时调用,则无需新创建Step类实例来处理。若处理过程涉及耗时异步调用,则应创建Step - * 类实例,并向框架层注册Step类实例,调用Step.Start()后即返回。 - * @param stCtx 消息来源上下文 + * 类实例,并向框架层注册Step类实例,调用Step.Emit()后即返回。 + * @param pChannel 消息来源通道 * @param oMsgHead 数据包头 * @param oMsgBody 数据包体 * @return 命令是否处理成功 diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 2561d856..105582da 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -1093,6 +1093,7 @@ bool WorkerImpl::Init(CJsonObject& oJsonConf) oJsonConf.Get("cpu_affinity", bCpuAffinity); if (bCpuAffinity) { +#ifndef __CYGWIN__ /* get logical cpu number */ int iCpuNum = sysconf(_SC_NPROCESSORS_CONF);; ///< cpu数量 cpu_set_t stCpuMask; ///< cpu set @@ -1102,6 +1103,7 @@ bool WorkerImpl::Init(CJsonObject& oJsonConf) { LOG4_WARNING("sched_setaffinity failed."); } +#endif } if (oJsonConf["with_ssl"]("config_path").length() > 0) From d58f9b3f30b410e39b179b82b3c35048862e9088 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 27 Jul 2019 18:32:58 +0800 Subject: [PATCH 051/176] add docment for cmd and module. --- README_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_cn.md b/README_cn.md index 12366e58..78be7af6 100644 --- a/README_cn.md +++ b/README_cn.md @@ -66,7 +66,7 @@ * [安装部署说明](docs/cn/install.md) * [配置说明](docs/cn/configuration.md) * [协议说明](docs/cn/protocol.md) -* [开发组件说明] +* 开发组件说明 * [Actor组件概述](docs/cn/actor_overview.md) * [Cmd和Module组件](docs/cn/cmd_and_module.md) * Step组件 From 8b0e4021c3ec8e6c67305613cbf47fddaf0c94a2 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 18 Aug 2019 21:45:14 +0800 Subject: [PATCH 052/176] add documents --- docs/cn/step.md | 84 ++++++++++++++++++++++++++++++++++ docs/image/step.png | Bin 0 -> 38596 bytes src/actor/session/Session.hpp | 5 ++ 3 files changed, 89 insertions(+) create mode 100644 docs/cn/step.md create mode 100644 docs/image/step.png diff --git a/docs/cn/step.md b/docs/cn/step.md new file mode 100644 index 00000000..e009f5db --- /dev/null +++ b/docs/cn/step.md @@ -0,0 +1,84 @@ +### Step定义 +  Step是最为核心的Actor组件,Nebula高性能的关键在于Step,IO密集型应用的逻辑都是围绕Step展开,暂且先把Step理解为一个操作步骤。Step起到C语言中异步回调函数的作用,却又比异步回调函数适用要简单得多。首先来看一下Step类图: + +![Step类定义](../image/step.png) + +  如图所示,Step类分为三种PbStep、HttpStep和RedisStep,分别用在需要Nebula通信协议、Http通信协议和Redis通信协议的场景,其中PbStep应用最多,文档中的Step示例以PbStep为主。 + + +Step.hpp: + +```C++ +#include "labor/Worker.hpp" +#include "actor/Actor.hpp" +#include "actor/DynamicCreator.hpp" + +namespace neb +{ + +class Chain; + +class Step: public Actor +{ +public: + Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + Step(const Step&) = delete; + Step& operator=(const Step&) = delete; + virtual ~Step(); + + /** + * @brief 提交,发出 + * @note 注册了一个回调步骤之后执行Emit()就开始等待回调。 + */ + virtual E_CMD_STATUS Emit(int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL) = 0; + + /** + * @brief 步骤超时回调 + */ + virtual E_CMD_STATUS Timeout() = 0; + +protected: + /** + * @brief 执行当前步骤接下来的步骤 + */ + void NextStep(int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); + + uint32 GetChainId() const + { + return(m_uiChainId); + } + +private: + ... +}; + +} /* namespace neb */ +``` + +  首先来看Step最重要最核心的两个函数Emit()和Callback()。Emit()用于发出网络IO,在发出IO前做开发者所需要的业务逻辑处理,包括组装用于向网络发出的数据包。Callback()在从Emit()发出的网络请求得到响应时由框架回调,响应数据通过Callback()的参数传递进来。 + +### Step是如何提高并发的 + +  CPU与内存的速度远高于磁盘和网络,对IO密集型的应用而言,大部分时间消耗在IO等待上。把一次业务逻辑处理过程的CPU处理时间和IO等待时间放在一条时间轴上,CPU处理是时间轴上的一个个点,而IO等待是时间轴上的一个个线段。在多线程同步的处理模型里,是把一个个线段用其他线程处理了,在业务主线程里留下一个个点,一条时间轴可以排布非常多的点,并发量也就被提高了。然而,线程数量并不是越多越好的,线程有调度代价,当到了某个线程数量时,再增加线程数可能不仅不会提高并发量,反而会降低并发。事件驱动是在不增加线程的情况下把一个个线段移除出去只留下一个个点,理论上一条时间轴(一秒也可以看成一条时间轴)上可以排布多少个点并发就有多大。把线段从处理时间轴移除只保留两个端点只是为其他点腾出空间(时间),而不是把线段所需时间给消化掉了,所以事件驱动不会降低单个请求的处理时间,只是降低了请求之间相互等待的时间,可近似看作请求与请求之间无等待。 + +  Step的Emit()和Callback()就是线段上的两个端点,Emit()和Callback之间的线段是IO等待时间,通过Step把这段时间移除出去只保留了Emit()和Callback()两个端点,Nebula实现了单个服务进程的高并发。Emit()和Callback()是Step最重要的两个函数,Emit()在Step中定义,Callback()因为参数不同分别在PbStep、HttpStep、RedisStep中定义,两个函数都是纯虚函数。Step还有一个纯虚函数Timeout()用于超时回调,当Emit()执行发出网络请求后超过指定超时时间未收到响应,Timeout()将会被调用。Step的派生类应选择从PbStep、HttpStep、RedisStep其中之一派生。 + +### Step调用链 + +  一次客户端请求,服务端往往需要不止一次网络IO才能完成对客户端的服务,多次网络IO就有多个Step,多个Step有调用的先后顺序构成了Step调用链。Step调用链分为静态调用链和动态调用链。__静态调用链__是代码写完之后,调用顺序就固定下来了,哪怕有if条件或switch开关控制走不同的调用链分支也只是多了几个固定的调用顺序可以选择,从根本上说调用顺序就是已经固定下来了,这种调用链称为静态调用链。静态调用链的特点是调用顺序完全由编写Step业务代码的开发人员决定,想定义成什么样的调用顺序就写什么样的代码,框架是完全不会介入静态调用链的调用顺序,不会在一个Step执行完毕后自动调用它的NextStep()。__动态调用链__指的是通过配置文件将一系列相关(需要用到同一个Context或Session上下文)但又没有固定顺序(既没有在代码中制定NextStep,又没有在Step代码中创建Step并执行Emit())的Step动态配置成调用链。动态调用链的特点是灵活,可以根据业务需要随时调整调用顺序。 + +  静态调用链在Step的下文中有详细说明,动态调用链的使用在Chain组件的章节里说明。 + +### 构造Step + +  回过头来看看Step的构造函数: + +```C++ +Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + +PbStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); +HttpStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); +RedisStep(std::shared_ptr pNextStep = nullptr); +``` + +  Step的构造函数有三个参数,因派生类是从PbStep、HttpStep、RedisStep其中之一派生,所以只关注这三个构造函数即可,Step构造函数的第一个参数是PbStep、HttpStep、RedisStep自动填充的。PbStep和HttpStep都有两个带缺省值的参数:pNextStep用于指定当前Step执行完之后将执行的Step;dTimeout用于指定当前Step的等待响应的超时时间。如果pNextStep不为空,意味着在构造当前Step时已经存在pNextStep这样一个实例,需要在正在构造的当前Step的Callback()函数里(return语句之前,这时当前Step事实上已经完成但尚未退出)显式调用NextStep()。NextStep()只用于静态调用链显式执行NextStep的Emit(),无论什么情况框架都不会自动调用NextStep()。pNextStep的缺省值为nullptr,因为很多情况下是用不到pNextStep的,对静态调用链而言当前Step的Callback()调用完是知道该调用哪个Step的,在需要调用时才创建,创建完立刻调用新Step的Emit()会更好更节省资源。dTimeout参数是Step等待响应的超时时间,只在有特殊需要时会用到,使用缺省值gc_dDefaultTimeout,实际上是在节点配置文件里step_timeout配置的值。RedisStep只有一个参数值,是因为RedisStep不需要作超时处理。 diff --git a/docs/image/step.png b/docs/image/step.png new file mode 100644 index 0000000000000000000000000000000000000000..c1e7599b735e398168a87cb34176b886148e976c GIT binary patch literal 38596 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 z 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

`oBj(xI*rb2JsGK`C zfE&pn7WbR6Hy5mb-5y2fOU3Gx4L5a_cEZF-jhw)nTxzSntTPH1KP9bF`}yZdTxWjD zlxyYGz(EVw0bqff9juFnUVqjkVcyMyJ5*1RQl4u%Z(dAt@_hVA^w$*B#vVTo{QNrw zO?WNx4UddP6i$ooch@?QEsoFA<@pNnu?<1RX*58h4-p8Fuv7&ipXcI|M7jUx+H)J8 zz_~zIsM*d)Jx$gX)dX!yhdz3RkK4HR{eM+N%fCMUUJtFgE7Ruh)zI3T9KloQs2rZ1 zU&v&J(KqOqLly zwriIa3a7-yb)}}&=#*P4swIGn^&0+b0K4*@4E6mo-8|FBdv-XXegQolozP=InTQXf zlxADU5KYYo6eL54KuAlhgzz@p?Z+fV*}JZFEU=0A{N&BY$5`)$_ZFY(`~h21ZZ#I% zg_PG%cB~=3(of~bWscH|%%Qd)tf6raQcQX!_QZ+dnrCpfx+&HdqEb1-Os6@^GapnO z4SF%b>2Y3lVN3ZtZVjW~XvxVw>bFzJnpBlZ`gvSbUp@Nc*SclP*rw6NdAtGl5^Z$> z46^h3qHTlTm_5_$rcf%%o3zu(<5VEyx|N#584Bl=#&uk{Cqx;&K26&zjOm$Np3lJd zB9GTyMGXeS)$T{ohsS!hca;tAqoZ@b9V=gBBmj`KO$1XhBI~dm)5}KTF)6f3!ADS0FWT4oUQ$paeA4vOVmSmRx<@&EYFNFaXE&yHIzBgD z1(hH_saldb%VD)kbLBQ{*{$v@Q0$*;_uMO>-(cQ@`cA{OM%pdY4mOg-4yMkN!U1Gzkzdxj{$nrp|=Tzc;>= zfrUk(vSGvd7h8H=;#+u;Km}9Uc3h4A@dqu`r!h$>)`gM^3J}lQAR+vLX5j0vTn|f! z!D4lq`U8UO=L7s|Q`N+t6HFhjT-IPZXHMeq+^w5hG(wPrtf)sCDtJ_V6lGgkU%qt7 zbCXt|=hpid8o0aHOhF~MnItsDAfgj5_y7O*zjy*!T|YL+)Nb4DE45lC06&T0B%_IO I6RbA;A5P}dDgXcg literal 0 HcmV?d00001 diff --git a/src/actor/session/Session.hpp b/src/actor/session/Session.hpp index 345cba16..e26d0e25 100644 --- a/src/actor/session/Session.hpp +++ b/src/actor/session/Session.hpp @@ -27,6 +27,11 @@ class Session: public Actor Session& operator=(const Session&) = delete; virtual ~Session(); + virtual bool Init() + { + return(true); + } + /** * @brief 会话超时回调 */ From 894224f477379d5483b2c3ec187ff336f68c024d Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 25 Aug 2019 18:36:55 +0800 Subject: [PATCH 053/176] add documents for Step. --- README_cn.md | 2 +- docs/cn/step.md | 255 +++++++++++++++++++++ docs/image/create_step_and_call_serial.png | Bin 0 -> 13034 bytes 3 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 docs/image/create_step_and_call_serial.png diff --git a/README_cn.md b/README_cn.md index 78be7af6..99211b96 100644 --- a/README_cn.md +++ b/README_cn.md @@ -69,7 +69,7 @@ * 开发组件说明 * [Actor组件概述](docs/cn/actor_overview.md) * [Cmd和Module组件](docs/cn/cmd_and_module.md) - * Step组件 + * [Step组件](docs/cn/step.md) * Session组件 * Context组件 * Model组件 diff --git a/docs/cn/step.md b/docs/cn/step.md index e009f5db..82a296c7 100644 --- a/docs/cn/step.md +++ b/docs/cn/step.md @@ -82,3 +82,258 @@ RedisStep(std::shared_ptr pNextStep = nullptr); ```   Step的构造函数有三个参数,因派生类是从PbStep、HttpStep、RedisStep其中之一派生,所以只关注这三个构造函数即可,Step构造函数的第一个参数是PbStep、HttpStep、RedisStep自动填充的。PbStep和HttpStep都有两个带缺省值的参数:pNextStep用于指定当前Step执行完之后将执行的Step;dTimeout用于指定当前Step的等待响应的超时时间。如果pNextStep不为空,意味着在构造当前Step时已经存在pNextStep这样一个实例,需要在正在构造的当前Step的Callback()函数里(return语句之前,这时当前Step事实上已经完成但尚未退出)显式调用NextStep()。NextStep()只用于静态调用链显式执行NextStep的Emit(),无论什么情况框架都不会自动调用NextStep()。pNextStep的缺省值为nullptr,因为很多情况下是用不到pNextStep的,对静态调用链而言当前Step的Callback()调用完是知道该调用哪个Step的,在需要调用时才创建,创建完立刻调用新Step的Emit()会更好更节省资源。dTimeout参数是Step等待响应的超时时间,只在有特殊需要时会用到,使用缺省值gc_dDefaultTimeout,实际上是在节点配置文件里step_timeout配置的值。RedisStep只有一个参数值,是因为RedisStep不需要作超时处理。 + + +### 静态调用链应用 + +  Step提高并发量,并且能以多种巧妙的组合方式满足个各种业务需求。 + +#### 即时创建Step并调用 + +  即时创建Step是最简单和最直接的组合方式,同时也是最节省资源的。 + +![创建Step和调用](../image/static_chain.png) + +  按需即时创建Step即在需要发出一个IO请求时创建一个Step1并调用Step1->Emit()发出IO请求,得到响应后Step1的Callback()会被框架自动调用。当收到并处理完一个响应后,还需要发出下一个IO请求时,在第一个Step1的Callback()函数里创建Step2并调用Step2->Emit()。依次类推,每个Step都是这样创建和调用,直到完成整个业务逻辑。Step的Emit()和Callback()函数里都可以按需要操作Context或Session里的数据,Context和Session会在后续章节里说明。 + +  即时创建Step的时序如下图: + +![即时创建Step顺序调用](../image/step_create_on_need.png) + +  即时创建Step可以通过给Step构造函数传递参数的方式准备Step所需的上下文数据也可以通过Context或Session准备Step所需上下文数据。即时创建Step的伪代码: + +```C++ +// 即时创建Step +class Cmd : public Actor +{ + bool AnyMessage(...) + { + Step_1 = MakeSharedStep("neb::StepOne", nullptr); + Step_1->Emit(); + } +}; + +// 顺序调用 +class StepOne : public Actor +{ + E_CMD_STATUS Emit(...){...} + E_CMD_STATUS Callback(...) + { + Step_2 = MakeSharedStep("neb::StepTwo", nullptr); + Step_2->Emit(); + } + E_CMD_STATUS Timeout(){...} +}; + +class StepTwo : public Actor +{ + E_CMD_STATUS Emit(...){...} + E_CMD_STATUS Callback(...) + { + Step_3 = MakeSharedStep("neb::StepThree", nullptr); + Step_3->Emit(); + } + E_CMD_STATUS Timeout(){...} +}; + +class StepThree : public Actor +{ + E_CMD_STATUS Emit(...){...} + E_CMD_STATUS Callback(...) + { + Response(); + } + E_CMD_STATUS Timeout(){...} +}; +``` + +#### 预先创建Step以next_step依次顺序调用 + +  预先创建Step以next_step依次调用是先将Step创建好,执行完一个Step再执行下一个Step,这样做的好处是一开始就给出了清晰的业务逻辑链,不过这种方式占用资源比即时创建Step要大。 + +![预先创建Step顺序调用](../image/create_step_and_call_serial.png) + +  预先创建Step以next_step依次调用时序: + +![预先创建Step顺序调用时序](../image/step_create_before_and_call_serial.png) + +  预先创建Step以next_step依次调用的方式因Step已经预先创建好,不能再通过Step构造函数传递上下文,只能通过Context或Session准备Step所需上下文数据。 + +  预先创建Step以next_step依次调用伪代码: + +```C++ +// 预先创建Step +class Cmd : public Actor +{ + bool AnyMessage(...) + { + Step_4 = MakeSharedStep("neb::StepFour", nullptr); + Step_3 = MakeSharedStep("neb::StepThree", Step_4); + Step_2 = MakeSharedStep("neb::StepTwo", Step_3); + Step_1 = MakeSharedStep("neb::StepOne", Step_2); + Step_1->Emit(); + } +}; + +// 顺序调用 +class StepOne : public Actor +{ + E_CMD_STATUS Emit(...){...} + E_CMD_STATUS Callback(...) + { + next_step->Emit(); + } + E_CMD_STATUS Timeout(){...} +}; +``` + +#### 预先创建Step以next_step归并调用 + +  上述预先创建Step以next_step顺序调用的方式其实并没有即时创建Step的方式优,Nebula之所以会支持预先创建Step方式是因为预先创建Step的归并调用可以高效优雅地实现某些业务逻辑。 + +![Step归并调用链](../image/merging_step_call.png) + +  举个例子说明一下归并调用的好处:类似朋友圈这种应用场景,朋友圈消息是以发朋友圈的用户视角分布式存储的,而查看朋友圈消息多是以接收者视角去拉取各个好友的最新朋友圈消息。朋友圈消息内容不可能按每个接收者存储一份,在接收者那里往往只有一个消息ID,具体内容还是要到各存储发送者消息内容的地方去拉取的。消息的分布式存储意味着服务端不可能通过一次请求获取到若干好友若干消息去满足客户端的一次拉取朋友圈请求所需要的内容,依次发送多个读取存储请求必然会让处理时间变长而可能导致客户端等待超时。预先创建Step归并调用就可以让多个读取朋友圈内容的同时发出,读到内容后再回到同一个Step里处理统一给客户端响应。 + +![预先创建Step以next_step归并调用](../image/step_create_before_and_call_merging.png) + +  预先创建Step以next_step依次调用伪代码: + +```C++ +// 预先创建Step +class Cmd : public Actor +{ + bool AnyMessage(...) + { + Step_4 = MakeSharedStep("neb::StepFour", nullptr); + Step_3 = MakeSharedStep("neb::StepThree", Step_4); + Step_2 = MakeSharedStep("neb::StepTwo", Step_4); + Step_1 = MakeSharedStep("neb::StepOne", Step_4); + Step_1->Emit(); + Step_2->Emit(); + Step_3->Emit(); + } +}; + +// 顺序调用 +class StepOne : public Actor +{ + E_CMD_STATUS Emit(...){...} + E_CMD_STATUS Callback(...) + { + next_step->Emit(); + } + E_CMD_STATUS Timeout(){...} +}; + +class StepTwo : public Actor +{ + E_CMD_STATUS Emit(...){...} + E_CMD_STATUS Callback(...) + { + next_step->Emit(); + } + E_CMD_STATUS Timeout(){...} +}; + +class StepThree : public Actor +{ + E_CMD_STATUS Emit(...){...} + E_CMD_STATUS Callback(...) + { + next_step->Emit(); + } + E_CMD_STATUS Timeout(){...} +}; +``` + +  拉取朋友圈消息这个例子适合用预先创建Step归并调用的方式来解决,下面这段伪代码是预先创建Step归并调用的一种演变,比上面那种多个Step的方式更适合于朋友圈这种类似业务逻辑。 + +```C++ + +class StepMerge : public Actor +{ +public: + E_CMD_STATUS Emit(...) + { + SendTo(Channel_1, ...); + ++m_iEmitNum; + SendTo(Channel_2, ...); + ++m_iEmitNum; + SendTo(Channel_3, ...); + ++m_iEmitNum; + } + E_CMD_STATUS Callback(...) + { + ++m_iCallbackNum; + if (m_iCallbackNum == m_iEmitNum) + { + Response(); + } + else + { + return CMD_STATUS_RUNNING; + } + } + E_CMD_STATUS Timeout(){...} + +private: + int m_iEmitNum = 0; + int m_iCallbackNum = 0; +}; + +``` + +#### 单个Step递归调用 + +  单个Step递归调用,严格而言不是Step调用链,而且这也是Nebula所不推荐的一种应用方式。但如果有需要,并且不会造成业务逻辑混乱的情况下可以适当使用。这种方式是只创建一个Step,在Step的Callback()里再调用自己的Emit(),什么时候结束完全由Step在自身决定。伪代码如下: + +```C++ +class StepOne : public Actor +{ +public: + E_CMD_STATUS Emit(...) + { + ++m_iEmitNo; + switch (m_iEmitNo) + { + case 1: + return EmitFirst(); + case 2: + return EmitSecond(); + case 3: + return EmitThird(); + } + } + E_CMD_STATUS Callback(...) + { + switch (m_iEmitNo) + { + case 1: + CallbackFirst(...); + return Emit(); + case 2: + CallbackSecond(...); + return Emit(); + case 3: + return CallbackThird(...); + } + } + E_CMD_STATUS Timeout(){...} + +protected: + E_CMD_STATUS EmitFirst(); + E_CMD_STATUS CallbackFirst(...); + E_CMD_STATUS EmitSecond(); + E_CMD_STATUS CallbackSecond(...); + E_CMD_STATUS EmitThird(); + E_CMD_STATUS CallbackThird(...); + +private: + int m_iEmitNo = 0; +}; +``` + +### Step调用链小结 + +  Step设计比较巧妙,有多种组合方式,上述调用链是Nebula所建议的方式,也可能还有其他组合方式开发者可以向Nebula官方建议。另外,Step调用链的动态组合方式会在Chain章节描述。 diff --git a/docs/image/create_step_and_call_serial.png b/docs/image/create_step_and_call_serial.png new file mode 100644 index 0000000000000000000000000000000000000000..e0b61831d4dbb7656b1c0cc7ed44abe8288e2d20 GIT binary patch literal 13034 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 Date: Sat, 31 Aug 2019 20:15:17 +0800 Subject: [PATCH 054/176] add documents. --- README_cn.md | 9 +++++---- docs/cn/SUMMARY.md | 5 +++-- docs/cn/how_nebula_works.md | 17 +++++++++++++++++ docs/cn/step.md | 29 ++++++++++++++--------------- docs/image/nebula_process.png | Bin 0 -> 34204 bytes 5 files changed, 39 insertions(+), 21 deletions(-) create mode 100644 docs/cn/how_nebula_works.md create mode 100644 docs/image/nebula_process.png diff --git a/README_cn.md b/README_cn.md index 99211b96..94f88e98 100644 --- a/README_cn.md +++ b/README_cn.md @@ -31,7 +31,7 @@   Nebula是一个产线级的框架和分布式解决方案项目,适用于即时通讯、数据采集、实时计算、消息推送等应用场景,也适用于web后台服务。Nebula已有即时通讯、埋点数据采集及实时分析的生产应用案例,很快将有一个面向庞大用户群的推荐引擎产线应用案例。 -  把Nebula用于学习交流也不错,Bwar欢迎更多有兴趣的开发者加入到Nebula这个项目中来,Bwar也乐意解答项目中遇到的问题。Nebula是个proactor模式开发框架,不错,是proactor不是reactor(框架层实现的proactor而不是操作系统支持),应用于IO密集型的项目可以达到非常好的性能。Nebula现在不支持同步调用不支持rpc,以后也应该不会支持rpc,做全异步的通信框架目标不变。对了解异步回调编程方式的开发者,Nebula是个非常简单的框架,比写常见的异步回调写法要简单多了。Nebula网络框架的技术分享和交流见[C++网络框架Nebula](https://zhuanlan.zhihu.com/c_216558336) +  把Nebula用于学习交流也不错,Bwar欢迎更多有兴趣的开发者加入到Nebula这个项目中来。Nebula是个proactor模式开发框架,不错,是proactor不是reactor(框架层实现的proactor而不是操作系统支持),应用于IO密集型的项目可以达到非常好的性能。对用惯了RPC框架的人而言,Nebula跟RPC很不一样,不过使用起来并不会比RPC复杂多少,但比RPC性能要高很多;对了解异步回调编程方式的开发者,Nebula是个非常简单的框架,比写常见的异步回调写法要简单多了。Nebula网络框架的技术分享和交流见[C++网络框架Nebula](https://zhuanlan.zhihu.com/c_216558336)   Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(也是Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++14)是Starship(C++03)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的第一个应用Nebio(埋点数据采集和实时分析项目)在2018年7月底上线并稳定运行,Bwar还准备开发基于Nebula的IM应用Nebim。 @@ -64,6 +64,7 @@ * [分布式服务示例](docs/cn/nebula_distributed_demo.md) * [安装部署说明](docs/cn/install.md) +* [Nebula工作原理](how_nebula_works.md) * [配置说明](docs/cn/configuration.md) * [协议说明](docs/cn/protocol.md) * 开发组件说明 @@ -107,9 +108,9 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 开发任务 - - 2019年8月底前完成开发指南 - - NebulaMydis数据代理服务 - - 应用Nebula开发IM项目 + - 完成开发指南 + - NebulaMydis数据代理服务 + - 应用Nebula开发IM项目 ## 版本历史 diff --git a/docs/cn/SUMMARY.md b/docs/cn/SUMMARY.md index e815d0b1..068c7e96 100644 --- a/docs/cn/SUMMARY.md +++ b/docs/cn/SUMMARY.md @@ -1,7 +1,7 @@ * [开始](getting_start.md) -* [高并发单机Server示例](independent_service.md) -* [分布式服务示例](distributed_service.md) +* [高并发单机Server示例](nebula_server_demo.md) +* [分布式服务示例](nebula_distributed_demo.md) * [Nebula工作原理](how_nebula_works.md) * [Nebula分布式服务简析](nebula_cluster.md) * [快速开发组件](actor_components.md) @@ -18,6 +18,7 @@ * [会话](session.md) * [Model计算模型](model.md) * [Chain调用链](chain.md) +* [安装部署说明](docs/cn/install.md) * [通信方式](way_of_communication.md) * [配置说明](configuration.md) * [通信协议](protocol.md) diff --git a/docs/cn/how_nebula_works.md b/docs/cn/how_nebula_works.md new file mode 100644 index 00000000..f7394338 --- /dev/null +++ b/docs/cn/how_nebula_works.md @@ -0,0 +1,17 @@ +  Nebula框架基于libev事件驱动,可以类比java的netty。 + +  Labor为进程处理者,有两个实体类Manager和Worker,进程级资源都由Labor进行管理和调度。 + +  Actor为事件(消息)处理者,所有业务逻辑均抽象成事件和事件处理,Actor分为Cmd、Module、Step、Session等不同类型。业务逻辑代码均通过从这四种不同类型时间处理者派生子类来实现,专注于业务逻辑实现,而无须关注业务逻辑之外的东西。 + +  Channel为通信通道,所有消息均通过Channel进行收发,Channel都是非阻塞的,由Labor调用Channel的方法进行消息发送和接收。消息的编解码由Codec完成,每个Channel创建时均同步创建一个Codec并绑定,在发生应用层通信协议变更时,通过切换Channel的Codec来完成同一通道的不同协议编解码。 + +![nebula process](../image/nebula_process.png) + +  Nebula的进程模型如上图所示,由管理进程与工作进程组成。启动时引导程序先创建管理进程,管理进程读取配置文件,加载业务插件,然后绑定IP、监听端口、拉起配置文件中指定数量的工作进程。 + +  管理进程负责监控和管理工作进程,与注册中心通信上报当前节点信息,接收其他节点信息,配置下发,接收新连接并将连接文件描述符传送给工作进程。 + +  工作进程负责数据收发和处理。 + +  管理进程与工作进程之间通过两个(控制流与数据流分开)UnixSocket进行通信。 diff --git a/docs/cn/step.md b/docs/cn/step.md index 82a296c7..daa02a6d 100644 --- a/docs/cn/step.md +++ b/docs/cn/step.md @@ -1,11 +1,10 @@ ### Step定义 -  Step是最为核心的Actor组件,Nebula高性能的关键在于Step,IO密集型应用的逻辑都是围绕Step展开,暂且先把Step理解为一个操作步骤。Step起到C语言中异步回调函数的作用,却又比异步回调函数适用要简单得多。首先来看一下Step类图: +  Step是最为核心的Actor组件,Nebula高性能的关键在于Step,IO密集型应用的逻辑都是围绕Step展开,暂且先把Step理解为一个操作步骤。Step起到C语言中异步回调函数的作用,却又比异步回调函数使用要简单得多。首先来看一下Step类图: ![Step类定义](../image/step.png)   如图所示,Step类分为三种PbStep、HttpStep和RedisStep,分别用在需要Nebula通信协议、Http通信协议和Redis通信协议的场景,其中PbStep应用最多,文档中的Step示例以PbStep为主。 - Step.hpp: ```C++ @@ -18,7 +17,7 @@ namespace neb class Chain; -class Step: public Actor +class Step { public: Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); @@ -104,7 +103,7 @@ RedisStep(std::shared_ptr pNextStep = nullptr); ```C++ // 即时创建Step -class Cmd : public Actor +class Cmd { bool AnyMessage(...) { @@ -114,7 +113,7 @@ class Cmd : public Actor }; // 顺序调用 -class StepOne : public Actor +class StepOne { E_CMD_STATUS Emit(...){...} E_CMD_STATUS Callback(...) @@ -125,7 +124,7 @@ class StepOne : public Actor E_CMD_STATUS Timeout(){...} }; -class StepTwo : public Actor +class StepTwo { E_CMD_STATUS Emit(...){...} E_CMD_STATUS Callback(...) @@ -136,7 +135,7 @@ class StepTwo : public Actor E_CMD_STATUS Timeout(){...} }; -class StepThree : public Actor +class StepThree { E_CMD_STATUS Emit(...){...} E_CMD_STATUS Callback(...) @@ -163,7 +162,7 @@ class StepThree : public Actor ```C++ // 预先创建Step -class Cmd : public Actor +class Cmd { bool AnyMessage(...) { @@ -176,7 +175,7 @@ class Cmd : public Actor }; // 顺序调用 -class StepOne : public Actor +class StepOne { E_CMD_STATUS Emit(...){...} E_CMD_STATUS Callback(...) @@ -201,7 +200,7 @@ class StepOne : public Actor ```C++ // 预先创建Step -class Cmd : public Actor +class Cmd { bool AnyMessage(...) { @@ -216,7 +215,7 @@ class Cmd : public Actor }; // 顺序调用 -class StepOne : public Actor +class StepOne { E_CMD_STATUS Emit(...){...} E_CMD_STATUS Callback(...) @@ -226,7 +225,7 @@ class StepOne : public Actor E_CMD_STATUS Timeout(){...} }; -class StepTwo : public Actor +class StepTwo { E_CMD_STATUS Emit(...){...} E_CMD_STATUS Callback(...) @@ -236,7 +235,7 @@ class StepTwo : public Actor E_CMD_STATUS Timeout(){...} }; -class StepThree : public Actor +class StepThree { E_CMD_STATUS Emit(...){...} E_CMD_STATUS Callback(...) @@ -251,7 +250,7 @@ class StepThree : public Actor ```C++ -class StepMerge : public Actor +class StepMerge { public: E_CMD_STATUS Emit(...) @@ -289,7 +288,7 @@ private:   单个Step递归调用,严格而言不是Step调用链,而且这也是Nebula所不推荐的一种应用方式。但如果有需要,并且不会造成业务逻辑混乱的情况下可以适当使用。这种方式是只创建一个Step,在Step的Callback()里再调用自己的Emit(),什么时候结束完全由Step在自身决定。伪代码如下: ```C++ -class StepOne : public Actor +class StepOne { public: E_CMD_STATUS Emit(...) diff --git a/docs/image/nebula_process.png b/docs/image/nebula_process.png new file mode 100644 index 0000000000000000000000000000000000000000..dc541dfa571d1854d16629da62971ef695f260b8 GIT binary patch literal 34204 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∾ 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 literal 0 HcmV?d00001 From 464c668f5e000ac9327cb8807060ad44083ab53b Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 10 Sep 2019 23:19:55 +0800 Subject: [PATCH 055/176] MakeSharedActor() bug fixed --- src/actor/Actor.hpp | 12 ++++++------ src/labor/Worker.hpp | 12 ++++++------ src/labor/WorkerImpl.hpp | 2 +- src/labor/WorkerImpl.inl | 20 ++++++++++---------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 712defe6..385f2abe 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -266,37 +266,37 @@ class Actor: public std::enable_shared_from_this template void Actor::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { - m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); + m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } template std::shared_ptr Actor::MakeSharedStep(const std::string& strStepName, Targs&&... args) { - return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); + return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); } template std::shared_ptr Actor::MakeSharedSession(const std::string& strSessionName, Targs&&... args) { - return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); + return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); } template std::shared_ptr Actor::MakeSharedContext(const std::string& strContextName, Targs&&... args) { - return(m_pWorker->MakeSharedContext(this, strContextName, std::forward(args)...)); + return(m_pWorker->MakeSharedContext(this, strContextName, std::forward(args)...)); } template std::shared_ptr Actor::MakeSharedActor(const std::string& strActorName, Targs&&... args) { - return(m_pWorker->MakeSharedActor(this, strActorName, std::forward(args)...)); + return(m_pWorker->MakeSharedActor(this, strActorName, std::forward(args)...)); } template std::shared_ptr Actor::MakeSharedChain(const std::string& strChainName, Targs&&... args) { - return(m_pWorker->MakeSharedChain(this, strChainName, std::forward(args)...)); + return(m_pWorker->MakeSharedChain(this, strChainName, std::forward(args)...)); } } /* namespace neb */ diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 18f179b1..418df2c8 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -96,37 +96,37 @@ class Worker: public Labor template void Worker::Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { - m_pImpl->Logger(strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); + m_pImpl->Logger(strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } template std::shared_ptr Worker::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args) { - return(m_pImpl->MakeSharedActor(pCreator, strActorName, std::forward(args)...)); + return(m_pImpl->MakeSharedActor(pCreator, strActorName, std::forward(args)...)); } template std::shared_ptr Worker::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args) { - return(m_pImpl->MakeSharedStep(pCreator, strStepName, std::forward(args)...)); + return(m_pImpl->MakeSharedStep(pCreator, strStepName, std::forward(args)...)); } template std::shared_ptr Worker::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args) { - return(m_pImpl->MakeSharedSession(pCreator, strSessionName, std::forward(args)...)); + return(m_pImpl->MakeSharedSession(pCreator, strSessionName, std::forward(args)...)); } template std::shared_ptr Worker::MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args) { - return(m_pImpl->MakeSharedContext(pCreator, strContextName, std::forward(args)...)); + return(m_pImpl->MakeSharedContext(pCreator, strContextName, std::forward(args)...)); } template std::shared_ptr Worker::MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args) { - return(m_pImpl->MakeSharedChain(pCreator, strChainName, std::forward(args)...)); + return(m_pImpl->MakeSharedChain(pCreator, strChainName, std::forward(args)...)); } } /* namespace neb */ diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index e2c9fc44..48c92ead 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -194,7 +194,7 @@ class WorkerImpl void Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); template - std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args); + std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args); template std::shared_ptr MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs&&... args); diff --git a/src/labor/WorkerImpl.inl b/src/labor/WorkerImpl.inl index 4e183096..655ed2ed 100644 --- a/src/labor/WorkerImpl.inl +++ b/src/labor/WorkerImpl.inl @@ -22,13 +22,13 @@ void WorkerImpl::Logger(int iLogLevel, const char* szFileName, unsigned int uiFi template void WorkerImpl::Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { - m_pLogger->WriteLog(strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); + m_pLogger->WriteLog(strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } template -std::shared_ptr WorkerImpl::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args) +std::shared_ptr WorkerImpl::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args) { - Actor* pActor = ActorFactory::Instance()->Create(strActorName, std::forward(args)...); + Actor* pActor = ActorFactory::Instance()->Create(strActorName, std::forward(args)...); if (nullptr == pActor) { LOG4_ERROR("failed to make shared actor \"%s\"", strActorName.c_str()); @@ -43,43 +43,43 @@ std::shared_ptr WorkerImpl::MakeSharedActor(Actor* pCreator, const std::s template std::shared_ptr WorkerImpl::MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strCmdName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strCmdName, std::forward(args)...))); } template std::shared_ptr WorkerImpl::MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModuleName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModuleName, std::forward(args)...))); } template std::shared_ptr WorkerImpl::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strStepName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strStepName, std::forward(args)...))); } template std::shared_ptr WorkerImpl::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strSessionName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strSessionName, std::forward(args)...))); } template std::shared_ptr WorkerImpl::MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strContextName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strContextName, std::forward(args)...))); } template std::shared_ptr WorkerImpl::MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModelName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModelName, std::forward(args)...))); } template std::shared_ptr WorkerImpl::MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strChainName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strChainName, std::forward(args)...))); } } From 9df2bb550618e1df8406c3a44e50a245d4dae3e4 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 14 Sep 2019 15:29:14 +0800 Subject: [PATCH 056/176] MakeSharedActor() optimization and dynamic load optimization. --- conf/nebula.json | 4 +- docs/cn/actor.md | 144 ++++++++++++++++++++++++++++++++++ docs/cn/session.md | 133 +++++++++++++++++++++++++++++++ src/actor/Actor.cpp | 4 +- src/actor/Actor.hpp | 2 +- src/actor/session/Session.cpp | 4 +- src/actor/session/Session.hpp | 2 +- src/labor/Manager.cpp | 2 +- src/labor/Worker.cpp | 4 +- src/labor/Worker.hpp | 2 +- src/labor/WorkerImpl.cpp | 47 ++++++----- src/labor/WorkerImpl.hpp | 8 +- src/labor/WorkerImpl.inl | 20 ++++- 13 files changed, 341 insertions(+), 35 deletions(-) create mode 100644 docs/cn/actor.md create mode 100644 docs/cn/session.md diff --git a/conf/nebula.json b/conf/nebula.json index 19973626..770dbd76 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -67,7 +67,7 @@ "dynamic_loading": [{ "so_path": "plugins/User.so", "load": false, - "version": 1, + "version": "1.0", "cmd": [ { "cmd": 2001, "class": "neb::CmdUserLogin" }, { "cmd": 2003, "class": "neb::CmdUserLogout" } @@ -84,7 +84,7 @@ { "so_path": "plugins/ChatMsg.so", "load": false, - "version": 1, + "version": "1.0", "cmd": [ { "cmd": 2001, "class": "neb::CmdChat" } ], diff --git a/docs/cn/actor.md b/docs/cn/actor.md new file mode 100644 index 00000000..16d860ee --- /dev/null +++ b/docs/cn/actor.md @@ -0,0 +1,144 @@ +  Actor类自身提供各组件与框架交互的成员函数,通过这些成员函数即可完成所有业务逻辑所需的功能,换句话说,基于Nebula框架的业务开发只需了解Actor类的30余成员函数的功能就足够。这样设计一方面是为了尽可能地降低Nebula学习和使用门槛,另一方面又能非常巧妙地带来一些令人惊喜的有且而强大的功能。 + +  Actor类是所有Actor组件的基类,但Actor类不是一个虚基类,它更像是一个工具类。工具类通常会以组合的方式嵌入到其他类成员中使用,而Actor以继承的方式是因为Nebula的设计是所有组件所有逻辑皆Actor,是is关系不是has关系继承,更重要的是继承方式在Nebula可以很方便地实现一些组合方式难以实现的强大功能。 + +  直接从Actor类派生的类都是ACT_UNDEFINE类型,Nebula框架不会管理这些类,所以请勿直接从Actor类派生,除非有非常充分且合理的理由直接从Actor类派生业务自己的Actor基类并自己管理。 + +### Actor类定义 + +  Actor类继承std::enable_shared_from_this是为了方便各Actor派生类方便从this指针获取shared_ptr进行共享,开发者无需关注std::enable_shared_from_this。 + +  Actor类的ACTOR_TYPE枚举定义了Nebula框架支持的所有Actor组件类型,Cmd、Module、Step、Session等组件会自动填充ACTOR_TYPE,从这些组件派生的业务类无需关注ACTOR_TYPE,ACTOR_TYPE主要是Nebula框架管理组件用的。 + +  Logger()模板函数是写日志用的,但并不需要直接调用Logger()函数写日志,调用LOG4_INFO()/LOG4_ERROR()等相关宏写日志即可,实际上这些宏就是调用Logger()函数写的日志,宏会自动填充一些文件名、函数名之类参数,比直接调用Logger()函数简单。Logger()函数的实现更是无须关注,小小提示一下,Nebula提供不需要开发者做任何额外工作的日志跟踪服务,其原理就在Logger()函数的实现里,这也是令人惊喜的有用且强大的功能之一。 + +  MakeSharedStep()/MakeSharedSession()等相关模板函数用于通过类名反射动态创建具体业务逻辑类,动态创建具体Actor实例的同时会初始化并向框架进行注册,还有一些上下文信息的传递也会在动态创建的过程中完成传递。GetActorName()、GetTraceId()、GetSequence()、GetContext()等用起来觉得很方便很神奇,这些都是MakeShared相关模板函数默默地做了许多复杂的事情,把简单都留给用户。所以,切记不要自己去调用new来创建Actor的各种派生类,都调用MakeSharedStep/MakeSharedSession()等相关模板函数。至于非Actor组件派生类和用户自己的数据结构,MakeShared相关模板函数是不支持的,也没必要支持。 + +```C++ +class Actor: public std::enable_shared_from_this +{ +public: + enum ACTOR_TYPE + { + ACT_UNDEFINE = 0, ///< 未定义 + ACT_CMD = 1, ///< Cmd对象,处理带命令字的pb请求 + ACT_MODULE = 2, ///< Module对象,处理带url path的http请求 + ACT_SESSION = 3, ///< Session会话对象 + ACT_TIMER = 4, ///< 定时器对象 + ACT_CONTEXT = 5, ///< 会话上下文对象 + ACT_PB_STEP = 6, ///< Step步骤对象,处理pb请求或响应 + ACT_HTTP_STEP = 7, ///< Step步骤对象,处理http请求或响应 + ACT_REDIS_STEP = 8, ///< Step步骤对象,处理redis请求或响应 + ACT_MODEL = 9, ///< Model模型对象,Model(IO无关)与Step(异步IO相关)共同构成功能链 + ACT_CHAIN = 10, ///< Chain链对象,用于将Model和Step组合成功能链 + }; + +public: + Actor(ACTOR_TYPE eActorType = ACT_UNDEFINE, ev_tstamp dTimeout = 0.0); + Actor(const Actor&) = delete; + Actor& operator=(const Actor&) = delete; + virtual ~Actor(); + + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); + template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs&&... args); + template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs&&... args); + template std::shared_ptr MakeSharedContext(const std::string& strContextName, Targs&&... args); + template std::shared_ptr MakeSharedChain(const std::string& strChainName, Targs&&... args); + template std::shared_ptr MakeSharedActor(const std::string& strActorName, Targs&&... args); + + ACTOR_TYPE GetActorType() const + { + return(m_eActorType); + } + + const std::string& GetActorName() const + { + return(m_strActorName); + } + + const std::string& GetTraceId() const + { + return(m_strTraceId); + } + + uint32 GetSequence(); + +protected: + uint32 GetNodeId() const; + uint32 GetWorkerIndex() const; + ev_tstamp GetDefaultTimeout() const; + const std::string& GetNodeType() const; + const std::string& GetWorkPath() const; + const std::string& GetNodeIdentify() const; + time_t GetNowTime() const; + const CJsonObject& GetCustomConf() const; + + std::shared_ptr GetSession(uint32 uiSessionId); + std::shared_ptr GetSession(const std::string& strSessionId); + bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); + std::shared_ptr GetModel(const std::string& strModelName); + std::shared_ptr GetContext(); + void SetContext(std::shared_ptr pContext); + void AddAssemblyLine(std::shared_ptr pSession); + +protected: + bool SendTo(std::shared_ptr pChannel); + bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg); + bool SendTo(std::shared_ptr pChannel); + bool SendTo(const std::string& strIdentify); + bool SendTo(const std::string& strHost, int iPort); + bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + +protected: + virtual void SetActiveTime(ev_tstamp dActiveTime) + { + m_dActiveTime = dActiveTime; + } + + ev_tstamp GetActiveTime() const + { + return(m_dActiveTime); + } + + ev_tstamp GetTimeout() const + { + return(m_dTimeout); + } + +private: + void SetWorker(Worker* pWorker); + ev_timer* MutableTimerWatcher(); + void SetActorName(const std::string& strActorName); + void SetTraceId(const std::string& strTraceId); + +private: + ACTOR_TYPE m_eActorType; + uint32 m_uiSequence; + ev_tstamp m_dActiveTime; + ev_tstamp m_dTimeout; + Worker* m_pWorker; + ev_timer* m_pTimerWatcher; + std::string m_strActorName; + std::string m_strTraceId; // for log trace + std::shared_ptr m_pContext; + + friend class WorkerImpl; + friend class WorkerFriend; + friend class Chain; +}; +``` + +  GetActorName()获取的是带名字空间的类名,熟悉Java的开发者都知道.class用起来很方便,GetActorName()就是Nebula框架的.class。 + +  GetSequence()可以随时随地获取进程内唯一的一个32位无符号整数。 + +  GetNowTime()比较近似地获取当前时间,在高并发的服务里频繁调用time()、gettimeofday()等函数对性能是有影响的,频繁调用GetNowTime()则不会有这样的问题。如果需要获取非常精确的时间,GetNowTime()是不适用的。 + +  GetNodeId()/GetNodeType()/GetCustomConf()等用于获取当前节点、当前进程的一些重要信息,获取用户自定义配置信息。这些函数都非常有用,不用刻意去记,当需要用的时候再到Actor类里找,通常都有,毕竟Nebula是个久经多种业务生产环境使用的框架,功能比较全。再小提示一下,这些函数使得snowflake算法分布式生成唯一ID非常方便。 + +   \ No newline at end of file diff --git a/docs/cn/session.md b/docs/cn/session.md new file mode 100644 index 00000000..6313cbb0 --- /dev/null +++ b/docs/cn/session.md @@ -0,0 +1,133 @@ +  Session是仅次于Step的核心Actor组件,用于保存状态和数据。Cmd、Step等其他Actor组件也可以用Session来传递数据。在一些实时数据流应用中(比如Nebio)也适合把Session作最主要的组件使用。 + +### Session的方便性 + +  Session是使用最为灵活的Actor组件,保存状态和数据是其最主要的用途,还有许多能想到的用途绝大部分都可用Session来实现。还是再提醒一句,使用灵活,但请别滥用。为什么Session可以如此灵活,因为只要知道SessionId就可以随时随地获取到指定的Session实例,而Session又是完全由用户自己定义,想如何用也就完全由用户自己控制。来看一个Session的使用例子: + +```C++ +std::shared_ptr pSessionOnlineNode = std::dynamic_pointer_cast(GetSession("beacon::SessionOnlineNodes")); +if (nullptr != pSessionOnlineNode) +{ + pSessionOnlineNode->AddBeaconBeat(); +} +``` + +  只要已知SessionId,在任何地方都可以调用GetSession()获取到对应Session实例。 + +### Session定义 + +  Session类有一个子类Timer,两者的用途几乎完全一致。唯一区别在于Timer类总是会在指定的超时时间dSessionTimeout耗尽时被调用Timeout()回调函数;Session则会在每一次GetSession()调用时顺延其超时时间,只有在dSessionTimeout耗尽期间均未被访问过,Timeout()回调函数才会被调用。通俗点讲,Timer就是个定时器,时间一到,Timeout()一定会被调用;Session是会话,在用则不会超时,超过指定时间未被使用则会过期,Timeout()被调用。无论Session还是Timer,Timeout()被调用后,资源是应该被回收还是进入下一个计时周期完全取决于Timeout()的返回值,返回CMD_STATUS_RUNNING则继续下一个计时周期,返回其它值则进入结束状态,Session或Timer被回收。 + +![Session类图]() + +  Session和Timer类定义: + +```C++ +class Session: public Actor +{ +public: + Session(uint64 ullSessionId, ev_tstamp dSessionTimeout = 60.0); + Session(const std::string& strSessionId, ev_tstamp dSessionTimeout = 60.0); + Session(const Session&) = delete; + Session& operator=(const Session&) = delete; + virtual ~Session(); + + /** + * @brief 会话超时回调 + */ + virtual E_CMD_STATUS Timeout() = 0; + + const std::string& GetSessionId() const + { + return(m_strSessionId); + } + + /** + * @brief 检查Session内数据是否已加载 + * @note 为满足数据共享并确保同一数据在同一个Worker内只需从 + * 外部存储中加载一次,提供了IsReady(),SetReady(),IsLoading(), + * SetLoading()四个方法。如果一个或若干个Step获取到一个已创建好 + * 的Session,则需先调用IsReady(this)判断数据是否就绪,若就绪则直接 + * 从Session中读取,若未就绪则调用IsLoading()判断数据是否正在加 + * 载,若正在加载则直接return(CMD_STATUS_RUNNING)(框架会在数据 + * 加载完毕后调用该Step的Callback),否则加载数据并且SetLoading(), + * 数据加载完毕后SetReady()。 + * @param pStep 调用IsReady()方法的调用者Step的this指针,用于记录 + * 哪些Step依赖于Session的数据,在数据就绪时由框架主动调用 + * 依赖这些数据的Step回调而不需要等到超时才回调。 + */ + bool IsReady(Step* pStep); + void SetReady(); + bool IsLoading(); + void SetLoading(); + +private: + uint32 PopWaitingStep(); + +private: + friend class WorkerImpl; + bool m_bDataReady; + bool m_bDataLoading; + std::string m_strSessionId; + std::queue m_vecWaitingStep; +}; + +class Timer: public Session +{ +public: + Timer(uint64 ullSessionId, ev_tstamp dSessionTimeout = 60.0); + Timer(const std::string& strSessionId, ev_tstamp dSessionTimeout = 60.0); + Timer(const Timer&) = delete; + Timer& operator=(const Timer&) = delete; + virtual ~Timer(); + + /** + * @brief 会话超时回调 + */ + virtual E_CMD_STATUS Timeout() = 0; + +protected: + /** + * @brief Timer与Session的差异是通过覆盖Actor类的SetActiveTime()实现的, + * 注意Timer的子类不可以再覆盖SetActiveTime()。 + */ + virtual void SetActiveTime(ev_tstamp dActiveTime) + { + ; + } +}; +``` + +  构造Session或Timer需要两个参数,一个SessionId,一个超时时间。SessionId可以是64位无符号整数,也可以是字符串,最终SessionId都会转换成字符串来使用。如果需要永不超时,可以将SessionTimeout指定为gc_dNoTimeout。Session的私有成员除m_strSessionId之外还有好几个,开发者不需要理解。IsReady(),SetReady(),IsLoading(),SetLoading()四个保护成员方法会在少数场景中用到,注释中有比较充分的说明,本篇下文也会描述。 + +### Session应用 + +  Session主要应用于状态和数据保存,下面结合几个具体场景更好清晰地描述Session的应用。 + +#### HTTP本地会话 + +  当客户端浏览器通过HTTP1.1协议建立与Nebula的连接,服务端收到第一个请求时完成了客户端身份验证,将验证信息、浏览器cookie等以cookie为SessionId保存成一个Session。后续请求处理流程中GetSession(cookie)就可以获得整个会话数据。Session在整个HTTP生命周期都有效,每次请求均刷新超时起始时间,当连接中断或长时间没有请求,Session超时回收。 + +#### 推荐系统预估引擎的模型数据和字典 + +  服务启动时将模型及其它字典加载到Session中,Timeout()用于检测本地模型文件或其它字典文件是否有更新,有更新则加载,不同的模型和字典更新频率会不一样,分别设置不同的超时时间避免无谓的检测操作非常合适。将模型数据和字典数据保存在Session里,可以很方便通过Cmd接收外部请求实时增量字典数据,有效解决大模型更新时引起的服务中断。当然,预估引擎模型和字典通常会分读、写两份数据,有专用线程做数据的更新,在数据更新完毕后才做读写指针互换。但实时增量更新带来的好处是显而易见的,提供了双内存操作的另一种很有价值的方式。 + +  加载在Session里的数据可以很方便的给各算子和模型计算器通过SessionId读取到。 + +#### 即时通讯群组会话 + +  像QQ群这样一个即时通信软件群组,在群消息通讯时需要用到许多信息:群名称、群头像、群创建时间、群成员数量、群成员在线情况、群主、群管理员、群成员昵称头像…… 这些信息在数据库或redis里都有存储,因群人数可能很多,即便去redis里查,代价也是比较大的。如果把这些信息都保存在Session里,在群活跃期间(假设有群消息等任何跟当前群相关的操作视为群活跃,20分钟内无任何操作则视为不活跃),需要用到这些信息的操作均可直接从本服务内存Session中获取到而无需远程请求redis,这将极大缩短了响应时间同时大幅降低了处理代价,缩短处理时间降低服务代价将直接带来每个请求服务质量和单机并发量的大幅提高。 + +  把群相关数据保存在Session里,减少查询存储是有前提的,就是保证Session里的数据的完整性。如何保证Session数据的完整性不在这里展开,只给出两点提示:(1) 所有与同一个群相关的操作均由同一个服务节点完成;(2) 群相关数据的写操作先向存储系统(redis或消息队列,数据库太慢要异步写)发写请求,在存储系统返回成功响应后立即将刚写的数据更新到Session。如何让所有与同一个群相关的血操作均由同一个服务节点完成?使用Nebula构建的微服务系统实现非常简单,调用Actor基类的SendOriented()函数轻松解决。 + +  群很容易产生并发,当一个未处于活跃状态的群的两个群成员恰好在同一时间发送消息或查看群信息时,请求又恰好同时路由到一个服务节点时,当前节点并不存在该群的Session,此时要去加载Session数据可能会导致多次加载。多次加载Session数据是不合理的,也违背了Session的设计原则。基于Nebula的服务是全异步的,所以不存在阻塞其他请求去等一个请求加载完Session数据这种降低并发的不合理的做法,也正因全异步“同时”到达的请求其实也是有先后顺序的,请求间可能有微秒甚至更细粒度的时间差异。这就是Session的四个保护成员方法IsReady(),SetReady(),IsLoading(),SetLoading()的用武之地了: + +* 第一步:A请求发现群Session在当前服务节点不存在,创建群Session。 +* 第二步:new一个StepA,执行StepA->Emit()向存储发起数据请求同时调用Session的SetLoading()设置Session数据加载中标识。 +* 第三步:B请求发现群Session存在,new一个StepB执行StepB->Emit()调用Session的IsReady(this),如果IsReady(this)返回true正常继续B请求的后续处理流程;如果IsReady(this)返回false,再调用IsLoading(),若IsLoading()返回true当前StepB->Emit()返回CMD_STATUS_RUNNING;若IsLoading()返回false,StepB->Emit()向存储发起数据请求同时调用Session的SetLoading()设置Session数据加载中标识。 +* 第四步:A请求向存储发出读数据得到成功响应,StepA->Callback()被框架回调,在StepA的Callback()函数里将先数据存储到群Session里并调用该群Session的SetReady(),再继续A请求的后续逻辑(比如new StepA2并执行StepA2->Emit())。 +* 第五步:B请求StepB->Callback()被框架回调,StepA->Callback()的入参又给了StepB->Callback()(因为如果Session的IsLoading()返回false,将会由StepB->Emit()去加载数据,那样的话StepB->Callback()得到的入参跟StepA->Callback()的入参也还是一样的)。 + +  这些步骤中只有一个发往存储系统的加载数据请求,框架得到存储系统的数据响应时给每个等待同一份数据的Step回调,同时将群Session的数据填充完毕,后续请求可以直接从群Session中获取数据,不再需要向存储系统发请求。 + +  群Session应用场景有个流程图或时序图说明会更清晰,时间关系,没有画。 \ No newline at end of file diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 58313d09..53dee234 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -81,9 +81,9 @@ const CJsonObject& Actor::GetCustomConf() const { return(m_pWorker->GetCustomConf()); } -std::shared_ptr Actor::GetSession(uint32 uiSessionId) +std::shared_ptr Actor::GetSession(uint64 ullSessionId) { - return(m_pWorker->GetSession(uiSessionId)); + return(m_pWorker->GetSession(ullSessionId)); } std::shared_ptr Actor::GetSession(const std::string& strSessionId) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 385f2abe..c08ed2a0 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -111,7 +111,7 @@ class Actor: public std::enable_shared_from_this */ const CJsonObject& GetCustomConf() const; - std::shared_ptr GetSession(uint32 uiSessionId); + std::shared_ptr GetSession(uint64 ullSessionId); std::shared_ptr GetSession(const std::string& strSessionId); bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); std::shared_ptr GetModel(const std::string& strModelName); diff --git a/src/actor/session/Session.cpp b/src/actor/session/Session.cpp index 11414834..0680f3f4 100644 --- a/src/actor/session/Session.cpp +++ b/src/actor/session/Session.cpp @@ -14,12 +14,12 @@ namespace neb { -Session::Session(uint32 ulSessionId, ev_tstamp dSessionTimeout) +Session::Session(uint64 ullSessionId, ev_tstamp dSessionTimeout) : Actor(Actor::ACT_SESSION, dSessionTimeout), m_bDataReady(false), m_bDataLoading(false) { std::ostringstream oss; - oss << ulSessionId; + oss << ullSessionId; m_strSessionId = std::move(oss.str()); } diff --git a/src/actor/session/Session.hpp b/src/actor/session/Session.hpp index e26d0e25..06985754 100644 --- a/src/actor/session/Session.hpp +++ b/src/actor/session/Session.hpp @@ -21,7 +21,7 @@ namespace neb class Session: public Actor { public: - Session(uint32 ulSessionId, ev_tstamp dSessionTimeout = 60.0); + Session(uint64 ullSessionId, ev_tstamp dSessionTimeout = 60.0); Session(const std::string& strSessionId, ev_tstamp dSessionTimeout = 60.0); Session(const Session&) = delete; Session& operator=(const Session&) = delete; diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index fe184c85..40c031a8 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -645,7 +645,7 @@ bool Manager::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq } else { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); + LOG4_WARNING("no online node match node_type \"%s\"", strNodeType.c_str()); return(false); } } diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 51d5f30f..e1358d96 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -39,9 +39,9 @@ uint32 Worker::GetSequence() const return(m_pImpl->GetSequence()); } -std::shared_ptr Worker::GetSession(uint32 uiSessionId) +std::shared_ptr Worker::GetSession(uint64 ullSessionId) { - return(m_pImpl->GetSession(uiSessionId)); + return(m_pImpl->GetSession(ullSessionId)); } std::shared_ptr Worker::GetSession(const std::string& strSessionId) diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 418df2c8..cc06e0e7 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -48,7 +48,7 @@ class Worker: public Labor std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args); virtual uint32 GetSequence() const; - virtual std::shared_ptr GetSession(uint32 uiSessionId); + virtual std::shared_ptr GetSession(uint64 ullSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); virtual std::shared_ptr GetModel(const std::string& strModelName); diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index 105582da..a89caa46 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -2029,7 +2029,7 @@ void WorkerImpl::BootLoadCmd(CJsonObject& oCmdConf) void WorkerImpl::DynamicLoad(CJsonObject& oDynamicLoadingConf) { LOG4_TRACE(" "); - int iVersion = 0; + std::string strVersion = "1.0"; bool bIsload = false; std::string strSoPath; std::unordered_map::iterator so_iter; @@ -2039,42 +2039,52 @@ void WorkerImpl::DynamicLoad(CJsonObject& oDynamicLoadingConf) oDynamicLoadingConf[i].Get("load", bIsload); if (bIsload) { - if (oDynamicLoadingConf[i].Get("so_path", strSoPath) && oDynamicLoadingConf[i].Get("version", iVersion)) + if (oDynamicLoadingConf[i].Get("so_path", strSoPath) && oDynamicLoadingConf[i].Get("version", strVersion)) { so_iter = m_mapLoadedSo.find(strSoPath); if (so_iter == m_mapLoadedSo.end()) { - std::string strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); + std::string strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") + + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); if (0 != access(strSoFile.c_str(), F_OK)) { - LOG4_WARNING("%s not exist!", strSoFile.c_str()); - continue; + strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); + if (0 != access(strSoFile.c_str(), F_OK)) + { + LOG4_WARNING("%s not exist!", strSoFile.c_str()); + continue; + } } - pSo = LoadSo(strSoFile, iVersion); + pSo = LoadSo(strSoFile, strVersion); if (pSo != nullptr) { - LOG4_INFO("succeed in loading %s", strSoPath.c_str()); + LOG4_INFO("succeed in loading %s", strSoFile.c_str()); m_mapLoadedSo.insert(std::make_pair(strSoPath, pSo)); LoadDynamicSymbol(oDynamicLoadingConf[i]); } } else { - if (iVersion != so_iter->second->iVersion) // 版本升级,先卸载再加载 + if (strVersion != so_iter->second->strVersion) // 版本升级,先卸载再加载 { - std::string strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); + std::string strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") + + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); if (0 != access(strSoFile.c_str(), F_OK)) { - LOG4_WARNING("%s not exist!", strSoFile.c_str()); - continue; + strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); + if (0 != access(strSoFile.c_str(), F_OK)) + { + LOG4_WARNING("%s not exist!", strSoFile.c_str()); + continue; + } } UnloadDynamicSymbol(oDynamicLoadingConf[i]); dlclose(so_iter->second->pSoHandle); delete so_iter->second; - pSo = LoadSo(strSoFile, iVersion); + pSo = LoadSo(strSoFile, strVersion); if (pSo != nullptr) { - LOG4_INFO("succeed in loading %s", strSoPath.c_str()); + LOG4_INFO("succeed in loading %s", strSoFile.c_str()); so_iter->second = pSo; LoadDynamicSymbol(oDynamicLoadingConf[i]); } @@ -2097,14 +2107,15 @@ void WorkerImpl::DynamicLoad(CJsonObject& oDynamicLoadingConf) dlclose(so_iter->second->pSoHandle); delete so_iter->second; m_mapLoadedSo.erase(so_iter); - LOG4_INFO("unload %s", strSoPath.c_str()); + LOG4_INFO("unload %s.%s", strSoPath.c_str(), + oDynamicLoadingConf[i]("version").c_str()); } } } } } -WorkerImpl::tagSo* WorkerImpl::LoadSo(const std::string& strSoPath, int iVersion) +WorkerImpl::tagSo* WorkerImpl::LoadSo(const std::string& strSoPath, std::string strVersion) { LOG4_TRACE(" "); tagSo* pSo = new tagSo(); @@ -2126,7 +2137,7 @@ WorkerImpl::tagSo* WorkerImpl::LoadSo(const std::string& strSoPath, int iVersion return(nullptr); } pSo->pSoHandle = pHandle; - pSo->iVersion = iVersion; + pSo->strVersion = strVersion; return(pSo); } @@ -2385,10 +2396,10 @@ bool WorkerImpl::ReloadCmdConf() return(true); } -std::shared_ptr WorkerImpl::GetSession(uint32 uiSessionId) +std::shared_ptr WorkerImpl::GetSession(uint64 ullSessionId) { std::ostringstream oss; - oss << uiSessionId; + oss << ullSessionId; auto id_iter = m_mapCallbackSession.find(oss.str()); if (id_iter == m_mapCallbackSession.end()) { diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp index 48c92ead..dc69a986 100644 --- a/src/labor/WorkerImpl.hpp +++ b/src/labor/WorkerImpl.hpp @@ -122,7 +122,7 @@ class WorkerImpl struct tagSo { void* pSoHandle; - int iVersion; + std::string strVersion; std::vector vecCmd; std::vector vecPath; tagSo(){}; @@ -217,6 +217,8 @@ class WorkerImpl template std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args); +protected: + template Actor* NewActor(const std::string& strActorName, Targs... args); std::shared_ptr InitializeSharedActor(Actor* pCreator, std::shared_ptr pSharedActor, const std::string& strActorName); bool TransformToSharedCmd(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor); @@ -256,7 +258,7 @@ class WorkerImpl public: // about session - virtual std::shared_ptr GetSession(uint32 uiSessionId); + virtual std::shared_ptr GetSession(uint64 ullSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); // about step @@ -337,7 +339,7 @@ class WorkerImpl void BootLoadCmd(CJsonObject& oCmdConf); void DynamicLoad(CJsonObject& oSoConf); - tagSo* LoadSo(const std::string& strSoPath, int iVersion); + tagSo* LoadSo(const std::string& strSoPath, std::string strVersion); void LoadDynamicSymbol(CJsonObject& oOneSoConf); void UnloadDynamicSymbol(CJsonObject& oOneSoConf); diff --git a/src/labor/WorkerImpl.inl b/src/labor/WorkerImpl.inl index 655ed2ed..836599fd 100644 --- a/src/labor/WorkerImpl.inl +++ b/src/labor/WorkerImpl.inl @@ -31,8 +31,17 @@ std::shared_ptr WorkerImpl::MakeSharedActor(Actor* pCreator, const std::s Actor* pActor = ActorFactory::Instance()->Create(strActorName, std::forward(args)...); if (nullptr == pActor) { - LOG4_ERROR("failed to make shared actor \"%s\"", strActorName.c_str()); - return(nullptr); + /** + * @brief 为兼容&&参数推导差异导致ActorFactory未Regist进而导致 + * ActorFactory::Instance()->Create()调用不到对应的创建函数而增加。 + * NewActor()参数将按值传递,如果调用到NewActor()才new成功,代价会相对大些。 + */ + pActor = NewActor(strActorName, std::forward(args)...); + if (nullptr == pActor) + { + LOG4_ERROR("failed to make shared actor \"%s\"", strActorName.c_str()); + return(nullptr); + } } std::shared_ptr pSharedActor; pSharedActor.reset(pActor); @@ -82,6 +91,13 @@ std::shared_ptr WorkerImpl::MakeSharedChain(Actor* pCreator, const std::s return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strChainName, std::forward(args)...))); } +template +Actor* WorkerImpl::NewActor(const std::string& strActorName, Targs... args) +{ + LOG4_TRACE("%s() called by MakeSharedActor().", __FUNCTION__); + return(ActorFactory::Instance()->Create(strActorName, std::forward(args)...)); +} + } #endif /* LABOR_WORKERIMPL_INL_ */ From 0b17308daa97a438e993d64d508a761b6a38ae23 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 15 Sep 2019 16:11:13 +0800 Subject: [PATCH 057/176] MakeSharedActor() optimization and dynamic load optimization test passing. --- README_cn.md | 6 +++--- docs/cn/actor.md | 2 +- src/codec/CodecPrivate.cpp | 2 +- src/codec/CodecProto.cpp | 2 +- src/codec/CodecWsExtentJson.cpp | 2 +- src/codec/CodecWsExtentPb.cpp | 2 +- src/labor/WorkerImpl.cpp | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README_cn.md b/README_cn.md index 94f88e98..2a27eb74 100644 --- a/README_cn.md +++ b/README_cn.md @@ -33,7 +33,7 @@   把Nebula用于学习交流也不错,Bwar欢迎更多有兴趣的开发者加入到Nebula这个项目中来。Nebula是个proactor模式开发框架,不错,是proactor不是reactor(框架层实现的proactor而不是操作系统支持),应用于IO密集型的项目可以达到非常好的性能。对用惯了RPC框架的人而言,Nebula跟RPC很不一样,不过使用起来并不会比RPC复杂多少,但比RPC性能要高很多;对了解异步回调编程方式的开发者,Nebula是个非常简单的框架,比写常见的异步回调写法要简单多了。Nebula网络框架的技术分享和交流见[C++网络框架Nebula](https://zhuanlan.zhihu.com/c_216558336) -  Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(也是Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++14)是Starship(C++03)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的第一个应用Nebio(埋点数据采集和实时分析项目)在2018年7月底上线并稳定运行,Bwar还准备开发基于Nebula的IM应用Nebim。 +  Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(也是Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++14)是Starship(C++03)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的应用Nebio(埋点数据采集和实时分析项目)在2018年7月底上线并稳定运行。 ## 功能 @@ -71,11 +71,11 @@ * [Actor组件概述](docs/cn/actor_overview.md) * [Cmd和Module组件](docs/cn/cmd_and_module.md) * [Step组件](docs/cn/step.md) - * Session组件 + * [Session组件](docs/cn/session.md) * Context组件 * Model组件 * Chain组件 - * Actor类 + * [Actor类](docs/cn/actor.md) ![nebula_cluster](https://github.com/Bwar/NebulaBootstrap/blob/master/image/nebula_cluster.png?raw=true) diff --git a/docs/cn/actor.md b/docs/cn/actor.md index 16d860ee..bbe4bdec 100644 --- a/docs/cn/actor.md +++ b/docs/cn/actor.md @@ -10,7 +10,7 @@   Actor类的ACTOR_TYPE枚举定义了Nebula框架支持的所有Actor组件类型,Cmd、Module、Step、Session等组件会自动填充ACTOR_TYPE,从这些组件派生的业务类无需关注ACTOR_TYPE,ACTOR_TYPE主要是Nebula框架管理组件用的。 -  Logger()模板函数是写日志用的,但并不需要直接调用Logger()函数写日志,调用LOG4_INFO()/LOG4_ERROR()等相关宏写日志即可,实际上这些宏就是调用Logger()函数写的日志,宏会自动填充一些文件名、函数名之类参数,比直接调用Logger()函数简单。Logger()函数的实现更是无须关注,小小提示一下,Nebula提供不需要开发者做任何额外工作的日志跟踪服务,其原理就在Logger()函数的实现里,这也是令人惊喜的有用且强大的功能之一。 +  Logger()模板函数是写日志用的,但并不需要直接调用Logger()函数写日志,调用LOG4_INFO()/LOG4_ERROR()等相关宏写日志即可,实际上这些宏就是调用Logger()函数写的日志,宏会自动填充一些文件名、函数名之类参数,比直接调用Logger()函数简单。Logger()函数的实现更是无须关注,提示一下,Nebula提供不需要开发者做任何额外工作的日志跟踪服务,其原理就在Logger()函数的实现里,这也是令人惊喜的有用且强大的功能之一。   MakeSharedStep()/MakeSharedSession()等相关模板函数用于通过类名反射动态创建具体业务逻辑类,动态创建具体Actor实例的同时会初始化并向框架进行注册,还有一些上下文信息的传递也会在动态创建的过程中完成传递。GetActorName()、GetTraceId()、GetSequence()、GetContext()等用起来觉得很方便很神奇,这些都是MakeShared相关模板函数默默地做了许多复杂的事情,把简单都留给用户。所以,切记不要自己去调用new来创建Actor的各种派生类,都调用MakeSharedStep/MakeSharedSession()等相关模板函数。至于非Actor组件派生类和用户自己的数据结构,MakeShared相关模板函数是不支持的,也没必要支持。 diff --git a/src/codec/CodecPrivate.cpp b/src/codec/CodecPrivate.cpp index d13efb74..747a58c0 100644 --- a/src/codec/CodecPrivate.cpp +++ b/src/codec/CodecPrivate.cpp @@ -298,7 +298,7 @@ E_CODEC_STATUS CodecPrivate::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& } else { - LOG4_ERROR("cmd[%u], seq[%lu] oMsgBody.ParseFromArray() error!", oMsgHead.cmd(), oMsgHead.seq()); + LOG4_ERROR("cmd[%u], seq[%u] oMsgBody.ParseFromArray() error!", oMsgHead.cmd(), oMsgHead.seq()); return(CODEC_STATUS_ERR); } } diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp index 9f6c30b8..e809c3ef 100644 --- a/src/codec/CodecProto.cpp +++ b/src/codec/CodecProto.cpp @@ -86,7 +86,7 @@ E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oM } else { - LOG4_ERROR("cmd[%u], seq[%lu] oMsgBody.ParseFromArray() error!", oMsgHead.cmd(), oMsgHead.seq()); + LOG4_ERROR("cmd[%u], seq[%u] oMsgBody.ParseFromArray() error!", oMsgHead.cmd(), oMsgHead.seq()); return(CODEC_STATUS_ERR); } } diff --git a/src/codec/CodecWsExtentJson.cpp b/src/codec/CodecWsExtentJson.cpp index 050034e9..6729908b 100644 --- a/src/codec/CodecWsExtentJson.cpp +++ b/src/codec/CodecWsExtentJson.cpp @@ -413,7 +413,7 @@ E_CODEC_STATUS CodecWsExtentJson::Decode(CBuffer* pBuff, } else { - LOG4_ERROR("cmd[%u], seq[%lu] json string to MsgBody error!", + LOG4_ERROR("cmd[%u], seq[%u] json string to MsgBody error!", oMsgHead.cmd(), oMsgHead.seq()); return (CODEC_STATUS_ERR); } diff --git a/src/codec/CodecWsExtentPb.cpp b/src/codec/CodecWsExtentPb.cpp index 580b0645..31d129b5 100644 --- a/src/codec/CodecWsExtentPb.cpp +++ b/src/codec/CodecWsExtentPb.cpp @@ -404,7 +404,7 @@ E_CODEC_STATUS CodecWsExtentPb::Decode(CBuffer* pBuff, } else { - LOG4_ERROR("cmd[%u], seq[%lu] oMsgBody.ParseFromArray() error!", + LOG4_ERROR("cmd[%u], seq[%u] oMsgBody.ParseFromArray() error!", oMsgHead.cmd(), oMsgHead.seq()); return (CODEC_STATUS_ERR); } diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp index a89caa46..308d7490 100644 --- a/src/labor/WorkerImpl.cpp +++ b/src/labor/WorkerImpl.cpp @@ -538,7 +538,7 @@ bool WorkerImpl::OnStepTimeout(std::shared_ptr pStep) } else // 步骤已超时 { - LOG4_TRACE("seq %lu: active_time %lf, now_time %lf, lifetime %lf", + LOG4_TRACE("seq %u: active_time %lf, now_time %lf, lifetime %lf", pStep->GetSequence(), pStep->GetActiveTime(), ev_now(m_loop), pStep->GetTimeout()); E_CMD_STATUS eResult = pStep->Timeout(); if (CMD_STATUS_RUNNING == eResult) @@ -2646,7 +2646,7 @@ void WorkerImpl::ChannelNotice(std::shared_ptr pChannel, const st bool WorkerImpl::Handle(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) { - LOG4_DEBUG("cmd %u, seq %lu", oMsgHead.cmd(), oMsgHead.seq()); + LOG4_DEBUG("cmd %u, seq %u", oMsgHead.cmd(), oMsgHead.seq()); if (gc_uiCmdReq & oMsgHead.cmd()) // 新请求 { MsgHead oOutMsgHead; From 988c08a13f6b73cffaf6a5831532394471bb31cb Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 22 Sep 2019 20:26:59 +0800 Subject: [PATCH 058/176] upadate CJsonObject --- src/util/json/CJsonObject.cpp | 6434 ++++++++++++++++++--------------- src/util/json/CJsonObject.hpp | 308 +- src/util/json/cJSON.c | 2184 ++++++----- 3 files changed, 4712 insertions(+), 4214 deletions(-) diff --git a/src/util/json/CJsonObject.cpp b/src/util/json/CJsonObject.cpp index 9ce94af4..1a76f468 100644 --- a/src/util/json/CJsonObject.cpp +++ b/src/util/json/CJsonObject.cpp @@ -1,2970 +1,3464 @@ -/******************************************************************************* - * Project: neb - * @file CJsonObject.cpp - * @brief - * @author bwarliao - * @date: 2014-7-16 - * @note - * Modify history: - ******************************************************************************/ - -#include "CJsonObject.hpp" - -namespace neb -{ - -CJsonObject::CJsonObject() - : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) -{ - // m_pJsonData = cJSON_CreateObject(); -} - -CJsonObject::CJsonObject(const std::string& strJson) - : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) -{ - Parse(strJson); -} - -CJsonObject::CJsonObject(const CJsonObject* pJsonObject) - : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) -{ - if (pJsonObject) - { - Parse(pJsonObject->ToString()); - } -} - -CJsonObject::CJsonObject(const CJsonObject& oJsonObject) - : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) -{ - Parse(oJsonObject.ToString()); -} - -CJsonObject::~CJsonObject() -{ - Clear(); -} - -CJsonObject& CJsonObject::operator=(const CJsonObject& oJsonObject) -{ - Parse(oJsonObject.ToString().c_str()); - return(*this); -} - -bool CJsonObject::operator==(const CJsonObject& oJsonObject) const -{ - return(this->ToString() == oJsonObject.ToString()); -} - -bool CJsonObject::AddEmptySubObject(const std::string& strKey) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateObject(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateObject(); - if (pJsonStruct == NULL) - { - m_strErrMsg = std::string("create sub empty object error!"); - return(false); - } - cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::AddEmptySubArray(const std::string& strKey) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateObject(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateArray(); - if (pJsonStruct == NULL) - { - m_strErrMsg = std::string("create sub empty array error!"); - return(false); - } - cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::GetKey(std::string& strKey) -{ - if (IsArray()) - { - return(false); - } - if (m_listKeys.size() == 0) - { - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - return(false); - } - - cJSON *c = pFocusData->child; - while (c) - { - m_listKeys.push_back(c->string); - c = c->next; - } - m_itKey = m_listKeys.begin(); - } - - if (m_itKey == m_listKeys.end()) - { - strKey = ""; - m_itKey = m_listKeys.begin(); - return(false); - } - else - { - strKey = *m_itKey; - ++m_itKey; - return(true); - } -} - -CJsonObject& CJsonObject::operator[](const std::string& strKey) -{ - std::map::iterator iter; - iter = m_mapJsonObjectRef.find(strKey); - if (iter == m_mapJsonObjectRef.end()) - { - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if (m_pExternJsonDataRef->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); - } - } - if (pJsonStruct == NULL) - { - CJsonObject* pJsonObject = new CJsonObject(); - m_mapJsonObjectRef.insert(std::pair(strKey, pJsonObject)); - return(*pJsonObject); - } - else - { - CJsonObject* pJsonObject = new CJsonObject(pJsonStruct); - m_mapJsonObjectRef.insert(std::pair(strKey, pJsonObject)); - return(*pJsonObject); - } - } - else - { - return(*(iter->second)); - } -} - -CJsonObject& CJsonObject::operator[](unsigned int uiWhich) -{ - std::map::iterator iter; - iter = m_mapJsonArrayRef.find(uiWhich); - if (iter == m_mapJsonArrayRef.end()) - { - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pJsonData, uiWhich); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if (m_pExternJsonDataRef->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, uiWhich); - } - } - if (pJsonStruct == NULL) - { - CJsonObject* pJsonObject = new CJsonObject(); - m_mapJsonArrayRef.insert(std::pair(uiWhich, pJsonObject)); - return(*pJsonObject); - } - else - { - CJsonObject* pJsonObject = new CJsonObject(pJsonStruct); - m_mapJsonArrayRef.insert(std::pair(uiWhich, pJsonObject)); - return(*pJsonObject); - } - } - else - { - return(*(iter->second)); - } -} - -std::string CJsonObject::operator()(const std::string& strKey) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); - } - } - if (pJsonStruct == NULL) - { - return(std::string("")); - } - if (pJsonStruct->type == cJSON_String) - { - return(pJsonStruct->valuestring); - } - else if (pJsonStruct->type == cJSON_Int) - { - char szNumber[128] = {0}; - if (pJsonStruct->sign == -1) - { - if ((int64)pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) - { - snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); - } - else - { - snprintf(szNumber, sizeof(szNumber), "%lld", (int64)pJsonStruct->valueint); - } - } - else - { - if (pJsonStruct->valueint <= (uint64)UINT_MAX) - { - snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); - } - else - { - snprintf(szNumber, sizeof(szNumber), "%llu", pJsonStruct->valueint); - } - } - return(std::string(szNumber)); - } - else if (pJsonStruct->type == cJSON_Double) - { - char szNumber[128] = {0}; - if (fabs(pJsonStruct->valuedouble) < 1.0e-6 || fabs(pJsonStruct->valuedouble) > 1.0e9) - { - snprintf(szNumber, sizeof(szNumber), "%e", pJsonStruct->valuedouble); - } - else - { - snprintf(szNumber, sizeof(szNumber), "%f", pJsonStruct->valuedouble); - } - } - else if (pJsonStruct->type == cJSON_False) - { - return(std::string("false")); - } - else if (pJsonStruct->type == cJSON_True) - { - return(std::string("true")); - } - return(std::string("")); -} - -std::string CJsonObject::operator()(unsigned int uiWhich) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pJsonData, uiWhich); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, uiWhich); - } - } - if (pJsonStruct == NULL) - { - return(std::string("")); - } - if (pJsonStruct->type == cJSON_String) - { - return(pJsonStruct->valuestring); - } - else if (pJsonStruct->type == cJSON_Int) - { - char szNumber[128] = {0}; - if (pJsonStruct->sign == -1) - { - if ((int64)pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) - { - snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); - } - else - { - snprintf(szNumber, sizeof(szNumber), "%lld", (int64)pJsonStruct->valueint); - } - } - else - { - if (pJsonStruct->valueint <= (uint64)UINT_MAX) - { - snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); - } - else - { - snprintf(szNumber, sizeof(szNumber), "%llu", pJsonStruct->valueint); - } - } - return(std::string(szNumber)); - } - else if (pJsonStruct->type == cJSON_Double) - { - char szNumber[128] = {0}; - if (fabs(pJsonStruct->valuedouble) < 1.0e-6 || fabs(pJsonStruct->valuedouble) > 1.0e9) - { - snprintf(szNumber, sizeof(szNumber), "%e", pJsonStruct->valuedouble); - } - else - { - snprintf(szNumber, sizeof(szNumber), "%f", pJsonStruct->valuedouble); - } - } - else if (pJsonStruct->type == cJSON_False) - { - return(std::string("false")); - } - else if (pJsonStruct->type == cJSON_True) - { - return(std::string("true")); - } - return(std::string("")); -} - -bool CJsonObject::Parse(const std::string& strJson) -{ - Clear(); - m_pJsonData = cJSON_Parse(strJson.c_str()); - if (m_pJsonData == NULL) - { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); - return(false); - } - return(true); -} - -void CJsonObject::Clear() -{ - m_pExternJsonDataRef = NULL; - if (m_pJsonData != NULL) - { - cJSON_Delete(m_pJsonData); - m_pJsonData = NULL; - } - for (std::map::iterator iter = m_mapJsonArrayRef.begin(); - iter != m_mapJsonArrayRef.end(); ++iter) - { - if (iter->second != NULL) - { - delete (iter->second); - iter->second = NULL; - } - } - m_mapJsonArrayRef.clear(); - for (std::map::iterator iter = m_mapJsonObjectRef.begin(); - iter != m_mapJsonObjectRef.end(); ++iter) - { - if (iter->second != NULL) - { - delete (iter->second); - iter->second = NULL; - } - } - m_mapJsonObjectRef.clear(); - m_listKeys.clear(); -} - -bool CJsonObject::IsEmpty() const -{ - if (m_pJsonData != NULL) - { - return(false); - } - else if (m_pExternJsonDataRef != NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::IsArray() const -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - - if (pFocusData == NULL) - { - return(false); - } - - if (pFocusData->type == cJSON_Array) - { - return(true); - } - else - { - return(false); - } -} - -std::string CJsonObject::ToString() const -{ - char* pJsonString = NULL; - std::string strJsonData = ""; - if (m_pJsonData != NULL) - { - pJsonString = cJSON_PrintUnformatted(m_pJsonData); - } - else if (m_pExternJsonDataRef != NULL) - { - pJsonString = cJSON_PrintUnformatted(m_pExternJsonDataRef); - } - if (pJsonString != NULL) - { - strJsonData = pJsonString; - free(pJsonString); - } - return(strJsonData); -} - -std::string CJsonObject::ToFormattedString() const -{ - char* pJsonString = NULL; - std::string strJsonData = ""; - if (m_pJsonData != NULL) - { - pJsonString = cJSON_Print(m_pJsonData); - } - else if (m_pExternJsonDataRef != NULL) - { - pJsonString = cJSON_Print(m_pExternJsonDataRef); - } - if (pJsonString != NULL) - { - strJsonData = pJsonString; - free(pJsonString); - } - return(strJsonData); -} - - -bool CJsonObject::Get(const std::string& strKey, CJsonObject& oJsonObject) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - char* pJsonString = cJSON_Print(pJsonStruct); - std::string strJsonData = pJsonString; - free(pJsonString); - if (oJsonObject.Parse(strJsonData)) - { - return(true); - } - else - { - return(false); - } -} - -bool CJsonObject::Get(const std::string& strKey, std::string& strValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_String) - { - return(false); - } - strValue = pJsonStruct->valuestring; - return(true); -} - -bool CJsonObject::Get(const std::string& strKey, int32& iValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Int) - { - return(false); - } - iValue = (int32)(pJsonStruct->valueint); - return(true); -} - -bool CJsonObject::Get(const std::string& strKey, uint32& uiValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Int) - { - return(false); - } - uiValue = (uint32)(pJsonStruct->valueint); - return(true); -} - -bool CJsonObject::Get(const std::string& strKey, int64& llValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Int) - { - return(false); - } - llValue = (int64)pJsonStruct->valueint; - return(true); -} - -bool CJsonObject::Get(const std::string& strKey, uint64& ullValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Int) - { - return(false); - } - ullValue = (uint64)pJsonStruct->valueint; - return(true); -} - -bool CJsonObject::Get(const std::string& strKey, bool& bValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type > cJSON_True) - { - return(false); - } - bValue = pJsonStruct->type; - return(true); -} - -bool CJsonObject::Get(const std::string& strKey, float& fValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Double) - { - return(false); - } - fValue = (float)(pJsonStruct->valuedouble); - return(true); -} - -bool CJsonObject::Get(const std::string& strKey, double& dValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Object) - { - pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Double) - { - return(false); - } - dValue = pJsonStruct->valuedouble; - return(true); -} - -bool CJsonObject::Add(const std::string& strKey, const CJsonObject& oJsonObject) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateObject(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); - if (pJsonStruct == NULL) - { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); - return(false); - } - cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - std::map::iterator iter = m_mapJsonObjectRef.find(strKey); - if (iter != m_mapJsonObjectRef.end()) - { - if (iter->second != NULL) - { - delete (iter->second); - iter->second = NULL; - } - m_mapJsonObjectRef.erase(iter); - } - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::Add(const std::string& strKey, const std::string& strValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateObject(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::Add(const std::string& strKey, int32 iValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateObject(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::Add(const std::string& strKey, uint32 uiValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateObject(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::Add(const std::string& strKey, int64 llValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateObject(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::Add(const std::string& strKey, uint64 ullValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateObject(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt(ullValue, 1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::Add(const std::string& strKey, bool bValue, bool bValueAgain) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateObject(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateBool(bValue); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::Add(const std::string& strKey, float fValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateObject(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::Add(const std::string& strKey, double dValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateObject(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::Delete(const std::string& strKey) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON_DeleteItemFromObject(pFocusData, strKey.c_str()); - std::map::iterator iter = m_mapJsonObjectRef.find(strKey); - if (iter != m_mapJsonObjectRef.end()) - { - if (iter->second != NULL) - { - delete (iter->second); - iter->second = NULL; - } - m_mapJsonObjectRef.erase(iter); - } - m_listKeys.clear(); - return(true); -} - -bool CJsonObject::Replace(const std::string& strKey, const CJsonObject& oJsonObject) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); - if (pJsonStruct == NULL) - { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); - return(false); - } - cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - std::map::iterator iter = m_mapJsonObjectRef.find(strKey); - if (iter != m_mapJsonObjectRef.end()) - { - if (iter->second != NULL) - { - delete (iter->second); - iter->second = NULL; - } - m_mapJsonObjectRef.erase(iter); - } - return(true); -} - -bool CJsonObject::Replace(const std::string& strKey, const std::string& strValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(const std::string& strKey, int32 iValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(const std::string& strKey, uint32 uiValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(const std::string& strKey, int64 llValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(const std::string& strKey, uint64 ullValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, 1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(const std::string& strKey, bool bValue, bool bValueAgain) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateBool(bValue); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(const std::string& strKey, float fValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(const std::string& strKey, double dValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Object) - { - m_strErrMsg = "not a json object! json array?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); - if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) - { - return(false); - } - return(true); -} - -int CJsonObject::GetArraySize() -{ - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - return(cJSON_GetArraySize(m_pJsonData)); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Array) - { - return(cJSON_GetArraySize(m_pExternJsonDataRef)); - } - } - return(0); -} - -bool CJsonObject::Get(int iWhich, CJsonObject& oJsonObject) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - char* pJsonString = cJSON_Print(pJsonStruct); - std::string strJsonData = pJsonString; - free(pJsonString); - if (oJsonObject.Parse(strJsonData)) - { - return(true); - } - else - { - return(false); - } -} - -bool CJsonObject::Get(int iWhich, std::string& strValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_String) - { - return(false); - } - strValue = pJsonStruct->valuestring; - return(true); -} - -bool CJsonObject::Get(int iWhich, int32& iValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Int) - { - return(false); - } - iValue = (int32)(pJsonStruct->valueint); - return(true); -} - -bool CJsonObject::Get(int iWhich, uint32& uiValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Int) - { - return(false); - } - uiValue = (uint32)(pJsonStruct->valueint); - return(true); -} - -bool CJsonObject::Get(int iWhich, int64& llValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Int) - { - return(false); - } - llValue = (int64)pJsonStruct->valueint; - return(true); -} - -bool CJsonObject::Get(int iWhich, uint64& ullValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Int) - { - return(false); - } - ullValue = (uint64)pJsonStruct->valueint; - return(true); -} - -bool CJsonObject::Get(int iWhich, bool& bValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type > cJSON_True) - { - return(false); - } - bValue = pJsonStruct->type; - return(true); -} - -bool CJsonObject::Get(int iWhich, float& fValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Double) - { - return(false); - } - fValue = (float)(pJsonStruct->valuedouble); - return(true); -} - -bool CJsonObject::Get(int iWhich, double& dValue) const -{ - cJSON* pJsonStruct = NULL; - if (m_pJsonData != NULL) - { - if (m_pJsonData->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); - } - } - else if (m_pExternJsonDataRef != NULL) - { - if(m_pExternJsonDataRef->type == cJSON_Array) - { - pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); - } - } - if (pJsonStruct == NULL) - { - return(false); - } - if (pJsonStruct->type != cJSON_Double) - { - return(false); - } - dValue = pJsonStruct->valuedouble; - return(true); -} - -bool CJsonObject::Add(const CJsonObject& oJsonObject) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); - if (pJsonStruct == NULL) - { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArray(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - unsigned int uiLastIndex = (unsigned int)cJSON_GetArraySize(pFocusData) - 1; - for (std::map::iterator iter = m_mapJsonArrayRef.begin(); - iter != m_mapJsonArrayRef.end(); ) - { - if (iter->first >= uiLastIndex) - { - if (iter->second != NULL) - { - delete (iter->second); - iter->second = NULL; - } - m_mapJsonArrayRef.erase(iter++); - } - else - { - iter++; - } - } - return(true); -} - -bool CJsonObject::Add(const std::string& strValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArray(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::Add(int32 iValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArray(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::Add(uint32 uiValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArray(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::Add(int64 llValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArray(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::Add(uint64 ullValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, 1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArray(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::Add(int iAnywhere, bool bValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateBool(bValue); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArray(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::Add(float fValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArray(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::Add(double dValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArray(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::AddAsFirst(const CJsonObject& oJsonObject) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); - if (pJsonStruct == NULL) - { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - for (std::map::iterator iter = m_mapJsonArrayRef.begin(); - iter != m_mapJsonArrayRef.end(); ) - { - if (iter->second != NULL) - { - delete (iter->second); - iter->second = NULL; - } - m_mapJsonArrayRef.erase(iter++); - } - return(true); -} - -bool CJsonObject::AddAsFirst(const std::string& strValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::AddAsFirst(int32 iValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::AddAsFirst(uint32 uiValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::AddAsFirst(int64 llValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::AddAsFirst(uint64 ullValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::AddAsFirst(int iAnywhere, bool bValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateBool(bValue); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::AddAsFirst(float fValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::AddAsFirst(double dValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData != NULL) - { - pFocusData = m_pJsonData; - } - else if (m_pExternJsonDataRef != NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - m_pJsonData = cJSON_CreateArray(); - pFocusData = m_pJsonData; - } - - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); - cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); - int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); - if (iArraySizeAfterAdd == iArraySizeBeforeAdd) - { - return(false); - } - return(true); -} - -bool CJsonObject::Delete(int iWhich) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON_DeleteItemFromArray(pFocusData, iWhich); - for (std::map::iterator iter = m_mapJsonArrayRef.begin(); - iter != m_mapJsonArrayRef.end(); ) - { - if (iter->first >= (unsigned int)iWhich) - { - if (iter->second != NULL) - { - delete (iter->second); - iter->second = NULL; - } - m_mapJsonArrayRef.erase(iter++); - } - else - { - iter++; - } - } - return(true); -} - -bool CJsonObject::Replace(int iWhich, const CJsonObject& oJsonObject) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); - if (pJsonStruct == NULL) - { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); - return(false); - } - cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); - if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) - { - return(false); - } - std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); - if (iter != m_mapJsonArrayRef.end()) - { - if (iter->second != NULL) - { - delete (iter->second); - iter->second = NULL; - } - m_mapJsonArrayRef.erase(iter); - } - return(true); -} - -bool CJsonObject::Replace(int iWhich, const std::string& strValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); - if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(int iWhich, int32 iValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); - if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(int iWhich, uint32 uiValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); - if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(int iWhich, int64 llValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)((uint64)llValue), -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); - if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(int iWhich, uint64 ullValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, 1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); - if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(int iWhich, bool bValue, bool bValueAgain) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateBool(bValue); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); - if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(int iWhich, float fValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); - if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) - { - return(false); - } - return(true); -} - -bool CJsonObject::Replace(int iWhich, double dValue) -{ - cJSON* pFocusData = NULL; - if (m_pJsonData == NULL) - { - pFocusData = m_pExternJsonDataRef; - } - else - { - pFocusData = m_pJsonData; - } - if (pFocusData == NULL) - { - m_strErrMsg = "json data is null!"; - return(false); - } - if (pFocusData->type != cJSON_Array) - { - m_strErrMsg = "not a json array! json object?"; - return(false); - } - cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); - if (pJsonStruct == NULL) - { - return(false); - } - cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); - if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) - { - return(false); - } - return(true); -} - -CJsonObject::CJsonObject(cJSON* pJsonData) - : m_pJsonData(NULL), m_pExternJsonDataRef(pJsonData) -{ -} - -} - - +/******************************************************************************* + * Project: neb + * @file CJsonObject.cpp + * @brief + * @author bwarliao + * @date: 2014-7-16 + * @note + * Modify history: + ******************************************************************************/ + +#include "CJsonObject.hpp" + +namespace neb +{ + +CJsonObject::CJsonObject() + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) +{ + // m_pJsonData = cJSON_CreateObject(); +} + +CJsonObject::CJsonObject(const std::string& strJson) + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) +{ + Parse(strJson); +} + +CJsonObject::CJsonObject(const CJsonObject* pJsonObject) + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) +{ + if (pJsonObject) + { + Parse(pJsonObject->ToString()); + } +} + +CJsonObject::CJsonObject(const CJsonObject& oJsonObject) + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) +{ + Parse(oJsonObject.ToString()); +} + +CJsonObject::~CJsonObject() +{ + Clear(); +} + +CJsonObject& CJsonObject::operator=(const CJsonObject& oJsonObject) +{ + Parse(oJsonObject.ToString().c_str()); + return(*this); +} + +bool CJsonObject::operator==(const CJsonObject& oJsonObject) const +{ + return(this->ToString() == oJsonObject.ToString()); +} + +bool CJsonObject::AddEmptySubObject(const std::string& strKey) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateObject(); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("create sub empty object error!"); + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::AddEmptySubArray(const std::string& strKey) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateArray(); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("create sub empty array error!"); + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::GetKey(std::string& strKey) +{ + if (IsArray()) + { + return(false); + } + if (m_listKeys.size() == 0) + { + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + return(false); + } + + cJSON *c = pFocusData->child; + while (c) + { + m_listKeys.push_back(c->string); + c = c->next; + } + m_itKey = m_listKeys.begin(); + } + + if (m_itKey == m_listKeys.end()) + { + strKey = ""; + m_itKey = m_listKeys.begin(); + return(false); + } + else + { + strKey = *m_itKey; + ++m_itKey; + return(true); + } +} + +void CJsonObject::ResetTraversing() +{ + m_itKey = m_listKeys.begin(); +} + +CJsonObject& CJsonObject::operator[](const std::string& strKey) +{ + std::map::iterator iter; + iter = m_mapJsonObjectRef.find(strKey); + if (iter == m_mapJsonObjectRef.end()) + { + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if (m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + CJsonObject* pJsonObject = new CJsonObject(); + m_mapJsonObjectRef.insert(std::pair(strKey, pJsonObject)); + return(*pJsonObject); + } + else + { + CJsonObject* pJsonObject = new CJsonObject(pJsonStruct); + m_mapJsonObjectRef.insert(std::pair(strKey, pJsonObject)); + return(*pJsonObject); + } + } + else + { + return(*(iter->second)); + } +} + +CJsonObject& CJsonObject::operator[](unsigned int uiWhich) +{ + std::map::iterator iter; + iter = m_mapJsonArrayRef.find(uiWhich); + if (iter == m_mapJsonArrayRef.end()) + { + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, uiWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if (m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, uiWhich); + } + } + if (pJsonStruct == NULL) + { + CJsonObject* pJsonObject = new CJsonObject(); + m_mapJsonArrayRef.insert(std::pair(uiWhich, pJsonObject)); + return(*pJsonObject); + } + else + { + CJsonObject* pJsonObject = new CJsonObject(pJsonStruct); + m_mapJsonArrayRef.insert(std::pair(uiWhich, pJsonObject)); + return(*pJsonObject); + } + } + else + { + return(*(iter->second)); + } +} + +std::string CJsonObject::operator()(const std::string& strKey) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(std::string("")); + } + if (pJsonStruct->type == cJSON_String) + { + return(pJsonStruct->valuestring); + } + else if (pJsonStruct->type == cJSON_Int) + { + char szNumber[128] = {0}; + if (pJsonStruct->sign == -1) + { + if ((int64)pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) + { + snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%lld", (int64)pJsonStruct->valueint); + } + } + else + { + if (pJsonStruct->valueint <= (uint64)UINT_MAX) + { + snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%llu", pJsonStruct->valueint); + } + } + return(std::string(szNumber)); + } + else if (pJsonStruct->type == cJSON_Double) + { + char szNumber[128] = {0}; + if (fabs(pJsonStruct->valuedouble) < 1.0e-6 || fabs(pJsonStruct->valuedouble) > 1.0e9) + { + snprintf(szNumber, sizeof(szNumber), "%e", pJsonStruct->valuedouble); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%f", pJsonStruct->valuedouble); + } + } + else if (pJsonStruct->type == cJSON_False) + { + return(std::string("false")); + } + else if (pJsonStruct->type == cJSON_True) + { + return(std::string("true")); + } + return(std::string("")); +} + +std::string CJsonObject::operator()(unsigned int uiWhich) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, uiWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, uiWhich); + } + } + if (pJsonStruct == NULL) + { + return(std::string("")); + } + if (pJsonStruct->type == cJSON_String) + { + return(pJsonStruct->valuestring); + } + else if (pJsonStruct->type == cJSON_Int) + { + char szNumber[128] = {0}; + if (pJsonStruct->sign == -1) + { + if ((int64)pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) + { + snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%lld", (int64)pJsonStruct->valueint); + } + } + else + { + if (pJsonStruct->valueint <= (uint64)UINT_MAX) + { + snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%llu", pJsonStruct->valueint); + } + } + return(std::string(szNumber)); + } + else if (pJsonStruct->type == cJSON_Double) + { + char szNumber[128] = {0}; + if (fabs(pJsonStruct->valuedouble) < 1.0e-6 || fabs(pJsonStruct->valuedouble) > 1.0e9) + { + snprintf(szNumber, sizeof(szNumber), "%e", pJsonStruct->valuedouble); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%f", pJsonStruct->valuedouble); + } + } + else if (pJsonStruct->type == cJSON_False) + { + return(std::string("false")); + } + else if (pJsonStruct->type == cJSON_True) + { + return(std::string("true")); + } + return(std::string("")); +} + +bool CJsonObject::Parse(const std::string& strJson) +{ + Clear(); + m_pJsonData = cJSON_Parse(strJson.c_str()); + if (m_pJsonData == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + return(true); +} + +void CJsonObject::Clear() +{ + m_pExternJsonDataRef = NULL; + if (m_pJsonData != NULL) + { + cJSON_Delete(m_pJsonData); + m_pJsonData = NULL; + } + for (std::map::iterator iter = m_mapJsonArrayRef.begin(); + iter != m_mapJsonArrayRef.end(); ++iter) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + } + m_mapJsonArrayRef.clear(); + for (std::map::iterator iter = m_mapJsonObjectRef.begin(); + iter != m_mapJsonObjectRef.end(); ++iter) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + } + m_mapJsonObjectRef.clear(); + m_listKeys.clear(); +} + +bool CJsonObject::IsEmpty() const +{ + if (m_pJsonData != NULL) + { + return(false); + } + else if (m_pExternJsonDataRef != NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::IsArray() const +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + + if (pFocusData == NULL) + { + return(false); + } + + if (pFocusData->type == cJSON_Array) + { + return(true); + } + else + { + return(false); + } +} + +std::string CJsonObject::ToString() const +{ + char* pJsonString = NULL; + std::string strJsonData = ""; + if (m_pJsonData != NULL) + { + pJsonString = cJSON_PrintUnformatted(m_pJsonData); + } + else if (m_pExternJsonDataRef != NULL) + { + pJsonString = cJSON_PrintUnformatted(m_pExternJsonDataRef); + } + if (pJsonString != NULL) + { + strJsonData = pJsonString; + free(pJsonString); + } + return(strJsonData); +} + +std::string CJsonObject::ToFormattedString() const +{ + char* pJsonString = NULL; + std::string strJsonData = ""; + if (m_pJsonData != NULL) + { + pJsonString = cJSON_Print(m_pJsonData); + } + else if (m_pExternJsonDataRef != NULL) + { + pJsonString = cJSON_Print(m_pExternJsonDataRef); + } + if (pJsonString != NULL) + { + strJsonData = pJsonString; + free(pJsonString); + } + return(strJsonData); +} + + +bool CJsonObject::Get(const std::string& strKey, CJsonObject& oJsonObject) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + char* pJsonString = cJSON_Print(pJsonStruct); + std::string strJsonData = pJsonString; + free(pJsonString); + if (oJsonObject.Parse(strJsonData)) + { + return(true); + } + else + { + return(false); + } +} + +bool CJsonObject::Get(const std::string& strKey, std::string& strValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type != cJSON_String) + { + return(false); + } + strValue = pJsonStruct->valuestring; + return(true); +} + +bool CJsonObject::Get(const std::string& strKey, int32& iValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + iValue = (int32)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + iValue = (int32)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(const std::string& strKey, uint32& uiValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + uiValue = (uint32)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + uiValue = (uint32)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(const std::string& strKey, int64& llValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + llValue = (int64)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + llValue = (int64)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(const std::string& strKey, uint64& ullValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + ullValue = (uint64)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + ullValue = (uint64)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(const std::string& strKey, bool& bValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type > cJSON_True) + { + return(false); + } + bValue = pJsonStruct->type; + return(true); +} + +bool CJsonObject::Get(const std::string& strKey, float& fValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Double) + { + fValue = (float)(pJsonStruct->valuedouble); + return(true); + } + else if (pJsonStruct->type == cJSON_Int) + { + fValue = (float)(pJsonStruct->valueint); + return(true); + } + return(false); +} + +bool CJsonObject::Get(const std::string& strKey, double& dValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Double) + { + dValue = pJsonStruct->valuedouble; + return(true); + } + else if (pJsonStruct->type == cJSON_Int) + { + dValue = (double)(pJsonStruct->valueint); + return(true); + } + return(false); +} + +bool CJsonObject::IsNull(const std::string& strKey) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type != cJSON_NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, const CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, const std::string& strValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, int32 iValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, uint32 uiValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, int64 llValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, uint64 ullValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt(ullValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, bool bValue, bool bValueAgain) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateBool(bValue); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, float fValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, double dValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::AddNull(const std::string& strKey) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateNull(); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::Delete(const std::string& strKey) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON_DeleteItemFromObject(pFocusData, strKey.c_str()); + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + m_listKeys.clear(); + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, const CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, const std::string& strValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, int32 iValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, uint32 uiValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, int64 llValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, uint64 ullValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, bool bValue, bool bValueAgain) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateBool(bValue); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, float fValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, double dValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::ReplaceWithNull(const std::string& strKey) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateNull(); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +int CJsonObject::GetArraySize() +{ + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + return(cJSON_GetArraySize(m_pJsonData)); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + return(cJSON_GetArraySize(m_pExternJsonDataRef)); + } + } + return(0); +} + +bool CJsonObject::Get(int iWhich, CJsonObject& oJsonObject) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + char* pJsonString = cJSON_Print(pJsonStruct); + std::string strJsonData = pJsonString; + free(pJsonString); + if (oJsonObject.Parse(strJsonData)) + { + return(true); + } + else + { + return(false); + } +} + +bool CJsonObject::Get(int iWhich, std::string& strValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type != cJSON_String) + { + return(false); + } + strValue = pJsonStruct->valuestring; + return(true); +} + +bool CJsonObject::Get(int iWhich, int32& iValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + iValue = (int32)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + iValue = (int32)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(int iWhich, uint32& uiValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + uiValue = (uint32)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + uiValue = (uint32)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(int iWhich, int64& llValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + llValue = (int64)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + llValue = (int64)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(int iWhich, uint64& ullValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + ullValue = (uint64)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + ullValue = (uint64)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(int iWhich, bool& bValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type > cJSON_True) + { + return(false); + } + bValue = pJsonStruct->type; + return(true); +} + +bool CJsonObject::Get(int iWhich, float& fValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Double) + { + fValue = (float)(pJsonStruct->valuedouble); + return(true); + } + else if (pJsonStruct->type == cJSON_Int) + { + fValue = (float)(pJsonStruct->valueint); + return(true); + } + return(false); +} + +bool CJsonObject::Get(int iWhich, double& dValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Double) + { + dValue = pJsonStruct->valuedouble; + return(true); + } + else if (pJsonStruct->type == cJSON_Int) + { + dValue = (double)(pJsonStruct->valueint); + return(true); + } + return(false); +} + +bool CJsonObject::IsNull(int iWhich) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type != cJSON_NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(const CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + unsigned int uiLastIndex = (unsigned int)cJSON_GetArraySize(pFocusData) - 1; + for (std::map::iterator iter = m_mapJsonArrayRef.begin(); + iter != m_mapJsonArrayRef.end(); ) + { + if (iter->first >= uiLastIndex) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter++); + } + else + { + iter++; + } + } + return(true); +} + +bool CJsonObject::Add(const std::string& strValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(int32 iValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(uint32 uiValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(int64 llValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(uint64 ullValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(int iAnywhere, bool bValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateBool(bValue); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(float fValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(double dValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddNull() +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateNull(); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(const CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + for (std::map::iterator iter = m_mapJsonArrayRef.begin(); + iter != m_mapJsonArrayRef.end(); ) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter++); + } + return(true); +} + +bool CJsonObject::AddAsFirst(const std::string& strValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(int32 iValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(uint32 uiValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(int64 llValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(uint64 ullValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(int iAnywhere, bool bValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateBool(bValue); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(float fValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(double dValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddNullAsFirst() +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateNull(); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Delete(int iWhich) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON_DeleteItemFromArray(pFocusData, iWhich); + for (std::map::iterator iter = m_mapJsonArrayRef.begin(); + iter != m_mapJsonArrayRef.end(); ) + { + if (iter->first >= (unsigned int)iWhich) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter++); + } + else + { + iter++; + } + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, const CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, const std::string& strValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, int32 iValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, uint32 uiValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, int64 llValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)((uint64)llValue), -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, uint64 ullValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, bool bValue, bool bValueAgain) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateBool(bValue); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, float fValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, double dValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::ReplaceWithNull(int iWhich) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateNull(); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +CJsonObject::CJsonObject(cJSON* pJsonData) + : m_pJsonData(NULL), m_pExternJsonDataRef(pJsonData) +{ +} + +} + + diff --git a/src/util/json/CJsonObject.hpp b/src/util/json/CJsonObject.hpp index 6fe4e4e6..bb25b0e4 100644 --- a/src/util/json/CJsonObject.hpp +++ b/src/util/json/CJsonObject.hpp @@ -1,151 +1,157 @@ -/******************************************************************************* - * Project: neb - * @file CJsonObject.hpp - * @brief Json - * @author bwarliao - * @date: 2014-7-16 - * @note - * Modify history: - ******************************************************************************/ - -#ifndef CJSONOBJECT_HPP_ -#define CJSONOBJECT_HPP_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __cplusplus -extern "C" { -#endif -#include "cJSON.h" -#ifdef __cplusplus -} -#endif - - -namespace neb -{ - -class CJsonObject -{ -public: // method of ordinary json object or json array - CJsonObject(); - CJsonObject(const std::string& strJson); - CJsonObject(const CJsonObject* pJsonObject); - CJsonObject(const CJsonObject& oJsonObject); - virtual ~CJsonObject(); - - CJsonObject& operator=(const CJsonObject& oJsonObject); - bool operator==(const CJsonObject& oJsonObject) const; - bool Parse(const std::string& strJson); - void Clear(); - bool IsEmpty() const; - bool IsArray() const; - std::string ToString() const; - std::string ToFormattedString() const; - const std::string& GetErrMsg() const - { - return(m_strErrMsg); - } - -public: // method of ordinary json object - bool AddEmptySubObject(const std::string& strKey); - bool AddEmptySubArray(const std::string& strKey); - bool GetKey(std::string& strKey); - CJsonObject& operator[](const std::string& strKey); - std::string operator()(const std::string& strKey) const; - bool Get(const std::string& strKey, CJsonObject& oJsonObject) const; - bool Get(const std::string& strKey, std::string& strValue) const; - bool Get(const std::string& strKey, int32& iValue) const; - bool Get(const std::string& strKey, uint32& uiValue) const; - bool Get(const std::string& strKey, int64& llValue) const; - bool Get(const std::string& strKey, uint64& ullValue) const; - bool Get(const std::string& strKey, bool& bValue) const; - bool Get(const std::string& strKey, float& fValue) const; - bool Get(const std::string& strKey, double& dValue) const; - bool Add(const std::string& strKey, const CJsonObject& oJsonObject); - bool Add(const std::string& strKey, const std::string& strValue); - bool Add(const std::string& strKey, int32 iValue); - bool Add(const std::string& strKey, uint32 uiValue); - bool Add(const std::string& strKey, int64 llValue); - bool Add(const std::string& strKey, uint64 ullValue); - bool Add(const std::string& strKey, bool bValue, bool bValueAgain); - bool Add(const std::string& strKey, float fValue); - bool Add(const std::string& strKey, double dValue); - bool Delete(const std::string& strKey); - bool Replace(const std::string& strKey, const CJsonObject& oJsonObject); - bool Replace(const std::string& strKey, const std::string& strValue); - bool Replace(const std::string& strKey, int32 iValue); - bool Replace(const std::string& strKey, uint32 uiValue); - bool Replace(const std::string& strKey, int64 llValue); - bool Replace(const std::string& strKey, uint64 ullValue); - bool Replace(const std::string& strKey, bool bValue, bool bValueAgain); - bool Replace(const std::string& strKey, float fValue); - bool Replace(const std::string& strKey, double dValue); - -public: // method of json array - int GetArraySize(); - CJsonObject& operator[](unsigned int uiWhich); - std::string operator()(unsigned int uiWhich) const; - bool Get(int iWhich, CJsonObject& oJsonObject) const; - bool Get(int iWhich, std::string& strValue) const; - bool Get(int iWhich, int32& iValue) const; - bool Get(int iWhich, uint32& uiValue) const; - bool Get(int iWhich, int64& llValue) const; - bool Get(int iWhich, uint64& ullValue) const; - bool Get(int iWhich, bool& bValue) const; - bool Get(int iWhich, float& fValue) const; - bool Get(int iWhich, double& dValue) const; - bool Add(const CJsonObject& oJsonObject); - bool Add(const std::string& strValue); - bool Add(int32 iValue); - bool Add(uint32 uiValue); - bool Add(int64 llValue); - bool Add(uint64 ullValue); - bool Add(int iAnywhere, bool bValue); - bool Add(float fValue); - bool Add(double dValue); - bool AddAsFirst(const CJsonObject& oJsonObject); - bool AddAsFirst(const std::string& strValue); - bool AddAsFirst(int32 iValue); - bool AddAsFirst(uint32 uiValue); - bool AddAsFirst(int64 llValue); - bool AddAsFirst(uint64 ullValue); - bool AddAsFirst(int iAnywhere, bool bValue); - bool AddAsFirst(float fValue); - bool AddAsFirst(double dValue); - bool Delete(int iWhich); - bool Replace(int iWhich, const CJsonObject& oJsonObject); - bool Replace(int iWhich, const std::string& strValue); - bool Replace(int iWhich, int32 iValue); - bool Replace(int iWhich, uint32 uiValue); - bool Replace(int iWhich, int64 llValue); - bool Replace(int iWhich, uint64 ullValue); - bool Replace(int iWhich, bool bValue, bool bValueAgain); - bool Replace(int iWhich, float fValue); - bool Replace(int iWhich, double dValue); - -private: - CJsonObject(cJSON* pJsonData); - -private: - cJSON* m_pJsonData; - cJSON* m_pExternJsonDataRef; - std::string m_strErrMsg; - std::map m_mapJsonArrayRef; - std::map m_mapJsonObjectRef; - std::list m_listKeys; - std::list::const_iterator m_itKey; -}; - -} - -#endif /* CJSONHELPER_HPP_ */ +/******************************************************************************* + * Project: neb + * @file CJsonObject.hpp + * @brief Json + * @author bwarliao + * @date: 2014-7-16 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef CJSONOBJECT_HPP_ +#define CJSONOBJECT_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif +#include "cJSON.h" +#ifdef __cplusplus +} +#endif + + +namespace neb +{ + +class CJsonObject +{ +public: // method of ordinary json object or json array + CJsonObject(); + CJsonObject(const std::string& strJson); + CJsonObject(const CJsonObject* pJsonObject); + CJsonObject(const CJsonObject& oJsonObject); + virtual ~CJsonObject(); + + CJsonObject& operator=(const CJsonObject& oJsonObject); + bool operator==(const CJsonObject& oJsonObject) const; + bool Parse(const std::string& strJson); + void Clear(); + bool IsEmpty() const; + bool IsArray() const; + std::string ToString() const; + std::string ToFormattedString() const; + const std::string& GetErrMsg() const + { + return(m_strErrMsg); + } + +public: // method of ordinary json object + bool AddEmptySubObject(const std::string& strKey); + bool AddEmptySubArray(const std::string& strKey); + bool GetKey(std::string& strKey); + void ResetTraversing(); + CJsonObject& operator[](const std::string& strKey); + std::string operator()(const std::string& strKey) const; + bool Get(const std::string& strKey, CJsonObject& oJsonObject) const; + bool Get(const std::string& strKey, std::string& strValue) const; + bool Get(const std::string& strKey, int32& iValue) const; + bool Get(const std::string& strKey, uint32& uiValue) const; + bool Get(const std::string& strKey, int64& llValue) const; + bool Get(const std::string& strKey, uint64& ullValue) const; + bool Get(const std::string& strKey, bool& bValue) const; + bool Get(const std::string& strKey, float& fValue) const; + bool Get(const std::string& strKey, double& dValue) const; + bool IsNull(const std::string& strKey) const; + bool Add(const std::string& strKey, const CJsonObject& oJsonObject); + bool Add(const std::string& strKey, const std::string& strValue); + bool Add(const std::string& strKey, int32 iValue); + bool Add(const std::string& strKey, uint32 uiValue); + bool Add(const std::string& strKey, int64 llValue); + bool Add(const std::string& strKey, uint64 ullValue); + bool Add(const std::string& strKey, bool bValue, bool bValueAgain); + bool Add(const std::string& strKey, float fValue); + bool Add(const std::string& strKey, double dValue); + bool AddNull(const std::string& strKey); // add null like this: "key":null + bool Delete(const std::string& strKey); + bool Replace(const std::string& strKey, const CJsonObject& oJsonObject); + bool Replace(const std::string& strKey, const std::string& strValue); + bool Replace(const std::string& strKey, int32 iValue); + bool Replace(const std::string& strKey, uint32 uiValue); + bool Replace(const std::string& strKey, int64 llValue); + bool Replace(const std::string& strKey, uint64 ullValue); + bool Replace(const std::string& strKey, bool bValue, bool bValueAgain); + bool Replace(const std::string& strKey, float fValue); + bool Replace(const std::string& strKey, double dValue); + bool ReplaceWithNull(const std::string& strKey); // replace value with null + +public: // method of json array + int GetArraySize(); + CJsonObject& operator[](unsigned int uiWhich); + std::string operator()(unsigned int uiWhich) const; + bool Get(int iWhich, CJsonObject& oJsonObject) const; + bool Get(int iWhich, std::string& strValue) const; + bool Get(int iWhich, int32& iValue) const; + bool Get(int iWhich, uint32& uiValue) const; + bool Get(int iWhich, int64& llValue) const; + bool Get(int iWhich, uint64& ullValue) const; + bool Get(int iWhich, bool& bValue) const; + bool Get(int iWhich, float& fValue) const; + bool Get(int iWhich, double& dValue) const; + bool IsNull(int iWhich) const; + bool Add(const CJsonObject& oJsonObject); + bool Add(const std::string& strValue); + bool Add(int32 iValue); + bool Add(uint32 uiValue); + bool Add(int64 llValue); + bool Add(uint64 ullValue); + bool Add(int iAnywhere, bool bValue); + bool Add(float fValue); + bool Add(double dValue); + bool AddNull(); // add a null value + bool AddAsFirst(const CJsonObject& oJsonObject); + bool AddAsFirst(const std::string& strValue); + bool AddAsFirst(int32 iValue); + bool AddAsFirst(uint32 uiValue); + bool AddAsFirst(int64 llValue); + bool AddAsFirst(uint64 ullValue); + bool AddAsFirst(int iAnywhere, bool bValue); + bool AddAsFirst(float fValue); + bool AddAsFirst(double dValue); + bool AddNullAsFirst(); // add a null value + bool Delete(int iWhich); + bool Replace(int iWhich, const CJsonObject& oJsonObject); + bool Replace(int iWhich, const std::string& strValue); + bool Replace(int iWhich, int32 iValue); + bool Replace(int iWhich, uint32 uiValue); + bool Replace(int iWhich, int64 llValue); + bool Replace(int iWhich, uint64 ullValue); + bool Replace(int iWhich, bool bValue, bool bValueAgain); + bool Replace(int iWhich, float fValue); + bool Replace(int iWhich, double dValue); + bool ReplaceWithNull(int iWhich); // replace with a null value + +private: + CJsonObject(cJSON* pJsonData); + +private: + cJSON* m_pJsonData; + cJSON* m_pExternJsonDataRef; + std::string m_strErrMsg; + std::map m_mapJsonArrayRef; + std::map m_mapJsonObjectRef; + std::list m_listKeys; + std::list::const_iterator m_itKey; +}; + +} + +#endif /* CJSONHELPER_HPP_ */ diff --git a/src/util/json/cJSON.c b/src/util/json/cJSON.c index 5449d0c3..840e8902 100644 --- a/src/util/json/cJSON.c +++ b/src/util/json/cJSON.c @@ -1,1093 +1,1091 @@ -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - */ - -/* cJSON */ -/* JSON parser in C. */ - -#include -#include -#include -#include -#include -#include -#include -#include "cJSON.h" - -#ifndef INT_MAX -#define INT_MAX 2147483647 -#define INT_MIN (-INT_MAX - 1) -#define UINT_MAX 4294967295U -#endif - -static const char *ep; - -const char *cJSON_GetErrorPtr() -{ - return ep; -} - -static int cJSON_strcasecmp(const char *s1, const char *s2) -{ - if (!s1) - return (s1 == s2) ? 0 : 1; - if (!s2) - return 1; - for (; tolower(*s1) == tolower(*s2); ++s1, ++s2) - if (*s1 == 0) - return 0; - return tolower(*(const unsigned char *)s1) - - tolower(*(const unsigned char *)s2); -} - -static void *(*cJSON_malloc)(size_t sz) = malloc; -static void (*cJSON_free)(void *ptr) = free; - -static char* cJSON_strdup(const char* str) -{ - size_t len; - char* copy; - - len = strlen(str) + 1; - if (!(copy = (char*) cJSON_malloc(len))) - return 0; - memcpy(copy, str, len); - return copy; -} - -void cJSON_InitHooks(cJSON_Hooks* hooks) -{ - if (!hooks) - { /* Reset hooks */ - cJSON_malloc = malloc; - cJSON_free = free; - return; - } - - cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc; - cJSON_free = (hooks->free_fn) ? hooks->free_fn : free; -} - -/* Internal constructor. */ -static cJSON *cJSON_New_Item() -{ - cJSON* node = (cJSON*) cJSON_malloc(sizeof(cJSON)); - if (node) - memset(node, 0, sizeof(cJSON)); - return node; -} - -/* Delete a cJSON structure. */ -void cJSON_Delete(cJSON *c) -{ - cJSON *next; - while (c) - { - next = c->next; - if (!(c->type & cJSON_IsReference) && c->child) - cJSON_Delete(c->child); - if (!(c->type & cJSON_IsReference) && c->valuestring) - cJSON_free(c->valuestring); - if (c->string) - cJSON_free(c->string); - cJSON_free(c); - c = next; - } -} - -/* Parse the input text to generate a number, and populate the result into item. */ -static const char *parse_number(cJSON *item, const char *num) -{ - long double n = 0, scale = 0; - int subscale = 0, signsubscale = 1; - item->sign = 1; - - /* Could use sscanf for this? */ - if (*num == '-') - item->sign = -1, num++; /* Has sign? */ - if (*num == '0') - num++; /* is zero */ - if (*num >= '1' && *num <= '9') - do - n = (n * 10.0) + (*num++ - '0'); - while (*num >= '0' && *num <= '9'); /* Number? */ - if (*num == '.' && num[1] >= '0' && num[1] <= '9') - { - num++; - do - n = (n * 10.0) + (*num++ - '0'), scale--; - while (*num >= '0' && *num <= '9'); - } /* Fractional part? */ - if (*num == 'e' || *num == 'E') /* Exponent? */ - { - num++; - if (*num == '+') - num++; - else if (*num == '-') - signsubscale = -1, num++; /* With sign? */ - while (*num >= '0' && *num <= '9') - subscale = (subscale * 10) + (*num++ - '0'); /* Number? */ - } - - if (scale == 0 && subscale == 0) - { - item->valuedouble = (double)(item->sign * (uint64)n); - item->valueint = (uint64)(item->sign * (uint64)n); - item->type = cJSON_Int; - } - else - { - n = item->sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ - item->valuedouble = (double)n; - item->valueint = (uint64)n; - item->type = cJSON_Double; - } - return num; -} - -/* Render the number nicely from the given item into a string. */ -static char *print_double(cJSON *item) -{ - char *str; - double d = item->valuedouble; - str = (char*) cJSON_malloc(64); /* This is a nice tradeoff. */ - if (str) - { - if (fabs(floor(d) - d) <= DBL_EPSILON) - sprintf(str, "%.0f", d); - else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) - sprintf(str, "%lf", d); - else - sprintf(str, "%f", d); - } - return str; -} - -static char *print_int(cJSON *item) -{ - char *str; - str = (char*) cJSON_malloc(22); /* 2^64+1 can be represented in 21 chars. */ - if (str) - { - if (item->sign == -1) - { - if ((int64)item->valueint <= (int64)INT_MAX && (int64)item->valueint >= (int64)INT_MIN) - { - sprintf(str, "%d", (int32)item->valueint); - } - else - { - sprintf(str, "%lld", (int64)item->valueint); - } - } - else - { - if (item->valueint <= (uint64)UINT_MAX) - { - sprintf(str, "%u", (uint32)item->valueint); - } - else - { - sprintf(str, "%llu", item->valueint); - } - } - } - return str; -} - -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, - 0xF8, 0xFC }; -static const char *parse_string(cJSON *item, const char *str) -{ - const char *ptr = str + 1; - char *ptr2; - char *out; - int len = 0; - unsigned uc, uc2; - if (*str != '\"') - { - ep = str; - return 0; - } /* not a string! */ - - while (*ptr != '\"' && *ptr && ++len) - if (*ptr++ == '\\') - ptr++; /* Skip escaped quotes. */ - - out = (char*) cJSON_malloc(len + 1); /* This is how long we need for the string, roughly. */ - if (!out) - return 0; - - ptr = str + 1; - ptr2 = out; - while (*ptr != '\"' && *ptr) - { - if (*ptr != '\\') - *ptr2++ = *ptr++; - else - { - ptr++; - switch (*ptr) - { - case 'b': - *ptr2++ = '\b'; - break; - case 'f': - *ptr2++ = '\f'; - break; - case 'n': - *ptr2++ = '\n'; - break; - case 'r': - *ptr2++ = '\r'; - break; - case 't': - *ptr2++ = '\t'; - break; - case 'u': /* transcode utf16 to utf8. */ - sscanf(ptr + 1, "%4x", &uc); - ptr += 4; /* get the unicode char. */ - - if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) - break; // check for invalid. - - if (uc >= 0xD800 && uc <= 0xDBFF) // UTF16 surrogate pairs. - { - if (ptr[1] != '\\' || ptr[2] != 'u') - break; // missing second-half of surrogate. - sscanf(ptr + 3, "%4x", &uc2); - ptr += 6; - if (uc2 < 0xDC00 || uc2 > 0xDFFF) - break; // invalid second-half of surrogate. - uc = 0x10000 | ((uc & 0x3FF) << 10) | (uc2 & 0x3FF); - } - - len = 4; - if (uc < 0x80) - len = 1; - else if (uc < 0x800) - len = 2; - else if (uc < 0x10000) - len = 3; - ptr2 += len; - - switch (len) - { - case 4: - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 3: - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 2: - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 1: - *--ptr2 = (uc | firstByteMark[len]); - } - ptr2 += len; - break; - default: - *ptr2++ = *ptr; - break; - } - ptr++; - } - } - *ptr2 = 0; - if (*ptr == '\"') - ptr++; - item->valuestring = out; - item->type = cJSON_String; - return ptr; -} - -/* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const char *str) -{ - const char *ptr; - char *ptr2, *out; - int len = 0; - unsigned char token; - - if (!str) - return cJSON_strdup(""); - ptr = str; - while ((token = *ptr) && ++len) - { - if (strchr("\"\\\b\f\n\r\t", token)) - len++; - else if (token < 32) - len += 5; - ptr++; - } - - out = (char*) cJSON_malloc(len + 3); - if (!out) - return 0; - - ptr2 = out; - ptr = str; - *ptr2++ = '\"'; - while (*ptr) - { - if ((unsigned char) *ptr > 31 && *ptr != '\"' && *ptr != '\\') - *ptr2++ = *ptr++; - else - { - *ptr2++ = '\\'; - switch (token = *ptr++) - { - case '\\': - *ptr2++ = '\\'; - break; - case '\"': - *ptr2++ = '\"'; - break; - case '\b': - *ptr2++ = 'b'; - break; - case '\f': - *ptr2++ = 'f'; - break; - case '\n': - *ptr2++ = 'n'; - break; - case '\r': - *ptr2++ = 'r'; - break; - case '\t': - *ptr2++ = 't'; - break; - default: - sprintf(ptr2, "u%04x", token); - ptr2 += 5; - break; /* escape and print */ - } - } - } - *ptr2++ = '\"'; - *ptr2++ = 0; - return out; -} -/* Invote print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item) -{ - return print_string_ptr(item->valuestring); -} - -/* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item, const char *value); -static char *print_value(cJSON *item, int depth, int fmt); -static const char *parse_array(cJSON *item, const char *value); -static char *print_array(cJSON *item, int depth, int fmt); -static const char *parse_object(cJSON *item, const char *value); -static char *print_object(cJSON *item, int depth, int fmt); - -/* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) -{ - while (in && *in && (unsigned char) *in <= 32) - in++; - return in; -} - -/* Parse an object - create a new root, and populate. */ -cJSON *cJSON_Parse(const char *value) -{ - cJSON *c = cJSON_New_Item(); - ep = 0; - if (!c) - return 0; /* memory fail */ - - if (!parse_value(c, skip(value))) - { - cJSON_Delete(c); - return 0; - } - return c; -} - -/* Render a cJSON item/entity/structure to text. */ -char *cJSON_Print(cJSON *item) -{ - return print_value(item, 0, 1); -} -char *cJSON_PrintUnformatted(cJSON *item) -{ - return print_value(item, 0, 0); -} - -/* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item, const char *value) -{ - if (!value) - return 0; /* Fail on null. */ - if (!strncmp(value, "null", 4)) - { - item->type = cJSON_NULL; - return value + 4; - } - if (!strncmp(value, "false", 5)) - { - item->type = cJSON_False; - return value + 5; - } - if (!strncmp(value, "true", 4)) - { - item->type = cJSON_True; - item->valueint = 1; - return value + 4; - } - if (*value == '\"') - { - return parse_string(item, value); - } - if (*value == '-' || (*value >= '0' && *value <= '9')) - { - return parse_number(item, value); - } - if (*value == '[') - { - return parse_array(item, value); - } - if (*value == '{') - { - return parse_object(item, value); - } - - ep = value; - return 0; /* failure. */ -} - -/* Render a value to text. */ -static char *print_value(cJSON *item, int depth, int fmt) -{ - char *out = 0; - if (!item) - return 0; - switch ((item->type) & 255) - { - case cJSON_NULL: - out = cJSON_strdup("null"); - break; - case cJSON_False: - out = cJSON_strdup("false"); - break; - case cJSON_True: - out = cJSON_strdup("true"); - break; - case cJSON_Int: - out = print_int(item); - break; - case cJSON_Double: - out = print_double(item); - break; - case cJSON_String: - out = print_string(item); - break; - case cJSON_Array: - out = print_array(item, depth, fmt); - break; - case cJSON_Object: - out = print_object(item, depth, fmt); - break; - } - return out; -} - -/* Build an array from input text. */ -static const char *parse_array(cJSON *item, const char *value) -{ - cJSON *child; - if (*value != '[') - { - ep = value; - return 0; - } /* not an array! */ - - item->type = cJSON_Array; - value = skip(value + 1); - if (*value == ']') - return value + 1; /* empty array. */ - - item->child = child = cJSON_New_Item(); - if (!item->child) - return 0; /* memory fail */ - value = skip(parse_value(child, skip(value))); /* skip any spacing, get the value. */ - if (!value) - return 0; - - while (*value == ',') - { - cJSON *new_item; - if (!(new_item = cJSON_New_Item())) - return 0; /* memory fail */ - child->next = new_item; - new_item->prev = child; - child = new_item; - value = skip(parse_value(child, skip(value + 1))); - if (!value) - return 0; /* memory fail */ - } - - if (*value == ']') - return value + 1; /* end of array */ - ep = value; - return 0; /* malformed. */ -} - -/* Render an array to text */ -static char *print_array(cJSON *item, int depth, int fmt) -{ - char **entries; - char *out = 0, *ptr, *ret; - int len = 5; - cJSON *child = item->child; - int numentries = 0, i = 0, fail = 0; - - /* How many entries in the array? */ - while (child) - numentries++, child = child->next; - /* Allocate an array to hold the values for each */ - entries = (char**) cJSON_malloc(numentries * sizeof(char*)); - if (!entries) - return 0; - memset(entries, 0, numentries * sizeof(char*)); - /* Retrieve all the results: */ - child = item->child; - while (child && !fail) - { - ret = print_value(child, depth + 1, fmt); - entries[i++] = ret; - if (ret) - len += strlen(ret) + 2 + (fmt ? 1 : 0); - else - fail = 1; - child = child->next; - } - - /* If we didn't fail, try to malloc the output string */ - if (!fail) - out = (char*) cJSON_malloc(len); - /* If that fails, we fail. */ - if (!out) - fail = 1; - - /* Handle failure. */ - if (fail) - { - for (i = 0; i < numentries; i++) - if (entries[i]) - cJSON_free(entries[i]); - cJSON_free(entries); - return 0; - } - - /* Compose the output array. */ - *out = '['; - ptr = out + 1; - *ptr = 0; - for (i = 0; i < numentries; i++) - { - strcpy(ptr, entries[i]); - ptr += strlen(entries[i]); - if (i != numentries - 1) - { - *ptr++ = ','; - if (fmt) - *ptr++ = ' '; - *ptr = 0; - } - cJSON_free(entries[i]); - } - cJSON_free(entries); - *ptr++ = ']'; - *ptr++ = 0; - return out; -} - -/* Build an object from the text. */ -static const char *parse_object(cJSON *item, const char *value) -{ - cJSON *child; - if (*value != '{') - { - ep = value; - return 0; - } /* not an object! */ - - item->type = cJSON_Object; - value = skip(value + 1); - if (*value == '}') - return value + 1; /* empty array. */ - - item->child = child = cJSON_New_Item(); - if (!item->child) - return 0; - value = skip(parse_string(child, skip(value))); - if (!value) - return 0; - child->string = child->valuestring; - child->valuestring = 0; - if (*value != ':') - { - ep = value; - return 0; - } /* fail! */ - value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ - if (!value) - return 0; - - while (*value == ',') - { - cJSON *new_item; - if (!(new_item = cJSON_New_Item())) - return 0; /* memory fail */ - child->next = new_item; - new_item->prev = child; - child = new_item; - value = skip(parse_string(child, skip(value + 1))); - if (!value) - return 0; - child->string = child->valuestring; - child->valuestring = 0; - if (*value != ':') - { - ep = value; - return 0; - } /* fail! */ - value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ - if (!value) - return 0; - } - - if (*value == '}') - return value + 1; /* end of array */ - ep = value; - return 0; /* malformed. */ -} - -/* Render an object to text. */ -static char *print_object(cJSON *item, int depth, int fmt) -{ - char **entries = 0, **names = 0; - char *out = 0, *ptr, *ret, *str; - int len = 7, i = 0, j; - cJSON *child = item->child; - int numentries = 0, fail = 0; - /* Count the number of entries. */ - while (child) - numentries++, child = child->next; - /* Allocate space for the names and the objects */ - entries = (char**) cJSON_malloc(numentries * sizeof(char*)); - if (!entries) - return 0; - names = (char**) cJSON_malloc(numentries * sizeof(char*)); - if (!names) - { - cJSON_free(entries); - return 0; - } - memset(entries, 0, sizeof(char*) * numentries); - memset(names, 0, sizeof(char*) * numentries); - - /* Collect all the results into our arrays: */ - child = item->child; - depth++; - if (fmt) - len += depth; - while (child) - { - names[i] = str = print_string_ptr(child->string); - entries[i++] = ret = print_value(child, depth, fmt); - if (str && ret) - len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0); - else - fail = 1; - child = child->next; - } - - /* Try to allocate the output string */ - if (!fail) - out = (char*) cJSON_malloc(len); - if (!out) - fail = 1; - - /* Handle failure */ - if (fail) - { - for (i = 0; i < numentries; i++) - { - if (names[i]) - cJSON_free(names[i]); - if (entries[i]) - cJSON_free(entries[i]); - } - cJSON_free(names); - cJSON_free(entries); - return 0; - } - - /* Compose the output: */ - *out = '{'; - ptr = out + 1; - if (fmt) - *ptr++ = '\n'; - *ptr = 0; - for (i = 0; i < numentries; i++) - { - if (fmt) - for (j = 0; j < depth; j++) - *ptr++ = '\t'; - strcpy(ptr, names[i]); - ptr += strlen(names[i]); - *ptr++ = ':'; - if (fmt) - *ptr++ = '\t'; - strcpy(ptr, entries[i]); - ptr += strlen(entries[i]); - if (i != numentries - 1) - *ptr++ = ','; - if (fmt) - *ptr++ = '\n'; - *ptr = 0; - cJSON_free(names[i]); - cJSON_free(entries[i]); - } - - cJSON_free(names); - cJSON_free(entries); - if (fmt) - for (i = 0; i < depth - 1; i++) - *ptr++ = '\t'; - *ptr++ = '}'; - *ptr++ = 0; - return out; -} - -/* Get Array size/item / object item. */ -int cJSON_GetArraySize(cJSON *array) -{ - cJSON *c = array->child; - int i = 0; - while (c) - i++, c = c->next; - return i; -} -cJSON *cJSON_GetArrayItem(cJSON *array, int item) -{ - cJSON *c = array->child; - while (c && item > 0) - item--, c = c->next; - return c; -} -cJSON *cJSON_GetObjectItem(cJSON *object, const char *string) -{ - cJSON *c = object->child; - while (c && cJSON_strcasecmp(c->string, string)) - c = c->next; - return c; -} - -/* Utility for array list handling. */ -static void suffix_object(cJSON *prev, cJSON *item) -{ - prev->next = item; - item->prev = prev; -} -/* Utility for handling references. */ -static cJSON *create_reference(cJSON *item) -{ - cJSON *ref = cJSON_New_Item(); - if (!ref) - return 0; - memcpy(ref, item, sizeof(cJSON)); - ref->string = 0; - ref->type |= cJSON_IsReference; - ref->next = ref->prev = 0; - return ref; -} - -/* Add item to array/object. */ -void cJSON_AddItemToArray(cJSON *array, cJSON *item) -{ - cJSON *c = array->child; - if (!item) - return; - if (!c) - { - array->child = item; - } - else - { - while (c && c->next) - c = c->next; - suffix_object(c, item); - } -} - -void cJSON_AddItemToArrayHead(cJSON *array, cJSON *item) -{ - cJSON *c = array->child; - if (!item) - return; - if (!c) - { - array->child = item; - } - else - { - item->prev = c->prev; - item->next = c; - c->prev = item; - array->child = item; - } -} - -void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) -{ - if (!item) - return; - if (item->string) - cJSON_free(item->string); - item->string = cJSON_strdup(string); - cJSON_AddItemToArray(object, item); -} -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) -{ - cJSON_AddItemToArray(array, create_reference(item)); -} -void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, - cJSON *item) -{ - cJSON_AddItemToObject(object, string, create_reference(item)); -} - -cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) -{ - cJSON *c = array->child; - while (c && which > 0) - c = c->next, which--; - if (!c) - return 0; - if (c->prev) - c->prev->next = c->next; - if (c->next) - c->next->prev = c->prev; - if (c == array->child) - array->child = c->next; - c->prev = c->next = 0; - return c; -} -void cJSON_DeleteItemFromArray(cJSON *array, int which) -{ - cJSON_Delete(cJSON_DetachItemFromArray(array, which)); -} -cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string) -{ - int i = 0; - cJSON *c = object->child; - while (c && cJSON_strcasecmp(c->string, string)) - i++, c = c->next; - if (c) - return cJSON_DetachItemFromArray(object, i); - return 0; -} -void cJSON_DeleteItemFromObject(cJSON *object, const char *string) -{ - cJSON_Delete(cJSON_DetachItemFromObject(object, string)); -} - -/* Replace array/object items with new ones. */ -void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) -{ - cJSON *c = array->child; - while (c && which > 0) - c = c->next, which--; - if (!c) - return; - newitem->next = c->next; - newitem->prev = c->prev; - if (newitem->next) - newitem->next->prev = newitem; - if (c == array->child) - array->child = newitem; - else - newitem->prev->next = newitem; - c->next = c->prev = 0; - cJSON_Delete(c); -} -void cJSON_ReplaceItemInObject(cJSON *object, const char *string, - cJSON *newitem) -{ - int i = 0; - cJSON *c = object->child; - while (c && cJSON_strcasecmp(c->string, string)) - i++, c = c->next; - if (c) - { - newitem->string = cJSON_strdup(string); - cJSON_ReplaceItemInArray(object, i, newitem); - } -} - -/* Create basic types: */ -cJSON *cJSON_CreateNull() -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_NULL; - return item; -} -cJSON *cJSON_CreateTrue() -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_True; - return item; -} -cJSON *cJSON_CreateFalse() -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_False; - return item; -} -cJSON *cJSON_CreateBool(int b) -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = b ? cJSON_True : cJSON_False; - return item; -} -cJSON *cJSON_CreateDouble(double num, int sign) -{ - cJSON *item = cJSON_New_Item(); - if (item) - { - item->type = cJSON_Double; - item->valuedouble = num; - item->valueint = (uint64)num; - item->sign = sign; - } - return item; -} -cJSON *cJSON_CreateInt(uint64 num, int sign) -{ - cJSON *item = cJSON_New_Item(); - if (item) - { - item->type = cJSON_Int; - item->valuedouble = (double)num; - item->valueint = (uint64)num; - item->sign = sign; - } - return item; -} -cJSON *cJSON_CreateString(const char *string) -{ - cJSON *item = cJSON_New_Item(); - if (item) - { - item->type = cJSON_String; - item->valuestring = cJSON_strdup(string); - } - return item; -} -cJSON *cJSON_CreateArray() -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_Array; - return item; -} -cJSON *cJSON_CreateObject() -{ - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_Object; - return item; -} - -/* Create Arrays: */ -cJSON *cJSON_CreateIntArray(int *numbers, int sign, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) - { - n = cJSON_CreateDouble((long double)((unsigned int)numbers[i]), sign); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; -} -cJSON *cJSON_CreateFloatArray(float *numbers, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) - { - n = cJSON_CreateDouble((long double)numbers[i], -1); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; -} -cJSON *cJSON_CreateDoubleArray(double *numbers, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) - { - n = cJSON_CreateDouble((long double)numbers[i], -1); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; -} -cJSON *cJSON_CreateStringArray(const char **strings, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) - { - n = cJSON_CreateString(strings[i]); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; -} - +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +#ifndef INT_MAX +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX 4294967295U +#endif + +static const char *ep; + +const char *cJSON_GetErrorPtr() +{ + return ep; +} + +static int cJSON_strcasecmp(const char *s1, const char *s2) +{ + if (!s1) + return (s1 == s2) ? 0 : 1; + if (!s2) + return 1; + for (; tolower(*s1) == tolower(*s2); ++s1, ++s2) + if (*s1 == 0) + return 0; + return tolower(*(const unsigned char *)s1) + - tolower(*(const unsigned char *)s2); +} + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*) cJSON_malloc(len))) + return 0; + memcpy(copy, str, len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) + { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc; + cJSON_free = (hooks->free_fn) ? hooks->free_fn : free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item() +{ + cJSON* node = (cJSON*) cJSON_malloc(sizeof(cJSON)); + if (node) + memset(node, 0, sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next = c->next; + if (!(c->type & cJSON_IsReference) && c->child) + cJSON_Delete(c->child); + if (!(c->type & cJSON_IsReference) && c->valuestring) + cJSON_free(c->valuestring); + if (c->string) + cJSON_free(c->string); + cJSON_free(c); + c = next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item, const char *num) +{ + long double n = 0, scale = 0; + int subscale = 0, signsubscale = 1; + item->sign = 1; + + /* Could use sscanf for this? */ + if (*num == '-') + item->sign = -1, num++; /* Has sign? */ + if (*num == '0') + num++; /* is zero */ + if (*num >= '1' && *num <= '9') + do + n = (n * 10.0) + (*num++ - '0'); + while (*num >= '0' && *num <= '9'); /* Number? */ + if (*num == '.' && num[1] >= '0' && num[1] <= '9') + { + num++; + do + n = (n * 10.0) + (*num++ - '0'), scale--; + while (*num >= '0' && *num <= '9'); + } /* Fractional part? */ + if (*num == 'e' || *num == 'E') /* Exponent? */ + { + num++; + if (*num == '+') + num++; + else if (*num == '-') + signsubscale = -1, num++; /* With sign? */ + while (*num >= '0' && *num <= '9') + subscale = (subscale * 10) + (*num++ - '0'); /* Number? */ + } + + if (scale == 0 && subscale == 0) + { + item->valuedouble = (double)(item->sign * n); + item->valueint = (uint64)(item->sign * (uint64)n); + item->type = cJSON_Int; + } + else + { + n = item->sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + item->valuedouble = (double)n; + item->valueint = (uint64)n; + item->type = cJSON_Double; + } + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_double(cJSON *item) +{ + char *str; + double d = item->valuedouble; + str = (char*) cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) + sprintf(str, "%lf", d); + else + sprintf(str, "%f", d); + } + return str; +} + +static char *print_int(cJSON *item) +{ + char *str; + str = (char*) cJSON_malloc(22); /* 2^64+1 can be represented in 21 chars. */ + if (str) + { + if (item->sign == -1) + { + if ((int64)item->valueint <= (int64)INT_MAX && (int64)item->valueint >= (int64)INT_MIN) + { + sprintf(str, "%d", (int32)item->valueint); + } + else + { + sprintf(str, "%lld", (int64)item->valueint); + } + } + else + { + if (item->valueint <= (uint64)UINT_MAX) + { + sprintf(str, "%u", (uint32)item->valueint); + } + else + { + sprintf(str, "%llu", item->valueint); + } + } + } + return str; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, + 0xF8, 0xFC }; +static const char *parse_string(cJSON *item, const char *str) +{ + const char *ptr = str + 1; + char *ptr2; + char *out; + int len = 0; + unsigned uc, uc2; + if (*str != '\"') + { + ep = str; + return 0; + } /* not a string! */ + + while (*ptr != '\"' && *ptr && ++len) + if (*ptr++ == '\\') + ptr++; /* Skip escaped quotes. */ + + out = (char*) cJSON_malloc(len + 1); /* This is how long we need for the string, roughly. */ + if (!out) + return 0; + + ptr = str + 1; + ptr2 = out; + while (*ptr != '\"' && *ptr) + { + if (*ptr != '\\') + *ptr2++ = *ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': + *ptr2++ = '\b'; + break; + case 'f': + *ptr2++ = '\f'; + break; + case 'n': + *ptr2++ = '\n'; + break; + case 'r': + *ptr2++ = '\r'; + break; + case 't': + *ptr2++ = '\t'; + break; + case 'u': /* transcode utf16 to utf8. */ + sscanf(ptr + 1, "%4x", &uc); + ptr += 4; /* get the unicode char. */ + + if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) + break; // check for invalid. + + if (uc >= 0xD800 && uc <= 0xDBFF) // UTF16 surrogate pairs. + { + if (ptr[1] != '\\' || ptr[2] != 'u') + break; // missing second-half of surrogate. + sscanf(ptr + 3, "%4x", &uc2); + ptr += 6; + if (uc2 < 0xDC00 || uc2 > 0xDFFF) + break; // invalid second-half of surrogate. + uc = 0x10000 | ((uc & 0x3FF) << 10) | (uc2 & 0x3FF); + } + + len = 4; + if (uc < 0x80) + len = 1; + else if (uc < 0x800) + len = 2; + else if (uc < 0x10000) + len = 3; + ptr2 += len; + + switch (len) + { + case 4: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 3: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 2: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 1: + *--ptr2 = (uc | firstByteMark[len]); + } + ptr2 += len; + break; + default: + *ptr2++ = *ptr; + break; + } + ptr++; + } + } + *ptr2 = 0; + if (*ptr == '\"') + ptr++; + item->valuestring = out; + item->type = cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str) +{ + const char *ptr; + char *ptr2, *out; + int len = 0; + unsigned char token; + + if (!str) + return cJSON_strdup(""); + ptr = str; + while ((token = *ptr) && ++len) + { + if (strchr("\"\\\b\f\n\r\t", token)) + len++; + else if (token < 32) + len += 5; + ptr++; + } + + out = (char*) cJSON_malloc(len + 3); + if (!out) + return 0; + + ptr2 = out; + ptr = str; + *ptr2++ = '\"'; + while (*ptr) + { + if ((unsigned char) *ptr > 31 && *ptr != '\"' && *ptr != '\\') + *ptr2++ = *ptr++; + else + { + *ptr2++ = '\\'; + switch (token = *ptr++) + { + case '\\': + *ptr2++ = '\\'; + break; + case '\"': + *ptr2++ = '\"'; + break; + case '\b': + *ptr2++ = 'b'; + break; + case '\f': + *ptr2++ = 'f'; + break; + case '\n': + *ptr2++ = 'n'; + break; + case '\r': + *ptr2++ = 'r'; + break; + case '\t': + *ptr2++ = 't'; + break; + default: + sprintf(ptr2, "u%04x", token); + ptr2 += 5; + break; /* escape and print */ + } + } + } + *ptr2++ = '\"'; + *ptr2++ = 0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item) +{ + return print_string_ptr(item->valuestring); +} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item, const char *value); +static char *print_value(cJSON *item, int depth, int fmt); +static const char *parse_array(cJSON *item, const char *value); +static char *print_array(cJSON *item, int depth, int fmt); +static const char *parse_object(cJSON *item, const char *value); +static char *print_object(cJSON *item, int depth, int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) +{ + while (in && *in && (unsigned char) *in <= 32) + in++; + return in; +} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_Parse(const char *value) +{ + cJSON *c = cJSON_New_Item(); + ep = 0; + if (!c) + return 0; /* memory fail */ + + if (!parse_value(c, skip(value))) + { + cJSON_Delete(c); + return 0; + } + return c; +} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) +{ + return print_value(item, 0, 1); +} +char *cJSON_PrintUnformatted(cJSON *item) +{ + return print_value(item, 0, 0); +} + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item, const char *value) +{ + if (!value) + return 0; /* Fail on null. */ + if (!strncmp(value, "null", 4)) + { + item->type = cJSON_NULL; + return value + 4; + } + if (!strncmp(value, "false", 5)) + { + item->type = cJSON_False; + return value + 5; + } + if (!strncmp(value, "true", 4)) + { + item->type = cJSON_True; + item->valueint = 1; + return value + 4; + } + if (*value == '\"') + { + return parse_string(item, value); + } + if (*value == '-' || (*value >= '0' && *value <= '9')) + { + return parse_number(item, value); + } + if (*value == '[') + { + return parse_array(item, value); + } + if (*value == '{') + { + return parse_object(item, value); + } + + ep = value; + return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item, int depth, int fmt) +{ + char *out = 0; + if (!item) + return 0; + switch ((item->type) & 255) + { + case cJSON_NULL: + out = cJSON_strdup("null"); + break; + case cJSON_False: + out = cJSON_strdup("false"); + break; + case cJSON_True: + out = cJSON_strdup("true"); + break; + case cJSON_Int: + out = print_int(item); + break; + case cJSON_Double: + out = print_double(item); + break; + case cJSON_String: + out = print_string(item); + break; + case cJSON_Array: + out = print_array(item, depth, fmt); + break; + case cJSON_Object: + out = print_object(item, depth, fmt); + break; + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item, const char *value) +{ + cJSON *child; + if (*value != '[') + { + ep = value; + return 0; + } /* not an array! */ + + item->type = cJSON_Array; + value = skip(value + 1); + if (*value == ']') + return value + 1; /* empty array. */ + + item->child = child = cJSON_New_Item(); + if (!item->child) + return 0; /* memory fail */ + value = skip(parse_value(child, skip(value))); /* skip any spacing, get the value. */ + if (!value) + return 0; + + while (*value == ',') + { + cJSON *new_item; + if (!(new_item = cJSON_New_Item())) + return 0; /* memory fail */ + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip(parse_value(child, skip(value + 1))); + if (!value) + return 0; /* memory fail */ + } + + if (*value == ']') + return value + 1; /* end of array */ + ep = value; + return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item, int depth, int fmt) +{ + char **entries; + char *out = 0, *ptr, *ret; + int len = 5; + cJSON *child = item->child; + int numentries = 0, i = 0, fail = 0; + + /* How many entries in the array? */ + while (child) + numentries++, child = child->next; + /* Allocate an array to hold the values for each */ + entries = (char**) cJSON_malloc(numentries * sizeof(char*)); + if (!entries) + return 0; + memset(entries, 0, numentries * sizeof(char*)); + /* Retrieve all the results: */ + child = item->child; + while (child && !fail) + { + ret = print_value(child, depth + 1, fmt); + entries[i++] = ret; + if (ret) + len += strlen(ret) + 2 + (fmt ? 1 : 0); + else + fail = 1; + child = child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) + out = (char*) cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) + fail = 1; + + /* Handle failure. */ + if (fail) + { + for (i = 0; i < numentries; i++) + if (entries[i]) + cJSON_free(entries[i]); + cJSON_free(entries); + return 0; + } + + /* Compose the output array. */ + *out = '['; + ptr = out + 1; + *ptr = 0; + for (i = 0; i < numentries; i++) + { + strcpy(ptr, entries[i]); + ptr += strlen(entries[i]); + if (i != numentries - 1) + { + *ptr++ = ','; + if (fmt) + *ptr++ = ' '; + *ptr = 0; + } + cJSON_free(entries[i]); + } + cJSON_free(entries); + *ptr++ = ']'; + *ptr++ = 0; + return out; +} + +/* Build an object from the text. */ +static const char *parse_object(cJSON *item, const char *value) +{ + cJSON *child; + if (*value != '{') + { + ep = value; + return 0; + } /* not an object! */ + + item->type = cJSON_Object; + value = skip(value + 1); + if (*value == '}') + return value + 1; /* empty array. */ + + item->child = child = cJSON_New_Item(); + if (!item->child) + return 0; + value = skip(parse_string(child, skip(value))); + if (!value) + return 0; + child->string = child->valuestring; + child->valuestring = 0; + if (*value != ':') + { + ep = value; + return 0; + } /* fail! */ + value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) + return 0; + + while (*value == ',') + { + cJSON *new_item; + if (!(new_item = cJSON_New_Item())) + return 0; /* memory fail */ + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip(parse_string(child, skip(value + 1))); + if (!value) + return 0; + child->string = child->valuestring; + child->valuestring = 0; + if (*value != ':') + { + ep = value; + return 0; + } /* fail! */ + value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) + return 0; + } + + if (*value == '}') + return value + 1; /* end of array */ + ep = value; + return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item, int depth, int fmt) +{ + char **entries = 0, **names = 0; + char *out = 0, *ptr, *ret, *str; + int len = 7, i = 0, j; + cJSON *child = item->child; + int numentries = 0, fail = 0; + /* Count the number of entries. */ + while (child) + numentries++, child = child->next; + /* Allocate space for the names and the objects */ + entries = (char**) cJSON_malloc(numentries * sizeof(char*)); + if (!entries) + return 0; + names = (char**) cJSON_malloc(numentries * sizeof(char*)); + if (!names) + { + cJSON_free(entries); + return 0; + } + memset(entries, 0, sizeof(char*) * numentries); + memset(names, 0, sizeof(char*) * numentries); + + /* Collect all the results into our arrays: */ + child = item->child; + depth++; + if (fmt) + len += depth; + while (child) + { + names[i] = str = print_string_ptr(child->string); + entries[i++] = ret = print_value(child, depth, fmt); + if (str && ret) + len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0); + else + fail = 1; + child = child->next; + } + + /* Try to allocate the output string */ + if (!fail) + out = (char*) cJSON_malloc(len); + if (!out) + fail = 1; + + /* Handle failure */ + if (fail) + { + for (i = 0; i < numentries; i++) + { + if (names[i]) + cJSON_free(names[i]); + if (entries[i]) + cJSON_free(entries[i]); + } + cJSON_free(names); + cJSON_free(entries); + return 0; + } + + /* Compose the output: */ + *out = '{'; + ptr = out + 1; + if (fmt) + *ptr++ = '\n'; + *ptr = 0; + for (i = 0; i < numentries; i++) + { + if (fmt) + for (j = 0; j < depth; j++) + *ptr++ = '\t'; + strcpy(ptr, names[i]); + ptr += strlen(names[i]); + *ptr++ = ':'; + if (fmt) + *ptr++ = '\t'; + strcpy(ptr, entries[i]); + ptr += strlen(entries[i]); + if (i != numentries - 1) + *ptr++ = ','; + if (fmt) + *ptr++ = '\n'; + *ptr = 0; + cJSON_free(names[i]); + cJSON_free(entries[i]); + } + + cJSON_free(names); + cJSON_free(entries); + if (fmt) + for (i = 0; i < depth - 1; i++) + *ptr++ = '\t'; + *ptr++ = '}'; + *ptr++ = 0; + return out; +} + +/* Get Array size/item / object item. */ +int cJSON_GetArraySize(cJSON *array) +{ + cJSON *c = array->child; + int i = 0; + while (c) + i++, c = c->next; + return i; +} +cJSON *cJSON_GetArrayItem(cJSON *array, int item) +{ + cJSON *c = array->child; + while (c && item > 0) + item--, c = c->next; + return c; +} +cJSON *cJSON_GetObjectItem(cJSON *object, const char *string) +{ + cJSON *c = object->child; + while (c && cJSON_strcasecmp(c->string, string)) + c = c->next; + return c; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) +{ + cJSON *ref = cJSON_New_Item(); + if (!ref) + return 0; + memcpy(ref, item, sizeof(cJSON)); + ref->string = 0; + ref->type |= cJSON_IsReference; + ref->next = ref->prev = 0; + return ref; +} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + cJSON *c = array->child; + if (!item) + return; + if (!c) + { + array->child = item; + } + else + { + while (c && c->next) + c = c->next; + suffix_object(c, item); + } +} + +void cJSON_AddItemToArrayHead(cJSON *array, cJSON *item) +{ + cJSON *c = array->child; + if (!item) + return; + if (!c) + { + array->child = item; + } + else + { + item->prev = c->prev; + item->next = c; + c->prev = item; + array->child = item; + } +} + +void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + if (!item) + return; + if (item->string) + cJSON_free(item->string); + item->string = cJSON_strdup(string); + cJSON_AddItemToArray(object, item); +} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + cJSON_AddItemToArray(array, create_reference(item)); +} +void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, + cJSON *item) +{ + cJSON_AddItemToObject(object, string, create_reference(item)); +} + +cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) +{ + cJSON *c = array->child; + while (c && which > 0) + c = c->next, which--; + if (!c) + return 0; + if (c->prev) + c->prev->next = c->next; + if (c->next) + c->next->prev = c->prev; + if (c == array->child) + array->child = c->next; + c->prev = c->next = 0; + return c; +} +void cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} +cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + int i = 0; + cJSON *c = object->child; + while (c && cJSON_strcasecmp(c->string, string)) + i++, c = c->next; + if (c) + return cJSON_DetachItemFromArray(object, i); + return 0; +} +void cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +/* Replace array/object items with new ones. */ +void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *c = array->child; + while (c && which > 0) + c = c->next, which--; + if (!c) + return; + newitem->next = c->next; + newitem->prev = c->prev; + if (newitem->next) + newitem->next->prev = newitem; + if (c == array->child) + array->child = newitem; + else + newitem->prev->next = newitem; + c->next = c->prev = 0; + cJSON_Delete(c); +} +void cJSON_ReplaceItemInObject(cJSON *object, const char *string, + cJSON *newitem) +{ + int i = 0; + cJSON *c = object->child; + while (c && cJSON_strcasecmp(c->string, string)) + i++, c = c->next; + if (c) + { + newitem->string = cJSON_strdup(string); + cJSON_ReplaceItemInArray(object, i, newitem); + } +} + +/* Create basic types: */ +cJSON *cJSON_CreateNull() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_NULL; + return item; +} +cJSON *cJSON_CreateTrue() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_True; + return item; +} +cJSON *cJSON_CreateFalse() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_False; + return item; +} +cJSON *cJSON_CreateBool(int b) +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = b ? cJSON_True : cJSON_False; + return item; +} +cJSON *cJSON_CreateDouble(double num, int sign) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_Double; + item->valuedouble = num; + item->valueint = (uint64)num; + item->sign = sign; + } + return item; +} +cJSON *cJSON_CreateInt(uint64 num, int sign) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_Int; + item->valuedouble = (double)num; + item->valueint = (uint64)num; + item->sign = sign; + } + return item; +} +cJSON *cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_String; + item->valuestring = cJSON_strdup(string); + } + return item; +} +cJSON *cJSON_CreateArray() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_Array; + return item; +} +cJSON *cJSON_CreateObject() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_Object; + return item; +} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(int *numbers, int sign, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateDouble((long double)((unsigned int)numbers[i]), sign); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} +cJSON *cJSON_CreateFloatArray(float *numbers, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateDouble((long double)numbers[i], -1); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} +cJSON *cJSON_CreateDoubleArray(double *numbers, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateDouble((long double)numbers[i], -1); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} +cJSON *cJSON_CreateStringArray(const char **strings, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateString(strings[i]); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} + From b987c2a631d47609e1384ee6681f3ceb5fbdad7e Mon Sep 17 00:00:00 2001 From: Bwar Date: Thu, 3 Oct 2019 20:30:10 +0800 Subject: [PATCH 059/176] add tag --- README.md | 3 +++ README_cn.md | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b61f3ab0..070e30c3 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,9 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v0.10 + - the plugin version control dynamically unloads and loads the instant validation feature. + - optimize reflection dynamic creation of Actor. #### v0.9 - add Model - add Chain diff --git a/README_cn.md b/README_cn.md index 2a27eb74..cc797962 100644 --- a/README_cn.md +++ b/README_cn.md @@ -108,12 +108,15 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 开发任务 - - 完成开发指南 - - NebulaMydis数据代理服务 + - 完成开发指南 + - NebulaMydis数据代理服务 - 应用Nebula开发IM项目 ## 版本历史 +#### v0.10 + - 增加插件so版本控制动态卸载和加载即时生效功能 + - 优化反射动态创建Actor #### v0.9 - 增加Model模型组件 - 增加Chain调用链组件 From dd06deb54e3c1624867782696f9e9a61c1a27f19 Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 7 Oct 2019 22:41:25 +0800 Subject: [PATCH 060/176] seprate Dispatcher and ActorBuilder from Manager and Worker --- .gitignore | 3 + conf/nebula.json | 94 +- src/actor/Actor.cpp | 68 +- src/actor/Actor.hpp | 33 +- src/actor/ActorBuilder.cpp | 1313 ++++++++ src/actor/ActorBuilder.hpp | 285 ++ src/actor/ActorFriend.hpp | 31 + src/actor/chain/Chain.hpp | 5 +- src/actor/cmd/Cmd.hpp | 5 +- src/actor/cmd/Module.hpp | 6 +- src/actor/cmd/sys_cmd/CmdBeat.hpp | 5 +- src/actor/cmd/sys_cmd/CmdNodeNotice.cpp | 6 +- src/actor/cmd/sys_cmd/CmdNodeNotice.hpp | 5 +- src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp | 2 +- src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp | 5 +- src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp | 3 +- src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp | 5 +- .../cmd/sys_cmd/CmdSetNodeCustomConf.cpp | 3 +- .../cmd/sys_cmd/CmdSetNodeCustomConf.hpp | 5 +- src/actor/cmd/sys_cmd/CmdToldWorker.cpp | 3 +- src/actor/cmd/sys_cmd/CmdToldWorker.hpp | 5 +- src/actor/cmd/sys_cmd/CmdUpdateNodeId.cpp | 2 +- src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp | 5 +- src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp | 3 +- src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp | 5 +- .../sys_cmd/manager/CmdOnGetCustomConf.cpp | 80 + .../sys_cmd/manager/CmdOnGetCustomConf.hpp | 37 + .../cmd/sys_cmd/manager/CmdOnGetNodeConf.cpp | 44 + .../cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp | 37 + .../manager/CmdOnGetNodeCustomConf.cpp | 45 + .../manager/CmdOnGetNodeCustomConf.hpp | 39 + .../cmd/sys_cmd/manager/CmdOnNodeNotice.cpp | 107 + .../cmd/sys_cmd/manager/CmdOnNodeNotice.hpp | 40 + .../manager/CmdOnOrientationFdTransfer.cpp | 74 + .../manager/CmdOnOrientationFdTransfer.hpp | 41 + .../sys_cmd/manager/CmdOnSetCustomConf.cpp | 87 + .../sys_cmd/manager/CmdOnSetCustomConf.hpp | 41 + .../cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp | 98 + .../cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp | 41 + .../manager/CmdOnSetNodeCustomConf.cpp | 88 + .../manager/CmdOnSetNodeCustomConf.hpp | 41 + .../cmd/sys_cmd/manager/CmdOnTellWorker.cpp | 61 + .../cmd/sys_cmd/manager/CmdOnTellWorker.hpp | 38 + .../cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp | 46 + .../cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp | 40 + src/actor/context/Context.hpp | 5 +- src/actor/context/HttpContext.hpp | 5 +- src/actor/context/PbContext.hpp | 5 +- src/actor/model/Model.hpp | 5 +- src/actor/session/Session.cpp | 4 +- src/actor/session/Session.hpp | 12 +- .../session/sys_session/SessionLogger.cpp | 1 + .../sys_session/SessionManagerLogger.cpp | 44 - .../sys_session/SessionManagerLogger.hpp | 39 - .../sys_session/manager/SessionManager.cpp | 331 ++ .../sys_session/manager/SessionManager.hpp | 53 + src/actor/step/PbStep.hpp | 4 +- src/actor/step/Step.hpp | 4 +- src/actor/step/sys_step/StepConnectWorker.cpp | 1 + src/actor/step/sys_step/StepIoTimeout.cpp | 6 +- src/actor/step/sys_step/StepIoTimeout.hpp | 6 +- src/actor/step/sys_step/StepTellWorker.cpp | 5 +- src/actor/step/sys_step/StepTellWorker.hpp | 6 +- .../sys_step/manager/StepReportToBeacon.cpp | 85 + .../sys_step/manager/StepReportToBeacon.hpp | 48 + src/channel/RedisChannel.hpp | 6 +- src/channel/SocketChannel.cpp | 15 + src/channel/SocketChannel.hpp | 9 +- src/channel/SocketChannelImpl.cpp | 1 - src/channel/SocketChannelImpl.hpp | 6 +- src/ios/Dispatcher.cpp | 1667 ++++++++++ src/ios/Dispatcher.hpp | 219 ++ .../SessionNode.cpp => ios/Nodes.cpp} | 28 +- .../SessionNode.hpp => ios/Nodes.hpp} | 14 +- src/labor/Labor.hpp | 40 +- src/labor/Manager.cpp | 2151 +----------- src/labor/Manager.hpp | 297 +- src/labor/NodeInfo.hpp | 73 + src/labor/Worker.cpp | 452 ++- src/labor/Worker.hpp | 174 +- src/labor/WorkerFriend.hpp | 30 - src/labor/WorkerImpl.cpp | 2927 ----------------- src/labor/WorkerImpl.hpp | 398 --- src/labor/WorkerImpl.inl | 103 - src/logger/NetLogger.cpp | 15 +- 85 files changed, 6141 insertions(+), 6183 deletions(-) create mode 100644 src/actor/ActorBuilder.cpp create mode 100644 src/actor/ActorBuilder.hpp create mode 100644 src/actor/ActorFriend.hpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.cpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.cpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.cpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.cpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp delete mode 100644 src/actor/session/sys_session/SessionManagerLogger.cpp delete mode 100644 src/actor/session/sys_session/SessionManagerLogger.hpp create mode 100644 src/actor/session/sys_session/manager/SessionManager.cpp create mode 100644 src/actor/session/sys_session/manager/SessionManager.hpp create mode 100644 src/actor/step/sys_step/manager/StepReportToBeacon.cpp create mode 100644 src/actor/step/sys_step/manager/StepReportToBeacon.hpp create mode 100644 src/ios/Dispatcher.cpp create mode 100644 src/ios/Dispatcher.hpp rename src/{actor/session/sys_session/SessionNode.cpp => ios/Nodes.cpp} (88%) rename src/{actor/session/sys_session/SessionNode.hpp => ios/Nodes.hpp} (91%) create mode 100644 src/labor/NodeInfo.hpp delete mode 100644 src/labor/WorkerFriend.hpp delete mode 100644 src/labor/WorkerImpl.cpp delete mode 100644 src/labor/WorkerImpl.hpp delete mode 100644 src/labor/WorkerImpl.inl diff --git a/.gitignore b/.gitignore index 29188631..fdf1d2e5 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ compile install-sh missing *.swp +.cproject +.project +.settings diff --git a/conf/nebula.json b/conf/nebula.json index 770dbd76..13411f66 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -46,16 +46,6 @@ "log_levels": { "FATAL": 0, "CRITICAL": 1, "ERROR": 2, "NOTICE": 3, "WARNING": 4, "INFO": 5, "DEBUG": 6, "TRACE": 7 }, "log_level": 7, "net_log_level": 6, - "boot_load": { - "cmd": [ - { "cmd": 1001, "class": "neb::CmdHello" }, - { "cmd": 1003, "class": "neb::CmdDbAgent" } - ], - "module": [ - { "path": "neb/switch", "class": "neb::ModuleSwitch" }, - { "path": "neb/hello", "class": "neb::ModuleHello" } - ] - }, "//with_ssl": "SSL配置(可为空),路径为相对${WorkPath}的相对路径,公钥文件和私钥文件均为PEM格式", "with_ssl": { "config_path": "conf/ssl", @@ -64,41 +54,59 @@ }, "//refresh_interval": "刷新Server配置,检查、加载插件动态库时间周期(周期时间长短视服务器忙闲而定)", "refresh_interval": 60, - "dynamic_loading": [{ - "so_path": "plugins/User.so", - "load": false, - "version": "1.0", - "cmd": [ - { "cmd": 2001, "class": "neb::CmdUserLogin" }, - { "cmd": 2003, "class": "neb::CmdUserLogout" } - ], - "module": [ - { "path": "im/user/login", "class": "neb::ModuleLogin" }, - { "path": "im/user/logout", "class": "neb::ModuleLogout" } - ], - "session":["im::SessionUser", "im::SessionGroup"], - "step":["im::StepLogin", "im::StepLogout", "im::StepP2pChat"], - "matrix":[], - "chain":[] + "load_config":{ + "manager":{ }, - { - "so_path": "plugins/ChatMsg.so", - "load": false, - "version": "1.0", - "cmd": [ - { "cmd": 2001, "class": "neb::CmdChat" } + "worker":{ + "boot_load": { + "cmd": [ + { "cmd": 1001, "class": "neb::CmdHello" }, + { "cmd": 1003, "class": "neb::CmdDbAgent" } + ], + "module": [ + { "path": "neb/switch", "class": "neb::ModuleSwitch" }, + { "path": "neb/hello", "class": "neb::ModuleHello" } + ] + }, + "dynamic_loading": [{ + "so_path": "plugins/User.so", + "load": false, + "version": "1.0", + "cmd": [ + { "cmd": 2001, "class": "neb::CmdUserLogin" }, + { "cmd": 2003, "class": "neb::CmdUserLogout" } + ], + "module": [ + { "path": "im/user/login", "class": "neb::ModuleLogin" }, + { "path": "im/user/logout", "class": "neb::ModuleLogout" } + ], + "session":["im::SessionUser", "im::SessionGroup"], + "step":["im::StepLogin", "im::StepLogout", "im::StepP2pChat"], + "matrix":[], + "chain":[] + }, + { + "so_path": "plugins/ChatMsg.so", + "load": false, + "version": "1.0", + "cmd": [ + { "cmd": 2001, "class": "neb::CmdChat" } + ], + "module": [], + "session":[], + "step":[], + "matrix":[], + "chain":[] + } ], - "module": [], - "session":[], - "step":[], - "matrix":[], - "chain":[] - } - ], - "runtime":{ - "chains":{ - "chain_1":["step1", "matrix1", ["step2A", "step2B", "step2C"], "step3", "matrix2"], - "chain_2":[] + "runtime":{ + "chains":{ + "chain_1":["step1", "matrix1", ["step2A", "step2B", "step2C"], "step3", "matrix2"], + "chain_2":[] + } + } + }, + "loader":{ } }, "//custom": "自定义配置,用于通过框架层带给业务", diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 53dee234..ec9f711b 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -8,10 +8,11 @@ * Modify history: ******************************************************************************/ -#include "labor/Worker.hpp" #include "actor/Actor.hpp" +#include "ios/Dispatcher.hpp" #include "actor/session/Session.hpp" #include "actor/step/Step.hpp" +#include "labor/Worker.hpp" namespace neb { @@ -19,14 +20,14 @@ namespace neb Actor::Actor(ACTOR_TYPE eActorType, ev_tstamp dTimeout) : m_eActorType(eActorType), m_uiSequence(0), m_dActiveTime(0.0), m_dTimeout(dTimeout), - m_pWorker(nullptr), m_pTimerWatcher(NULL), m_pContext(nullptr) + m_pLabor(nullptr), m_pTimerWatcher(NULL), m_pContext(nullptr) { } Actor::~Actor() { FREE(m_pTimerWatcher); - m_pWorker->Logger(m_strTraceId, Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "eActorType %d, seq %u", m_eActorType, m_uiSequence); + m_pLabor->GetActorBuilder()->Logger(m_strTraceId, Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "eActorType %d, seq %u", m_eActorType, m_uiSequence); } uint32 Actor::GetSequence() @@ -34,9 +35,9 @@ uint32 Actor::GetSequence() if ((ACT_CMD == m_eActorType || ACT_MODULE == m_eActorType) // Cmd和Module总是获取最新Seq || 0 == m_uiSequence) { - if (nullptr != m_pWorker) + if (nullptr != m_pLabor) { - m_uiSequence = m_pWorker->GetSequence(); + m_uiSequence = m_pLabor->GetSequence(); } } return(m_uiSequence); @@ -44,61 +45,56 @@ uint32 Actor::GetSequence() uint32 Actor::GetNodeId() const { - return(m_pWorker->GetNodeId()); + return(m_pLabor->GetNodeInfo().uiNodeId); } uint32 Actor::GetWorkerIndex() const { - return(m_pWorker->GetWorkerIndex()); -} - -ev_tstamp Actor::GetDefaultTimeout() const -{ - return(m_pWorker->GetDefaultTimeout()); + return(((Worker*)m_pLabor)->GetWorkerInfo().iWorkerIndex); } const std::string& Actor::GetNodeType() const { - return(m_pWorker->GetNodeType()); + return(m_pLabor->GetNodeInfo().strNodeType); } const std::string& Actor::GetWorkPath() const { - return(m_pWorker->GetWorkPath()); + return(m_pLabor->GetNodeInfo().strWorkPath); } const std::string& Actor::GetNodeIdentify() const { - return(m_pWorker->GetNodeIdentify()); + return(m_pLabor->GetNodeInfo().strNodeIdentify); } time_t Actor::GetNowTime() const { - return(m_pWorker->GetNowTime()); + return(m_pLabor->GetNowTime()); } const CJsonObject& Actor::GetCustomConf() const { - return(m_pWorker->GetCustomConf()); + return(((Worker*)m_pLabor)->GetCustomConf()); } -std::shared_ptr Actor::GetSession(uint64 ullSessionId) +std::shared_ptr Actor::GetSession(uint32 uiSessionId) { - return(m_pWorker->GetSession(ullSessionId)); + return(m_pLabor->GetActorBuilder()->GetSession(uiSessionId)); } std::shared_ptr Actor::GetSession(const std::string& strSessionId) { - return(m_pWorker->GetSession(strSessionId)); + return(m_pLabor->GetActorBuilder()->GetSession(strSessionId)); } bool Actor::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg, void* data) { - return(m_pWorker->ExecStep(uiStepSeq, iErrno, strErrMsg, data)); + return(m_pLabor->GetActorBuilder()->ExecStep(uiStepSeq, iErrno, strErrMsg, data)); } std::shared_ptr Actor::GetModel(const std::string& strModelName) { - return(m_pWorker->GetModel(strModelName)); + return(m_pLabor->GetActorBuilder()->GetModel(strModelName)); } std::shared_ptr Actor::GetContext() @@ -113,67 +109,67 @@ void Actor::SetContext(std::shared_ptr pContext) void Actor::AddAssemblyLine(std::shared_ptr pSession) { - m_pWorker->AddAssemblyLine(pSession); + m_pLabor->GetActorBuilder()->AddAssemblyLine(pSession); } bool Actor::SendTo(std::shared_ptr pChannel) { - return(m_pWorker->SendTo(pChannel)); + return(m_pLabor->GetDispatcher()->SendTo(pChannel)); } bool Actor::SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - return(m_pWorker->SendTo(pChannel, iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendTo(pChannel, iCmd, uiSeq, oMsgBody, this)); } bool Actor::SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) { - return(m_pWorker->SendTo(pChannel, oHttpMsg)); + return(m_pLabor->GetDispatcher()->SendTo(pChannel, oHttpMsg)); } bool Actor::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - return(m_pWorker->SendTo(strIdentify, iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendTo(strIdentify, iCmd, uiSeq, oMsgBody, this)); } bool Actor::SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg) { - return(m_pWorker->SendTo(strHost, iPort, strUrlPath, oHttpMsg, this->GetSequence())); + return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, strUrlPath, oHttpMsg, this->GetSequence())); } bool Actor::SendTo(std::shared_ptr pChannel) { - return(m_pWorker->SendTo(pChannel, this)); + return(m_pLabor->GetDispatcher()->SendTo(pChannel, this)); } bool Actor::SendTo(const std::string& strIdentify) { - return(m_pWorker->SendTo(strIdentify, this)); + return(m_pLabor->GetDispatcher()->SendTo(strIdentify, this)); } bool Actor::SendTo(const std::string& strHost, int iPort) { - return(m_pWorker->SendTo(strHost, iPort, this)); + return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, this)); } bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - return(m_pWorker->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, this)); } bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - return(m_pWorker->SendOriented(strNodeType, uiFactor, iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, uiFactor, iCmd, uiSeq, oMsgBody, this)); } bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - return(m_pWorker->SendOriented(strNodeType, iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, iCmd, uiSeq, oMsgBody, this)); } -void Actor::SetWorker(Worker* pWorker) +void Actor::SetLabor(Labor* pLabor) { - m_pWorker = pWorker; + m_pLabor = pLabor; } ev_timer* Actor::MutableTimerWatcher() diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index c08ed2a0..3e45fe23 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -28,13 +28,16 @@ #include "pb/http.pb.h" #include "util/json/CJsonObject.hpp" #include "channel/Channel.hpp" +#include "labor/Labor.hpp" +#include "ActorBuilder.hpp" namespace neb { -class Worker; -class WorkerImpl; -class WorkerFriend; +class Labor; +class Dispatcher; +class ActorBuilder; +class ActorFriend; class SocketChannel; class RedisChannel; @@ -99,7 +102,6 @@ class Actor: public std::enable_shared_from_this protected: uint32 GetNodeId() const; uint32 GetWorkerIndex() const; - ev_tstamp GetDefaultTimeout() const; const std::string& GetNodeType() const; const std::string& GetWorkPath() const; const std::string& GetNodeIdentify() const; @@ -111,7 +113,7 @@ class Actor: public std::enable_shared_from_this */ const CJsonObject& GetCustomConf() const; - std::shared_ptr GetSession(uint64 ullSessionId); + std::shared_ptr GetSession(uint32 uiSessionId); std::shared_ptr GetSession(const std::string& strSessionId); bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); std::shared_ptr GetModel(const std::string& strModelName); @@ -242,7 +244,7 @@ class Actor: public std::enable_shared_from_this } private: - void SetWorker(Worker* pWorker); + void SetLabor(Labor* pLabor); ev_timer* MutableTimerWatcher(); void SetActorName(const std::string& strActorName); void SetTraceId(const std::string& strTraceId); @@ -252,51 +254,52 @@ class Actor: public std::enable_shared_from_this uint32 m_uiSequence; ev_tstamp m_dActiveTime; ev_tstamp m_dTimeout; - Worker* m_pWorker; + Labor* m_pLabor; ev_timer* m_pTimerWatcher; std::string m_strActorName; std::string m_strTraceId; // for log trace std::shared_ptr m_pContext; - friend class WorkerImpl; - friend class WorkerFriend; + friend class Dispatcher; + friend class ActorBuilder; + friend class ActorFriend; friend class Chain; }; template void Actor::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { - m_pWorker->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); + m_pLabor->GetActorBuilder()->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } template std::shared_ptr Actor::MakeSharedStep(const std::string& strStepName, Targs&&... args) { - return(m_pWorker->MakeSharedStep(this, strStepName, std::forward(args)...)); + return(m_pLabor->GetActorBuilder()->MakeSharedStep(this, strStepName, std::forward(args)...)); } template std::shared_ptr Actor::MakeSharedSession(const std::string& strSessionName, Targs&&... args) { - return(m_pWorker->MakeSharedSession(this, strSessionName, std::forward(args)...)); + return(m_pLabor->GetActorBuilder()->MakeSharedSession(this, strSessionName, std::forward(args)...)); } template std::shared_ptr Actor::MakeSharedContext(const std::string& strContextName, Targs&&... args) { - return(m_pWorker->MakeSharedContext(this, strContextName, std::forward(args)...)); + return(m_pLabor->GetActorBuilder()->MakeSharedContext(this, strContextName, std::forward(args)...)); } template std::shared_ptr Actor::MakeSharedActor(const std::string& strActorName, Targs&&... args) { - return(m_pWorker->MakeSharedActor(this, strActorName, std::forward(args)...)); + return(m_pLabor->GetActorBuilder()->MakeSharedActor(this, strActorName, std::forward(args)...)); } template std::shared_ptr Actor::MakeSharedChain(const std::string& strChainName, Targs&&... args) { - return(m_pWorker->MakeSharedChain(this, strChainName, std::forward(args)...)); + return(m_pLabor->GetActorBuilder()->MakeSharedChain(this, strChainName, std::forward(args)...)); } } /* namespace neb */ diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp new file mode 100644 index 00000000..f3fd1957 --- /dev/null +++ b/src/actor/ActorBuilder.cpp @@ -0,0 +1,1313 @@ +/******************************************************************************* + * Project: Nebula + * @file ActorBuilder.cpp + * @brief Actor创建和管理 + * @author Bwar + * @date: 2019年9月15日 + * @note + * Modify history: + ******************************************************************************/ + +#include +#include "ActorBuilder.hpp" +#include "labor/NodeInfo.hpp" +#include "Actor.hpp" +#include "cmd/Cmd.hpp" +#include "cmd/Module.hpp" +#include "session/Session.hpp" +#include "step/HttpStep.hpp" +#include "step/PbStep.hpp" +#include "step/RedisStep.hpp" +#include "step/Step.hpp" +#include "model/Model.hpp" +#include "chain/Chain.hpp" +#include "actor/session/sys_session/SessionLogger.hpp" +#include "ios/Dispatcher.hpp" +#include "channel/SocketChannel.hpp" + +namespace neb +{ + +ActorBuilder::ActorBuilder(Labor* pLabor, std::shared_ptr pLogger) + : m_pErrBuff(nullptr), m_pLabor(pLabor), m_pLogger(pLogger) +{ + m_pErrBuff = (char*)malloc(gc_iErrBuffLen); +} + +ActorBuilder::~ActorBuilder() +{ + m_mapCmd.clear(); + m_mapCallbackStep.clear(); + m_mapCallbackSession.clear(); + + for (auto so_iter = m_mapLoadedSo.begin(); + so_iter != m_mapLoadedSo.end(); ++so_iter) + { + DELETE(so_iter->second); + } + m_mapLoadedSo.clear(); + if (m_pErrBuff != nullptr) + { + free(m_pErrBuff); + m_pErrBuff = nullptr; + } +} + +bool ActorBuilder::Init(CJsonObject& oBootLoadConf, CJsonObject& oDynamicLoadConf) +{ + LoadSysCmd(); + BootLoadCmd(oBootLoadConf); + DynamicLoad(oDynamicLoadConf); + return(true); +} + +void ActorBuilder::StepTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) +{ + if (watcher->data != NULL) + { + Step* pStep = (Step*)watcher->data; + pStep->m_pLabor->GetActorBuilder()->OnStepTimeout(std::dynamic_pointer_cast(pStep->shared_from_this())); + } +} + +void ActorBuilder::SessionTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) +{ + if (watcher->data != NULL) + { + Session* pSession = (Session*)watcher->data; + pSession->m_pLabor->GetActorBuilder()->OnSessionTimeout(std::dynamic_pointer_cast(pSession->shared_from_this())); + } +} + +void ActorBuilder::ChainTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) +{ + if (watcher->data != NULL) + { + Chain* pChain = (Chain*)watcher->data; + pChain->m_pLabor->GetActorBuilder()->OnChainTimeout(std::dynamic_pointer_cast(pChain->shared_from_this())); + } +} + +bool ActorBuilder::OnStepTimeout(std::shared_ptr pStep) +{ + ev_timer* watcher = pStep->MutableTimerWatcher(); + ev_tstamp after = pStep->GetActiveTime() - m_pLabor->GetNowTime() + pStep->GetTimeout(); + if (after > 0) // 在定时时间内被重新刷新过,重新设置定时器 + { + m_pLabor->GetDispatcher()->RefreshEvent(watcher, after); + return(true); + } + else // 步骤已超时 + { + LOG4_TRACE("seq %lu: active_time %lf, now_time %lf, lifetime %lf", + pStep->GetSequence(), pStep->GetActiveTime(), m_pLabor->GetNowTime(), pStep->GetTimeout()); + E_CMD_STATUS eResult = pStep->Timeout(); + if (CMD_STATUS_RUNNING == eResult) + { + ev_tstamp after = pStep->GetTimeout(); + m_pLabor->GetDispatcher()->RefreshEvent(watcher, after); + return(true); + } + else + { + RemoveChain(pStep->GetChainId()); + RemoveStep(pStep); + return(true); + } + } +} + +bool ActorBuilder::OnSessionTimeout(std::shared_ptr pSession) +{ + ev_timer* watcher = pSession->MutableTimerWatcher(); + LOG4_TRACE("CHECK watchar = 0x%x", watcher); + ev_tstamp after = pSession->GetActiveTime() - m_pLabor->GetNowTime() + pSession->GetTimeout(); + if (after > 0) // 定时时间内被重新刷新过,重新设置定时器 + { + m_pLabor->GetDispatcher()->RefreshEvent(watcher, after); + return(true); + } + else // 会话已超时 + { + LOG4_TRACE("session_id: %s", pSession->GetSessionId().c_str()); + if (CMD_STATUS_RUNNING == pSession->Timeout()) + { + m_pLabor->GetDispatcher()->RefreshEvent(watcher, pSession->GetTimeout()); + return(true); + } + else + { + RemoveSession(pSession); + return(true); + } + } +} + +bool ActorBuilder::OnChainTimeout(std::shared_ptr pChain) +{ + ev_timer* watcher = pChain->MutableTimerWatcher(); + LOG4_TRACE("CHECK watchar = 0x%x", watcher); + ev_tstamp after = pChain->GetActiveTime() - m_pLabor->GetNowTime() + pChain->GetTimeout(); + if (after > 0) // 定时时间内被重新刷新过,重新设置定时器 + { + m_pLabor->GetDispatcher()->RefreshEvent(watcher, after); + return(true); + } + else // 会话已超时 + { + if (CMD_STATUS_RUNNING == pChain->Timeout()) + { + m_pLabor->GetDispatcher()->RefreshEvent(watcher, pChain->GetTimeout()); + return(true); + } + else + { + RemoveChain(pChain->GetSequence()); + return(true); + } + } +} + +bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) +{ + LOG4_DEBUG("cmd %u, seq %lu", oMsgHead.cmd(), oMsgHead.seq()); + if (gc_uiCmdReq & oMsgHead.cmd()) // 新请求 + { + MsgHead oOutMsgHead; + MsgBody oOutMsgBody; + auto cmd_iter = m_mapCmd.find(gc_uiCmdBit & oMsgHead.cmd()); + if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) + { + if (oMsgBody.trace_id().length() > 10) + { + cmd_iter->second->SetTraceId(oMsgBody.trace_id()); + } + else + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + cmd_iter->second->SetTraceId(oss.str()); + } + cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); + } + else // 没有对应的cmd,是需由接入层转发的请求 + { + if (CMD_REQ_SET_LOG_LEVEL == oMsgHead.cmd()) + { + LogLevel oLogLevel; + oLogLevel.ParseFromString(oMsgBody.data()); + LOG4_INFO("log level set to %d", oLogLevel.log_level()); + m_pLogger->SetLogLevel(oLogLevel.log_level()); + } + else if (CMD_REQ_RELOAD_SO == oMsgHead.cmd()) + { + CJsonObject oSoConfJson; + if (oSoConfJson.Parse(oMsgBody.data())) + { + DynamicLoad(oSoConfJson); + } + else + { + LOG4_ERROR("json parse string error: \"%s\""); + } + } + else + { + if (CODEC_NEBULA == pChannel->GetCodecType()) // 内部服务往客户端发送 if (std::string("0.0.0.0") == strFromIp) + { + cmd_iter = m_mapCmd.find(CMD_REQ_TO_CLIENT); + if (cmd_iter != m_mapCmd.end()) + { + if (oMsgBody.trace_id().length() > 10) + { + cmd_iter->second->SetTraceId(oMsgBody.trace_id()); + } + else + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + cmd_iter->second->SetTraceId(oss.str()); + } + cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); + } + else + { + snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); + LOG4_ERROR(m_pErrBuff); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); + oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); + oOutMsgHead.set_cmd(CMD_RSP_SYS_ERROR); + oOutMsgHead.set_seq(oMsgHead.seq()); + oOutMsgHead.set_len(oOutMsgBody.ByteSize()); + } + } + else + { + cmd_iter = m_mapCmd.find(CMD_REQ_FROM_CLIENT); + if (cmd_iter != m_mapCmd.end()) + { + if (oMsgBody.trace_id().length() > 10) + { + cmd_iter->second->SetTraceId(oMsgBody.trace_id()); + } + else + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + cmd_iter->second->SetTraceId(oss.str()); + } + cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); + } + else + { + snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); + LOG4_ERROR(m_pErrBuff); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); + oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); + oOutMsgHead.set_cmd(CMD_RSP_SYS_ERROR); + oOutMsgHead.set_seq(oMsgHead.seq()); + oOutMsgHead.set_len(oOutMsgBody.ByteSize()); + } + } +//#else + snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); + LOG4_ERROR(m_pErrBuff); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); + oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); + oOutMsgHead.set_cmd(CMD_RSP_SYS_ERROR); + oOutMsgHead.set_seq(oMsgHead.seq()); + oOutMsgHead.set_len(oOutMsgBody.ByteSize()); +//#endif + } + } + if (CMD_RSP_SYS_ERROR == oOutMsgHead.cmd()) + { + LOG4_TRACE("no cmd handler."); + m_pLabor->GetDispatcher()->SendTo(pChannel, oOutMsgHead.cmd(), oOutMsgHead.seq(), oOutMsgBody); + return(false); + } + } + else // 回调 + { + auto step_iter = m_mapCallbackStep.find(oMsgHead.seq()); + if (step_iter != m_mapCallbackStep.end()) // 步骤回调 + { + LOG4_TRACE("receive message, cmd = %d", + oMsgHead.cmd()); + if (step_iter->second != nullptr) + { + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + LOG4_TRACE("cmd %u, seq %u, step_seq %u, active_time %lf", + oMsgHead.cmd(), oMsgHead.seq(), step_iter->second->GetSequence(), + step_iter->second->GetActiveTime()); + eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); + if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + { + uint32 uiChainId = step_iter->second->GetChainId(); + RemoveStep(step_iter->second); + if (0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } + } + } + } + } + ExecAssemblyLine(pChannel, oMsgHead, oMsgBody); + } + else + { + snprintf(m_pErrBuff, gc_iErrBuffLen, "no callback or the callback for seq %u had been timeout!", oMsgHead.seq()); + LOG4_WARNING(m_pErrBuff); + return(false); + } + } + return(true); +} + +bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +{ + LOG4_DEBUG("oInHttpMsg.type() = %d, oInHttpMsg.path() = %s", + oHttpMsg.type(), oHttpMsg.path().c_str()); + if (HTTP_REQUEST == oHttpMsg.type()) // 新请求 + { + auto module_iter = m_mapModule.find(oHttpMsg.path()); + if (module_iter == m_mapModule.end()) + { + module_iter = m_mapModule.find("/switch"); + if (module_iter == m_mapModule.end()) + { + HttpMsg oOutHttpMsg; + snprintf(m_pErrBuff, gc_iErrBuffLen, "no module to dispose %s!", oHttpMsg.path().c_str()); + LOG4_ERROR(m_pErrBuff); + oOutHttpMsg.set_type(HTTP_RESPONSE); + oOutHttpMsg.set_status_code(404); + oOutHttpMsg.set_http_major(oHttpMsg.http_major()); + oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); + m_pLabor->GetDispatcher()->SendTo(pChannel, oOutHttpMsg, 0); + } + else + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + module_iter->second->SetTraceId(oss.str()); + module_iter->second->AnyMessage(pChannel, oHttpMsg); + } + } + else + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + module_iter->second->SetTraceId(oss.str()); + module_iter->second->AnyMessage(pChannel, oHttpMsg); + } + } + else + { + auto http_step_iter = m_mapCallbackStep.find(pChannel->GetStepSeq()); + if (http_step_iter == m_mapCallbackStep.end()) + { + LOG4_ERROR("no callback for http response from %s!", oHttpMsg.url().c_str()); + m_pLabor->GetDispatcher()->SetChannelStatus(pChannel, CHANNEL_STATUS_ESTABLISHED, 0); + m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. + } + else + { + E_CMD_STATUS eResult; + http_step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = (std::dynamic_pointer_cast(http_step_iter->second))->Callback(pChannel, oHttpMsg); + if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + { + uint32 uiChainId = http_step_iter->second->GetChainId(); + RemoveStep(http_step_iter->second); + m_pLabor->GetDispatcher()->SetChannelStatus(pChannel, CHANNEL_STATUS_ESTABLISHED, 0); + m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. + if (0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } + } + } + } + } + } + return(true); +} + +bool ActorBuilder::OnRedisConnected(std::shared_ptr pChannel, const redisAsyncContext *c, int status) +{ + if (status == REDIS_OK) + { + pChannel->bIsReady = true; + int iCmdStatus; + for (auto step_iter = pChannel->listPipelineStep.begin(); + step_iter != pChannel->listPipelineStep.end(); ) + { + size_t args_size = (*step_iter)->GetCmdArguments().size() + 1; + const char* argv[args_size]; + size_t arglen[args_size]; + argv[0] = (*step_iter)->GetCmd().c_str(); + arglen[0] = (*step_iter)->GetCmd().size(); + std::vector >::const_iterator c_iter = (*step_iter)->GetCmdArguments().begin(); + for (size_t i = 1; c_iter != (*step_iter)->GetCmdArguments().end(); ++c_iter, ++i) + { + argv[i] = c_iter->first.c_str(); + arglen[i] = c_iter->first.size(); + } + iCmdStatus = redisAsyncCommandArgv((redisAsyncContext*)c, Dispatcher::RedisCmdCallback, nullptr, args_size, argv, arglen); + if (iCmdStatus == REDIS_OK) + { + LOG4_TRACE("succeed in sending redis cmd: %s", (*step_iter)->CmdToString().c_str()); + } + else + { + E_CMD_STATUS eResult; + uint32 uiChainId; + auto interrupt_step_iter = step_iter; + for (; step_iter != pChannel->listPipelineStep.end(); ++step_iter) + { + eResult = (*step_iter)->Callback(c, status, nullptr); + uiChainId = (*step_iter)->GetChainId(); + RemoveStep(*step_iter); + if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + { + if (0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } + } + } + } + else + { + RemoveChain(uiChainId); + } + } + for (auto erase_step_iter = interrupt_step_iter; + erase_step_iter != pChannel->listPipelineStep.end();) + { + RemoveChain((*erase_step_iter)->GetChainId()); + } + pChannel->listPipelineStep.clear(); + return(false); + } + } + } + else + { + for (auto step_iter = pChannel->listPipelineStep.begin(); + step_iter != pChannel->listPipelineStep.end(); ++step_iter) + { + (*step_iter)->Callback(c, status, nullptr); + RemoveChain((*step_iter)->GetChainId()); + RemoveStep(*step_iter); + } + pChannel->listPipelineStep.clear(); + return(false); + } + return(true); +} + +void ActorBuilder::OnRedisDisconnected(std::shared_ptr pChannel, const redisAsyncContext *c, int status) +{ + for (auto step_iter = pChannel->listPipelineStep.begin(); + step_iter != pChannel->listPipelineStep.end(); ++step_iter) + { + LOG4_ERROR("RedisDisconnect callback error %d of redis cmd: %s", + c->err, (*step_iter)->CmdToString().c_str()); + (*step_iter)->Callback(c, c->err, nullptr); + RemoveChain((*step_iter)->GetChainId()); + RemoveStep(std::dynamic_pointer_cast(*step_iter)); + } + pChannel->listPipelineStep.clear(); +} + +bool ActorBuilder::OnRedisCmdResult(std::shared_ptr pChannel, redisAsyncContext *c, void *reply, void *privdata) +{ + if (pChannel->listPipelineStep.empty()) + { + LOG4_ERROR("no redis step!"); + return(false); + } + + auto step_iter = pChannel->listPipelineStep.begin(); + if (nullptr == reply) + { + LOG4_ERROR("redis %s error %d: %s", pChannel->GetIdentify().c_str(), c->err, c->errstr); + for ( ; step_iter != pChannel->listPipelineStep.end(); ++step_iter) + { + LOG4_ERROR("callback error %d of redis cmd: %s", c->err, (*step_iter)->CmdToString().c_str()); + (*step_iter)->Callback(c, c->err, (redisReply*)reply); + RemoveChain((*step_iter)->GetChainId()); + RemoveStep(*step_iter); + } + pChannel->listPipelineStep.clear(); + } + else + { + if (step_iter != pChannel->listPipelineStep.end()) + { + LOG4_TRACE("callback of redis cmd: %s", (*step_iter)->CmdToString().c_str()); + E_CMD_STATUS eResult = (*step_iter)->Callback(c, REDIS_OK, (redisReply*)reply); + pChannel->listPipelineStep.erase(step_iter); + if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + { + uint32 uiChainId = (*step_iter)->GetChainId(); + if (0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } + } + } + } + //freeReplyObject(reply); + } + else + { + LOG4_ERROR("no redis callback data found!"); + } + } + + RemoveStep(std::dynamic_pointer_cast(*step_iter)); + return(true); +} + +void ActorBuilder::AddAssemblyLine(std::shared_ptr pSession) +{ + m_setAssemblyLine.insert(pSession); +} + +void ActorBuilder::RemoveStep(std::shared_ptr pStep) +{ + if (nullptr == pStep) + { + return; + } + std::unordered_map >::iterator callback_iter; + for (auto step_seq_iter = pStep->m_setPreStepSeq.begin(); + step_seq_iter != pStep->m_setPreStepSeq.end(); ) + { + callback_iter = m_mapCallbackStep.find(*step_seq_iter); + if (callback_iter == m_mapCallbackStep.end()) + { + pStep->m_setPreStepSeq.erase(step_seq_iter++); + } + else + { + LOG4_DEBUG("step %u had pre step %u running, delay delete callback.", pStep->GetSequence(), *step_seq_iter); + ResetTimeout(std::dynamic_pointer_cast(pStep)); + return; + } + } + auto class_iter = m_mapLoadedStep.find(pStep->GetActorName()); + if (class_iter != m_mapLoadedStep.end()) + { + auto id_iter = class_iter->second.find(pStep->GetSequence()); + if (id_iter != class_iter->second.end()) + { + class_iter->second.erase(id_iter); + } + } + m_pLabor->GetDispatcher()->DelEvent(pStep->MutableTimerWatcher()); + callback_iter = m_mapCallbackStep.find(pStep->GetSequence()); + if (callback_iter != m_mapCallbackStep.end()) + { + LOG4_TRACE("erase step(seq %u)", pStep->GetSequence()); + m_mapCallbackStep.erase(callback_iter); + } +} + +void ActorBuilder::RemoveSession(std::shared_ptr pSession) +{ + if (nullptr == pSession) + { + return; + } + auto class_iter = m_mapLoadedSession.find(pSession->GetActorName()); + if (class_iter != m_mapLoadedSession.end()) + { + auto id_iter = class_iter->second.find(pSession->GetSessionId()); + if (id_iter != class_iter->second.end()) + { + class_iter->second.erase(id_iter); + } + } + m_pLabor->GetDispatcher()->DelEvent(pSession->MutableTimerWatcher()); + auto iter = m_mapCallbackSession.find(pSession->GetSessionId()); + if (iter != m_mapCallbackSession.end()) + { + LOG4_TRACE("erase session(session_id %s)", pSession->GetSessionId().c_str()); + m_mapCallbackSession.erase(iter); + } +} + +void ActorBuilder::RemoveChain(uint32 uiChainId) +{ + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + std::shared_ptr pChain = chain_iter->second; + m_pLabor->GetDispatcher()->DelEvent(pChain->MutableTimerWatcher()); + m_mapChain.erase(chain_iter); + } +} + +void ActorBuilder::ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData) +{ + LOG4_TRACE(" "); + auto cmd_iter = m_mapCmd.find(CMD_REQ_DISCONNECT); + if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) + { + MsgHead oMsgHead; + MsgBody oMsgBody; + oMsgBody.mutable_req_target()->set_route_id(0); + oMsgBody.set_data(strIdentify); + oMsgBody.set_add_on(strClientData); + oMsgHead.set_cmd(CMD_REQ_DISCONNECT); + oMsgHead.set_seq(m_pLabor->GetSequence()); + oMsgHead.set_len(oMsgBody.ByteSize()); + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + cmd_iter->second->SetTraceId(oss.str()); + cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); + } +} + +void ActorBuilder::ExecAssemblyLine(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) +{ + for (auto session_iter = m_setAssemblyLine.begin(); session_iter != m_setAssemblyLine.end(); ++session_iter) + { + uint32 uiStepSeq = 0; + do + { + uiStepSeq = (*session_iter)->PopWaitingStep(); + auto step_iter = m_mapCallbackStep.find(uiStepSeq); + if (step_iter != m_mapCallbackStep.end()) + { + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); + if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + { + uint32 uiChainId = step_iter->second->GetChainId(); + RemoveStep(step_iter->second); + if (0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } + } + } + } + } + } + while(uiStepSeq > 0); + } + m_setAssemblyLine.clear(); +} + +void ActorBuilder::AddChainConf(const std::string& strChainKey, std::queue >&& queChainBlocks) +{ + m_mapChainConf.insert(std::make_pair(strChainKey, std::move(queChainBlocks))); +} + +void ActorBuilder::LoadSysCmd() +{ + // 调用MakeSharedCmd等系列函数必须严格匹配参数类型,类型不符需显式转换,如 (int)CMD_REQ_CONNECT_TO_WORKER + MakeSharedCmd(nullptr, "neb::CmdBeat", (int)CMD_REQ_BEAT); + if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) + { + MakeSharedCmd(nullptr, "neb::CmdUpdateWorkerLoad", (int)CMD_REQ_UPDATE_WORKER_LOAD); + MakeSharedCmd(nullptr, "neb::CmdOnTellWorker", (int)CMD_REQ_TELL_WORKER); + MakeSharedCmd(nullptr, "neb::CmdOnOrientationFdTransfer", (int)CMD_REQ_CONNECT_TO_WORKER); + MakeSharedCmd(nullptr, "neb::CmdOnNodeNotice", (int)CMD_REQ_NODE_NOTICE); + MakeSharedCmd(nullptr, "neb::CmdOnSetNodeConf", (int)CMD_REQ_SET_NODE_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdOnGetNodeConf", (int)CMD_REQ_GET_NODE_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdOnSetNodeCustomConf", (int)CMD_REQ_SET_NODE_CUSTOM_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdOnGetNodeCustomConf", (int)CMD_REQ_GET_NODE_CUSTOM_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdOnSetCustomConf", (int)CMD_REQ_SET_CUSTOM_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdOnGetCustomConf", (int)CMD_REQ_GET_CUSTOM_CONFIG); + } + else + { + MakeSharedCmd(nullptr, "neb::CmdToldWorker", (int)CMD_REQ_TELL_WORKER); + MakeSharedCmd(nullptr, "neb::CmdUpdateNodeId", (int)CMD_REQ_REFRESH_NODE_ID); + MakeSharedCmd(nullptr, "neb::CmdNodeNotice", (int)CMD_REQ_NODE_NOTICE); + MakeSharedCmd(nullptr, "neb::CmdSetNodeConf", (int)CMD_REQ_SET_NODE_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdSetNodeCustomConf", (int)CMD_REQ_SET_NODE_CUSTOM_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdReloadCustomConf", (int)CMD_REQ_RELOAD_CUSTOM_CONFIG); + } + m_pSessionLogger = std::dynamic_pointer_cast(MakeSharedSession(nullptr, "neb::SessionLogger")); +} + +std::shared_ptr ActorBuilder::InitializeSharedActor(Actor* pCreator, std::shared_ptr pSharedActor, const std::string& strActorName) +{ + pSharedActor->SetLabor(m_pLabor); + pSharedActor->SetActiveTime(m_pLabor->GetNowTime()); + pSharedActor->SetActorName(strActorName); + if (nullptr != pCreator) + { + pSharedActor->SetContext(pCreator->GetContext()); + } + switch (pSharedActor->GetActorType()) + { + case Actor::ACT_PB_STEP: + case Actor::ACT_HTTP_STEP: + case Actor::ACT_REDIS_STEP: + if (TransformToSharedStep(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_SESSION: + case Actor::ACT_TIMER: + if (TransformToSharedSession(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_CONTEXT: + return(pSharedActor); + break; + case Actor::ACT_CMD: + if (TransformToSharedCmd(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_MODULE: + if (TransformToSharedModule(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_MODEL: + if (TransformToSharedModel(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + case Actor::ACT_CHAIN: + if (TransformToSharedChain(pCreator, pSharedActor)) + { + return(pSharedActor); + } + break; + default: + LOG4_ERROR("\"%s\" must be a Step, a Session, a Model, a Cmd or a Module.", + strActorName.c_str()); + return(nullptr); + } + return(nullptr); +} + +bool ActorBuilder::TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor) +{ + pSharedActor->m_dTimeout = (0 == pSharedActor->m_dTimeout) + ? m_pLabor->GetNodeInfo().dStepTimeout : pSharedActor->m_dTimeout; + ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); + if (NULL == timer_watcher) + { + return(false); + } + + if (nullptr != pCreator) + { + pSharedActor->SetTraceId(pCreator->GetTraceId()); + } + + std::shared_ptr pSharedStep = std::dynamic_pointer_cast(pSharedActor); + for (auto iter = pSharedStep->m_setNextStepSeq.begin(); iter != pSharedStep->m_setNextStepSeq.end(); ++iter) + { + auto callback_iter = m_mapCallbackStep.find(*iter); + if (callback_iter != m_mapCallbackStep.end()) + { + callback_iter->second->m_setPreStepSeq.insert(pSharedStep->GetSequence()); + } + } + + auto ret = m_mapCallbackStep.insert(std::make_pair(pSharedStep->GetSequence(), pSharedStep)); + if (ret.second) + { + if (gc_dNoTimeout != pSharedStep->m_dTimeout) + { + m_pLabor->GetDispatcher()->AddEvent(timer_watcher, StepTimeoutCallback, pSharedStep->m_dTimeout); + } + LOG4_TRACE("Step(seq %u, active_time %lf, lifetime %lf) register successful.", + pSharedStep->GetSequence(), pSharedStep->GetActiveTime(), pSharedStep->GetTimeout()); + auto step_class_iter = m_mapLoadedStep.find(pSharedStep->GetActorName()); + if (step_class_iter != m_mapLoadedStep.end()) + { + step_class_iter->second.insert(pSharedStep->GetSequence()); + } + return(true); + } + else + { + return(false); + } +} + +bool ActorBuilder::TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor) +{ + ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); + if (NULL == timer_watcher) + { + return(false); + } + + if (nullptr != pCreator) + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << pSharedActor->GetSequence(); + pSharedActor->SetTraceId(oss.str()); + } + std::shared_ptr pSharedSession = std::dynamic_pointer_cast(pSharedActor); + auto ret = m_mapCallbackSession.insert(std::make_pair(pSharedSession->GetSessionId(), pSharedSession)); + if (ret.second) + { + if (gc_dNoTimeout != pSharedSession->m_dTimeout) + { + m_pLabor->GetDispatcher()->AddEvent(timer_watcher, SessionTimeoutCallback, pSharedSession->m_dTimeout); + } + auto session_class_iter = m_mapLoadedSession.find(pSharedSession->GetActorName()); + if (session_class_iter != m_mapLoadedSession.end()) + { + session_class_iter->second.insert(pSharedSession->GetSessionId()); + } + return(true); + } + else + { + return(false); + } +} + +bool ActorBuilder::TransformToSharedCmd(Actor* pCreator, std::shared_ptr pSharedActor) +{ + if (nullptr != pCreator) + { + pSharedActor->SetTraceId(pCreator->GetTraceId()); + } + std::shared_ptr pSharedCmd = std::dynamic_pointer_cast(pSharedActor); + auto ret = m_mapCmd.insert(std::make_pair(pSharedCmd->GetCmd(), pSharedCmd)); + if (ret.second) + { + if (pSharedCmd->Init()) + { + auto cmd_class_iter = m_mapLoadedCmd.find(pSharedCmd->GetActorName()); + if (cmd_class_iter != m_mapLoadedCmd.end()) + { + cmd_class_iter->second.insert(pSharedCmd->GetCmd()); + } + return(true); + } + } + return(false); +} + +bool ActorBuilder::TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor) +{ + if (nullptr != pCreator) + { + pSharedActor->SetTraceId(pCreator->GetTraceId()); + } + + std::shared_ptr pSharedModule = std::dynamic_pointer_cast(pSharedActor); + auto ret = m_mapModule.insert(std::make_pair(pSharedModule->GetModulePath(), pSharedModule)); + if (ret.second) + { + if (pSharedModule->Init()) + { + auto module_class_iter = m_mapLoadedModule.find(pSharedModule->GetActorName()); + if (module_class_iter != m_mapLoadedModule.end()) + { + module_class_iter->second.insert(pSharedModule->GetModulePath()); + } + return(true); + } + } + return(false); +} + +bool ActorBuilder::TransformToSharedModel(Actor* pCreator, std::shared_ptr pSharedActor) +{ + std::shared_ptr pSharedModel = std::dynamic_pointer_cast(pSharedActor); + auto ret = m_mapModel.insert(std::make_pair(pSharedModel->GetActorName(), pSharedModel)); + if (ret.second) + { + if (pSharedModel->Init()) + { + return(true); + } + } + return(false); +} + +bool ActorBuilder::TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor) +{ + ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); + if (NULL == timer_watcher) + { + return(false); + } + + if (nullptr != pCreator) + { + pSharedActor->SetTraceId(pCreator->GetTraceId()); + } + + std::shared_ptr pSharedChain = std::dynamic_pointer_cast(pSharedActor); + auto chain_conf_iter = m_mapChainConf.find(pSharedChain->GetChainFlag()); + if (chain_conf_iter == m_mapChainConf.end()) + { + LOG4_ERROR("no chain block config for \"%s\"", pSharedChain->GetChainFlag().c_str()); + return(false); + } + else + { + pSharedChain->Init(chain_conf_iter->second); + } + auto ret = m_mapChain.insert(std::make_pair(pSharedChain->GetSequence(), pSharedChain)); + if (ret.second) + { + if (gc_dNoTimeout != pSharedChain->m_dTimeout) + { + m_pLabor->GetDispatcher()->AddEvent(timer_watcher, ChainTimeoutCallback, pSharedChain->m_dTimeout); + } + return(true); + } + return(false); +} + +std::shared_ptr ActorBuilder::GetSession(uint32 uiSessionId) +{ + std::ostringstream oss; + oss << uiSessionId; + auto id_iter = m_mapCallbackSession.find(oss.str()); + if (id_iter == m_mapCallbackSession.end()) + { + return(nullptr); + } + else + { + id_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + return(id_iter->second); + } +} + +std::shared_ptr ActorBuilder::GetSession(const std::string& strSessionId) +{ + auto id_iter = m_mapCallbackSession.find(strSessionId); + if (id_iter == m_mapCallbackSession.end()) + { + return(nullptr); + } + else + { + id_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + return(id_iter->second); + } +} + +bool ActorBuilder::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg, void* data) +{ + auto iter = m_mapCallbackStep.find(uiStepSeq); + if (iter == m_mapCallbackStep.end()) + { + return(false); + } + else + { + iter->second->Emit(iErrno, strErrMsg, data); + return(true); + } +} + +std::shared_ptr ActorBuilder::GetModel(const std::string& strModelName) +{ + auto iter = m_mapModel.find(strModelName); + if (iter == m_mapModel.end()) + { + return(nullptr); + } + else + { + return(iter->second); + } +} + +bool ActorBuilder::ResetTimeout(std::shared_ptr pSharedActor) +{ + ev_timer* watcher = pSharedActor->MutableTimerWatcher(); + ev_tstamp after = m_pLabor->GetNowTime() + pSharedActor->GetTimeout(); + m_pLabor->GetDispatcher()->RefreshEvent(watcher, after); + return(true); +} + +int32 ActorBuilder::GetStepNum() +{ + return((int32)m_mapCallbackStep.size()); +} + +bool ActorBuilder::ReloadCmdConf() +{ + for (auto cmd_iter = m_mapCmd.begin(); cmd_iter != m_mapCmd.end(); ++cmd_iter) + { + cmd_iter->second->Init(); + } + for (auto module_iter = m_mapModule.begin(); module_iter != m_mapModule.end(); ++module_iter) + { + module_iter->second->Init(); + } + return(true); +} + +bool ActorBuilder::AddNetLogMsg(const MsgBody& oMsgBody) +{ + // 此函数不能写日志,不然可能会导致写日志函数与此函数无限递归 + m_pSessionLogger->AddMsg(oMsgBody); + return(true); +} + +void ActorBuilder::BootLoadCmd(CJsonObject& oCmdConf) +{ + LOG4_TRACE(" "); + int32 iCmd = 0; + std::string strUrlPath; + for (int i = 0; i < oCmdConf["cmd"].GetArraySize(); ++i) + { + oCmdConf["cmd"][i].Get("cmd", iCmd); + MakeSharedCmd(nullptr, oCmdConf["cmd"][i]("class"), iCmd); + } + for (int j = 0; j < oCmdConf["module"].GetArraySize(); ++j) + { + oCmdConf["module"][j].Get("path", strUrlPath); + MakeSharedModule(nullptr, oCmdConf["module"][j]("class"), strUrlPath); + } +} + +void ActorBuilder::DynamicLoad(CJsonObject& oDynamicLoadingConf) +{ + LOG4_TRACE(" "); + std::string strVersion = "1.0"; + bool bIsload = false; + std::string strSoPath; + std::unordered_map::iterator so_iter; + tagSo* pSo = nullptr; + for (int i = 0; i < oDynamicLoadingConf.GetArraySize(); ++i) + { + oDynamicLoadingConf[i].Get("load", bIsload); + if (bIsload) + { + if (oDynamicLoadingConf[i].Get("so_path", strSoPath) && oDynamicLoadingConf[i].Get("version", strVersion)) + { + so_iter = m_mapLoadedSo.find(strSoPath); + if (so_iter == m_mapLoadedSo.end()) + { + std::string strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); + if (0 != access(strSoFile.c_str(), F_OK)) + { + strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); + if (0 != access(strSoFile.c_str(), F_OK)) + { + LOG4_WARNING("%s not exist!", strSoFile.c_str()); + continue; + } + } + pSo = LoadSo(strSoFile, strVersion); + if (pSo != nullptr) + { + LOG4_INFO("succeed in loading %s", strSoFile.c_str()); + m_mapLoadedSo.insert(std::make_pair(strSoPath, pSo)); + LoadDynamicSymbol(oDynamicLoadingConf[i]); + } + } + else + { + if (strVersion != so_iter->second->strVersion) // 版本升级,先卸载再加载 + { + std::string strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); + if (0 != access(strSoFile.c_str(), F_OK)) + { + strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); + if (0 != access(strSoFile.c_str(), F_OK)) + { + LOG4_WARNING("%s not exist!", strSoFile.c_str()); + continue; + } + } + UnloadDynamicSymbol(oDynamicLoadingConf[i]); + dlclose(so_iter->second->pSoHandle); + delete so_iter->second; + pSo = LoadSo(strSoFile, strVersion); + if (pSo != nullptr) + { + LOG4_INFO("succeed in loading %s", strSoFile.c_str()); + so_iter->second = pSo; + LoadDynamicSymbol(oDynamicLoadingConf[i]); + } + else + { + m_mapLoadedSo.erase(so_iter); + } + } + } + } + } + else // 卸载动态库 + { + if (oDynamicLoadingConf[i].Get("so_path", strSoPath)) + { + so_iter = m_mapLoadedSo.find(strSoPath); + if (so_iter != m_mapLoadedSo.end()) + { + UnloadDynamicSymbol(oDynamicLoadingConf[i]); + dlclose(so_iter->second->pSoHandle); + delete so_iter->second; + m_mapLoadedSo.erase(so_iter); + LOG4_INFO("unload %s.%s", strSoPath.c_str(), + oDynamicLoadingConf[i]("version").c_str()); + } + } + } + } +} + +ActorBuilder::tagSo* ActorBuilder::LoadSo(const std::string& strSoPath, const std::string& strVersion) +{ + LOG4_TRACE(" "); + tagSo* pSo = new tagSo(); + if (NULL == pSo) + { + return(nullptr); + } + void* pHandle = NULL; + pHandle = dlopen(strSoPath.c_str(), RTLD_NOW); + char* dlsym_error = dlerror(); + if (dlsym_error) + { + LOG4_FATAL("cannot load dynamic lib %s!" , dlsym_error); + if (pHandle != NULL) + { + dlclose(pHandle); + } + delete pSo; + return(nullptr); + } + pSo->pSoHandle = pHandle; + pSo->strVersion = strVersion; + return(pSo); +} + +void ActorBuilder::LoadDynamicSymbol(CJsonObject& oOneSoConf) +{ + int32 iCmd = 0; + std::string strUrlPath; + for (int i = 0; i < oOneSoConf["cmd"].GetArraySize(); ++i) + { + std::unordered_set setCmd; + m_mapLoadedCmd.insert(std::make_pair(oOneSoConf["cmd"][i]("class"), setCmd)); + oOneSoConf["cmd"][i].Get("cmd", iCmd); + MakeSharedCmd(nullptr, oOneSoConf["cmd"][i]("class"), iCmd); + } + for (int j = 0; j < oOneSoConf["module"].GetArraySize(); ++j) + { + std::unordered_set setModule; + m_mapLoadedModule.insert(std::make_pair(oOneSoConf["module"][j]("class"), setModule)); + oOneSoConf["module"][j].Get("path", strUrlPath); + MakeSharedModule(nullptr, oOneSoConf["module"][j]("class"), strUrlPath); + } + for (int k = 0; k < oOneSoConf["session"].GetArraySize(); ++k) + { + std::unordered_set setSession; + m_mapLoadedSession.insert(std::make_pair(oOneSoConf["session"](k), setSession)); + } + for (int l = 0; l < oOneSoConf["step"].GetArraySize(); ++l) + { + std::unordered_set setStep; + m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); + } +} + +void ActorBuilder::UnloadDynamicSymbol(CJsonObject& oOneSoConf) +{ + for (int i = 0; i < oOneSoConf["cmd"].GetArraySize(); ++i) + { + auto class_iter = m_mapLoadedCmd.find(oOneSoConf["cmd"][i]("class")); + if (class_iter != m_mapLoadedCmd.end()) + { + for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) + { + auto cmd_iter = m_mapCmd.find(*id_iter); + if (cmd_iter != m_mapCmd.end()) + { + m_mapCmd.erase(cmd_iter); + } + } + class_iter->second.clear(); + m_mapLoadedCmd.erase(class_iter); + } + } + for (int j = 0; j < oOneSoConf["module"].GetArraySize(); ++j) + { + auto class_iter = m_mapLoadedModule.find(oOneSoConf["module"][j]("class")); + if (class_iter != m_mapLoadedModule.end()) + { + for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) + { + auto module_iter = m_mapModule.find(*id_iter); + if (module_iter != m_mapModule.end()) + { + m_mapModule.erase(module_iter); + } + } + class_iter->second.clear(); + m_mapLoadedModule.erase(class_iter); + } + } + for (int k = 0; k < oOneSoConf["session"].GetArraySize(); ++k) + { + auto class_iter = m_mapLoadedSession.find(oOneSoConf["session"](k)); + if (class_iter != m_mapLoadedSession.end()) + { + for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) + { + auto session_iter = m_mapCallbackSession.find(*id_iter); + if (session_iter != m_mapCallbackSession.end()) + { + m_mapCallbackSession.erase(session_iter); + } + } + class_iter->second.clear(); + m_mapLoadedSession.erase(class_iter); + } + } + for (int l = 0; l < oOneSoConf["step"].GetArraySize(); ++l) + { + auto class_iter = m_mapLoadedStep.find(oOneSoConf["step"](l)); + if (class_iter != m_mapLoadedStep.end()) + { + for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) + { + auto step_iter = m_mapCallbackStep.find(*id_iter); + if (step_iter != m_mapCallbackStep.end()) + { + step_iter->second->Timeout(); + m_mapCallbackStep.erase(step_iter); + } + } + class_iter->second.clear(); + m_mapLoadedStep.erase(class_iter); + } + std::unordered_set setStep; + m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); + } + for (int k = 0; k < oOneSoConf["model"].GetArraySize(); ++k) + { + auto class_iter = m_mapModel.find(oOneSoConf["model"](k)); + if (class_iter != m_mapModel.end()) + { + m_mapModel.erase(class_iter); + } + } +} + +} /* namespace neb */ diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp new file mode 100644 index 00000000..63648533 --- /dev/null +++ b/src/actor/ActorBuilder.hpp @@ -0,0 +1,285 @@ +/******************************************************************************* + * Project: Nebula + * @file ActorBuilder.hpp + * @brief Actor创建和管理 + * @author Bwar + * @date: 2019年9月15日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_ACTORBUILDER_HPP_ +#define SRC_ACTOR_ACTORBUILDER_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +#include "ev.h" +#include "hiredis/hiredis.h" +#include "hiredis/async.h" +#include "hiredis/adapters/libev.h" +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#include "pb/msg.pb.h" +#include "pb/http.pb.h" +#include "pb/neb_sys.pb.h" +#include "Definition.hpp" +#include "Error.hpp" +#include "ActorFactory.hpp" +#include "logger/NetLogger.hpp" + +namespace neb +{ + +class Manager; +class Worker; + +class Actor; +class Cmd; +class Module; +class Session; +class Timer; +class Context; +class Step; +class RedisStep; +class HttpStep; +class Model; +class Chain; +class SessionLogger; + +class SocketChannel; +class RedisChannel; +class Dispatcher; + +class CJsonObject; + +class ActorBuilder +{ +public: + struct tagSo + { + void* pSoHandle = NULL; + std::string strVersion; + std::vector vecCmd; + std::vector vecPath; + tagSo(){}; + ~tagSo(){}; + }; + +public: + ActorBuilder(Labor* pLabor, std::shared_ptr pLogger); + virtual ~ActorBuilder(); + bool Init(CJsonObject& oBootLoadConf, CJsonObject& oDynamicLoadConf); + + static void StepTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); + static void SessionTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); + static void ChainTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); + bool OnStepTimeout(std::shared_ptr pStep); + bool OnSessionTimeout(std::shared_ptr pSession); + bool OnChainTimeout(std::shared_ptr pChain); + bool OnMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); + bool OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + bool OnRedisConnected(std::shared_ptr pChannel, const redisAsyncContext *c, int status); + void OnRedisDisconnected(std::shared_ptr pChannel, const redisAsyncContext *c, int status); + bool OnRedisCmdResult(std::shared_ptr pChannel, redisAsyncContext *c, void *reply, void *privdata); + +public: + template + void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); + template + void Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); + + template + std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args); + + template + std::shared_ptr MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs&&... args); + + template + std::shared_ptr MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs&&... args); + + template + std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args); + + template + std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args); + + template + std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args); + + template + std::shared_ptr MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs&&... args); + + template + std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args); + + template Actor* NewActor(const std::string& strActorName, Targs... args); + std::shared_ptr InitializeSharedActor(Actor* pCreator, std::shared_ptr pSharedActor, const std::string& strActorName); + bool TransformToSharedCmd(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedModel(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); + +public: + virtual std::shared_ptr GetSession(uint32 uiSessionId); + virtual std::shared_ptr GetSession(const std::string& strSessionId); + virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); + virtual std::shared_ptr GetModel(const std::string& strModelName); + virtual bool ResetTimeout(std::shared_ptr pSharedActor); + int32 GetStepNum(); + bool ReloadCmdConf(); + bool AddNetLogMsg(const MsgBody& oMsgBody); + +protected: + void AddAssemblyLine(std::shared_ptr pSession); + void RemoveStep(std::shared_ptr pStep); + void RemoveSession(std::shared_ptr pSession); + void RemoveChain(uint32 uiChainId); + void ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData); + void ExecAssemblyLine(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); + + void AddChainConf(const std::string& strChainKey, std::queue >&& queChainBlocks); + void LoadSysCmd(); + void BootLoadCmd(CJsonObject& oCmdConf); + void DynamicLoad(CJsonObject& oSoConf); + tagSo* LoadSo(const std::string& strSoPath, const std::string& strVersion); + void LoadDynamicSymbol(CJsonObject& oOneSoConf); + void UnloadDynamicSymbol(CJsonObject& oOneSoConf); + +private: + char* m_pErrBuff; + Labor* m_pLabor; + std::shared_ptr m_pLogger; + std::shared_ptr m_pSessionLogger; + + // dynamic load,use for load and unload. + std::unordered_map m_mapLoadedSo; + std::unordered_map > m_mapLoadedCmd; //key为CmdClassName,value为iCmd集合 + std::unordered_map > m_mapLoadedModule; //key为ModuleClassName,value为strModulePath集合 + std::unordered_map > m_mapLoadedStep; //key为StepClassName,value为uiSeq集合 + std::unordered_map > m_mapLoadedSession; //key为SessionClassName,value为strSessionId集合 + + // Cmd and Module + std::unordered_map > m_mapCmd; + std::unordered_map > m_mapModule; + + // Chain and Model + std::unordered_map > > m_mapChainConf; //key为Chain的配置名(ChainFlag),value为由Model类名和Step类名构成的ChainBlock链 + std::unordered_map > m_mapChain; //key为Chain的Sequence,称为ChainId + std::unordered_map > m_mapModel; //key为Model类名 + + // Step and Session + std::unordered_map > m_mapCallbackStep; + std::unordered_map > m_mapCallbackSession; + std::unordered_set > m_setAssemblyLine; ///< 资源就绪后执行队列 + + friend class Manager; + friend class Worker; + friend class Actor; + friend class Dispatcher; +}; + +template +void ActorBuilder::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) +{ + m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); +} + +template +void ActorBuilder::Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) +{ + m_pLogger->WriteLog(strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); +} + +template +std::shared_ptr ActorBuilder::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args) +{ + Actor* pActor = ActorFactory::Instance()->Create(strActorName, std::forward(args)...); + if (nullptr == pActor) + { + /** + * @brief 为兼容&&参数推导差异导致ActorFactory未Regist进而导致 + * ActorFactory::Instance()->Create()调用不到对应的创建函数而增加。 + * NewActor()参数将按值传递,如果调用到NewActor()才new成功,代价会相对大些。 + */ + pActor = NewActor(strActorName, std::forward(args)...); + if (nullptr == pActor) + { + LOG4_ERROR("failed to make shared actor \"%s\"", strActorName.c_str()); + return(nullptr); + } + } + std::shared_ptr pSharedActor; + pSharedActor.reset(pActor); + pActor = nullptr; + return(InitializeSharedActor(pCreator, pSharedActor, strActorName)); +} + +template +std::shared_ptr ActorBuilder::MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs&&... args) +{ + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strCmdName, std::forward(args)...))); +} + +template +std::shared_ptr ActorBuilder::MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs&&... args) +{ + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModuleName, std::forward(args)...))); +} + +template +std::shared_ptr ActorBuilder::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args) +{ + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strStepName, std::forward(args)...))); +} + +template +std::shared_ptr ActorBuilder::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args) +{ + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strSessionName, std::forward(args)...))); +} + +template +std::shared_ptr ActorBuilder::MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args) +{ + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strContextName, std::forward(args)...))); +} + +template +std::shared_ptr ActorBuilder::MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs&&... args) +{ + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModelName, std::forward(args)...))); +} + +template +std::shared_ptr ActorBuilder::MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args) +{ + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strChainName, std::forward(args)...))); +} + +template +Actor* ActorBuilder::NewActor(const std::string& strActorName, Targs... args) +{ + LOG4_TRACE("%s() called by MakeSharedActor().", __FUNCTION__); + return(ActorFactory::Instance()->Create(strActorName, std::forward(args)...)); +} + +} /* namespace neb */ + +#endif /* SRC_ACTOR_ACTORBUILDER_HPP_ */ diff --git a/src/actor/ActorFriend.hpp b/src/actor/ActorFriend.hpp new file mode 100644 index 00000000..2aa33dc7 --- /dev/null +++ b/src/actor/ActorFriend.hpp @@ -0,0 +1,31 @@ +/******************************************************************************* + * Project: Nebula + * @file ActorFriend.hpp + * @brief Actor友元类,用于访问Actor的保护或私有成员 + * @author Bwar + * @date: 2019年9月21日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_ACTORFRIEND_HPP_ +#define SRC_ACTOR_ACTORFRIEND_HPP_ + +#include "labor/Labor.hpp" +#include "Actor.hpp" + +namespace neb +{ + +class ActorFriend +{ +public: + Labor* GetLabor(Actor* pActor) + { + return(pActor->m_pLabor); + } +}; + +} + +#endif /* SRC_ACTOR_ACTORFRIEND_HPP_ */ diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index e16b47f6..f47d6f1b 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -12,13 +12,14 @@ #include #include -#include "labor/Worker.hpp" #include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" namespace neb { +class ActorBuilder; + class Chain final: public Actor, public neb::DynamicCreator { @@ -47,7 +48,7 @@ class Chain final: public Actor, // queue的每个元素称为链块(std::unordered_set) std::queue > m_queChainBlock; - friend class WorkerImpl; + friend class ActorBuilder; }; } /* namespace neb */ diff --git a/src/actor/cmd/Cmd.hpp b/src/actor/cmd/Cmd.hpp index f1883e85..1753b0b2 100644 --- a/src/actor/cmd/Cmd.hpp +++ b/src/actor/cmd/Cmd.hpp @@ -10,13 +10,14 @@ #ifndef SRC_ACTOR_CMD_CMD_HPP_ #define SRC_ACTOR_CMD_CMD_HPP_ -#include "labor/Worker.hpp" #include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" namespace neb { +class ActorBuilder; + class Cmd: public Actor { public: @@ -68,7 +69,7 @@ class Cmd: public Actor private: int32 m_iCmd; - friend class WorkerImpl; + friend class ActorBuilder; }; } /* namespace neb */ diff --git a/src/actor/cmd/Module.hpp b/src/actor/cmd/Module.hpp index ef639823..4a682dc7 100644 --- a/src/actor/cmd/Module.hpp +++ b/src/actor/cmd/Module.hpp @@ -11,12 +11,14 @@ #define SRC_ACTOR_CMD_MODULE_HPP_ #include "codec/CodecHttp.hpp" -#include "labor/Worker.hpp" #include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" namespace neb { + +class ActorBuilder; + class Module: public Actor { public: @@ -60,7 +62,7 @@ class Module: public Actor private: std::string m_strModulePath; - friend class WorkerImpl; + friend class ActorBuilder; }; } /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/CmdBeat.hpp b/src/actor/cmd/sys_cmd/CmdBeat.hpp index 80234615..39bf594d 100644 --- a/src/actor/cmd/sys_cmd/CmdBeat.hpp +++ b/src/actor/cmd/sys_cmd/CmdBeat.hpp @@ -11,12 +11,13 @@ #define SRC_ACTOR_CMD_SYS_CMD_CMDBEAT_HPP_ #include "actor/cmd/Cmd.hpp" -#include "labor/WorkerFriend.hpp" +#include "actor/ActorFriend.hpp" namespace neb { -class CmdBeat: public Cmd, public DynamicCreator, public WorkerFriend +class CmdBeat: public Cmd, + public DynamicCreator, public ActorFriend { public: CmdBeat(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp b/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp index 8de59b93..853113cb 100644 --- a/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp +++ b/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp @@ -9,7 +9,9 @@ ******************************************************************************/ #include "CmdNodeNotice.hpp" +#include "util/CBuffer.hpp" #include "util/json/CJsonObject.hpp" +#include "ios/Dispatcher.hpp" namespace neb { @@ -52,7 +54,7 @@ bool CmdNodeNotice::AnyMessage( for(int j = 1; j <= iWorkerNum; ++j) { snprintf(szIdentify, sizeof(szIdentify), "%s:%d.%d", strNodeHost.c_str(), iNodePort, j); - GetWorkerImpl(this)->DelNodeIdentify(strNodeType, std::string(szIdentify)); + GetLabor(this)->GetDispatcher()->DelNodeIdentify(strNodeType, std::string(szIdentify)); LOG4_DEBUG("DelNodeIdentify(%s,%s)", strNodeType.c_str(), szIdentify); } } @@ -68,7 +70,7 @@ bool CmdNodeNotice::AnyMessage( for(int j = 1; j <= iWorkerNum; ++j) { snprintf(szIdentify, sizeof(szIdentify), "%s:%d.%d", strNodeHost.c_str(), iNodePort, j); - GetWorkerImpl(this)->AddNodeIdentify(strNodeType, std::string(szIdentify)); + GetLabor(this)->GetDispatcher()->AddNodeIdentify(strNodeType, std::string(szIdentify)); LOG4_DEBUG("AddNodeIdentify(%s,%s)", strNodeType.c_str(), szIdentify); } } diff --git a/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp b/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp index 66b4a193..98b69d5a 100644 --- a/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp +++ b/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp @@ -11,7 +11,7 @@ #define SRC_ACTOR_CMD_SYS_CMD_CMDNODENOTICE_HPP_ #include "actor/cmd/Cmd.hpp" -#include "labor/WorkerFriend.hpp" +#include "actor/ActorFriend.hpp" #include "pb/neb_sys.pb.h" namespace neb @@ -23,7 +23,8 @@ namespace neb * @date 2015年8月9日 * @note 各个模块启动时需要向BEACON进行注册 */ -class CmdNodeNotice : public Cmd, public DynamicCreator, public WorkerFriend +class CmdNodeNotice : public Cmd, + public DynamicCreator, public ActorFriend { public: CmdNodeNotice(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp index 93c7afae..898be2ef 100644 --- a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp +++ b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp @@ -26,7 +26,7 @@ bool CmdReloadCustomConf::AnyMessage( const MsgHead& oInMsgHead, const MsgBody& oInMsgBody) { - return(GetWorkerImpl(this)->ReloadCmdConf()); + return(GetLabor(this)->GetActorBuilder()->ReloadCmdConf()); } } /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp index b972dab7..b4a23497 100644 --- a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp @@ -11,12 +11,13 @@ #define SRC_ACTOR_CMD_SYS_CMD_CMDRELOADCUSTOMCONF_HPP_ #include "actor/cmd/Cmd.hpp" -#include "labor/WorkerFriend.hpp" +#include "actor/ActorFriend.hpp" namespace neb { -class CmdReloadCustomConf: public Cmd, public DynamicCreator, public WorkerFriend +class CmdReloadCustomConf: public Cmd, + public DynamicCreator, public ActorFriend { public: CmdReloadCustomConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp b/src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp index b8a7b806..d028256c 100644 --- a/src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp +++ b/src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp @@ -32,7 +32,8 @@ bool CmdSetNodeConf::AnyMessage( CJsonObject oConf; if (oConf.Parse(oConfigInfo.file_content())) { - return(GetWorkerImpl(this)->SetWorkerConf(oConf)); + GetLabor(this)->SetNodeConf(oConf); + return(true); } } return(false); diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp b/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp index c063db7b..4dfefb9f 100644 --- a/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp +++ b/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp @@ -11,12 +11,13 @@ #define SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECONF_HPP_ #include "actor/cmd/Cmd.hpp" -#include "labor/WorkerFriend.hpp" +#include "actor/ActorFriend.hpp" namespace neb { -class CmdSetNodeConf: public Cmd, public DynamicCreator, public WorkerFriend +class CmdSetNodeConf: public Cmd, + public DynamicCreator, public ActorFriend { public: CmdSetNodeConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.cpp b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.cpp index a81ad7cb..0f40b804 100644 --- a/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.cpp +++ b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.cpp @@ -8,6 +8,7 @@ * Modify history: ******************************************************************************/ #include "actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp" +#include "labor/Worker.hpp" namespace neb { @@ -32,7 +33,7 @@ bool CmdSetNodeCustomConf::AnyMessage( CJsonObject oConf; if (oConf.Parse(oConfigInfo.file_content())) { - return(GetWorkerImpl(this)->SetCustomConf(oConf)); + return(((Worker*)GetLabor(this))->SetCustomConf(oConf)); } } return(false); diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp index 2fd15ca0..338f79bd 100644 --- a/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp @@ -11,12 +11,13 @@ #define SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECUSTOMCONF_HPP_ #include "actor/cmd/Cmd.hpp" -#include "labor/WorkerFriend.hpp" +#include "actor/ActorFriend.hpp" namespace neb { -class CmdSetNodeCustomConf: public Cmd, public DynamicCreator, public WorkerFriend +class CmdSetNodeCustomConf: public Cmd, + public DynamicCreator, public ActorFriend { public: CmdSetNodeCustomConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdToldWorker.cpp b/src/actor/cmd/sys_cmd/CmdToldWorker.cpp index 28be178f..05647368 100644 --- a/src/actor/cmd/sys_cmd/CmdToldWorker.cpp +++ b/src/actor/cmd/sys_cmd/CmdToldWorker.cpp @@ -9,6 +9,7 @@ ******************************************************************************/ #include "CmdToldWorker.hpp" #include "pb/neb_sys.pb.h" +#include "ios/Dispatcher.hpp" namespace neb { @@ -37,7 +38,7 @@ bool CmdToldWorker::AnyMessage( LOG4_DEBUG("AddNodeIdentify(%s, %s)!", oInTargetWorker.node_type().c_str(), oInTargetWorker.worker_identify().c_str()); // 发起连接的节点执行autosend时已添加过,这里已不再需要添加 GetWorkerImpl(this)->AddNamedSocketChannel(oInTargetWorker.worker_identify(), pChannel); - GetWorkerImpl(this)->AddNodeIdentify(oInTargetWorker.node_type(), oInTargetWorker.worker_identify()); + GetLabor(this)->GetDispatcher()->AddNodeIdentify(oInTargetWorker.node_type(), oInTargetWorker.worker_identify()); oOutTargetWorker.set_worker_identify(GetNodeIdentify()); oOutTargetWorker.set_node_type(GetNodeType()); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); diff --git a/src/actor/cmd/sys_cmd/CmdToldWorker.hpp b/src/actor/cmd/sys_cmd/CmdToldWorker.hpp index 08062c28..0b89c787 100644 --- a/src/actor/cmd/sys_cmd/CmdToldWorker.hpp +++ b/src/actor/cmd/sys_cmd/CmdToldWorker.hpp @@ -11,12 +11,13 @@ #define SRC_ACTOR_CMD_SYS_CMD_CMDTOLDWORKER_HPP_ #include "actor/cmd/Cmd.hpp" -#include "labor/WorkerFriend.hpp" +#include "actor/ActorFriend.hpp" namespace neb { -class CmdToldWorker: public Cmd, public DynamicCreator, public WorkerFriend +class CmdToldWorker: public Cmd, + public DynamicCreator, public ActorFriend { public: CmdToldWorker(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdUpdateNodeId.cpp b/src/actor/cmd/sys_cmd/CmdUpdateNodeId.cpp index c33cb3a7..066e4ef4 100644 --- a/src/actor/cmd/sys_cmd/CmdUpdateNodeId.cpp +++ b/src/actor/cmd/sys_cmd/CmdUpdateNodeId.cpp @@ -31,7 +31,7 @@ bool CmdUpdateNodeId::AnyMessage( { uint32 uiNodeId = 0; oNode.Get("node_id", uiNodeId); - GetWorkerImpl(this)->SetNodeId(uiNodeId); + GetLabor(this)->SetNodeId(uiNodeId); return(true); } return(false); diff --git a/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp b/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp index 018cd892..8ef1ad14 100644 --- a/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp +++ b/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp @@ -11,12 +11,13 @@ #define SRC_ACTOR_CMD_SYS_CMD_CMDUPDATENODEID_HPP_ #include "actor/cmd/Cmd.hpp" -#include "labor/WorkerFriend.hpp" +#include "actor/ActorFriend.hpp" namespace neb { -class CmdUpdateNodeId: public Cmd, public DynamicCreator, public WorkerFriend +class CmdUpdateNodeId: public Cmd, + public DynamicCreator, public ActorFriend { public: CmdUpdateNodeId(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp index b6105cb8..3999ad17 100644 --- a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp +++ b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp @@ -12,6 +12,7 @@ #include #include #include +#include "ios/Dispatcher.hpp" namespace neb { @@ -119,7 +120,7 @@ bool ModuleHttpUpgrade::WebSocket(std::shared_ptr pChannel, const oOutHttpMsg.mutable_headers()->insert(google::protobuf::MapPair("Sec-WebSocket-Extensions", "private-extension")); oOutHttpMsg.mutable_headers()->insert(google::protobuf::MapPair("Sec-WebSocket-Accept", strBase64EncodeAcceptKey)); SendTo(pChannel, oOutHttpMsg); - GetWorkerImpl(this)->SwitchCodec(pChannel, CODEC_WS_EXTEND_JSON); + GetLabor(this)->GetDispatcher()->SwitchCodec(pChannel, CODEC_WS_EXTEND_JSON); } else { diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp index c4ea5d06..1fef38a8 100644 --- a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp +++ b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp @@ -12,12 +12,13 @@ #include "codec/Codec.hpp" #include "actor/cmd/Module.hpp" -#include "labor/WorkerFriend.hpp" +#include "actor/ActorFriend.hpp" namespace neb { -class ModuleHttpUpgrade: public Module, public DynamicCreator, public WorkerFriend +class ModuleHttpUpgrade: public Module, + public DynamicCreator, public ActorFriend { public: ModuleHttpUpgrade(const std::string& strModulePath); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.cpp new file mode 100644 index 00000000..2875ab91 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.cpp @@ -0,0 +1,80 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnGetCustomConf.cpp + * @brief 获取节点自定义配置 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#include "CmdOnGetCustomConf.hpp" +#include "labor/NodeInfo.hpp" + +namespace neb +{ + +CmdOnGetCustomConf::CmdOnGetCustomConf(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdOnGetCustomConf::~CmdOnGetCustomConf() +{ +} + +bool CmdOnGetCustomConf::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + MsgBody oOutMsgBody; + ConfigInfo oConfigInfo; + if (oConfigInfo.ParseFromString(oInMsgBody.data())) + { + std::stringstream ssConfFile; + if (oConfigInfo.file_path().size() > 0) + { + ssConfFile << GetLabor(this)->GetNodeInfo().strWorkPath + << "/conf/" << oConfigInfo.file_path() + << "/" << oConfigInfo.file_name(); + } + else + { + ssConfFile << GetLabor(this)->GetNodeInfo().strWorkPath + << "/conf/" << oConfigInfo.file_name(); + } + + std::ifstream fin(ssConfFile.str().c_str()); + if (fin.good()) + { + std::stringstream ssContent; + ssContent << fin.rdbuf(); + oConfigInfo.set_file_content(ssContent.str()); + //oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + oConfigInfo.SerializeToString(&m_strDataString); + oOutMsgBody.set_data(m_strDataString); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + fin.close(); + SendTo(pChannel, oInMsgHead.cmd(), oInMsgHead.seq(), oOutMsgBody); + return(true); + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_FILE_NOT_EXIST); + oOutMsgBody.mutable_rsp_result()->set_msg("file \"" + ssConfFile.str() + "\" not exist!"); + SendTo(pChannel, oInMsgHead.cmd(), oInMsgHead.seq(), oOutMsgBody); + return(false); + } + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); + oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); + SendTo(pChannel, oInMsgHead.cmd(), oInMsgHead.seq(), oOutMsgBody); + return(false); + } +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp new file mode 100644 index 00000000..fe2adc16 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp @@ -0,0 +1,37 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnGetCustomConf.hpp + * @brief 获取节点自定义配置 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETCUSTOMCONF_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETCUSTOMCONF_HPP_ + +#include "actor/ActorFriend.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class CmdOnGetCustomConf: public Cmd, + public DynamicCreator, public ActorFriend +{ +public: + CmdOnGetCustomConf(int32 iCmd); + virtual ~CmdOnGetCustomConf(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); + +private: + std::string m_strDataString; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETCUSTOMCONF_HPP_ */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.cpp new file mode 100644 index 00000000..c807b8ab --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.cpp @@ -0,0 +1,44 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnGetNodeConf.cpp + * @brief 获取节点配置 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#include "CmdOnGetNodeConf.hpp" +#include "labor/NodeInfo.hpp" + +namespace neb +{ + +CmdOnGetNodeConf::CmdOnGetNodeConf(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdOnGetNodeConf::~CmdOnGetNodeConf() +{ +} + +bool CmdOnGetNodeConf::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + MsgBody oOutMsgBody; + ConfigInfo oConfigInfo; + oConfigInfo.set_file_name(GetLabor(this)->GetNodeInfo().strConfFile); + oConfigInfo.set_file_content(GetLabor(this)->GetNodeConf().ToFormattedString()); + //oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + oConfigInfo.SerializeToString(&m_strDataString); + oOutMsgBody.set_data(m_strDataString); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + SendTo(pChannel, oInMsgHead.cmd(), oInMsgHead.seq(), oOutMsgBody); + return(true); +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp new file mode 100644 index 00000000..9da5b230 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp @@ -0,0 +1,37 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnGetNodeConf.hpp + * @brief 获取节点配置 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECONF_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECONF_HPP_ + +#include "actor/ActorFriend.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class CmdOnGetNodeConf: public Cmd, + public DynamicCreator, public ActorFriend +{ +public: + CmdOnGetNodeConf(int32 iCmd); + virtual ~CmdOnGetNodeConf(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); + +private: + std::string m_strDataString; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECONF_HPP_ */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.cpp new file mode 100644 index 00000000..7fef22a0 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.cpp @@ -0,0 +1,45 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnGetNodeCustomConf.cpp + * @brief 获取节点自定义配置 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#include "CmdOnGetNodeCustomConf.hpp" +#include "labor/NodeInfo.hpp" + +namespace neb +{ + +CmdOnGetNodeCustomConf::CmdOnGetNodeCustomConf(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdOnGetNodeCustomConf::~CmdOnGetNodeCustomConf() +{ +} + +bool CmdOnGetNodeCustomConf::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + MsgBody oOutMsgBody; + ConfigInfo oConfigInfo; + CJsonObject oCurrentConf = GetLabor(this)->GetNodeConf(); + oConfigInfo.set_file_name(GetLabor(this)->GetNodeInfo().strConfFile); + oConfigInfo.set_file_content(oCurrentConf["custom"].ToFormattedString()); + //oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); + oConfigInfo.SerializeToString(&m_strDataString); + oOutMsgBody.set_data(m_strDataString); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + SendTo(pChannel, oInMsgHead.cmd(), oInMsgHead.seq(), oOutMsgBody); + return(true); +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp new file mode 100644 index 00000000..1a32631e --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp @@ -0,0 +1,39 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnGetNodeCustomConf.hpp + * @brief 获取节点自定义配置 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECUSTOMCONF_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECUSTOMCONF_HPP_ + +#include "actor/ActorFriend.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class CmdOnGetNodeCustomConf: public Cmd, + public DynamicCreator, + public ActorFriend +{ +public: + CmdOnGetNodeCustomConf(int32 iCmd); + virtual ~CmdOnGetNodeCustomConf(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); + +private: + std::string m_strDataString; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECUSTOMCONF_HPP_ */ + diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp new file mode 100644 index 00000000..450a262b --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp @@ -0,0 +1,107 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnNodeNotice.hpp + * @brief 来自注册中心的节点通知 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#include "CmdOnNodeNotice.hpp" + +#include "ios/Dispatcher.hpp" +#include "actor/session/sys_session/manager/SessionManager.hpp" + +namespace neb +{ + +CmdOnNodeNotice::CmdOnNodeNotice(int32 iCmd) + : Cmd(iCmd), m_pSessionManager(nullptr) +{ +} + +CmdOnNodeNotice::~CmdOnNodeNotice() +{ +} + +bool CmdOnNodeNotice::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + CJsonObject oJson; + if (!oJson.Parse(oInMsgBody.data())) + { + LOG4_ERROR("failed to parse msgbody content!"); + return(false); + } + if (m_pSessionManager == nullptr) + { + m_pSessionManager = std::dynamic_pointer_cast( + GetSession("neb::SessionManager")); + if (m_pSessionManager == nullptr) + { + LOG4_ERROR("no session named \"neb::SessionManager\"!"); + return(false); + } + } + m_pSessionManager->SendToWorker(CMD_REQ_NODE_NOTICE, GetSequence(), oInMsgBody); + std::ostringstream oss; + std::string strIdentify; + std::string strNodeType; + std::string strNodeHost; + int iNodePort = 0; + int iWorkerNum = 0; + for (int i = 0; i < oJson["add_nodes"].GetArraySize(); ++i) + { + if (oJson["add_nodes"][i].Get("node_type",strNodeType) + && oJson["add_nodes"][i].Get("node_ip",strNodeHost) + && oJson["add_nodes"][i].Get("node_port",iNodePort) + && oJson["add_nodes"][i].Get("worker_num",iWorkerNum)) + { + oss << strNodeHost << ":" << iNodePort; + strIdentify = std::move(oss.str()); + m_pSessionManager->AddOnlineNode(strIdentify, oJson["add_nodes"][i].ToString()); + if (std::string("LOGGER") == strNodeType) + { + for(int j = 1; j <= iWorkerNum; ++j) + { + oss << strNodeHost << ":" << iNodePort << "." << j; + strIdentify = std::move(oss.str()); + GetLabor(this)->GetDispatcher()->AddNodeIdentify(strNodeType, strIdentify); + LOG4_DEBUG("AddNodeIdentify(%s,%s)", strNodeType.c_str(), strIdentify.c_str()); + } + } + } + } + + for (int i = 0; i < oJson["del_nodes"].GetArraySize(); ++i) + { + if (oJson["del_nodes"][i].Get("node_type",strNodeType) + && oJson["del_nodes"][i].Get("node_ip",strNodeHost) + && oJson["del_nodes"][i].Get("node_port",iNodePort) + && oJson["del_nodes"][i].Get("worker_num",iWorkerNum)) + { + oss << strNodeHost << ":" << iNodePort; + strIdentify = std::move(oss.str()); + m_pSessionManager->DelOnlineNode(strIdentify); + if (std::string("LOGGER") == strNodeType) + { + for(int j = 1; j <= iWorkerNum; ++j) + { + oss << strNodeHost << ":" << iNodePort << "." << j; + strIdentify = std::move(oss.str()); + if (std::string("LOGGER") == strNodeType) + { + GetLabor(this)->GetDispatcher()->DelNodeIdentify(strNodeType, strIdentify); + LOG4_DEBUG("DelNodeIdentify(%s,%s)", strNodeType.c_str(), strIdentify.c_str()); + } + } + } + } + } + return(true); +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp new file mode 100644 index 00000000..33af0cec --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp @@ -0,0 +1,40 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnNodeNotice.hpp + * @brief 来自注册中心的节点通知 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONNODENOTICE_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONNODENOTICE_HPP_ + +#include "actor/ActorFriend.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class SessionManager; + +class CmdOnNodeNotice: public Cmd, + public DynamicCreator, + public ActorFriend +{ +public: + CmdOnNodeNotice(int32 iCmd); + virtual ~CmdOnNodeNotice(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); + +private: + std::shared_ptr m_pSessionManager; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONNODENOTICE_HPP_ */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp new file mode 100644 index 00000000..d91ba659 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp @@ -0,0 +1,74 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnOrientationFdTransfer.cpp + * @brief 定向文件描述符传送 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#include "CmdOnOrientationFdTransfer.hpp" +#include "labor/Manager.hpp" +#include "ios/Dispatcher.hpp" +#include "actor/session/sys_session/manager/SessionManager.hpp" + +namespace neb +{ + +CmdOnOrientationFdTransfer::CmdOnOrientationFdTransfer(int iCmd) + : Cmd(iCmd) +{ +} + +CmdOnOrientationFdTransfer::~CmdOnOrientationFdTransfer() +{ +} + +bool CmdOnOrientationFdTransfer::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + if (m_pSessionManager == nullptr) + { + m_pSessionManager = std::dynamic_pointer_cast( + GetSession("neb::SessionManager")); + if (m_pSessionManager == nullptr) + { + LOG4_ERROR("no session named \"neb::SessionManager\"!"); + return(false); + } + } + MsgBody oOutMsgBody; + int iWorkerIndex = std::stoi(oInMsgBody.data()); + auto pWorkerInfo = m_pSessionManager->GetWorkerInfo(iWorkerIndex); + if (pWorkerInfo == nullptr) + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_NO_SUCH_WORKER_INDEX); + oOutMsgBody.mutable_rsp_result()->set_msg("no such worker index!"); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(false); + } + std::unordered_map::iterator worker_iter; + int iErrno = GetLabor(this)->GetDispatcher()->SendFd( + pWorkerInfo->iDataFd, pChannel->GetFd(), + ((Manager*)GetLabor(this))->GetManagerInfo().iS2SFamily, + (int)pChannel->GetCodecType()); + if (iErrno != ERR_OK) + { + GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pChannel); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_TRANSFER_FD); + oOutMsgBody.mutable_rsp_result()->set_msg("send fd error!"); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(false); + } + + GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pChannel); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(true); +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp new file mode 100644 index 00000000..2327ab8e --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp @@ -0,0 +1,41 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnOrientationFdTransfer.hpp + * @brief 定向文件描述符传送 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONORIENTATIONFDTRANSFER_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONORIENTATIONFDTRANSFER_HPP_ + +#include "actor/ActorFriend.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class SessionManager; + +class CmdOnOrientationFdTransfer: public Cmd, + public DynamicCreator, + public ActorFriend +{ +public: + CmdOnOrientationFdTransfer(int32 iCmd); + virtual ~CmdOnOrientationFdTransfer(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); + +private: + std::shared_ptr m_pSessionManager; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONORIENTATIONFDTRANSFER_HPP_ */ + diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp new file mode 100644 index 00000000..a226b293 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp @@ -0,0 +1,87 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnSetCustomConf.hpp + * @brief 设置自定义配置文件 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#include "CmdOnSetCustomConf.hpp" +#include "actor/session/sys_session/manager/SessionManager.hpp" + +namespace neb +{ + +CmdOnSetCustomConf::CmdOnSetCustomConf(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdOnSetCustomConf::~CmdOnSetCustomConf() +{ +} + +bool CmdOnSetCustomConf::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + if (m_pSessionManager == nullptr) + { + m_pSessionManager = std::dynamic_pointer_cast( + GetSession("neb::SessionManager")); + if (m_pSessionManager == nullptr) + { + LOG4_ERROR("no session named \"neb::SessionManager\"!"); + return(false); + } + } + MsgBody oOutMsgBody; + ConfigInfo oConfigInfo; + if (oConfigInfo.ParseFromString(oInMsgBody.data())) + { + std::stringstream ssConfFile; + if (oConfigInfo.file_path().size() > 0) + { + ssConfFile << GetLabor(this)->GetNodeInfo().strWorkPath + << "/conf/" << oConfigInfo.file_path() + << "/" << oConfigInfo.file_name(); + } + else + { + ssConfFile << GetLabor(this)->GetNodeInfo().strWorkPath + << "/conf/" << oConfigInfo.file_name(); + } + + std::ofstream fout(ssConfFile.str().c_str()); + if (fout.good()) + { + fout.write(oConfigInfo.file_content().c_str(), oConfigInfo.file_content().size()); + fout.close(); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + m_pSessionManager->SendToWorker(CMD_REQ_SET_CUSTOM_CONFIG, GetSequence(), oInMsgBody); + m_pSessionManager->SendToWorker(CMD_REQ_RELOAD_CUSTOM_CONFIG, GetSequence(), oInMsgBody); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(true); + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_FILE_NOT_EXIST); + oOutMsgBody.mutable_rsp_result()->set_msg("file \"" + ssConfFile.str() + "\" not exist!"); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(false); + } + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); + oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(false); + } +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp new file mode 100644 index 00000000..88f9ef7e --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp @@ -0,0 +1,41 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnSetCustomConf.hpp + * @brief 设置自定义配置文件 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETCUSTOMCONF_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETCUSTOMCONF_HPP_ + +#include "actor/ActorFriend.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class SessionManager; + +class CmdOnSetCustomConf: public Cmd, + public DynamicCreator, public ActorFriend +{ +public: + CmdOnSetCustomConf(int32 iCmd); + virtual ~CmdOnSetCustomConf(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); + +private: + std::shared_ptr m_pSessionManager; + std::string m_strDataString; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETCUSTOMCONF_HPP_ */ + diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp new file mode 100644 index 00000000..2c32fa5f --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp @@ -0,0 +1,98 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnSetNodeConf.cpp + * @brief 设置节点信息 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#include "CmdOnSetNodeConf.hpp" +#include "util/json/CJsonObject.hpp" +#include "ios/Dispatcher.hpp" +#include "actor/session/sys_session/manager/SessionManager.hpp" + +namespace neb +{ + +CmdOnSetNodeConf::CmdOnSetNodeConf(int iCmd) + : Cmd(iCmd), m_pSessionManager(nullptr) +{ +} + +CmdOnSetNodeConf::~CmdOnSetNodeConf() +{ +} + +bool CmdOnSetNodeConf::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + if (m_pSessionManager == nullptr) + { + m_pSessionManager = std::dynamic_pointer_cast( + GetSession("neb::SessionManager")); + if (m_pSessionManager == nullptr) + { + LOG4_ERROR("no session named \"neb::SessionManager\"!"); + return(false); + } + } + MsgBody oOutMsgBody; + ConfigInfo oConfigInfo; + if (oConfigInfo.ParseFromString(oInMsgBody.data())) + { + CJsonObject oJsonData; + if (oJsonData.Parse(oConfigInfo.file_content())) + { + // some data can not be set by beacon. + CJsonObject oCurrentConf = GetLabor(this)->GetNodeConf(); + oJsonData.Replace("node_type", oCurrentConf("node_type")); + oJsonData.Replace("access_host", oCurrentConf("access_host")); + oJsonData.Replace("access_port", oCurrentConf("access_port")); + oJsonData.Replace("access_codec", oCurrentConf("access_codec")); + oJsonData.Replace("host", oCurrentConf("host")); + oJsonData.Replace("port", oCurrentConf("port")); + oJsonData.Replace("server_name", oCurrentConf("server_name")); + oJsonData.Replace("worker_num", oCurrentConf("worker_num")); + std::ofstream fout(GetLabor(this)->GetNodeInfo().strConfFile.c_str()); + if (fout.good()) + { + std::string strNewConfData = oJsonData.ToFormattedString(); + fout.write(strNewConfData.c_str(), strNewConfData.size()); + fout.close(); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + m_pSessionManager->SendToWorker(CMD_REQ_SET_NODE_CONFIG, GetSequence(), oInMsgBody); + GetLabor(this)->SetNodeConf(oCurrentConf); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(true); + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_FILE_NOT_EXIST); + oOutMsgBody.mutable_rsp_result()->set_msg("failed to open the server config file!"); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(false); + } + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_BODY_JSON); + oOutMsgBody.mutable_rsp_result()->set_msg("the server config must be in json format!"); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(false); + } + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); + oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(false); + } +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp new file mode 100644 index 00000000..0037e726 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp @@ -0,0 +1,41 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnSetNodeConf.hpp + * @brief 设置节点信息 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECONF_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECONF_HPP_ + +#include "actor/ActorFriend.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class SessionManager; + +class CmdOnSetNodeConf: public Cmd, + public DynamicCreator, public ActorFriend +{ +public: + CmdOnSetNodeConf(int32 iCmd); + virtual ~CmdOnSetNodeConf(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); + +private: + std::shared_ptr m_pSessionManager; + std::string m_strDataString; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECONF_HPP_ */ + diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.cpp new file mode 100644 index 00000000..b0afa5a5 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.cpp @@ -0,0 +1,88 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnSetNodeCustomConf.cpp + * @brief 设置节点自定义信息 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#include "CmdOnSetNodeCustomConf.hpp" +#include "actor/session/sys_session/manager/SessionManager.hpp" + +namespace neb +{ + +CmdOnSetNodeCustomConf::CmdOnSetNodeCustomConf(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdOnSetNodeCustomConf::~CmdOnSetNodeCustomConf() +{ +} + +bool CmdOnSetNodeCustomConf::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + if (m_pSessionManager == nullptr) + { + m_pSessionManager = std::dynamic_pointer_cast( + GetSession("neb::SessionManager")); + if (m_pSessionManager == nullptr) + { + LOG4_ERROR("no session named \"neb::SessionManager\"!"); + return(false); + } + } + MsgBody oOutMsgBody; + ConfigInfo oConfigInfo; + if (oConfigInfo.ParseFromString(oInMsgBody.data())) + { + CJsonObject oJsonData; + if (oJsonData.Parse(oConfigInfo.file_content())) + { + CJsonObject oCurrentConf = GetLabor(this)->GetNodeConf(); + oCurrentConf.Replace("custom", oJsonData); + std::ofstream fout(GetLabor(this)->GetNodeInfo().strConfFile.c_str()); + if (fout.good()) + { + std::string strNewConfData = oCurrentConf.ToFormattedString(); + fout.write(strNewConfData.c_str(), strNewConfData.size()); + fout.close(); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + m_pSessionManager->SendToWorker(CMD_REQ_SET_NODE_CUSTOM_CONFIG, GetSequence(), oInMsgBody); + GetLabor(this)->SetNodeConf(oCurrentConf); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(true); + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_FILE_NOT_EXIST); + oOutMsgBody.mutable_rsp_result()->set_msg("failed to open the server config file!"); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(false); + } + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_BODY_JSON); + oOutMsgBody.mutable_rsp_result()->set_msg("the server custom config must be in json format!"); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(false); + } + } + else + { + oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); + oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); + SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); + return(false); + } +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp new file mode 100644 index 00000000..27523581 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp @@ -0,0 +1,41 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnSetNodeCustomConf.hpp + * @brief 设置节点自定义信息 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECUSTOMCONF_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECUSTOMCONF_HPP_ + +#include "actor/ActorFriend.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class SessionManager; + +class CmdOnSetNodeCustomConf: public Cmd, + public DynamicCreator, public ActorFriend +{ +public: + CmdOnSetNodeCustomConf(int32 iCmd); + virtual ~CmdOnSetNodeCustomConf(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); + +private: + std::shared_ptr m_pSessionManager; + std::string m_strDataString; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECUSTOMCONF_HPP_ */ + diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp new file mode 100644 index 00000000..c39ee290 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp @@ -0,0 +1,61 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnTellWorker.cpp + * @brief 告知Worker信息 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#include "CmdOnTellWorker.hpp" +#include "labor/NodeInfo.hpp" +#include "ios/Dispatcher.hpp" + +namespace neb +{ + +CmdOnTellWorker::CmdOnTellWorker(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdOnTellWorker::~CmdOnTellWorker() +{ +} + +bool CmdOnTellWorker::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + MsgBody oOutMsgBody; + TargetWorker oInTargetWorker; + TargetWorker oOutTargetWorker; + if (oInTargetWorker.ParseFromString(oInMsgBody.data())) + { + LOG4_DEBUG("AddNodeIdentify(%s, %s)!", oInTargetWorker.node_type().c_str(), + oInTargetWorker.worker_identify().c_str()); + GetLabor(this)->GetDispatcher()->AddNamedSocketChannel(oInTargetWorker.worker_identify(), pChannel); + GetLabor(this)->GetDispatcher()->AddNodeIdentify(oInTargetWorker.node_type(), oInTargetWorker.worker_identify()); + oOutTargetWorker.set_worker_identify(GetNodeIdentify()); + oOutTargetWorker.set_node_type(GetLabor(this)->GetNodeInfo().strNodeType); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("OK"); + //oOutMsgBody.set_data(oOutTargetWorker.SerializeAsString()); + oOutTargetWorker.SerializeToString(&m_strDataString); + oOutMsgBody.set_data(m_strDataString); + return(SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody)); + } + else + { + LOG4_ERROR("error %d: WorkerLoad ParseFromString error!", ERR_PARASE_PROTOBUF); + oOutTargetWorker.set_worker_identify("unknow"); + oOutTargetWorker.set_node_type(GetLabor(this)->GetNodeInfo().strNodeType); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); + oOutMsgBody.mutable_rsp_result()->set_msg("WorkerLoad ParseFromString error!"); + return(SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody)); + } +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp new file mode 100644 index 00000000..f333d8a0 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp @@ -0,0 +1,38 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnTellWorker.hpp + * @brief 告知Worker信息 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONTELLWORKER_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONTELLWORKER_HPP_ + +#include "actor/cmd/Cmd.hpp" +#include "actor/ActorFriend.hpp" + +namespace neb +{ + +class CmdOnTellWorker: public Cmd, + public DynamicCreator, public ActorFriend +{ +public: + CmdOnTellWorker(int32 iCmd); + virtual ~CmdOnTellWorker(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); + +private: + std::string m_strDataString; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONTELLWORKER_HPP_ */ + diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp new file mode 100644 index 00000000..5978172a --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp @@ -0,0 +1,46 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnWorkerLoad.hpp + * @brief 接收Worker节点负载等信息 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#include "CmdOnWorkerLoad.hpp" + +#include "util/json/CJsonObject.hpp" +#include "channel/SocketChannel.hpp" +#include "actor/session/sys_session/manager/SessionManager.hpp" + +namespace neb +{ + +CmdOnWorkerLoad::CmdOnWorkerLoad(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdOnWorkerLoad::~CmdOnWorkerLoad() +{ +} + +bool CmdOnWorkerLoad::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + CJsonObject oJsonLoad; + if (oJsonLoad.Parse(oInMsgBody.data())) + { + return(m_pSessionManager->SetWorkerLoad(pChannel->GetFd(), oJsonLoad)); + } + else + { + LOG4_ERROR("oInMsgBody.data() is not a json!"); + return(false); + } +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp new file mode 100644 index 00000000..4a6a127e --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp @@ -0,0 +1,40 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnWorkerLoad.hpp + * @brief 接收Worker节点负载等信息 + * @author Bwar + * @date: 2019年9月14日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONWORKERLOAD_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONWORKERLOAD_HPP_ + +#include "actor/ActorFriend.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class SessionManager; + +class CmdOnWorkerLoad: public Cmd, + public DynamicCreator, public ActorFriend +{ +public: + CmdOnWorkerLoad(int32 iCmd); + virtual ~CmdOnWorkerLoad(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); + +private: + std::shared_ptr m_pSessionManager; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONWORKERLOAD_HPP_ */ + diff --git a/src/actor/context/Context.hpp b/src/actor/context/Context.hpp index 697ef226..08a5be3c 100644 --- a/src/actor/context/Context.hpp +++ b/src/actor/context/Context.hpp @@ -10,13 +10,14 @@ #ifndef SRC_ACTOR_CONTEXT_HPP_ #define SRC_ACTOR_CONTEXT_HPP_ -#include "labor/Worker.hpp" #include "actor/DynamicCreator.hpp" #include "actor/Actor.hpp" namespace neb { +class ActorBuilder; + /** * @brief Actor上下文信息 * @note Actor上下文信息用于保存Actor(主要是Step,也可用于Session)运行 @@ -70,7 +71,7 @@ class Context: public Actor private: std::shared_ptr m_pChannel; - friend class WorkerImpl; + friend class ActorBuilder; }; } /* namespace neb */ diff --git a/src/actor/context/HttpContext.hpp b/src/actor/context/HttpContext.hpp index bd1acca6..84ccc0ee 100644 --- a/src/actor/context/HttpContext.hpp +++ b/src/actor/context/HttpContext.hpp @@ -11,13 +11,14 @@ #define SRC_ACTOR_HTTPCONTEXT_HPP_ #include "pb/http.pb.h" -#include "labor/Worker.hpp" #include "actor/DynamicCreator.hpp" #include "Context.hpp" namespace neb { +class ActorBuilder; + class HttpContext: public Context { public: @@ -49,7 +50,7 @@ class HttpContext: public Context private: HttpMsg m_oReqHttpMsg; - friend class WorkerImpl; + friend class ActorBuilder; }; } /* namespace neb */ diff --git a/src/actor/context/PbContext.hpp b/src/actor/context/PbContext.hpp index 53a735c5..c1ef3218 100644 --- a/src/actor/context/PbContext.hpp +++ b/src/actor/context/PbContext.hpp @@ -10,13 +10,14 @@ #ifndef SRC_ACTOR_PBCONTEXT_HPP_ #define SRC_ACTOR_PBCONTEXT_HPP_ -#include "labor/Worker.hpp" #include "actor/DynamicCreator.hpp" #include "Context.hpp" namespace neb { +class ActorBuilder; + class PbContext: public Context { public: @@ -64,7 +65,7 @@ class PbContext: public Context uint32 m_uiReqSeq; MsgBody m_oReqMsgBody; - friend class WorkerImpl; + friend class ActorBuilder; }; } /* namespace neb */ diff --git a/src/actor/model/Model.hpp b/src/actor/model/Model.hpp index d872b989..3c2da09c 100644 --- a/src/actor/model/Model.hpp +++ b/src/actor/model/Model.hpp @@ -10,13 +10,14 @@ #ifndef SRC_ACTOR_MODEL_HPP_ #define SRC_ACTOR_MODEL_HPP_ -#include "labor/Worker.hpp" #include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" namespace neb { +class ActorBuilder; + class Model: public Actor { public: @@ -49,7 +50,7 @@ class Model: public Actor virtual E_CMD_STATUS Submit() = 0; private: - friend class WorkerImpl; + friend class ActorBuilder; }; } /* namespace neb */ diff --git a/src/actor/session/Session.cpp b/src/actor/session/Session.cpp index 0680f3f4..e67de718 100644 --- a/src/actor/session/Session.cpp +++ b/src/actor/session/Session.cpp @@ -14,12 +14,12 @@ namespace neb { -Session::Session(uint64 ullSessionId, ev_tstamp dSessionTimeout) +Session::Session(uint32 uiSessionId, ev_tstamp dSessionTimeout) : Actor(Actor::ACT_SESSION, dSessionTimeout), m_bDataReady(false), m_bDataLoading(false) { std::ostringstream oss; - oss << ullSessionId; + oss << uiSessionId; m_strSessionId = std::move(oss.str()); } diff --git a/src/actor/session/Session.hpp b/src/actor/session/Session.hpp index 06985754..e35eea46 100644 --- a/src/actor/session/Session.hpp +++ b/src/actor/session/Session.hpp @@ -11,27 +11,23 @@ #define SRC_ACTOR_SESSION_SESSION_HPP_ #include -#include "labor/Worker.hpp" #include "actor/DynamicCreator.hpp" #include "actor/Actor.hpp" namespace neb { +class ActorBuilder; + class Session: public Actor { public: - Session(uint64 ullSessionId, ev_tstamp dSessionTimeout = 60.0); + Session(uint32 uiSessionId, ev_tstamp dSessionTimeout = 60.0); Session(const std::string& strSessionId, ev_tstamp dSessionTimeout = 60.0); Session(const Session&) = delete; Session& operator=(const Session&) = delete; virtual ~Session(); - virtual bool Init() - { - return(true); - } - /** * @brief 会话超时回调 */ @@ -71,11 +67,11 @@ class Session: public Actor uint32 PopWaitingStep(); private: - friend class WorkerImpl; bool m_bDataReady; bool m_bDataLoading; std::string m_strSessionId; std::queue m_vecWaitingStep; + friend class ActorBuilder; }; } /* namespace neb */ diff --git a/src/actor/session/sys_session/SessionLogger.cpp b/src/actor/session/sys_session/SessionLogger.cpp index 437cb747..be9c5b23 100644 --- a/src/actor/session/sys_session/SessionLogger.cpp +++ b/src/actor/session/sys_session/SessionLogger.cpp @@ -9,6 +9,7 @@ ******************************************************************************/ #include "SessionLogger.hpp" +#include "actor/cmd/CW.hpp" namespace neb { diff --git a/src/actor/session/sys_session/SessionManagerLogger.cpp b/src/actor/session/sys_session/SessionManagerLogger.cpp deleted file mode 100644 index 433585be..00000000 --- a/src/actor/session/sys_session/SessionManagerLogger.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file SessionManagerLogger.cpp - * @brief - * @author bwar - * @date: Sep 20, 2016 - * @note - * Modify history: - ******************************************************************************/ - -#include "labor/Manager.hpp" -#include "SessionManagerLogger.hpp" - -namespace neb -{ - -SessionManagerLogger::SessionManagerLogger(Manager* pManager) - : m_pManager(pManager) -{ -} - -SessionManagerLogger::~SessionManagerLogger() -{ - m_listLogMsgBody.clear(); -} - -E_CMD_STATUS SessionManagerLogger::Timeout() -{ - uint32 uiNeedSendNum = m_listLogMsgBody.size(); - for (uint32 i = 0; i < uiNeedSendNum; ++i) - { - m_pManager->SendOriented("LOGGER", CMD_REQ_LOG4_TRACE, 1, m_listLogMsgBody.front()); - m_listLogMsgBody.pop_front(); - } - return(CMD_STATUS_RUNNING); -} - -void SessionManagerLogger::AddMsg(const MsgBody& oMsgBody) -{ - // 此函数不能写日志,不然可能会导致写日志函数与此函数无限递归 - m_listLogMsgBody.push_back(oMsgBody); -} - -} diff --git a/src/actor/session/sys_session/SessionManagerLogger.hpp b/src/actor/session/sys_session/SessionManagerLogger.hpp deleted file mode 100644 index 73aa45a6..00000000 --- a/src/actor/session/sys_session/SessionManagerLogger.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file SessionManagerLogger.hpp - * @brief Manager写网络日志会话 - * @author bwar - * @date: Auguest 19, 2018 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SESSIONMANAGERLOGGER_HPP_ -#define SESSIONMANAGERLOGGER_HPP_ - -#include -#include "pb/msg.pb.h" -#include "labor/Manager.hpp" - -namespace neb -{ - -class Manager; - -class SessionManagerLogger -{ -public: - SessionManagerLogger(Manager* pManager); - virtual ~SessionManagerLogger(); - - virtual E_CMD_STATUS Timeout(); - - void AddMsg(const MsgBody& oMsgBody); - -private: - Manager* m_pManager; - std::list m_listLogMsgBody; -}; - -} - -#endif /* SESSIONMANAGERLOGGER_HPP_ */ diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp new file mode 100644 index 00000000..050e128f --- /dev/null +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -0,0 +1,331 @@ +/******************************************************************************* + * Project: Nebula + * @file SessionManager.cpp + * @brief 管理进程信息会话 + * @author bwar + * @date: 2019年9月21日 + * @note + * Modify history: + ******************************************************************************/ + +#include "SessionManager.hpp" +#include "util/json/CJsonObject.hpp" +#include "labor/NodeInfo.hpp" +#include "labor/Manager.hpp" +#include "ios/Dispatcher.hpp" +#include "actor/cmd/CW.hpp" + +namespace neb +{ + +SessionManager::SessionManager() + : Session("neb::SessionManager", gc_dNoTimeout) +{ +} + +SessionManager::~SessionManager() +{ + for (auto it = m_mapWorker.begin(); it != m_mapWorker.end(); ++it) + { + delete it->second; + it->second = nullptr; + } + m_mapWorker.clear(); + m_mapWorkerStartNum.clear(); + m_mapWorkerFdPid.clear(); + m_mapOnlineNodes.clear(); + m_vecFreeWorkerIdx.clear(); +} + +E_CMD_STATUS SessionManager::Timeout() +{ + return(CMD_STATUS_RUNNING); +} + +void SessionManager::AddOnlineNode(const std::string& strNodeIdentify, const std::string& strNodeInfo) +{ + auto iter = m_mapOnlineNodes.find(strNodeIdentify); + if (iter == m_mapOnlineNodes.end()) + { + m_mapOnlineNodes.insert( + std::make_pair(strNodeIdentify, strNodeInfo)); + } + else + { + iter->second = strNodeInfo; + } +} + +void SessionManager::DelOnlineNode(const std::string& strNodeIdentify) +{ + auto iter = m_mapOnlineNodes.find(strNodeIdentify); + if (iter == m_mapOnlineNodes.end()) + { + m_mapOnlineNodes.erase(iter); + } +} + +bool SessionManager::SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +{ + for (auto worker_iter = m_mapWorker.begin(); + worker_iter != m_mapWorker.end(); ++worker_iter) + { + GetLabor(this)->GetDispatcher()->SendTo(worker_iter->second->iControlFd); + } + return(true); +} + +void SessionManager::AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd) +{ + WorkerInfo* pWorkerInfo = nullptr; + try + { + pWorkerInfo = new WorkerInfo(); + } + catch (std::bad_alloc& e) + { + LOG4_ERROR("new WorkerInfo error: %s", e.what()); + return; + } + pWorkerInfo->iWorkerIndex = iWorkerIndex; + pWorkerInfo->iControlFd = iControlFd; + pWorkerInfo->iDataFd = iDataFd; + pWorkerInfo->dBeatTime = GetNowTime(); + m_mapWorker.insert(std::make_pair(iPid, pWorkerInfo)); + m_mapWorkerFdPid.insert(std::make_pair(iControlFd, iPid)); + m_mapWorkerFdPid.insert(std::make_pair(iDataFd, iPid)); + auto start_num_iter = m_mapWorkerStartNum.find(iWorkerIndex); + if (start_num_iter == m_mapWorkerStartNum.end()) + { + m_mapWorkerStartNum.insert(std::make_pair(iWorkerIndex, 1)); + } + else + { + start_num_iter->second++; + } +} + +const WorkerInfo* SessionManager::GetWorkerInfo(int32 iWorkerIndex) const +{ + for (auto worker_iter = m_mapWorker.begin(); + worker_iter != m_mapWorker.end(); ++worker_iter) + { + if (iWorkerIndex == worker_iter->second->iWorkerIndex) + { + return(worker_iter->second); + } + } + return(nullptr); +} + +bool SessionManager::SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad) +{ + auto fd_pid_iter = m_mapWorkerFdPid.find(iWorkerFd); + if (fd_pid_iter != m_mapWorkerFdPid.end()) + { + auto iPid = fd_pid_iter->second; + auto it = m_mapWorker.find(iPid); + if (it != m_mapWorker.end()) + { + oJsonLoad.Get("load", it->second->iLoad); + oJsonLoad.Get("connect", it->second->iConnect); + oJsonLoad.Get("recv_num", it->second->iRecvNum); + oJsonLoad.Get("recv_byte", it->second->iRecvByte); + oJsonLoad.Get("send_num", it->second->iSendNum); + oJsonLoad.Get("send_byte", it->second->iSendByte); + oJsonLoad.Get("client", it->second->iClientNum); + it->second->dBeatTime = GetNowTime(); + return(true); + } + else + { + LOG4_ERROR("no worker info found for pid %d!", iPid); + return(false); + } + } + else + { + LOG4_ERROR("%d is not a worker fd!", iWorkerFd); + return(false); + } +} + +std::pair SessionManager::GetMinLoadWorkerDataFd() +{ + int iMinLoadWorkerFd = 0; + int iMinLoad = -1; + std::pair worker_pid_fd; + for (auto iter = m_mapWorker.begin(); iter != m_mapWorker.end(); ++iter) + { + if (iter == m_mapWorker.begin()) + { + iMinLoadWorkerFd = iter->second->iDataFd; + iMinLoad = iter->second->iLoad; + worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); + } + else if (iter->second->iLoad < iMinLoad) + { + iMinLoadWorkerFd = iter->second->iDataFd; + iMinLoad = iter->second->iLoad; + worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); + } + } + return(worker_pid_fd); +} + +bool SessionManager::CheckWorker() +{ + for (auto worker_iter = m_mapWorker.begin(); + worker_iter != m_mapWorker.end(); ++worker_iter) + { + if (GetNowTime() - worker_iter->second->dBeatTime + > ((Manager*)GetLabor(this))->GetManagerInfo().iWorkerBeat) + { + LOG4_INFO("worker_%d pid %d is unresponsive, terminate it.", + worker_iter->second->iWorkerIndex, worker_iter->first); + kill(worker_iter->first, SIGKILL); + } + } + return(true); +} + +bool SessionManager::WorkerDeath(int iPid, int& iWorkerIndex) +{ + auto worker_iter = m_mapWorker.find(iPid); + if (worker_iter != m_mapWorker.end()) + { + LOG4_TRACE("restart worker %d, close control fd %d and data fd %d first.", + worker_iter->second->iWorkerIndex, worker_iter->second->iControlFd, worker_iter->second->iDataFd); + int iWorkerIndex = worker_iter->second->iWorkerIndex; + auto fd_iter = m_mapWorkerFdPid.find(worker_iter->second->iControlFd); + if (fd_iter != m_mapWorkerFdPid.end()) + { + m_mapWorkerFdPid.erase(fd_iter); + } + fd_iter = m_mapWorkerFdPid.find(worker_iter->second->iDataFd); + if (fd_iter != m_mapWorkerFdPid.end()) + { + m_mapWorkerFdPid.erase(fd_iter); + } + auto pControlChannel = GetLabor(this)->GetDispatcher()->GetChannel(worker_iter->second->iControlFd); + GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pControlChannel); + auto pDataChannel = GetLabor(this)->GetDispatcher()->GetChannel(worker_iter->second->iDataFd); + GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pDataChannel); + delete worker_iter->second; + m_mapWorker.erase(worker_iter); + + auto restart_num_iter = m_mapWorkerStartNum.find(iWorkerIndex); + if (restart_num_iter != m_mapWorkerStartNum.end()) + { + LOG4_INFO("worker %d had been restarted %d times!", iWorkerIndex, restart_num_iter->second); + } + return(true); + } + else + { + return(false); + } +} + +void SessionManager::SendOnlineNodesToWorker() +{ + // 重启Worker进程后下发其他节点的信息 + MsgBody oMsgBody; + CJsonObject oSubscribeNode; + oSubscribeNode.Add("add_nodes", CJsonObject("[]")); + for (auto it = m_mapOnlineNodes.begin(); it != m_mapOnlineNodes.end(); ++it) + { + oSubscribeNode["add_nodes"].Add(CJsonObject(it->second)); + } + oMsgBody.set_data(oSubscribeNode.ToString()); + SendToWorker(CMD_REQ_NODE_NOTICE, GetSequence(), oMsgBody); +} + +/** + * @brief 上报节点状态信息 + * @return 上报是否成功 + * @note 节点状态信息结构如: + * { + * "node_type":"ACCESS", + * "node_ip":"192.168.11.12", + * "node_port":9988, + * "access_ip":"120.234.2.106", + * "access_port":10001, + * "worker_num":10, + * "active_time":16879561651.06, + * "node":{ + * "load":1885792, "connect":495873, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":495870 + * }, + * "worker": + * [ + * {"load":655666, "connect":495873, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":195870}}, + * {"load":655235, "connect":485872, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":195870}}, + * {"load":585696, "connect":415379, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":195870}} + * ] + * } + */ +void SessionManager::MakeReportData(CJsonObject& oReportJson) +{ + int iLoad = 0; + int iConnect = 0; + int iRecvNum = 0; + int iRecvByte = 0; + int iSendNum = 0; + int iSendByte = 0; + int iClientNum = 0; + CJsonObject oReportData; + CJsonObject oMember; + oReportData.Add("node_type", GetLabor(this)->GetNodeInfo().strNodeType); + oReportData.Add("node_id", GetLabor(this)->GetNodeInfo().uiNodeId); + oReportData.Add("node_ip", GetLabor(this)->GetNodeInfo().strHostForServer); + oReportData.Add("node_port", GetLabor(this)->GetNodeInfo().iPortForServer); + if (GetLabor(this)->GetNodeInfo().strGateway.size() > 0) + { + oReportData.Add("access_ip", GetLabor(this)->GetNodeInfo().strGateway); + } + else + { + oReportData.Add("access_ip", GetLabor(this)->GetNodeInfo().strHostForClient); + } + if (GetLabor(this)->GetNodeInfo().iGatewayPort > 0) + { + oReportData.Add("access_port", GetLabor(this)->GetNodeInfo().iGatewayPort); + } + else + { + oReportData.Add("access_port", GetLabor(this)->GetNodeInfo().iPortForClient); + } + oReportData.Add("worker_num", (int)m_mapWorker.size()); + oReportData.Add("active_time", (uint64)GetNowTime()); + oReportData.Add("node", CJsonObject("{}")); + oReportData.Add("worker", CJsonObject("[]")); + auto worker_iter = m_mapWorker.begin(); + for (; worker_iter != m_mapWorker.end(); ++worker_iter) + { + iLoad += worker_iter->second->iLoad; + iConnect += worker_iter->second->iConnect; + iRecvNum += worker_iter->second->iRecvNum; + iRecvByte += worker_iter->second->iRecvByte; + iSendNum += worker_iter->second->iSendNum; + iSendByte += worker_iter->second->iSendByte; + iClientNum += worker_iter->second->iClientNum; + oMember.Clear(); + oMember.Add("load", worker_iter->second->iLoad); + oMember.Add("connect", worker_iter->second->iConnect); + oMember.Add("recv_num", worker_iter->second->iRecvNum); + oMember.Add("recv_byte", worker_iter->second->iRecvByte); + oMember.Add("send_num", worker_iter->second->iSendNum); + oMember.Add("send_byte", worker_iter->second->iSendByte); + oMember.Add("client", worker_iter->second->iClientNum); + oReportData["worker"].Add(oMember); + } + oReportData["node"].Add("load", iLoad); + oReportData["node"].Add("connect", iConnect); + oReportData["node"].Add("recv_num", iRecvNum); + oReportData["node"].Add("recv_byte", iRecvByte); + oReportData["node"].Add("send_num", iSendNum); + oReportData["node"].Add("send_byte", iSendByte); + oReportData["node"].Add("client", iClientNum); +} + +} /* namespace neb */ diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp new file mode 100644 index 00000000..fea10c3f --- /dev/null +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -0,0 +1,53 @@ +/******************************************************************************* + * Project: Nebula + * @file SessionManager.hpp + * @brief 管理进程信息会话 + * @author Bwar + * @date: 2019年9月21日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_SESSION_SYS_SESSION_MANAGER_SESSIONMANAGER_HPP_ +#define SRC_ACTOR_SESSION_SYS_SESSION_MANAGER_SESSIONMANAGER_HPP_ + +#include "labor/NodeInfo.hpp" +#include "actor/ActorFriend.hpp" +#include "actor/session/Session.hpp" + +namespace neb +{ + +class SessionManager : public Session, + public DynamicCreator, public ActorFriend +{ +public: + SessionManager(); + virtual ~SessionManager(); + + virtual E_CMD_STATUS Timeout(); + + void AddOnlineNode(const std::string& strNodeIdentify, const std::string& strNodeInfo); + void DelOnlineNode(const std::string& strNodeIdentify); + bool SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); // 向Worker发送数据 + void AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); + const WorkerInfo* GetWorkerInfo(int32 iWorkerIndex) const; + bool SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad); + std::pair GetMinLoadWorkerDataFd(); + bool CheckWorker(); + bool WorkerDeath(int iPid, int& iWorkerIndex); + void SendOnlineNodesToWorker(); + void MakeReportData(CJsonObject& oReportJson); + +private: + std::unordered_map m_mapWorker; ///< 业务逻辑工作进程及进程属性,key为pid + std::unordered_map m_mapWorkerStartNum; ///< 进程被启动次数,key为WorkerIdx + std::unordered_map m_mapWorkerFdPid; ///< 工作进程通信FD对应的进程号 + std::unordered_map m_mapOnlineNodes; ///< 订阅的节点在线信息 + std::vector m_vecFreeWorkerIdx; ///< 空闲进程编号 +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_SESSION_SYS_SESSION_MANAGER_SESSIONMANAGER_HPP_ */ + diff --git a/src/actor/step/PbStep.hpp b/src/actor/step/PbStep.hpp index 87f81a0e..6c88e153 100644 --- a/src/actor/step/PbStep.hpp +++ b/src/actor/step/PbStep.hpp @@ -15,7 +15,7 @@ namespace neb { -class WorkerImpl; +class ActorBuilder; class PbStep: public Step { @@ -42,7 +42,7 @@ class PbStep: public Step void* data = NULL) = 0; private: - friend class WorkerImpl; + friend class ActorBuilder; }; } /* namespace neb */ diff --git a/src/actor/step/Step.hpp b/src/actor/step/Step.hpp index 58646ba7..fe3ea61b 100644 --- a/src/actor/step/Step.hpp +++ b/src/actor/step/Step.hpp @@ -10,13 +10,13 @@ #ifndef SRC_ACTOR_STEP_STEP_HPP_ #define SRC_ACTOR_STEP_STEP_HPP_ -#include "labor/Worker.hpp" #include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" namespace neb { +class ActorBuilder; class Chain; class Step: public Actor @@ -56,7 +56,7 @@ class Step: public Actor std::unordered_set m_setNextStepSeq; std::unordered_set m_setPreStepSeq; - friend class WorkerImpl; + friend class ActorBuilder; friend class Chain; }; diff --git a/src/actor/step/sys_step/StepConnectWorker.cpp b/src/actor/step/sys_step/StepConnectWorker.cpp index 06b74706..d987eb2b 100644 --- a/src/actor/step/sys_step/StepConnectWorker.cpp +++ b/src/actor/step/sys_step/StepConnectWorker.cpp @@ -8,6 +8,7 @@ * Modify history: ******************************************************************************/ #include +#include "actor/cmd/CW.hpp" namespace neb { diff --git a/src/actor/step/sys_step/StepIoTimeout.cpp b/src/actor/step/sys_step/StepIoTimeout.cpp index 9d88cad2..151c6ba4 100644 --- a/src/actor/step/sys_step/StepIoTimeout.cpp +++ b/src/actor/step/sys_step/StepIoTimeout.cpp @@ -8,7 +8,7 @@ * Modify history: ******************************************************************************/ #include "actor/step/sys_step/StepIoTimeout.hpp" -#include "labor/WorkerImpl.hpp" +#include "ios/Dispatcher.hpp" namespace neb { @@ -39,13 +39,13 @@ E_CMD_STATUS StepIoTimeout::Emit(int iErrno, const std::string& strErrMsg, E_CMD_STATUS StepIoTimeout::Callback(std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody, void* data) { - GetWorkerImpl(this)->AddIoTimeout(pChannel); + GetLabor(this)->GetDispatcher()->AddIoTimeout(pChannel); return(CMD_STATUS_COMPLETED); } E_CMD_STATUS StepIoTimeout::Timeout() { - GetWorkerImpl(this)->Disconnect(m_pChannel); + GetLabor(this)->GetDispatcher()->Disconnect(m_pChannel); return(CMD_STATUS_FAULT); } diff --git a/src/actor/step/sys_step/StepIoTimeout.hpp b/src/actor/step/sys_step/StepIoTimeout.hpp index 6ea5b463..4b5aa987 100644 --- a/src/actor/step/sys_step/StepIoTimeout.hpp +++ b/src/actor/step/sys_step/StepIoTimeout.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_STEP_SYS_STEP_STEPIOTIMEOUT_HPP_ #define SRC_ACTOR_STEP_SYS_STEP_STEPIOTIMEOUT_HPP_ -#include "labor/WorkerFriend.hpp" +#include "actor/ActorFriend.hpp" #include "actor/step/PbStep.hpp" #include "Definition.hpp" @@ -23,7 +23,9 @@ namespace neb * 若该步骤正常回调,则重置连接超时,若该步骤超时,则关闭连接,销毁连接资源。该步骤实现的是服务 * 端发起的心跳机制,心跳时间间隔就是IO超时时间。 */ -class StepIoTimeout: public PbStep, public DynamicCreator >, public WorkerFriend +class StepIoTimeout: public PbStep, + public DynamicCreator >, + public ActorFriend { public: StepIoTimeout(std::shared_ptr pChannel); diff --git a/src/actor/step/sys_step/StepTellWorker.cpp b/src/actor/step/sys_step/StepTellWorker.cpp index 3a33c048..3cff2f85 100644 --- a/src/actor/step/sys_step/StepTellWorker.cpp +++ b/src/actor/step/sys_step/StepTellWorker.cpp @@ -8,6 +8,7 @@ * Modify history: ******************************************************************************/ #include +#include "ios/Dispatcher.hpp" namespace neb { @@ -49,8 +50,8 @@ E_CMD_STATUS StepTellWorker::Callback( if (oInTargetWorker.ParseFromString(oInMsgBody.data())) { LOG4_DEBUG("AddNodeIdentify(%s)!", oInTargetWorker.worker_identify().c_str()); - GetWorkerImpl(this)->AddNamedSocketChannel(oInTargetWorker.worker_identify(), pChannel); - GetWorkerImpl(this)->AddNodeIdentify(oInTargetWorker.node_type(), oInTargetWorker.worker_identify()); + GetLabor(this)->GetDispatcher()->AddNamedSocketChannel(oInTargetWorker.worker_identify(), pChannel); + GetLabor(this)->GetDispatcher()->AddNodeIdentify(oInTargetWorker.node_type(), oInTargetWorker.worker_identify()); SendTo(pChannel); return(CMD_STATUS_COMPLETED); } diff --git a/src/actor/step/sys_step/StepTellWorker.hpp b/src/actor/step/sys_step/StepTellWorker.hpp index c8db7d44..2b0ba35d 100644 --- a/src/actor/step/sys_step/StepTellWorker.hpp +++ b/src/actor/step/sys_step/StepTellWorker.hpp @@ -10,14 +10,16 @@ #ifndef SRC_ACTOR_STEP_SYS_STEP_STEPTELLWORKER_HPP_ #define SRC_ACTOR_STEP_SYS_STEP_STEPTELLWORKER_HPP_ -#include "labor/WorkerFriend.hpp" +#include "actor/ActorFriend.hpp" #include "actor/step/PbStep.hpp" #include "pb/neb_sys.pb.h" namespace neb { -class StepTellWorker: public PbStep, public DynamicCreator >, public WorkerFriend +class StepTellWorker: public PbStep, + public DynamicCreator >, + public ActorFriend { public: StepTellWorker(std::shared_ptr pChannel); diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp new file mode 100644 index 00000000..affbb281 --- /dev/null +++ b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp @@ -0,0 +1,85 @@ +/******************************************************************************* + * Project: Nebula + * @file StepReportToBeacon.cpp + * @brief 向注册中心上报 + * @author Bwar + * @date: 2019年9月22日 + * @note + * Modify history: + ******************************************************************************/ + +#include "StepReportToBeacon.hpp" +#include "labor/NodeInfo.hpp" +#include "ios/Dispatcher.hpp" +#include "actor/session/sys_session/manager/SessionManager.hpp" + +namespace neb +{ + +StepReportToBeacon::StepReportToBeacon(ev_tstamp dTimeout) + : PbStep(nullptr, dTimeout) +{ +} + +StepReportToBeacon::~StepReportToBeacon() +{ +} + +E_CMD_STATUS StepReportToBeacon::Emit( + int iErrno, const std::string& strErrMsg, void* data) +{ + if (std::string("BEACON") == GetLabor(this)->GetNodeInfo().strNodeType) + { + return(CMD_STATUS_RUNNING); + } + MsgBody oMsgBody; + CJsonObject oReportData; + if (m_pSessionManager == nullptr) + { + m_pSessionManager = std::dynamic_pointer_cast( + GetSession("neb::SessionManager")); + if (m_pSessionManager == nullptr) + { + LOG4_ERROR("no session named \"neb::SessionManager\"!"); + return(CMD_STATUS_FAULT); + } + } + m_pSessionManager->MakeReportData(oReportData); + oMsgBody.set_data(oReportData.ToString()); + GetLabor(this)->GetDispatcher()->Broadcast("BEACON", + (int32)CMD_REQ_NODE_STATUS_REPORT, GetSequence(), oMsgBody); + return(CMD_STATUS_RUNNING); +} + +E_CMD_STATUS StepReportToBeacon::Callback( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody, + void* data) +{ + if (ERR_OK == oInMsgBody.rsp_result().code()) + { + CJsonObject oNode(oInMsgBody.data()); + uint32 uiNodeId = 0; + oNode.Get("node_id", uiNodeId); + if (uiNodeId != GetLabor(this)->GetNodeInfo().uiNodeId) + { + GetLabor(this)->SetNodeId((uiNodeId)); + m_pSessionManager->SendToWorker(CMD_REQ_REFRESH_NODE_ID, oInMsgHead.seq(), oInMsgBody); + } + return(CMD_STATUS_RUNNING); + } + else + { + LOG4_ERROR("report to beacon error %d: %s!", oInMsgBody.rsp_result().code(), + oInMsgBody.rsp_result().msg().c_str()); + return(CMD_STATUS_RUNNING); + } +} + +E_CMD_STATUS StepReportToBeacon::Timeout() +{ + return(Emit()); +} + +} /* namespace neb */ diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.hpp b/src/actor/step/sys_step/manager/StepReportToBeacon.hpp new file mode 100644 index 00000000..b0f851f7 --- /dev/null +++ b/src/actor/step/sys_step/manager/StepReportToBeacon.hpp @@ -0,0 +1,48 @@ +/******************************************************************************* + * Project: Nebula + * @file StepReportToBeacon.hpp + * @brief 向注册中心上报 + * @author Bwar + * @date: 2019年9月22日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_STEP_SYS_STEP_MANAGER_STEPREPORTTOBEACON_HPP_ +#define SRC_ACTOR_STEP_SYS_STEP_MANAGER_STEPREPORTTOBEACON_HPP_ + +#include "actor/ActorFriend.hpp" +#include "actor/step/PbStep.hpp" + +namespace neb +{ + +class SessionManager; + +class StepReportToBeacon: public PbStep, + public DynamicCreator, public ActorFriend +{ +public: + StepReportToBeacon(ev_tstamp dTimeout); + virtual ~StepReportToBeacon(); + + virtual E_CMD_STATUS Emit( + int iErrno = 0, + const std::string& strErrMsg = "", + void* data = NULL); + + virtual E_CMD_STATUS Callback( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody, + void* data = NULL); + + virtual E_CMD_STATUS Timeout(); + +private: + std::shared_ptr m_pSessionManager; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_STEP_SYS_STEP_MANAGER_STEPREPORTTOBEACON_HPP_ */ diff --git a/src/channel/RedisChannel.hpp b/src/channel/RedisChannel.hpp index 836f0373..5cbfc965 100644 --- a/src/channel/RedisChannel.hpp +++ b/src/channel/RedisChannel.hpp @@ -20,7 +20,8 @@ namespace neb { -class WorkerImpl; +class Dispatcher; +class ActorBuilder; class RedisStep; class RedisChannel: public Channel @@ -49,7 +50,8 @@ class RedisChannel: public Channel redisAsyncContext* m_pRedisCtx; ///< redis连接上下文地址 std::string m_strIdentify; ///< 连接标识(可以为空,不为空时用于标识业务层与连接的关系) std::list< std::shared_ptr > listPipelineStep; ///< RedisStep*的创建和回收在WorkerImpl - friend WorkerImpl; + friend Dispatcher; + friend ActorBuilder; }; } /* namespace neb */ diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index ce14788a..3d222669 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -45,6 +45,11 @@ SocketChannel::~SocketChannel() m_pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ""); } +int SocketChannel::GetFd() const +{ + return(m_pImpl->GetFd()); +} + const std::string& SocketChannel::GetIdentify() const { return(m_pImpl->GetIdentify()); @@ -60,6 +65,16 @@ const std::string& SocketChannel::GetClientData() const return(m_pImpl->GetClientData()); } +E_CODEC_TYPE SocketChannel::GetCodecType() const +{ + return(m_pImpl->GetCodecType()); +} + +uint32 SocketChannel::GetStepSeq() const +{ + return(m_pImpl->GetStepSeq()); +} + int SocketChannel::SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, std::shared_ptr pLogger) { ssize_t n; diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index 2d9f7466..d7acf35b 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -18,6 +18,8 @@ namespace neb { +class Dispatcher; + class SocketChannel: public Channel, public std::enable_shared_from_this { public: @@ -34,10 +36,12 @@ class SocketChannel: public Channel, public std::enable_shared_from_this pLogger); static int RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, int& iCodecType, std::shared_ptr pLogger); - + int GetFd() const; const std::string& GetIdentify() const; const std::string& GetRemoteAddr() const; const std::string& GetClientData() const; + E_CODEC_TYPE GetCodecType() const; + uint32 GetStepSeq() const; // 设置对称加密密钥 void SetSecretKey(const std::string& strKey) @@ -50,8 +54,7 @@ class SocketChannel: public Channel, public std::enable_shared_from_this m_pImpl; std::shared_ptr m_pLogger; - friend class WorkerImpl; - friend class Manager; + friend class Dispatcher; }; } /* namespace neb */ diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index cd3dd974..eb6537b9 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -8,7 +8,6 @@ * Modify history: ******************************************************************************/ #include -#include "labor/WorkerImpl.hpp" #include "codec/CodecProto.hpp" #include "codec/CodecPrivate.hpp" #include "codec/CodecHttp.hpp" diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 05182ca9..25ad5e07 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -35,8 +35,7 @@ namespace neb { class Labor; -class WorkerImpl; -class Manager; +class Dispatcher; class NetLogger; class SocketChannel; @@ -219,8 +218,7 @@ class SocketChannelImpl: public Channel std::shared_ptr m_pLogger; - friend class WorkerImpl; - friend class Manager; + friend class Dispatcher; }; template diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp new file mode 100644 index 00000000..f556b772 --- /dev/null +++ b/src/ios/Dispatcher.cpp @@ -0,0 +1,1667 @@ +/******************************************************************************* + * Project: Nebula + * @file Dispatcher.cpp + * @brief 事件管理、事件分发 + * @author Bwar + * @date: 2019年9月7日 + * @note + * Modify history: + ******************************************************************************/ + +#include "Dispatcher.hpp" +#include +#include +#include +#include +#include +#include +#include "util/process_helper.h" +#include "Definition.hpp" +#include "labor/Labor.hpp" +#include "labor/Manager.hpp" +#include "labor/Worker.hpp" +#include "actor/Actor.hpp" +#include "actor/step/Step.hpp" +#include "actor/step/RedisStep.hpp" +#include "actor/session/sys_session/manager/SessionManager.hpp" +#include "Nodes.hpp" + +namespace neb +{ + +Dispatcher::Dispatcher(Labor* pLabor, std::shared_ptr pLogger) + : m_pErrBuff(NULL), m_pLabor(pLabor), m_loop(NULL), m_iClientNum(0), + m_pLogger(pLogger), m_pSessionNode(nullptr) +{ + m_pErrBuff = (char*)malloc(gc_iErrBuffLen); + m_loop = ev_loop_new(EVFLAG_FORKCHECK | EVFLAG_SIGNALFD); +#if __cplusplus >= 201401L + m_pSessionNode = std::make_unique(); +#else + m_pSessionNode = std::unique_ptr(new Nodes()); +#endif +} + +Dispatcher::~Dispatcher() +{ + Destroy(); +} + +void Dispatcher::IoCallback(struct ev_loop* loop, struct ev_io* watcher, int revents) +{ + if (watcher->data != NULL) + { + SocketChannel* pChannel = static_cast(watcher->data); + Dispatcher* pDispatcher = pChannel->m_pImpl->m_pLabor->GetDispatcher(); + std::shared_ptr pSharedChannel = pChannel->shared_from_this(); + if (revents & EV_READ) + { + pDispatcher->OnIoRead(pSharedChannel); + } + if ((revents & EV_WRITE) && (CHANNEL_STATUS_CLOSED != pChannel->m_pImpl->GetChannelStatus())) // the channel maybe closed by OnIoRead() + { + pDispatcher->OnIoWrite(pSharedChannel); + } + if (revents & EV_ERROR) + { + pDispatcher->OnIoError(pSharedChannel); + } + } +} + +void Dispatcher::IoTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) +{ + if (watcher->data != NULL) + { + SocketChannel* pChannel = static_cast(watcher->data); + Dispatcher* pDispatcher = pChannel->m_pImpl->m_pLabor->GetDispatcher(); + if (pChannel->m_pImpl->GetFd() < 3) // TODO 这个判断是不得已的做法,需查找fd为0回调到这里的原因 + { + return; + } + pDispatcher->OnIoTimeout(pChannel->shared_from_this()); + } +} + +void Dispatcher::RedisConnectCallback(const redisAsyncContext *c, int status) +{ + if (c->data != NULL) + { + Labor* pLabor = (Labor*)c->data; + pLabor->GetDispatcher()->OnRedisConnected(c, status); + } +} + +void Dispatcher::RedisDisconnectCallback(const redisAsyncContext *c, int status) +{ + if (c->data != NULL) + { + Labor* pLabor = (Labor*)c->data; + pLabor->GetDispatcher()->OnRedisDisconnected(c, status); + } +} + +void Dispatcher::RedisCmdCallback(redisAsyncContext *c, void *reply, void *privdata) +{ + if (c->data != NULL) + { + Labor* pLabor = (Labor*)c->data; + pLabor->GetDispatcher()->OnRedisCmdResult(c, reply, privdata); + } +} + +void Dispatcher::PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, int revents) +{ + if (watcher->data != NULL) + { + Dispatcher* pDispatcher = (Dispatcher*)(watcher->data); + if (Labor::LABOR_MANAGER == pDispatcher->m_pLabor->GetLaborType()) + { + ((Manager*)(pDispatcher->m_pLabor))->GetSessionManager()->CheckWorker(); + ((Manager*)(pDispatcher->m_pLabor))->RefreshServer(); + } + else + { + ((Worker*)(pDispatcher->m_pLabor))->CheckParent(); + } + } + ev_timer_stop (loop, watcher); + ev_timer_set (watcher, NODE_BEAT + ev_time() - ev_now(loop), 0); + ev_timer_start (loop, watcher); +} + +void Dispatcher::SignalCallback(struct ev_loop* loop, struct ev_signal* watcher, int revents) +{ + if (watcher->data != NULL) + { + Labor* pLabor = (Labor*)watcher->data; + if (SIGCHLD == watcher->signum) + { + ((Manager*)pLabor)->OnChildTerminated(watcher); + } + else + { + pLabor->OnTerminated(watcher); + } + } +} + +void Dispatcher::ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) +{ + if (watcher->data != NULL) + { + tagClientConnWatcherData* pData = (tagClientConnWatcherData*)watcher->data; + pData->pDispatcher->OnClientConnFrequencyTimeout(pData, watcher); + } +} + +bool Dispatcher::OnIoRead(std::shared_ptr pChannel) +{ + LOG4_TRACE("fd[%d]", pChannel->m_pImpl->GetFd()); + if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) + { + if (pChannel->m_pImpl->GetFd() == ((Manager*)m_pLabor)->GetManagerInfo().iS2SListenFd) + { + return(AcceptServerConn(pChannel->m_pImpl->GetFd())); + } + else if (((Manager*)m_pLabor)->GetManagerInfo().iC2SListenFd > 2 + && pChannel->m_pImpl->GetFd() == ((Manager*)m_pLabor)->GetManagerInfo().iC2SListenFd) + { + return(AcceptFdAndTransfer(((Manager*)m_pLabor)->GetManagerInfo().iC2SListenFd, + ((Manager*)m_pLabor)->GetManagerInfo().iC2SFamily)); + } + else + { + return(DataRecvAndHandle(pChannel)); + } + } + else + { + if (pChannel->m_pImpl->GetFd() == ((Worker*)m_pLabor)->GetWorkerInfo().iDataFd) + { + return(FdTransfer(pChannel->m_pImpl->GetFd())); + } + else + { + return(DataRecvAndHandle(pChannel)); + } + } +} + +bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) +{ + LOG4_TRACE(" "); + E_CODEC_STATUS eCodecStatus; + if (CODEC_HTTP == pChannel->m_pImpl->GetCodecType()) + { + for (int i = 0; ; ++i) + { + HttpMsg oHttpMsg; + if (0 == i) + { + eCodecStatus = pChannel->m_pImpl->Recv(oHttpMsg); + } + else + { + eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); + } + + if (CODEC_STATUS_OK == eCodecStatus) + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + } + else if (CODEC_STATUS_WANT_WRITE == eCodecStatus) + { + return(SendTo(pChannel)); + } + else + { + break; + } + } + } + else + { + MsgHead oMsgHead; + MsgBody oMsgBody; + eCodecStatus = pChannel->m_pImpl->Recv(oMsgHead, oMsgBody); + while (CODEC_STATUS_OK == eCodecStatus) + { + if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) + { + if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 + { + LOG4_DEBUG("invalid request, please login first!"); + DiscardSocketChannel(pChannel); + return(false); + } + } + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); + oMsgHead.Clear(); // 注意protobuf的Clear()使用不当容易造成内存泄露 + oMsgBody.Clear(); + eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); + } + } + + if (CODEC_STATUS_PAUSE == eCodecStatus) + { + return(true); + } + else if (CODEC_STATUS_WANT_WRITE == eCodecStatus) + { + return(SendTo(pChannel)); + } + else if (CODEC_STATUS_WANT_READ == eCodecStatus) + { + RemoveIoWriteEvent(pChannel); + return(true); + } + else // 编解码出错或连接关闭或连接中断 + { + LOG4_DEBUG("codec error or connection closed!"); + DiscardSocketChannel(pChannel); + return(false); + } +} + +bool Dispatcher::FdTransfer(int iFd) +{ + LOG4_TRACE(" "); + int iAcceptFd = -1; + int iAiFamily = AF_INET; + int iCodec = 0; + int iErrno = SocketChannel::RecvChannelFd(iFd, iAcceptFd, iAiFamily, iCodec, m_pLogger); + if (iErrno != ERR_OK) + { + if (iErrno == ERR_CHANNEL_EOF) + { + LOG4_WARNING("recv_fd from fd %d error %d", iFd, errno); + Destroy(); + exit(2); // manager与worker通信fd已关闭,worker进程退出 + } + } + else + { + int iKeepAlive = 1; + int iKeepIdle = 60; + int iKeepInterval = 5; + int iKeepCount = 3; + int iTcpNoDelay = 1; + if (setsockopt(iAcceptFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPALIVE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)) < 0) + { + LOG4_WARNING("fail to set TCP_KEEPIDLE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)) < 0) + { + LOG4_WARNING("fail to set TCP_KEEPINTVL"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)) < 0) + { + LOG4_WARNING("fail to set TCP_KEEPCNT"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)) < 0) + { + LOG4_WARNING("fail to set TCP_NODELAY"); + } + std::shared_ptr pChannel = nullptr; + LOG4_TRACE("fd[%d] transfer successfully.", iAcceptFd); + if (CODEC_NEBULA != iCodec && ((Worker*)m_pLabor)->WithSsl()) + { + pChannel = CreateSocketChannel(iAcceptFd, E_CODEC_TYPE(iCodec), false, true); + } + else + { + pChannel = CreateSocketChannel(iAcceptFd, E_CODEC_TYPE(iCodec), false, false); + } + if (nullptr != pChannel) + { + if (AF_INET == iAiFamily) + { + char szClientAddr[64] = {0}; + int z; /* status return code */ + struct sockaddr_in stClientAddr; + socklen_t iClientAddrSize = sizeof(stClientAddr); + z = getpeername(iAcceptFd, (struct sockaddr *)&stClientAddr, &iClientAddrSize); + if (z == 0) + { + inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientAddr, sizeof(szClientAddr)); + LOG4_TRACE("set fd %d's remote addr \"%s\"", iAcceptFd, szClientAddr); + pChannel->m_pImpl->SetRemoteAddr(std::string(szClientAddr)); + } + else + { + LOG4_ERROR("getpeername error %d", errno); + } + } + else // AF_INET6 + { + char szClientAddr[64] = {0}; + int z; /* status return code */ + struct sockaddr_in6 stClientAddr; + socklen_t iClientAddrSize = sizeof(stClientAddr); + z = getpeername(iAcceptFd, (struct sockaddr *)&stClientAddr, &iClientAddrSize); + if (z == 0) + { + inet_ntop(AF_INET6, &stClientAddr.sin6_addr, szClientAddr, sizeof(szClientAddr)); + LOG4_TRACE("set fd %d's remote addr \"%s\"", iAcceptFd, szClientAddr); + pChannel->m_pImpl->SetRemoteAddr(std::string(szClientAddr)); + } + else + { + LOG4_ERROR("getpeername error %d", errno); + } + } + AddIoReadEvent(pChannel); + if (CODEC_NEBULA == iCodec) + { + AddIoTimeout(pChannel, m_pLabor->GetNodeInfo().dIoTimeout); + std::shared_ptr pStepTellWorker + = m_pLabor->GetActorBuilder()->MakeSharedStep(nullptr, "neb::StepTellWorker", pChannel); + if (nullptr == pStepTellWorker) + { + return(false); + } + pStepTellWorker->Emit(ERR_OK); + } + else + { + pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + AddIoTimeout(pChannel, 1.0); // 为了防止大量连接攻击,初始化连接只有一秒即超时,在正常发送第一个数据包之后才采用正常配置的网络IO超时检查 + } + return(true); + } + else // 没有足够资源分配给新连接,直接close掉 + { + close(iAcceptFd); + } + } + return(false); +} + +bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) +{ + if (CODEC_NEBULA == pChannel->m_pImpl->GetCodecType()) // 系统内部Server间通信 + { + if (CHANNEL_STATUS_TRY_CONNECT == pChannel->m_pImpl->GetChannelStatus()) // connect之后的第一个写事件 + { + if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) + { + MsgBody oMsgBody; + oMsgBody.set_data(std::to_string(pChannel->m_pImpl->m_unRemoteWorkerIdx)); + LOG4_DEBUG("send after connect, oMsgBody.ByteSize() = %d", oMsgBody.ByteSize()); + SendTo(pChannel, CMD_REQ_CONNECT_TO_WORKER, m_pLabor->GetSequence(), oMsgBody); + return(true); + } + else + { + std::shared_ptr pStepConnectWorker = m_pLabor->GetActorBuilder()->MakeSharedStep( + nullptr, "neb::StepConnectWorker", pChannel, pChannel->m_pImpl->m_unRemoteWorkerIdx); + if (nullptr == pStepConnectWorker) + { + LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); + return(false); + } + if (CMD_STATUS_RUNNING != pStepConnectWorker->Emit(ERR_OK)) + { + m_pLabor->GetActorBuilder()->RemoveStep(pStepConnectWorker); + } + return(true); + } + } + } + else + { + if (CHANNEL_STATUS_CLOSED != pChannel->m_pImpl->GetChannelStatus()) + { + pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + } + } + + E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(); + if (CODEC_STATUS_OK == eCodecStatus) + { + RemoveIoWriteEvent(pChannel); + } + else if (CODEC_STATUS_PAUSE == eCodecStatus || CODEC_STATUS_WANT_WRITE == eCodecStatus) + { + AddIoWriteEvent(pChannel); + } + else if (CODEC_STATUS_WANT_READ == eCodecStatus) + { + RemoveIoWriteEvent(pChannel); + } + else + { + DiscardSocketChannel(pChannel); + } + return(true); +} + +bool Dispatcher::OnIoError(std::shared_ptr pChannel) +{ + LOG4_TRACE(" "); + return(false); +} + +bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) +{ + ev_tstamp after = pChannel->m_pImpl->GetActiveTime() - ev_now(m_loop) + m_pLabor->GetNodeInfo().dIoTimeout; + if (after > 0) // IO在定时时间内被重新刷新过,重新设置定时器 + { + ev_timer_stop (m_loop, pChannel->m_pImpl->MutableTimerWatcher()); + ev_timer_set (pChannel->m_pImpl->MutableTimerWatcher(), after + ev_time() - ev_now(m_loop), 0); + ev_timer_start (m_loop, pChannel->m_pImpl->MutableTimerWatcher()); + return(true); + } + + LOG4_TRACE("fd %d, seq %u:", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); + if (pChannel->m_pImpl->NeedAliveCheck()) // 需要发送心跳检查 + { + std::shared_ptr pStepIoTimeout = m_pLabor->GetActorBuilder()->MakeSharedStep( + nullptr, "neb::StepIoTimeout", pChannel); + if (nullptr == pStepIoTimeout) + { + LOG4_ERROR("new StepIoTimeout error!"); + DiscardSocketChannel(pChannel); + } + E_CMD_STATUS eStatus = pStepIoTimeout->Emit(); + if (CMD_STATUS_RUNNING != eStatus) + { + // 若返回非running状态,则表明发包时已出错, + // 销毁连接过程在SendTo里已经完成,这里不需要再销毁连接 + m_pLabor->GetActorBuilder()->RemoveStep(pStepIoTimeout); + } + } + else // 关闭文件描述符并清理相关资源 + { + if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType()) // 非内部服务器间的连接才会在超时中关闭 + { + LOG4_TRACE("io timeout!"); + DiscardSocketChannel(pChannel); + } + } + return(true); +} + +bool Dispatcher::OnRedisConnected(const redisAsyncContext *c, int status) +{ + LOG4_TRACE(" "); + auto channel_iter = m_mapRedisChannel.find((redisAsyncContext*)c); + if (channel_iter != m_mapRedisChannel.end()) + { + if (!m_pLabor->GetActorBuilder()->OnRedisConnected(channel_iter->second, c, status)) + { + DelNamedRedisChannel(channel_iter->second->GetIdentify()); + m_mapRedisChannel.erase(channel_iter); + } + } + return(true); +} + +bool Dispatcher::OnRedisDisconnected(const redisAsyncContext *c, int status) +{ + LOG4_TRACE(" "); + auto channel_iter = m_mapRedisChannel.find((redisAsyncContext*)c); + if (channel_iter != m_mapRedisChannel.end()) + { + m_pLabor->GetActorBuilder()->OnRedisDisconnected(channel_iter->second, c, status); + DelNamedRedisChannel(channel_iter->second->GetIdentify()); + m_mapRedisChannel.erase(channel_iter); + } + redisAsyncFree(const_cast(c)); + return(true); +} + +bool Dispatcher::OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privdata) +{ + LOG4_TRACE(" "); + auto channel_iter = m_mapRedisChannel.find((redisAsyncContext*)c); + if (channel_iter != m_mapRedisChannel.end()) + { + m_pLabor->GetActorBuilder()->OnRedisCmdResult(channel_iter->second, c, reply, privdata); + if (nullptr == reply) + { + DelNamedRedisChannel(channel_iter->second->GetIdentify()); + m_mapRedisChannel.erase(channel_iter); + } + } + return(true); +} + +bool Dispatcher::OnClientConnFrequencyTimeout(tagClientConnWatcherData* pData, ev_timer* watcher) +{ + bool bRes = false; + auto iter = m_mapClientConnFrequency.find(std::string(pData->pAddr)); + if (iter == m_mapClientConnFrequency.end()) + { + bRes = false; + } + else + { + m_mapClientConnFrequency.erase(iter); + bRes = true; + } + + DelEvent(watcher); + pData->pDispatcher = nullptr; + delete pData; + watcher->data = NULL; + delete watcher; + watcher = NULL; + return(bRes); +} + +void Dispatcher::EeventRun() +{ + ev_run (m_loop, 0); +} + +bool Dispatcher::AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout) +{ + LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); + ev_timer* timer_watcher = pChannel->m_pImpl->MutableTimerWatcher(); + if (NULL == timer_watcher) + { + return(false); + } + else + { + if (ev_is_active(timer_watcher)) + { + ev_timer_stop(m_loop, timer_watcher); + ev_timer_set(timer_watcher, dTimeout + ev_time() - ev_now(m_loop), 0); + ev_timer_start (m_loop, timer_watcher); + } + else + { + ev_timer_init (timer_watcher, IoTimeoutCallback, dTimeout + ev_time() - ev_now(m_loop), 0.); + ev_timer_start (m_loop, timer_watcher); + } + return(true); + } +} + +bool Dispatcher::SendTo(std::shared_ptr pChannel) +{ + LOG4_TRACE("channel %d", pChannel->m_pImpl->GetFd()); + E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(); + if (CODEC_STATUS_OK == eStatus) + { + RemoveIoWriteEvent(pChannel); + return(true); + } + else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) + { + AddIoWriteEvent(pChannel); + return(true); + } + else if (CODEC_STATUS_WANT_READ == eStatus) + { + RemoveIoWriteEvent(pChannel); + return(true); + } + return(false); +} + +bool Dispatcher::SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +{ + if (nullptr != pSender) + { + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); + } + E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); + if (CODEC_STATUS_OK == eStatus) + { + RemoveIoWriteEvent(pChannel); + return(true); + } + else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) + { + AddIoWriteEvent(pChannel); + return(true); + } + else if (CODEC_STATUS_WANT_READ == eStatus) + { + RemoveIoWriteEvent(pChannel); + return(true); + } + return(false); +} + +bool Dispatcher::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +{ + LOG4_TRACE("identify: %s", strIdentify.c_str()); + auto named_iter = m_mapNamedSocketChannel.find(strIdentify); + if (named_iter == m_mapNamedSocketChannel.end()) + { + LOG4_TRACE("no channel match %s.", strIdentify.c_str()); + if (nullptr != pSender) + { + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); + } + return(AutoSend(strIdentify, iCmd, uiSeq, oMsgBody)); + } + else + { + if (nullptr != pSender) + { + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); + } + E_CODEC_STATUS eStatus = (*named_iter->second.begin())->m_pImpl->Send(iCmd, uiSeq, oMsgBody); + if (CODEC_STATUS_OK == eStatus) + { + RemoveIoWriteEvent(*named_iter->second.begin()); + return(true); + } + else if (CODEC_STATUS_PAUSE == eStatus) // || CODEC_STATUS_WANT_WRITE == eCodecStatus) + { + AddIoWriteEvent(*named_iter->second.begin()); + return(true); + } + else if (CODEC_STATUS_WANT_READ == eStatus) + { + RemoveIoWriteEvent(*named_iter->second.begin()); + return(true); + } + return(false); + } +} + +bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +{ + LOG4_TRACE("node_type: %s", strNodeType.c_str()); + std::string strOnlineNode; + if (m_pSessionNode->GetNode(strNodeType, strOnlineNode)) + { + if (nullptr != pSender) + { + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); + } + return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); + } + else + { + LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } +} + +bool Dispatcher::SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +{ + LOG4_TRACE("nody_type: %s, factor: %d", strNodeType.c_str(), uiFactor); + std::string strOnlineNode; + if (m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) + { + if (nullptr != pSender) + { + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); + } + return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); + } + else + { + LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } +} + +bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +{ + LOG4_TRACE("nody_type: %s", strNodeType.c_str()); + if (oMsgBody.has_req_target()) + { + if (0 != oMsgBody.req_target().route_id()) + { + return(SendOriented(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, pSender)); + } + else if (oMsgBody.req_target().route().length() > 0) + { + std::string strOnlineNode; + if (m_pSessionNode->GetNode(strNodeType, oMsgBody.req_target().route(), strOnlineNode)) + { + if (nullptr != pSender) + { + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); + } + return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); + } + else + { + LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } + } + else + { + return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); + } + } + else + { + LOG4_ERROR("MsgBody is not a request message!"); + return(false); + } +}; + +bool Dispatcher::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +{ + LOG4_TRACE("node_type: %s", strNodeType.c_str()); + std::unordered_set setOnlineNodes; + if (m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) + { + if (nullptr != pSender) + { + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); + } + bool bSendResult = false; + for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + { + bSendResult |= SendTo(*node_iter, iCmd, uiSeq, oMsgBody); + } + return(bSendResult); + } + else + { + if ("BEACON" == strNodeType) + { + LOG4_WARNING("no beacon config."); + } + else + { + LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); + } + return(false); + } +} + +bool Dispatcher::SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) +{ + E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); + switch (eStatus) + { + case CODEC_STATUS_OK: + return(true); + case CODEC_STATUS_PAUSE: + case CODEC_STATUS_WANT_WRITE: + AddIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_WANT_READ: + RemoveIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_EOF: // http1.0 respone and close + DiscardSocketChannel(pChannel); + return(true); + default: + DiscardSocketChannel(pChannel); + return(false); + } +} + +bool Dispatcher::SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) +{ + char szIdentify[256] = {0}; + snprintf(szIdentify, sizeof(szIdentify), "%s:%d%s", strHost.c_str(), iPort, strUrlPath.c_str()); + LOG4_TRACE("identify: %s", szIdentify); + auto named_iter = m_mapNamedSocketChannel.find(szIdentify); + if (named_iter == m_mapNamedSocketChannel.end()) + { + LOG4_TRACE("no channel match %s.", szIdentify); + return(AutoSend(strHost, iPort, strUrlPath, oHttpMsg, uiHttpStepSeq)); + } + else + { + if (named_iter->second.empty()) + { + return(AutoSend(strHost, iPort, strUrlPath, oHttpMsg, uiHttpStepSeq)); + } + else + { + auto channel_iter = named_iter->second.begin(); + E_CODEC_STATUS eStatus = (*channel_iter)->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); + if (CODEC_STATUS_OK == eStatus) + { + named_iter->second.erase(channel_iter); // erase from named channel pool, the channel remain in m_mapSocketChannel. + return(true); + } + else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) + { + AddIoWriteEvent(*channel_iter); + return(true); + } + else if (CODEC_STATUS_WANT_READ == eStatus) + { + RemoveIoWriteEvent(*channel_iter); + return(true); + } + return(false); + } + } +} + +bool Dispatcher::SendTo(std::shared_ptr pRedisChannel, Actor* pSender) +{ + LOG4_TRACE(" "); + if (nullptr == pSender) + { + LOG4_ERROR("pSender can't be nullptr!"); + return(false); + } + if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) + { + LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); + return(false); + } + std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); + if (pRedisChannel->bIsReady) + { + int status; + size_t args_size = pRedisStep->GetCmdArguments().size() + 1; + const char* argv[args_size]; + size_t arglen[args_size]; + argv[0] = pRedisStep->GetCmd().c_str(); + arglen[0] = pRedisStep->GetCmd().size(); + std::vector >::const_iterator c_iter = pRedisStep->GetCmdArguments().begin(); + for (size_t i = 1; c_iter != pRedisStep->GetCmdArguments().end(); ++c_iter, ++i) + { + argv[i] = c_iter->first.c_str(); + arglen[i] = c_iter->first.size(); + } + status = redisAsyncCommandArgv((redisAsyncContext*)pRedisChannel->RedisContext(), RedisCmdCallback, nullptr, args_size, argv, arglen); + if (status == REDIS_OK) + { + LOG4_DEBUG("succeed in sending redis cmd: %s", pRedisStep->CmdToString().c_str()); + pRedisChannel->listPipelineStep.push_back(pRedisStep); + return(true); + } + else + { + LOG4_ERROR("redis status %d!", status); + return(false); + } + } + else + { + pRedisChannel->listPipelineStep.push_back(pRedisStep); + return(true); + } +} + +bool Dispatcher::SendTo(const std::string& strIdentify, Actor* pSender) +{ + LOG4_TRACE("%s", strIdentify.c_str()); + if (nullptr == pSender) + { + LOG4_ERROR("pSender can't be nullptr!"); + return(false); + } + auto ctx_iter = m_mapNamedRedisChannel.find(strIdentify); + if (ctx_iter != m_mapNamedRedisChannel.end()) + { + return(SendTo(*(ctx_iter->second.begin()), pSender)); + } + else + { + size_t iPosIpPortSeparator = strIdentify.rfind(':'); + if (iPosIpPortSeparator == std::string::npos) + { + LOG4_ERROR("invalid node identify \"%s\"", strIdentify.c_str()); + return(false); + } + std::string strHost = strIdentify.substr(0, iPosIpPortSeparator); + std::string strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); + int iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + LOG4_ERROR("invalid node identify \"%s\"", strIdentify.c_str()); + return(false); + } + if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) + { + LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); + return(false); + } + std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); + return(AutoRedisCmd(strHost, iPort, pRedisStep)); + } +} + +bool Dispatcher::SendTo(const std::string& strHost, int iPort, Actor* pSender) +{ + LOG4_TRACE("%s, %d", strHost.c_str(), iPort); + if (nullptr == pSender) + { + LOG4_ERROR("pSender can't be nullptr!"); + return(false); + } + std::ostringstream oss; + oss << strHost << ":" << iPort; + std::string strIdentify = std::move(oss.str()); + auto ctx_iter = m_mapNamedRedisChannel.find(strIdentify); + if (ctx_iter != m_mapNamedRedisChannel.end()) + { + return(SendTo(*(ctx_iter->second.begin()), pSender)); + } + else + { + if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) + { + LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); + return(false); + } + std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); + return(AutoRedisCmd(strHost, iPort, pRedisStep)); + } +} + +bool Dispatcher::AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +{ + LOG4_TRACE("%s", strIdentify.c_str()); + size_t iPosIpPortSeparator = strIdentify.rfind(':'); + if (iPosIpPortSeparator == std::string::npos) + { + return(false); + } + size_t iPosPortWorkerIndexSeparator = strIdentify.rfind('.'); + std::string strHost = strIdentify.substr(0, iPosIpPortSeparator); + std::string strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); + std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); + int iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + return(false); + } + int iWorkerIndex = atoi(strWorkerIndex.c_str()); + if (iWorkerIndex > 200) + { + return(false); + } + + struct addrinfo stAddrHints; + struct addrinfo* pAddrResult; + struct addrinfo* pAddrCurrent; + memset(&stAddrHints, 0, sizeof(struct addrinfo)); + stAddrHints.ai_family = AF_UNSPEC; + stAddrHints.ai_socktype = SOCK_STREAM; + stAddrHints.ai_protocol = IPPROTO_IP; + int iCode = getaddrinfo(strHost.c_str(), strPort.c_str(), &stAddrHints, &pAddrResult); + if (0 != iCode) + { + LOG4_ERROR("getaddrinfo(\"%s\", \"%s\") error %d: %s", + strHost.c_str(), strPort.c_str(), iCode, gai_strerror(iCode)); + return(false); + } + int iFd = -1; + for (pAddrCurrent = pAddrResult; + pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) + { + iFd = socket(pAddrCurrent->ai_family, + pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); + if (iFd == -1) + { + continue; + } + + break; + } + + /* No address succeeded */ + if (pAddrCurrent == NULL) + { + LOG4_ERROR("Could not connect to \"%s:%s\"", strHost.c_str(), strPort.c_str()); + freeaddrinfo(pAddrResult); /* No longer needed */ + return(false); + } + + x_sock_set_block(iFd, 0); + int nREUSEADDR = 1; + setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); + std::shared_ptr pChannel = CreateSocketChannel(iFd, CODEC_NEBULA); + if (nullptr != pChannel) + { + connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); + freeaddrinfo(pAddrResult); /* No longer needed */ + AddIoTimeout(pChannel, 1.5); + AddIoReadEvent(pChannel); + AddIoWriteEvent(pChannel); + pChannel->m_pImpl->SetIdentify(strIdentify); + pChannel->m_pImpl->SetRemoteAddr(strHost); + E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); + if (CODEC_STATUS_OK != eCodecStatus + && CODEC_STATUS_PAUSE != eCodecStatus + && CODEC_STATUS_WANT_WRITE != eCodecStatus + && CODEC_STATUS_WANT_READ != eCodecStatus) + { + DiscardSocketChannel(pChannel); + } + + pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); + pChannel->m_pImpl->m_unRemoteWorkerIdx = iWorkerIndex; + AddNamedSocketChannel(strIdentify, pChannel); + return(true); + } + else // 没有足够资源分配给新连接,直接close掉 + { + freeaddrinfo(pAddrResult); /* No longer needed */ + close(iFd); + return(false); + } +} + +bool Dispatcher::AutoSend(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) +{ + LOG4_TRACE("%s, %d, %s", strHost.c_str(), iPort, strUrlPath.c_str()); + struct addrinfo stAddrHints; + struct addrinfo* pAddrResult; + struct addrinfo* pAddrCurrent; + memset(&stAddrHints, 0, sizeof(struct addrinfo)); + stAddrHints.ai_family = AF_UNSPEC; + stAddrHints.ai_socktype = SOCK_STREAM; + stAddrHints.ai_protocol = IPPROTO_IP; + int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); + if (0 != iCode) + { + LOG4_ERROR("getaddrinfo(\"%s\", \"%s\") error %d: %s", + strHost.c_str(), iPort, iCode, gai_strerror(iCode)); + return(false); + } + int iFd = -1; + for (pAddrCurrent = pAddrResult; + pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) + { + iFd = socket(pAddrCurrent->ai_family, + pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); + if (iFd == -1) + { + continue; + } + + break; + } + + /* No address succeeded */ + if (pAddrCurrent == NULL) + { + LOG4_ERROR("Could not connect to \"%s:%d\"", strHost.c_str(), iPort); + freeaddrinfo(pAddrResult); /* No longer needed */ + return(false); + } + + x_sock_set_block(iFd, 0); + int nREUSEADDR = 1; + setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); + std::shared_ptr pChannel = nullptr; + std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(':')); + std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c) -> unsigned char { return std::tolower(c); }); + if (strSchema == std::string("https")) + { + pChannel = CreateSocketChannel(iFd, CODEC_HTTP, true, true); + } + else + { + pChannel = CreateSocketChannel(iFd, CODEC_HTTP, true, false); + } + if (nullptr != pChannel) + { + connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); + freeaddrinfo(pAddrResult); /* No longer needed */ + char szIdentify[32] = {0}; + AddIoTimeout(pChannel, 1.5); + AddIoReadEvent(pChannel); + AddIoWriteEvent(pChannel); + snprintf(szIdentify, sizeof(szIdentify), "%s:%d", strHost.c_str(), iPort); + pChannel->m_pImpl->SetIdentify(szIdentify); + pChannel->m_pImpl->SetRemoteAddr(strHost); + E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); + if (CODEC_STATUS_OK != eCodecStatus + && CODEC_STATUS_PAUSE != eCodecStatus + && CODEC_STATUS_WANT_WRITE != eCodecStatus + && CODEC_STATUS_WANT_READ != eCodecStatus) + { + DiscardSocketChannel(pChannel); + } + + pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT, uiHttpStepSeq); + // AddNamedSocketChannel(szIdentify, pChannel); the channel should not add to named connection pool, because there is an uncompleted http request. + return(true); + } + else // 没有足够资源分配给新连接,直接close掉 + { + freeaddrinfo(pAddrResult); /* No longer needed */ + close(iFd); + return(false); + } +} + +bool Dispatcher::AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep) +{ + LOG4_TRACE("redisAsyncConnect(%s, %d)", strHost.c_str(), iPort); + redisAsyncContext *c = redisAsyncConnect(strHost.c_str(), iPort); + if (c->err) + { + LOG4_ERROR("error: %s", c->errstr); + redisAsyncFree(c); + return(false); + } + c->data = m_pLabor; + std::shared_ptr pRedisChannel = nullptr; + try + { + pRedisChannel = std::make_shared(c); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("new RedisChannel error: %s", e.what()); + return(false); + } + pRedisChannel->listPipelineStep.push_back(pRedisStep); + pRedisStep->SetLabor(m_pLabor); + m_mapRedisChannel.insert(std::make_pair(c, pRedisChannel)); + redisLibevAttach(m_loop, c); + redisAsyncSetConnectCallback(c, RedisConnectCallback); + redisAsyncSetDisconnectCallback(c, RedisDisconnectCallback); + + std::ostringstream oss; + oss << strHost << ":" << iPort; + std::string strIdentify = std::move(oss.str()); + AddNamedRedisChannel(strIdentify, pRedisChannel); + return(true); +} + +bool Dispatcher::SendTo(int iFd) +{ + auto iter = m_mapSocketChannel.find(iFd); + if (iter != m_mapSocketChannel.end()) + { + return(SendTo(iter->second)); + } + return(false); +} + +bool Dispatcher::Disconnect(std::shared_ptr pChannel, bool bChannelNotice) +{ + return(DiscardSocketChannel(pChannel, bChannelNotice)); +} + +bool Dispatcher::Disconnect(const std::string& strIdentify, bool bChannelNotice) +{ + auto named_iter = m_mapNamedSocketChannel.find(strIdentify); + if (named_iter != m_mapNamedSocketChannel.end()) + { + std::list>::iterator channel_iter; + while (named_iter->second.size() > 1) + { + channel_iter = named_iter->second.begin(); + DiscardSocketChannel(*channel_iter, bChannelNotice); + } + channel_iter = named_iter->second.begin(); + return(DiscardSocketChannel(*channel_iter, bChannelNotice)); + } + return(false); +} + +bool Dispatcher::DiscardNamedChannel(const std::string& strIdentify) +{ + LOG4_TRACE("identify: %s", strIdentify.c_str()); + auto named_iter = m_mapNamedSocketChannel.find(strIdentify); + if (named_iter == m_mapNamedSocketChannel.end()) + { + LOG4_DEBUG("no channel match %s.", strIdentify.c_str()); + return(false); + } + else + { + for (auto channel_iter = named_iter->second.begin(); + channel_iter != named_iter->second.end(); ++channel_iter) + { + (*channel_iter)->m_pImpl->SetIdentify(""); + (*channel_iter)->m_pImpl->SetClientData(""); + } + named_iter->second.clear(); + m_mapNamedSocketChannel.erase(named_iter); + return(true); + } +} + +bool Dispatcher::SwitchCodec(std::shared_ptr pChannel, E_CODEC_TYPE eCodecType) +{ + return(pChannel->m_pImpl->SwitchCodec(eCodecType, m_pLabor->GetNodeInfo().dIoTimeout)); +} + +bool Dispatcher::AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel) +{ + LOG4_TRACE("%s", strIdentify.c_str()); + auto named_iter = m_mapNamedSocketChannel.find(strIdentify); + if (named_iter == m_mapNamedSocketChannel.end()) + { + std::list> listChannel; + listChannel.push_back(pChannel); + m_mapNamedSocketChannel.insert(std::make_pair(strIdentify, listChannel)); + } + else + { + named_iter->second.push_back(pChannel); + } + pChannel->m_pImpl->SetIdentify(strIdentify); + return(true); +} + +void Dispatcher::DelNamedSocketChannel(const std::string& strIdentify) +{ + auto named_iter = m_mapNamedSocketChannel.find(strIdentify); + if (named_iter == m_mapNamedSocketChannel.end()) + { + ; + } + else + { + m_mapNamedSocketChannel.erase(named_iter); + } +} + +void Dispatcher::SetChannelIdentify(std::shared_ptr pChannel, const std::string& strIdentify) +{ + pChannel->m_pImpl->SetIdentify(strIdentify); +} + +bool Dispatcher::AddNamedRedisChannel(const std::string& strIdentify, std::shared_ptr pChannel) +{ + auto named_iter = m_mapNamedRedisChannel.find(strIdentify); + if (named_iter == m_mapNamedRedisChannel.end()) + { + std::list > listChannel; + listChannel.push_back(pChannel); + m_mapNamedRedisChannel.insert(std::make_pair(strIdentify, listChannel)); + return(true); + } + else + { + named_iter->second.push_back(pChannel); + } + pChannel->SetIdentify(strIdentify); + return(true); +} + +void Dispatcher::DelNamedRedisChannel(const std::string& strIdentify) +{ + auto named_iter = m_mapNamedRedisChannel.find(strIdentify); + if (named_iter == m_mapNamedRedisChannel.end()) + { + ; + } + else + { + m_mapNamedRedisChannel.erase(named_iter); + } +} + +void Dispatcher::AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify) +{ + LOG4_TRACE("%s, %s", strNodeType.c_str(), strIdentify.c_str()); + m_pSessionNode->AddNode(strNodeType, strIdentify); + + if (std::string("BEACON") != m_pLabor->GetNodeInfo().strNodeType + && std::string("LOGGER") != m_pLabor->GetNodeInfo().strNodeType) + { + std::string strOnlineNode; + if (std::string("LOGGER") == strNodeType && m_pSessionNode->GetNode(strNodeType, strOnlineNode)) + { + m_pLogger->EnableNetLogger(true); + } + } +} + +void Dispatcher::DelNodeIdentify(const std::string& strNodeType, const std::string& strIdentify) +{ + LOG4_TRACE("%s, %s", strNodeType.c_str(), strIdentify.c_str()); + m_pSessionNode->DelNode(strNodeType, strIdentify); + + std::string strOnlineNode; + if (std::string("LOGGER") == strNodeType + && !m_pSessionNode->GetNode(strNodeType, strOnlineNode)) + { + m_pLogger->EnableNetLogger(false); + } +} + +void Dispatcher::SetClientData(std::shared_ptr pChannel, const std::string& strClientData) +{ + pChannel->m_pImpl->SetClientData(strClientData); +} + +bool Dispatcher::IsNodeType(const std::string& strNodeIdentify, const std::string& strNodeType) +{ + return(m_pSessionNode->IsNodeType(strNodeIdentify, strNodeType)); +} + +bool Dispatcher::AddEvent(ev_signal* signal_watcher, signal_callback pFunc, int iSignum) +{ + if (NULL == signal_watcher) + { + return(false); + } + ev_signal_init (signal_watcher, pFunc, iSignum); + ev_signal_start (m_loop, signal_watcher); + return(true); +} + +bool Dispatcher::AddEvent(ev_timer* timer_watcher, timer_callback pFunc, ev_tstamp dTimeout) +{ + if (NULL == timer_watcher) + { + return(false); + } + ev_timer_init (timer_watcher, pFunc, dTimeout + ev_time() - ev_now(m_loop), 0.); + ev_timer_start (m_loop, timer_watcher); + return(true); +} + +bool Dispatcher::RefreshEvent(ev_timer* timer_watcher, ev_tstamp dTimeout) +{ + if (NULL == timer_watcher) + { + return(false); + } + ev_timer_stop (m_loop, timer_watcher); + ev_timer_set (timer_watcher, dTimeout + ev_time() - ev_now(m_loop), 0); + ev_timer_start (m_loop, timer_watcher); + return(true); +} + +bool Dispatcher::DelEvent(ev_io* io_watcher) +{ + if (NULL == io_watcher) + { + return(false); + } + ev_io_stop (m_loop, io_watcher); + return(true); +} + +bool Dispatcher::DelEvent(ev_timer* timer_watcher) +{ + if (NULL == timer_watcher) + { + return(false); + } + ev_timer_stop (m_loop, timer_watcher); + return(true); +} + +int Dispatcher::SendFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType) +{ + return(SocketChannel::SendChannelFd(iSocketFd, iSendFd, iAiFamily, iCodecType, m_pLogger)); +} + +bool Dispatcher::CreateListenFd(const std::string& strHost, int32 iPort, int& iFd, int& iFamily) +{ + int queueLen = 100; + int reuse = 1; + int timeout = 1; + + struct addrinfo stAddrHints; + struct addrinfo* pAddrResult; + struct addrinfo* pAddrCurrent; + memset(&stAddrHints, 0, sizeof(struct addrinfo)); + stAddrHints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + stAddrHints.ai_socktype = SOCK_STREAM; + stAddrHints.ai_protocol = IPPROTO_IP; + stAddrHints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + int iCode = getaddrinfo(strHost.c_str(), + std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); + if (0 != iCode) + { + LOG4_ERROR("getaddrinfo(\"%s\", \"%d\") error %d: %s", + strHost.c_str(), + iPort, iCode, gai_strerror(iCode)); + exit(errno); + } + for (pAddrCurrent = pAddrResult; + pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) + { + iFd = socket(pAddrCurrent->ai_family, + pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); + if (-1 == iFd) + { + continue; + } + if (-1 == ::setsockopt(iFd, + SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))) + { + close(iFd); + iFd = -1; + continue; + } + if (-1 == ::setsockopt(iFd, + IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, sizeof(int))) + { + close(iFd); + iFd = -1; + continue; + } + if (-1 == bind(iFd, + pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen)) + { + close(iFd); + iFd = -1; + continue; + } + if (-1 == listen(iFd, queueLen)) + { + close(iFd); + iFd = -1; + continue; + } + + iFamily = pAddrCurrent->ai_family; + break; + } + freeaddrinfo(pAddrResult); /* No longer needed */ + + /* No address succeeded */ + if (-1 == iFd) + { + LOG4_ERROR("Could not bind to \"%s:%d\", error %d: %s", + strHost.c_str(), + iPort, + errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); + return(false); + } + return(true); +} + +std::shared_ptr Dispatcher::GetChannel(int iFd) +{ + auto iter = m_mapSocketChannel.find(iFd); + if (iter != m_mapSocketChannel.end()) + { + return(iter->second); + } + return(nullptr); +} + +int32 Dispatcher::GetConnectionNum() const +{ + return((int32)m_mapSocketChannel.size()); +} +int32 Dispatcher::GetClientNum() const +{ + return(m_iClientNum); +} + +bool Dispatcher::Init() +{ +#if __cplusplus >= 201401L + m_pSessionNode = std::make_shared(); +#else + m_pSessionNode = std::unique_ptr(new Nodes()); +#endif + return(true); +} + +void Dispatcher::Destroy() +{ + m_mapSocketChannel.clear(); + m_mapRedisChannel.clear(); + m_mapNamedSocketChannel.clear(); + m_mapNamedRedisChannel.clear(); + if (m_loop != NULL) + { + ev_loop_destroy(m_loop); + m_loop = NULL; + } + if (m_pErrBuff != NULL) + { + free(m_pErrBuff); + m_pErrBuff = NULL; + } + m_pLabor = nullptr; +} + +std::shared_ptr Dispatcher::CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient, bool bWithSsl) +{ + LOG4_DEBUG("iFd %d, codec_type %d, with_ssl = %d", iFd, eCodecType, bWithSsl); + + auto iter = m_mapSocketChannel.find(iFd); + if (iter == m_mapSocketChannel.end()) + { + std::shared_ptr pChannel = nullptr; + try + { + pChannel = std::make_shared(m_pLogger, iFd, m_pLabor->GetSequence(), bWithSsl); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("new channel for fd %d error: %s", e.what()); + return(nullptr); + } + pChannel->m_pImpl->SetLabor(m_pLabor); + bool bInitResult = pChannel->m_pImpl->Init(eCodecType, bIsClient); + if (bInitResult) + { + m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); + if (CODEC_NEBULA != eCodecType) + { + ++m_iClientNum; + } + return(pChannel); + } + else + { + return(nullptr); + } + } + else + { + LOG4_WARNING("fd %d is exist!", iFd); + return(iter->second); + } +} + +bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice) +{ + LOG4_DEBUG("%s disconnect, fd %d, identify %s", pChannel->m_pImpl->GetRemoteAddr().c_str(), pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetIdentify().c_str()); + if (bChannelNotice) + { + m_pLabor->GetActorBuilder()->ChannelNotice(pChannel, pChannel->m_pImpl->GetIdentify(), pChannel->m_pImpl->GetClientData()); + } + bool bCloseResult = pChannel->m_pImpl->Close(); + if (bCloseResult) + { + ev_io_stop (m_loop, pChannel->m_pImpl->MutableIoWatcher()); + if (nullptr != pChannel->m_pImpl->MutableTimerWatcher()) + { + ev_timer_stop (m_loop, pChannel->m_pImpl->MutableTimerWatcher()); + } + + auto named_iter = m_mapNamedSocketChannel.find(pChannel->m_pImpl->GetIdentify()); + if (named_iter != m_mapNamedSocketChannel.end()) + { + for (auto it = named_iter->second.begin(); + it != named_iter->second.end(); ++it) + { + if ((*it)->m_pImpl->GetSequence() == pChannel->m_pImpl->GetSequence()) + { + named_iter->second.erase(it); + LOG4_TRACE("erase channel %d from m_mapNamedSocketChannel.", pChannel->m_pImpl->GetFd()); + break; + } + } + if (0 == named_iter->second.size()) + { + m_mapNamedSocketChannel.erase(named_iter); + } + } + + auto channel_iter = m_mapSocketChannel.find(pChannel->m_pImpl->GetFd()); + if (channel_iter != m_mapSocketChannel.end()) + { + m_mapSocketChannel.erase(channel_iter); + if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType()) + { + --m_iClientNum; + } + LOG4_TRACE("erase channel %d from m_mapSocketChannel.", pChannel->m_pImpl->GetFd()); + } + return(true); + } + else + { + return(bCloseResult); + } +} + +bool Dispatcher::AddIoReadEvent(std::shared_ptr pChannel) +{ + LOG4_TRACE("fd[%d], seq[%u]", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); + ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); + if (NULL == io_watcher) + { + return(false); + } + else + { + if (ev_is_active(io_watcher)) + { + ev_io_stop(m_loop, io_watcher); + ev_io_set(io_watcher, io_watcher->fd, io_watcher->events | EV_READ); + ev_io_start (m_loop, io_watcher); + } + else + { + ev_io_init (io_watcher, IoCallback, pChannel->m_pImpl->GetFd(), EV_READ); + ev_io_start (m_loop, io_watcher); + } + return(true); + } +} + +bool Dispatcher::AddIoWriteEvent(std::shared_ptr pChannel) +{ + LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); + ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); + if (NULL == io_watcher) + { + return(false); + } + else + { + if (ev_is_active(io_watcher)) + { + ev_io_stop(m_loop, io_watcher); + ev_io_set(io_watcher, io_watcher->fd, io_watcher->events | EV_WRITE); + ev_io_start (m_loop, io_watcher); + } + else + { + ev_io_init (io_watcher, IoCallback, pChannel->m_pImpl->GetFd(), EV_WRITE); + ev_io_start (m_loop, io_watcher); + } + return(true); + } +} + +} diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp new file mode 100644 index 00000000..389c68ff --- /dev/null +++ b/src/ios/Dispatcher.hpp @@ -0,0 +1,219 @@ +/******************************************************************************* + * Project: Nebula + * @file Dispatcher.hpp + * @brief 事件管理、事件分发 + * @author Bwar + * @date: 2019年9月7日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_IOS_DISPATCHER_HPP_ +#define SRC_IOS_DISPATCHER_HPP_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif +#include "ev.h" +#include "hiredis/async.h" +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#ifdef __cplusplus +} +#endif + +#include +#include +#include + +#include "pb/msg.pb.h" +#include "channel/SocketChannel.hpp" +#include "channel/RedisChannel.hpp" + +namespace neb +{ + +class Labor; +class Manager; +class Worker; +class Actor; +class ActorBuilder; +class Nodes; +struct tagClientConnWatcherData; + +typedef void (*signal_callback)(struct ev_loop*,ev_signal*,int); +typedef void (*timer_callback)(struct ev_loop*,ev_timer*,int); + +class Dispatcher +{ +public: + struct tagClientConnWatcherData + { + char* pAddr; + Dispatcher* pDispatcher; // 不在结构体析构时回收 + + tagClientConnWatcherData() : pAddr(NULL), pDispatcher(nullptr) + { + pAddr = (char*)malloc(gc_iAddrLen); + } + + ~tagClientConnWatcherData() + { + free(pAddr); + pAddr = nullptr; + } + }; + + Dispatcher(Labor* pLabor, std::shared_ptr pLogger); + virtual ~Dispatcher(); + +public: + static void IoCallback(struct ev_loop* loop, struct ev_io* watcher, int revents); + static void IoTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); + static void RedisConnectCallback(const redisAsyncContext *c, int status); + static void RedisDisconnectCallback(const redisAsyncContext *c, int status); + static void RedisCmdCallback(redisAsyncContext *c, void *reply, void *privdata); + static void PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, int revents); + static void SignalCallback(struct ev_loop* loop, struct ev_signal* watcher, int revents); + static void ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); + + bool OnIoRead(std::shared_ptr pChannel); + bool DataRecvAndHandle(std::shared_ptr pChannel); + bool FdTransfer(int iFd); + bool OnIoWrite(std::shared_ptr pChannel); + bool OnIoError(std::shared_ptr pChannel); + bool OnIoTimeout(std::shared_ptr pChannel); + bool OnRedisConnected(const redisAsyncContext *c, int status); + bool OnRedisDisconnected(const redisAsyncContext *c, int status); + bool OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privdata); + bool OnClientConnFrequencyTimeout(tagClientConnWatcherData* pData, ev_timer* watcher); + + template + void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); + + void EeventRun(); + +public: + bool AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout = 1.0); + + // SendTo() for nebula socket + bool SendTo(std::shared_ptr pChannel); + bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); + bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); + bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); + bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); + bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); + bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); + bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + + // SendTo() for http + bool SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); + bool SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); + bool AutoSend(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); + + // SendTo() for redis + bool SendTo(std::shared_ptr pRedisChannel, Actor* pSender); + bool SendTo(const std::string& strIdentify, Actor* pSender); + bool SendTo(const std::string& strHost, int iPort, Actor* pSender); + bool AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep); + + // SendTo() for unix domain socket + bool SendTo(int iFd); + + bool Disconnect(std::shared_ptr pChannel, bool bChannelNotice = true); + bool Disconnect(const std::string& strIdentify, bool bChannelNotice = true); + bool DiscardNamedChannel(const std::string& strIdentify); + bool SwitchCodec(std::shared_ptr pChannel, E_CODEC_TYPE eCodecType); + +public: + void SetChannelIdentify(std::shared_ptr pChannel, const std::string& strIdentify); + bool AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel); + void DelNamedSocketChannel(const std::string& strIdentify); + bool AddNamedRedisChannel(const std::string& strIdentify, std::shared_ptr pChannel); + void DelNamedRedisChannel(const std::string& strIdentify); + void AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); + void DelNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); + void SetClientData(std::shared_ptr pChannel, const std::string& strClientData); + bool IsNodeType(const std::string& strNodeIdentify, const std::string& strNodeType); + + time_t GetNowTime() const + { + return((time_t)ev_now(m_loop)); + } + std::shared_ptr CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient = false, bool bWithSsl = false); + bool DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice = false); + bool CreateListenFd(const std::string& strHost, int32 iPort, int& iFd, int& iFamily); + std::shared_ptr GetChannel(int iFd); + int SendFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType); + +protected: + bool Init(); + void Destroy(); + bool AddIoReadEvent(std::shared_ptr pChannel); + bool AddIoWriteEvent(std::shared_ptr pChannel); + bool RemoveIoWriteEvent(std::shared_ptr pChannel); + bool AddEvent(ev_signal* signal_watcher, signal_callback pFunc, int iSignum); + bool AddEvent(ev_timer* timer_watcher, timer_callback pFunc, ev_tstamp dTimeout); + bool RefreshEvent(ev_timer* timer_watcher, ev_tstamp dTimeout); + bool DelEvent(ev_io* io_watcher); + bool DelEvent(ev_timer* timer_watcher); + int32 GetConnectionNum() const; + int32 GetClientNum() const; + void SetChannelStatus(std::shared_ptr pChannel, E_CHANNEL_STATUS eStatus); + void SetChannelStatus(std::shared_ptr pChannel, E_CHANNEL_STATUS eStatus, uint32 ulStepSeq); + bool AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout = 60.0); + bool AcceptFdAndTransfer(int iFd, int iFamily = AF_INET); + bool AcceptServerConn(int iFd); + void EvBreak(); + +private: + char* m_pErrBuff; + Labor* m_pLabor; + struct ev_loop* m_loop; + int32 m_iClientNum; + std::shared_ptr m_pLogger; + std::unique_ptr m_pSessionNode; + + // Channel + std::unordered_map > m_mapSocketChannel; + std::unordered_map > m_mapRedisChannel; + + /* named Channel */ + std::unordered_map > > m_mapNamedSocketChannel; ///< key为Identify,连接存在时,if(http连接)list.size()>=1;else list.size()==1; + std::unordered_map > > m_mapNamedRedisChannel; ///< key为identify,list.size()==1 + + std::unordered_map m_mapClientConnFrequency; ///< 客户端连接频率 + + friend class Manager; + friend class Worker; + friend class ActorBuilder; +}; + +template +void Dispatcher::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) +{ + m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); +} + +} /* namespace neb */ + +#endif /* SRC_IOS_DISPATCHER_HPP_ */ diff --git a/src/actor/session/sys_session/SessionNode.cpp b/src/ios/Nodes.cpp similarity index 88% rename from src/actor/session/sys_session/SessionNode.cpp rename to src/ios/Nodes.cpp index 5daa2f7b..9c3c6045 100644 --- a/src/actor/session/sys_session/SessionNode.cpp +++ b/src/ios/Nodes.cpp @@ -1,7 +1,7 @@ /******************************************************************************* * Project: Nebula - * @file SessionNode.cpp + * @file Nodes.cpp * @brief * @author bwar * @date: 2016-3-19 @@ -11,22 +11,22 @@ #define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 #include "cryptopp/md5.h" //#include "cryptopp/hex.h" -#include "actor/session/sys_session/SessionNode.hpp" +#include "Nodes.hpp" namespace neb { -SessionNode::SessionNode(int iHashAlgorithm, int iVirtualNodeNum, ev_tstamp dSessionTimeout) +Nodes::Nodes(int iHashAlgorithm, int iVirtualNodeNum, ev_tstamp dSessionTimeout) : m_iHashAlgorithm(iHashAlgorithm), m_iVirtualNodeNum(iVirtualNodeNum) { } -SessionNode::~SessionNode() +Nodes::~Nodes() { m_mapNode.clear(); } -bool SessionNode::GetNode(const std::string& strNodeType, const std::string& strHashKey, std::string& strNodeIdentify) +bool Nodes::GetNode(const std::string& strNodeType, const std::string& strHashKey, std::string& strNodeIdentify) { uint32 uiKeyHash = 0; if (HASH_fnv1_64 == m_iHashAlgorithm) @@ -62,7 +62,7 @@ bool SessionNode::GetNode(const std::string& strNodeType, const std::string& str } } -bool SessionNode::GetNode(const std::string& strNodeType, uint32 uiHash, std::string& strNodeIdentify) +bool Nodes::GetNode(const std::string& strNodeType, uint32 uiHash, std::string& strNodeIdentify) { auto node_type_iter = m_mapNode.find(strNodeType); if (node_type_iter == m_mapNode.end()) @@ -84,7 +84,7 @@ bool SessionNode::GetNode(const std::string& strNodeType, uint32 uiHash, std::st } } -bool SessionNode::GetNode(const std::string& strNodeType, std::string& strNodeIdentify) +bool Nodes::GetNode(const std::string& strNodeType, std::string& strNodeIdentify) { auto node_type_iter = m_mapNode.find(strNodeType); if (node_type_iter == m_mapNode.end()) @@ -103,7 +103,7 @@ bool SessionNode::GetNode(const std::string& strNodeType, std::string& strNodeId } } -bool SessionNode::GetNode(const std::string& strNodeType, std::unordered_set& setNodeIdentify) +bool Nodes::GetNode(const std::string& strNodeType, std::unordered_set& setNodeIdentify) { auto node_type_iter = m_mapNode.find(strNodeType); if (node_type_iter == m_mapNode.end()) @@ -120,7 +120,7 @@ bool SessionNode::GetNode(const std::string& strNodeType, std::unordered_set #include @@ -40,7 +40,7 @@ enum E_HASH_ALGORITHM * 此Session也只是成为了一个定时器,不会真正超时。 * SessionNode不从Session派生,因为不会被应用层actor用到。 */ -class SessionNode +class Nodes { public: /** @@ -48,8 +48,8 @@ class SessionNode * @param iVirtualNodeNum 每个实体节点对应的虚拟节点数量 * @param dSessionTimeout 超时时间,0表示永不超时 */ - SessionNode(int iHashAlgorithm = HASH_fnv1a_64, int iVirtualNodeNum = 200, ev_tstamp dSessionTimeout = 0.0); - virtual ~SessionNode(); + Nodes(int iHashAlgorithm = HASH_fnv1a_64, int iVirtualNodeNum = 200, ev_tstamp dSessionTimeout = 0.0); + virtual ~Nodes(); virtual E_CMD_STATUS Timeout() { @@ -120,4 +120,4 @@ class SessionNode } /* namespace neb */ -#endif /* SRC_ACTOR_SESSION_SYS_SESSION_SESSIONREDISNODE_HPP_ */ +#endif /* SRC_IOS_NODES_HPP_ */ diff --git a/src/labor/Labor.hpp b/src/labor/Labor.hpp index 6df06b18..9fdfc3d9 100644 --- a/src/labor/Labor.hpp +++ b/src/labor/Labor.hpp @@ -1,6 +1,6 @@ /******************************************************************************* * Project: Nebula - * @file OssLabor.hpp + * @file Labor.hpp * @brief * @author Bwar * @date: 2016年8月8日 @@ -12,28 +12,58 @@ #include #include +#include "ev.h" #include "pb/msg.pb.h" #include "Definition.hpp" namespace neb { +class NodeInfo; class Actor; +class SocketChannel; +class Dispatcher; +class ActorBuilder; +class CJsonObject; class Labor { public: - Labor(){}; + enum LABOR_TYPE + { + LABOR_UNDEFINE = 0, + LABOR_MANAGER = 1, + LABOR_WORKER = 2, + LABOR_HOLDER = 3, + }; +public: + Labor(LABOR_TYPE eLaborType) + : m_eLaborType(eLaborType) + { + } Labor(const Labor&) = delete; Labor& operator=(const Labor&) = delete; virtual ~Labor(){}; + LABOR_TYPE GetLaborType() const + { + return(m_eLaborType); + } + public: - virtual uint32 GetNodeId() const = 0; + virtual Dispatcher* GetDispatcher() = 0; + virtual ActorBuilder* GetActorBuilder() = 0; + virtual uint32 GetSequence() const = 0; virtual time_t GetNowTime() const = 0; - virtual const std::string& GetNodeIdentify() const = 0; - virtual const std::string& GetNodeType() const = 0; + virtual const CJsonObject& GetNodeConf() const = 0; + virtual void SetNodeConf(const CJsonObject& oNodeConf) = 0; + virtual const NodeInfo& GetNodeInfo() const = 0; + virtual void SetNodeId(uint32 uiNodeId) = 0; virtual bool AddNetLogMsg(const MsgBody& oMsgBody) = 0; + virtual void OnTerminated(struct ev_signal* watcher) = 0; + +private: + LABOR_TYPE m_eLaborType; }; } /* namespace neb */ diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 40c031a8..af332c5e 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -21,98 +21,18 @@ extern "C" { #include "Manager.hpp" #include "Worker.hpp" +#include "channel/SocketChannel.hpp" +#include "ios/Dispatcher.hpp" +#include "actor/ActorBuilder.hpp" +#include "actor/step/Step.hpp" +#include "actor/session/sys_session/manager/SessionManager.hpp" namespace neb { -void Manager::SignalCallback(struct ev_loop* loop, struct ev_signal* watcher, int revents) -{ - if (watcher->data != NULL) - { - Manager* pManager = (Manager*)watcher->data; - if (SIGCHLD == watcher->signum) - { - pManager->OnChildTerminated(watcher); - } - else - { - pManager->OnManagerTerminated(watcher); - } - } -} - -void Manager::IdleCallback(struct ev_loop* loop, struct ev_idle* watcher, int revents) -{ - if (watcher->data != NULL) - { - Manager* pManager = (Manager*)watcher->data; - pManager->CheckWorker(); - pManager->ReportToBeacon(); - } -} - -void Manager::IoCallback(struct ev_loop* loop, struct ev_io* watcher, int revents) -{ - if (watcher->data != NULL) - { - SocketChannel* pChannel = (SocketChannel*)watcher->data; - Manager* pManager = (Manager*)pChannel->m_pImpl->m_pLabor; - std::shared_ptr pSharedChannel = pChannel->shared_from_this(); - if (revents & EV_READ) - { - pManager->OnIoRead(pSharedChannel); - } - else if (revents & EV_WRITE) - { - pManager->OnIoWrite(pSharedChannel); - } - else if (revents & EV_ERROR) - { - pManager->OnIoError(pSharedChannel); - } - if (CHANNEL_STATUS_CLOSED == pChannel->m_pImpl->GetChannelStatus()) - { - watcher->data = NULL; - } - } -} - -void Manager::IoTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) -{ - if (watcher->data != NULL) - { - SocketChannel* pChannel = (SocketChannel*)watcher->data; - Manager* pManager = (Manager*)pChannel->m_pImpl->m_pLabor; - pManager->OnIoTimeout(pChannel->shared_from_this()); - } -} - -void Manager::PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, int revents) -{ - if (watcher->data != NULL) - { - Manager* pManager = (Manager*)(watcher->data); - pManager->ReportToBeacon(); - pManager->CheckWorker(); - pManager->RefreshServer(); - } - ev_timer_stop (loop, watcher); - ev_timer_set (watcher, NODE_BEAT + ev_time() - ev_now(loop), 0); - ev_timer_start (loop, watcher); -} - -void Manager::ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) -{ - if (watcher->data != NULL) - { - tagClientConnWatcherData* pData = (tagClientConnWatcherData*)watcher->data; - Manager* pManager = (Manager*)pData->pLabor; - pManager->OnClientConnFrequencyTimeout(pData, watcher); - } -} - Manager::Manager(const std::string& strConfFile) - : m_uiSequence(0), m_loop(NULL), m_pPeriodicTaskWatcher(NULL) + : Labor(LABOR_MANAGER), + m_uiSequence(0), m_pDispatcher(nullptr), m_pActorBuilder(nullptr) { if (strConfFile == "") { @@ -120,19 +40,22 @@ Manager::Manager(const std::string& strConfFile) exit(1); } - m_stManagerInfo.strConfFile = strConfFile; + m_stNodeInfo.strConfFile = strConfFile; + m_pErrBuff = (char*)malloc(gc_iErrBuffLen); if (!GetConf()) { std::cerr << "GetConf() error!" << std::endl; - exit(-1); + exit(2); } ngx_setproctitle(m_oCurrentConf("server_name").c_str()); daemonize(m_oCurrentConf("server_name").c_str()); - Init(); + if (!Init()) + { + exit(3); + } m_stManagerInfo.iWorkerBeat = (gc_iBeatInterval * 2) + 1; CreateEvents(); CreateWorker(); - RegisterToBeacon(); } Manager::~Manager() @@ -140,14 +63,18 @@ Manager::~Manager() Destroy(); } -bool Manager::OnManagerTerminated(struct ev_signal* watcher) +void Manager::OnTerminated(struct ev_signal* watcher) { LOG4_WARNING("%s terminated by signal %d!", m_oCurrentConf("server_name").c_str(), watcher->signum); - ev_break (m_loop, EVBREAK_ALL); - exit(-1); + int iSignum = watcher->signum; + delete watcher; + LOG4_FATAL("terminated by signal %d!", iSignum); + m_pDispatcher->EvBreak(); + Destroy(); + exit(iSignum); } -bool Manager::OnChildTerminated(struct ev_signal* watcher) +void Manager::OnChildTerminated(struct ev_signal* watcher) { pid_t iPid = 0; int iStatus = 0; @@ -172,280 +99,12 @@ bool Manager::OnChildTerminated(struct ev_signal* watcher) iStatus, iPid, watcher->signum, iReturnCode); RestartWorker(iPid); } - return(true); -} - -bool Manager::OnIoRead(std::shared_ptr pChannel) -{ - LOG4_TRACE("fd[%d]", pChannel->m_pImpl->GetFd()); - if (pChannel->m_pImpl->GetFd() == m_stManagerInfo.iS2SListenFd) - { - return(AcceptServerConn(pChannel->m_pImpl->GetFd())); - } - else if (m_stManagerInfo.iC2SListenFd > 2 && pChannel->m_pImpl->GetFd() == m_stManagerInfo.iC2SListenFd) - { - return(FdTransfer(m_stManagerInfo.iC2SListenFd, m_stManagerInfo.iC2SFamily)); - } - else - { - return(DataRecvAndHandle(pChannel)); - } -} - -bool Manager::FdTransfer(int iFd, int iFamily) -{ - char szClientAddr[64] = {0}; - int iAcceptFd = -1; - if (AF_INET == iFamily) - { - struct sockaddr_in stClientAddr; - socklen_t clientAddrSize = sizeof(stClientAddr); - iAcceptFd = accept(iFd, (struct sockaddr*) &stClientAddr, &clientAddrSize); - if (iAcceptFd < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_szErrBuff, 1024)); - return(false); - } - inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientAddr, sizeof(szClientAddr)); - LOG4_TRACE("accept connect from \"%s\"", szClientAddr); - } - else // AF_INET6 - { - struct sockaddr_in6 stClientAddr; - socklen_t clientAddrSize = sizeof(stClientAddr); - iAcceptFd = accept(iFd, (struct sockaddr*) &stClientAddr, &clientAddrSize); - if (iAcceptFd < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_szErrBuff, 1024)); - return(false); - } - inet_ntop(AF_INET6, &stClientAddr.sin6_addr, szClientAddr, sizeof(szClientAddr)); - LOG4_TRACE("accept connect from \"%s\"", szClientAddr); - } - - auto iter = m_mapClientConnFrequency.find(std::string(szClientAddr)); - if (iter == m_mapClientConnFrequency.end()) - { - m_mapClientConnFrequency.insert(std::make_pair(std::string(szClientAddr), 1)); - AddClientConnFrequencyTimeout(szClientAddr, m_stManagerInfo.dAddrStatInterval); - } - else - { - iter->second++; - if (m_stManagerInfo.iC2SListenFd > 2 && iter->second > (uint32)m_stManagerInfo.iAddrPermitNum) - { - LOG4_WARNING("client addr %s had been connected more than %u times in %f seconds, it's not permitted", - szClientAddr, m_stManagerInfo.iAddrPermitNum, m_stManagerInfo.dAddrStatInterval); - close(iAcceptFd); - return(false); - } - } - - std::pair worker_pid_fd = GetMinLoadWorkerDataFd(); - if (worker_pid_fd.second > 0) - { - LOG4_DEBUG("send new fd %d to worker communication fd %d", - iAcceptFd, worker_pid_fd.second); - int iCodec = m_stManagerInfo.eCodec; - int iErrno = SocketChannel::SendChannelFd(worker_pid_fd.second, iAcceptFd, iFamily, iCodec, m_pLogger); - if (iErrno == 0) - { - AddWorkerLoad(worker_pid_fd.first); - } - else - { - LOG4_ERROR("error %d: %s", iErrno, strerror_r(iErrno, m_szErrBuff, 1024)); - } - close(iAcceptFd); - return(true); - } - LOG4_WARNING("GetMinLoadWorkerDataFd() found worker_pid_fd.second = %d", worker_pid_fd.second); - close(iAcceptFd); - return(false); -} - -bool Manager::AcceptServerConn(int iFd) -{ - struct sockaddr_in stClientAddr; - socklen_t clientAddrSize = sizeof(stClientAddr); - int iAcceptFd = accept(iFd, (struct sockaddr*) &stClientAddr, &clientAddrSize); - if (iAcceptFd < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_szErrBuff, 1024)); - return(false); - } - else - { - /* tcp连接检测 */ - int iKeepAlive = 1; - int iKeepIdle = 60; - int iKeepInterval = 5; - int iKeepCount = 3; - int iTcpNoDelay = 1; - if (setsockopt(iAcceptFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)) < 0) - { - LOG4_WARNING("fail to set SO_KEEPALIVE"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)) < 0) - { - LOG4_WARNING("fail to set SO_KEEPIDLE"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)) < 0) - { - LOG4_WARNING("fail to set SO_KEEPINTVL"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)) < 0) - { - LOG4_WARNING("fail to set SO_KEEPALIVE"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)) < 0) - { - LOG4_WARNING("fail to set TCP_NODELAY"); - } - x_sock_set_block(iAcceptFd, 0); - std::shared_ptr pChannel = CreateChannel(iAcceptFd, CODEC_NEBULA); - if (NULL != pChannel) - { - AddIoTimeout(pChannel, 1.0); // 为了防止大量连接攻击,初始化连接只有一秒即超时,在正常发送第一个数据包之后才采用正常配置的网络IO超时检查 - AddIoReadEvent(pChannel); - } - } - return(false); -} - -bool Manager::DataRecvAndHandle(std::shared_ptr pChannel) -{ - LOG4_DEBUG("fd %d, seq %llu", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - E_CODEC_STATUS eCodecStatus; - if (CODEC_HTTP == pChannel->m_pImpl->GetCodecType()) // Manager只有pb协议编解码 - { -// HttpMsg oHttpMsg; -// eCodecStatus = pChannel->m_pImpl->Recv(oHttpMsg); - DiscardSocketChannel(pChannel); - return(false); - } - else - { - MsgHead oMsgHead; - MsgBody oMsgBody; - eCodecStatus = pChannel->m_pImpl->Recv(oMsgHead, oMsgBody); - while (CODEC_STATUS_OK == eCodecStatus) - { - auto worker_fd_iter = m_mapWorkerFdPid.find(pChannel->m_pImpl->GetFd()); - if (worker_fd_iter == m_mapWorkerFdPid.end()) // 其他Server发过来要将连接传送到某个指定Worker进程信息 - { - if (m_pSessionNode->IsNodeType(pChannel->m_pImpl->GetIdentify(), "BEACON")) // 与beacon连接 - { - OnBeaconData(pChannel, oMsgHead, oMsgBody); - } - else // 不是与beacon连接 - { - OnDataAndTransferFd(pChannel, oMsgHead, oMsgBody); - } - } - else // Worker进程发过来的消息 - { - OnWorkerData(pChannel, oMsgHead, oMsgBody); - } - oMsgHead.Clear(); // 注意protobuf的Clear()使用不当容易造成内存泄露 - oMsgBody.Clear(); - eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); - } - - if (CODEC_STATUS_PAUSE == eCodecStatus) - { - return(true); - } - else // 编解码出错或连接关闭或连接中断 - { - DiscardSocketChannel(pChannel); - return(false); - } - } -} - -bool Manager::OnIoWrite(std::shared_ptr pChannel) -{ - LOG4_TRACE("%d", pChannel->m_pImpl->GetFd()); - if (CHANNEL_STATUS_TRY_CONNECT == pChannel->m_pImpl->GetChannelStatus()) // connect之后的第一个写事件 - { - MsgBody oMsgBody; - oMsgBody.set_data(std::to_string(pChannel->m_pImpl->m_unRemoteWorkerIdx)); - LOG4_DEBUG("send after connect, oMsgBody.ByteSize() = %d", oMsgBody.ByteSize()); - SendTo(pChannel, CMD_REQ_CONNECT_TO_WORKER, GetSequence(), oMsgBody); - return(true); - } - else - { - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(); - if (CODEC_STATUS_OK == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - } - else if (CODEC_STATUS_PAUSE == eCodecStatus) - { - AddIoWriteEvent(pChannel); - } - else - { - DiscardSocketChannel(pChannel); - } - } - return(true); -} - -bool Manager::OnIoError(std::shared_ptr pChannel) -{ - LOG4_TRACE(" "); - return(false); -} - -bool Manager::OnIoTimeout(std::shared_ptr pChannel) -{ - ev_tstamp after = pChannel->m_pImpl->GetActiveTime() - ev_now(m_loop) + m_stManagerInfo.dIoTimeout; - if (after > 0) // IO在定时时间内被重新刷新过,重新设置定时器 - { - ev_timer* watcher = pChannel->m_pImpl->MutableTimerWatcher(); - ev_timer_stop (m_loop, watcher); - ev_timer_set (watcher, after + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, watcher); - return(true); - } - else // IO已超时,关闭文件描述符并清理相关资源 - { - LOG4_TRACE(" "); - DiscardSocketChannel(pChannel); - return(false); - } -} - -bool Manager::OnClientConnFrequencyTimeout(tagClientConnWatcherData* pData, ev_timer* watcher) -{ - bool bRes = false; - auto iter = m_mapClientConnFrequency.find(std::string(pData->pAddr)); - if (iter == m_mapClientConnFrequency.end()) - { - bRes = false; - } - else - { - m_mapClientConnFrequency.erase(iter); - bRes = true; - } - - ev_timer_stop(m_loop, watcher); - pData->pLabor = NULL; - delete pData; - watcher->data = NULL; - delete watcher; - watcher = NULL; - return(bRes); } void Manager::Run() { LOG4_TRACE(" "); - ev_run (m_loop, 0); + m_pDispatcher->EeventRun(); } bool Manager::InitLogger(const CJsonObject& oJsonConf) @@ -465,523 +124,166 @@ bool Manager::InitLogger(const CJsonObject& oJsonConf) int32 iMaxLogFileSize = 0; int32 iMaxLogFileNum = 0; std::string strLoggingHost; - std::string strLogname = m_stManagerInfo.strWorkPath + std::string("/") + std::string strLogname = m_stNodeInfo.strWorkPath + std::string("/") + oJsonConf("log_path") + std::string("/") + getproctitle() + std::string(".log"); std::string strParttern = "[%D,%d{%q}][%p] [%l] %m%n"; std::ostringstream ssServerName; - ssServerName << getproctitle() << " " << m_stManagerInfo.strHostForServer << ":" << m_stManagerInfo.iPortForServer; + ssServerName << getproctitle() << " " << m_stNodeInfo.strHostForServer << ":" << m_stNodeInfo.iPortForServer; oJsonConf.Get("max_log_file_size", iMaxLogFileSize); oJsonConf.Get("max_log_file_num", iMaxLogFileNum); oJsonConf.Get("log_level", iLogLevel); m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, this); m_pLogger->SetNetLogLevel(iNetLogLevel); - LOG4_NOTICE("%s program begin, and work path %s...", oJsonConf("server_name").c_str(), m_stManagerInfo.strWorkPath.c_str()); + LOG4_NOTICE("%s program begin, and work path %s...", oJsonConf("server_name").c_str(), m_stNodeInfo.strWorkPath.c_str()); return(true); } } -bool Manager::SetProcessName(const CJsonObject& oJsonConf) -{ - ngx_setproctitle(oJsonConf("server_name").c_str()); - return(true); -} - -bool Manager::AddNetLogMsg(const MsgBody& oMsgBody) -{ - if (std::string("BEACON") != m_stManagerInfo.strNodeType - && std::string("LOGGER") != m_stManagerInfo.strNodeType) - { - m_pSessionLogger->AddMsg(oMsgBody); - } - return(true); -} - -bool Manager::SendTo(std::shared_ptr pChannel) +bool Manager::InitDispatcher() { - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(); - if (CODEC_STATUS_OK == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - } - else if (CODEC_STATUS_PAUSE == eCodecStatus) + try { - AddIoWriteEvent(pChannel); + m_pDispatcher = new Dispatcher(this, m_pLogger); } - else + catch(std::bad_alloc& e) { - DiscardSocketChannel(pChannel); + LOG4_ERROR("new Dispatcher error: %s", e.what()); return(false); } - return(true); + return(m_pDispatcher->Init()); } -bool Manager::SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Manager::InitActorBuilder() { - LOG4_TRACE("cmd[%u], seq[%u]", iCmd, uiSeq); - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - if (CODEC_STATUS_OK == eCodecStatus) + try { - RemoveIoWriteEvent(pChannel); + m_pActorBuilder = new ActorBuilder(this, m_pLogger); } - else if (CODEC_STATUS_PAUSE == eCodecStatus) + catch(std::bad_alloc& e) { - AddIoWriteEvent(pChannel); + LOG4_ERROR("new ActorBuilder error: %s", e.what()); + return(false); } - else + if (!m_pActorBuilder->Init(m_oCurrentConf["boot_load"], m_oCurrentConf["dynamic_loading"])) { - DiscardSocketChannel(pChannel); + LOG4_ERROR("ActorBuilder->Init() failed!"); return(false); } return(true); } -bool Manager::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Manager::AddNetLogMsg(const MsgBody& oMsgBody) { - LOG4_TRACE("identify: %s", strIdentify.c_str()); - auto named_iter = m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == m_mapNamedSocketChannel.end()) + if (std::string("BEACON") != m_stNodeInfo.strNodeType + && std::string("LOGGER") != m_stNodeInfo.strNodeType) { - LOG4_TRACE("no channel match %s.", strIdentify.c_str()); - return(AutoSend(strIdentify, iCmd, uiSeq, oMsgBody)); - } - else - { - E_CODEC_STATUS eStatus = named_iter->second->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - if (CODEC_STATUS_OK == eStatus) - { - // do not need to RemoveIoWriteEvent(pSocketChannel); because had not been AddIoWriteEvent(pSocketChannel). - return(true); - } - else if (CODEC_STATUS_PAUSE == eStatus) - { - AddIoWriteEvent(named_iter->second); - return(true); - } - return(false); + m_pActorBuilder->AddNetLogMsg(oMsgBody); } + return(true); } -bool Manager::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +time_t Manager::GetNowTime() const { - LOG4_TRACE("node_type: %s", strNodeType.c_str()); - std::string strOnlineNode; - if (m_pSessionNode->GetNode(strNodeType, strOnlineNode)) - { - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); - } - else - { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } + return(m_pDispatcher->GetNowTime()); } -bool Manager::SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Manager::GetConf() { - if (std::string("LOGGER") != strNodeType) + if (m_stNodeInfo.strWorkPath.length() == 0) { - LOG4_ERROR("SendOriented() only support node_type \"LOGGER\" in manager process!"); - return(false); + char szFilePath[256] = {0}; + if (getcwd(szFilePath, sizeof(szFilePath))) + { + m_stNodeInfo.strWorkPath = szFilePath; + } + else + { + std::cerr << "failed to open work dir: " << m_stNodeInfo.strWorkPath << std::endl; + return(false); + } } - - std::string strOnlineNode; - if (m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) + m_oLastConf = m_oCurrentConf; + //snprintf(szFileName, sizeof(szFileName), "%s/%s", m_strWorkPath.c_str(), m_strConfFile.c_str()); + std::ifstream fin(m_stNodeInfo.strConfFile.c_str()); + if (fin.good()) { - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); + std::stringstream ssContent; + ssContent << fin.rdbuf(); + if (!m_oCurrentConf.Parse(ssContent.str())) + { + ssContent.str(""); + fin.close(); + m_oCurrentConf = m_oLastConf; + std::cerr << "failed to parse json file " << m_stNodeInfo.strConfFile << std::endl; + return(false); + } + ssContent.str(""); + fin.close(); } else { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); + std::cerr << "failed to open file " << m_stNodeInfo.strConfFile << std::endl; return(false); } -} -bool Manager::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) -{ - LOG4_TRACE("nody_type: %s", strNodeType.c_str()); - if (oMsgBody.has_req_target()) + if (m_oLastConf.ToString() != m_oCurrentConf.ToString()) { - if (0 != oMsgBody.req_target().route_id()) - { - return(SendOriented(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, pSender)); - } - else if (oMsgBody.req_target().route().length() > 0) + m_oCurrentConf.Get("io_timeout", m_stNodeInfo.dIoTimeout); + if (m_oLastConf.ToString().length() == 0) { - std::string strOnlineNode; - if (m_pSessionNode->GetNode(strNodeType, oMsgBody.req_target().route(), strOnlineNode)) - { - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); - } - else - { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } + m_stNodeInfo.uiWorkerNum = strtoul(m_oCurrentConf("worker_num").c_str(), NULL, 10); + m_oCurrentConf.Get("node_type", m_stNodeInfo.strNodeType); + m_oCurrentConf.Get("host", m_stNodeInfo.strHostForServer); + m_oCurrentConf.Get("port", m_stNodeInfo.iPortForServer); + m_oCurrentConf.Get("access_host", m_stNodeInfo.strHostForClient); + m_oCurrentConf.Get("access_port", m_stNodeInfo.iPortForClient); + m_oCurrentConf.Get("gateway", m_stNodeInfo.strGateway); + m_oCurrentConf.Get("gateway_port", m_stNodeInfo.iGatewayPort); + m_stNodeInfo.strNodeIdentify = m_stNodeInfo.strHostForServer + std::string(":") + std::to_string(m_stNodeInfo.iPortForServer); } - else + int32 iCodec; + if (m_oCurrentConf.Get("access_codec", iCodec)) { - return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); + m_stNodeInfo.eCodec = E_CODEC_TYPE(iCodec); } + m_oCurrentConf["permission"]["addr_permit"].Get("stat_interval", m_stNodeInfo.dAddrStatInterval); + m_oCurrentConf["permission"]["addr_permit"].Get("permit_num", m_stNodeInfo.iAddrPermitNum); } - else + return(true); +} + +bool Manager::Init() +{ + if (!InitLogger(m_oCurrentConf) || !InitDispatcher() || !InitActorBuilder()) { - LOG4_ERROR("MsgBody is not a request message!"); return(false); } -} -bool Manager::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) -{ - LOG4_TRACE("node_type: %s", strNodeType.c_str()); - std::unordered_set setOnlineNodes; - if (m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) + if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) { - bool bSendResult = false; - for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + // 接入节点才需要监听客户端连接 + if (!m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForClient, + m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd, + m_stManagerInfo.iC2SFamily)) { - bSendResult |= SendTo(*node_iter, iCmd, uiSeq, oMsgBody); + return(false); } - return(bSendResult); } - else - { - LOG4_WARNING("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } -} - -bool Manager::AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) -{ - LOG4_TRACE("%s", strIdentify.c_str()); - int iPosIpPortSeparator = strIdentify.rfind(':'); - int iPosPortWorkerIndexSeparator = strIdentify.rfind('.'); - std::string strHost = strIdentify.substr(0, iPosIpPortSeparator); - std::string strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); - std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); - int iWorkerIndex = atoi(strWorkerIndex.c_str()); - - struct addrinfo stAddrHints; - struct addrinfo* pAddrResult; - struct addrinfo* pAddrCurrent; - memset(&stAddrHints, 0, sizeof(struct addrinfo)); - stAddrHints.ai_family = AF_UNSPEC; - stAddrHints.ai_socktype = SOCK_STREAM; - stAddrHints.ai_protocol = IPPROTO_IP; - int iCode = getaddrinfo(strHost.c_str(), strPort.c_str(), &stAddrHints, &pAddrResult); - if (0 != iCode) - { - LOG4_ERROR("getaddrinfo(\"%s\", \"%s\") error %d: %s", - strHost.c_str(), strPort.c_str(), iCode, gai_strerror(iCode)); - return(false); - } - int iFd = -1; - for (pAddrCurrent = pAddrResult; - pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) - { - iFd = socket(pAddrCurrent->ai_family, - pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); - if (iFd == -1) - { - continue; - } - - break; - } - - /* No address succeeded */ - if (pAddrCurrent == NULL) - { - LOG4_ERROR("Could not connect to \"%s:%s\"", strHost.c_str(), strPort.c_str()); - freeaddrinfo(pAddrResult); /* No longer needed */ - return(false); - } - - x_sock_set_block(iFd, 0); - int reuse = 1; - ::setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); - std::shared_ptr pChannel = CreateChannel(iFd, CODEC_NEBULA); - if (nullptr != pChannel) - { - int iKeepAlive = 1; - int iKeepIdle = 60; - int iKeepInterval = 5; - int iKeepCount = 3; - int iTcpNoDelay = 1; - if (setsockopt(iFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)) < 0) - { - LOG4_WARNING("fail to set SO_KEEPALIVE"); - } - if (setsockopt(iFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPIDLE"); - } - if (setsockopt(iFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPINTVL"); - } - if (setsockopt(iFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPCNT"); - } - if (setsockopt(iFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)) < 0) - { - LOG4_WARNING("fail to set TCP_NODELAY"); - } - connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); - freeaddrinfo(pAddrResult); /* No longer needed */ - AddIoTimeout(pChannel, 1.5); - AddIoReadEvent(pChannel); - AddIoWriteEvent(pChannel); - pChannel->m_pImpl->SetIdentify(strIdentify); - pChannel->m_pImpl->SetRemoteAddr(strHost); - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - if (CODEC_STATUS_OK != eCodecStatus && CODEC_STATUS_PAUSE != eCodecStatus) - { - DiscardSocketChannel(pChannel); - } - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); - pChannel->m_pImpl->m_unRemoteWorkerIdx = iWorkerIndex; - AddNamedSocketChannel(strIdentify, pChannel); - return(true); - } - freeaddrinfo(pAddrResult); /* No longer needed */ - close(iFd); - return(false); -} -bool Manager::GetConf() -{ - char szFilePath[256] = {0}; - //char szFileName[256] = {0}; - if (m_stManagerInfo.strWorkPath.length() == 0) - { - if (getcwd(szFilePath, sizeof(szFilePath))) - { - m_stManagerInfo.strWorkPath = szFilePath; - } - else - { - std::cerr << "failed to open work dir: " << m_stManagerInfo.strWorkPath << std::endl; - return(false); - } - } - m_oLastConf = m_oCurrentConf; - //snprintf(szFileName, sizeof(szFileName), "%s/%s", m_strWorkPath.c_str(), m_strConfFile.c_str()); - std::ifstream fin(m_stManagerInfo.strConfFile.c_str()); - if (fin.good()) - { - std::stringstream ssContent; - ssContent << fin.rdbuf(); - if (!m_oCurrentConf.Parse(ssContent.str())) - { - ssContent.str(""); - fin.close(); - m_oCurrentConf = m_oLastConf; - std::cerr << "failed to parse json file " << m_stManagerInfo.strConfFile << std::endl; - return(false); - } - ssContent.str(""); - fin.close(); - } - else + if (!m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForServer, + m_stNodeInfo.iPortForServer, m_stManagerInfo.iS2SListenFd, + m_stManagerInfo.iS2SFamily)) { - std::cerr << "failed to open file " << m_stManagerInfo.strConfFile << std::endl; return(false); } - if (m_oLastConf.ToString() != m_oCurrentConf.ToString()) - { - m_oCurrentConf.Get("io_timeout", m_stManagerInfo.dIoTimeout); - if (m_oLastConf.ToString().length() == 0) - { - m_stManagerInfo.uiWorkerNum = strtoul(m_oCurrentConf("worker_num").c_str(), NULL, 10); - m_oCurrentConf.Get("node_type", m_stManagerInfo.strNodeType); - m_oCurrentConf.Get("host", m_stManagerInfo.strHostForServer); - m_oCurrentConf.Get("port", m_stManagerInfo.iPortForServer); - m_oCurrentConf.Get("access_host", m_stManagerInfo.strHostForClient); - m_oCurrentConf.Get("access_port", m_stManagerInfo.iPortForClient); - m_oCurrentConf.Get("gateway", m_stManagerInfo.strGateway); - m_oCurrentConf.Get("gateway_port", m_stManagerInfo.iGatewayPort); - m_stManagerInfo.strNodeIdentify = m_stManagerInfo.strHostForServer + std::string(":") + std::to_string(m_stManagerInfo.iPortForServer); - } - int32 iCodec; - if (m_oCurrentConf.Get("access_codec", iCodec)) - { - m_stManagerInfo.eCodec = E_CODEC_TYPE(iCodec); - } - m_oCurrentConf["permission"]["addr_permit"].Get("stat_interval", m_stManagerInfo.dAddrStatInterval); - m_oCurrentConf["permission"]["addr_permit"].Get("permit_num", m_stManagerInfo.iAddrPermitNum); - } - return(true); -} - -bool Manager::Init() -{ - InitLogger(m_oCurrentConf); -#if __cplusplus >= 201401L - m_pSessionNode = std::make_unique(); - m_pSessionLogger = std::make_unique(this); -#else - m_pSessionNode = std::unique_ptr(new SessionNode()); - m_pSessionLogger = std::unique_ptr(new SessionManagerLogger(this)); -#endif - - - int queueLen = 100; - int reuse = 1; - int timeout = 1; - - if (m_stManagerInfo.strHostForClient.size() > 0 && m_stManagerInfo.iPortForClient > 0) - { - // 接入节点才需要监听客户端连接 - struct addrinfo stAddrHints; - struct addrinfo* pAddrResult; - struct addrinfo* pAddrCurrent; - memset(&stAddrHints, 0, sizeof(struct addrinfo)); - stAddrHints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - stAddrHints.ai_socktype = SOCK_STREAM; - stAddrHints.ai_protocol = IPPROTO_IP; - stAddrHints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ - int iCode = getaddrinfo(m_stManagerInfo.strHostForClient.c_str(), - std::to_string(m_stManagerInfo.iPortForClient).c_str(), &stAddrHints, &pAddrResult); - if (0 != iCode) - { - LOG4_ERROR("getaddrinfo(\"%s\", \"%d\") error %d: %s", - m_stManagerInfo.strHostForClient.c_str(), - m_stManagerInfo.iPortForClient, iCode, gai_strerror(iCode)); - exit(errno); - } - for (pAddrCurrent = pAddrResult; - pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) - { - m_stManagerInfo.iC2SListenFd = socket(pAddrCurrent->ai_family, - pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); - if (-1 == m_stManagerInfo.iC2SListenFd) - { - continue; - } - if (-1 == ::setsockopt(m_stManagerInfo.iC2SListenFd, - SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))) - { - close(m_stManagerInfo.iC2SListenFd); - m_stManagerInfo.iC2SListenFd = -1; - continue; - } - if (-1 == ::setsockopt(m_stManagerInfo.iC2SListenFd, - IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, sizeof(int))) - { - close(m_stManagerInfo.iC2SListenFd); - m_stManagerInfo.iC2SListenFd = -1; - continue; - } - if (-1 == bind(m_stManagerInfo.iC2SListenFd, - pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen)) - { - close(m_stManagerInfo.iC2SListenFd); - m_stManagerInfo.iC2SListenFd = -1; - continue; - } - if (-1 == listen(m_stManagerInfo.iC2SListenFd, queueLen)) - { - close(m_stManagerInfo.iC2SListenFd); - m_stManagerInfo.iC2SListenFd = -1; - continue; - } - - m_stManagerInfo.iC2SFamily = pAddrCurrent->ai_family; - break; - } - freeaddrinfo(pAddrResult); /* No longer needed */ - - /* No address succeeded */ - if (-1 == m_stManagerInfo.iC2SListenFd) - { - LOG4_ERROR("Could not bind to \"%s:%d\", error %d: %s", - m_stManagerInfo.strHostForClient.c_str(), - m_stManagerInfo.iPortForClient, - errno, strerror_r(errno, m_szErrBuff, 1024)); - exit(errno); - } - } - - struct addrinfo stAddrHints; - struct addrinfo* pAddrResult; - struct addrinfo* pAddrCurrent; - memset(&stAddrHints, 0, sizeof(struct addrinfo)); - stAddrHints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - stAddrHints.ai_socktype = SOCK_STREAM; - stAddrHints.ai_protocol = IPPROTO_IP; - stAddrHints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ - stAddrHints.ai_canonname = NULL; - stAddrHints.ai_addr = NULL; - stAddrHints.ai_next = NULL; - int iCode = getaddrinfo(m_stManagerInfo.strHostForServer.c_str(), - std::to_string(m_stManagerInfo.iPortForServer).c_str(), &stAddrHints, &pAddrResult); - if (0 != iCode) - { - LOG4_ERROR("getaddrinfo(\"%s\", \"%d\") error %d: %s", - m_stManagerInfo.strHostForServer.c_str(), - m_stManagerInfo.iPortForServer, iCode, gai_strerror(iCode)); - exit(errno); - } - for (pAddrCurrent = pAddrResult; - pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) - { - m_stManagerInfo.iS2SListenFd = socket(pAddrCurrent->ai_family, - pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); - if (-1 == m_stManagerInfo.iS2SListenFd) - { - continue; - } - if (-1 == ::setsockopt(m_stManagerInfo.iS2SListenFd, - SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))) - { - close(m_stManagerInfo.iS2SListenFd); - m_stManagerInfo.iS2SListenFd = -1; - continue; - } - if (-1 == ::setsockopt(m_stManagerInfo.iS2SListenFd, - IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, sizeof(int))) - { - close(m_stManagerInfo.iS2SListenFd); - m_stManagerInfo.iS2SListenFd = -1; - continue; - } - if (-1 == bind(m_stManagerInfo.iS2SListenFd, - pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen)) - { - m_stManagerInfo.iS2SListenFd = -1; - continue; - } - if (-1 == listen(m_stManagerInfo.iS2SListenFd, queueLen)) - { - close(m_stManagerInfo.iS2SListenFd); - m_stManagerInfo.iS2SListenFd = -1; - continue; - } - - m_stManagerInfo.iS2SFamily = pAddrCurrent->ai_family; - break; - } - freeaddrinfo(pAddrResult); /* No longer needed */ - - /* No address succeeded */ - if (-1 == m_stManagerInfo.iS2SListenFd) - { - LOG4_ERROR("Could not bind to \"%s:%d\", error %d: %s", - m_stManagerInfo.strHostForServer.c_str(), - m_stManagerInfo.iPortForServer, - errno, strerror_r(errno, m_szErrBuff, 1024)); - exit(errno); - } - // 创建到beacon的连接信息 for (int i = 0; i < m_oCurrentConf["beacon"].GetArraySize(); ++i) { std::string strIdentify = m_oCurrentConf["beacon"][i]("host") + std::string(":") + m_oCurrentConf["beacon"][i]("port") + std::string(".1"); // BeaconServer只有一个Worker - AddNodeIdentify(std::string("BEACON"), strIdentify); + m_pDispatcher->AddNodeIdentify(std::string("BEACON"), strIdentify); } return(true); @@ -990,185 +292,60 @@ bool Manager::Init() void Manager::Destroy() { LOG4_TRACE(" "); - m_mapWorker.clear(); - m_mapWorkerFdPid.clear(); - m_mapWorkerRestartNum.clear(); - for (auto iter = m_mapSocketChannel.begin(); iter != m_mapSocketChannel.end(); ++iter) - { - DiscardSocketChannel(iter->second); - } - m_mapSocketChannel.clear(); - m_mapClientConnFrequency.clear(); - m_vecFreeWorkerIdx.clear(); if (m_pPeriodicTaskWatcher != NULL) { + m_pDispatcher->DelEvent(m_pPeriodicTaskWatcher); free(m_pPeriodicTaskWatcher); + m_pPeriodicTaskWatcher = NULL; + } + if (m_pDispatcher != nullptr) + { + delete m_pDispatcher; + m_pDispatcher = nullptr; } - if (m_loop != NULL) + if (m_pActorBuilder != nullptr) { - ev_loop_destroy(m_loop); - m_loop = NULL; + delete m_pActorBuilder; + m_pActorBuilder = nullptr; } } bool Manager::CreateEvents() { LOG4_TRACE(" "); - m_loop = ev_loop_new(EVFLAG_FORKCHECK | EVFLAG_SIGNALFD); - if (m_loop == NULL) - { - return(false); - } std::shared_ptr pChannelListen = NULL; if (m_stManagerInfo.iC2SListenFd > 2) { LOG4_DEBUG("C2SListenFd[%d]", m_stManagerInfo.iC2SListenFd); - pChannelListen = CreateChannel(m_stManagerInfo.iC2SListenFd, m_stManagerInfo.eCodec); - pChannelListen->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - AddIoReadEvent(pChannelListen); + pChannelListen = m_pDispatcher->CreateSocketChannel(m_stManagerInfo.iC2SListenFd, m_stNodeInfo.eCodec); + m_pDispatcher->SetChannelStatus(pChannelListen, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->AddIoReadEvent(pChannelListen); } LOG4_DEBUG("S2SListenFd[%d]", m_stManagerInfo.iS2SListenFd); - pChannelListen = CreateChannel(m_stManagerInfo.iS2SListenFd, CODEC_NEBULA); - pChannelListen->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - AddIoReadEvent(pChannelListen); + pChannelListen = m_pDispatcher->CreateSocketChannel(m_stManagerInfo.iS2SListenFd, CODEC_NEBULA); + m_pDispatcher->SetChannelStatus(pChannelListen, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->AddIoReadEvent(pChannelListen); ev_signal* child_signal_watcher = new ev_signal(); - ev_signal_init (child_signal_watcher, SignalCallback, SIGCHLD); child_signal_watcher->data = (void*)this; - ev_signal_start (m_loop, child_signal_watcher); - - /* - ev_signal* int_signal_watcher = new ev_signal(); - ev_signal_init (int_signal_watcher, SignalCallback, SIGINT); - int_signal_watcher->data = (void*)this; - ev_signal_start (m_loop, int_signal_watcher); - */ + m_pDispatcher->AddEvent(child_signal_watcher, Dispatcher::SignalCallback, SIGCHLD); ev_signal* ill_signal_watcher = new ev_signal(); - ev_signal_init (ill_signal_watcher, SignalCallback, SIGILL); ill_signal_watcher->data = (void*)this; - ev_signal_start (m_loop, ill_signal_watcher); + m_pDispatcher->AddEvent(ill_signal_watcher, Dispatcher::SignalCallback, SIGILL); ev_signal* bus_signal_watcher = new ev_signal(); - ev_signal_init (bus_signal_watcher, SignalCallback, SIGBUS); bus_signal_watcher->data = (void*)this; - ev_signal_start (m_loop, bus_signal_watcher); + m_pDispatcher->AddEvent(bus_signal_watcher, Dispatcher::SignalCallback, SIGBUS); ev_signal* fpe_signal_watcher = new ev_signal(); - ev_signal_init (fpe_signal_watcher, SignalCallback, SIGFPE); fpe_signal_watcher->data = (void*)this; - ev_signal_start (m_loop, fpe_signal_watcher); + m_pDispatcher->AddEvent(fpe_signal_watcher, Dispatcher::SignalCallback, SIGFPE); + m_pSessionManager = std::dynamic_pointer_cast( + m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager")); AddPeriodicTaskEvent(); - // 注册idle事件在Server空闲时会导致CPU占用过高,暂时弃用之,改用定时器实现 -// ev_idle* idle_watcher = new ev_idle(); -// ev_idle_init (idle_watcher, IdleCallback); -// idle_watcher->data = (void*)this; -// ev_idle_start (m_loop, idle_watcher); - - return(true); -} - -bool Manager::AddPeriodicTaskEvent() -{ - LOG4_TRACE(" "); - m_pPeriodicTaskWatcher = (ev_timer*)malloc(sizeof(ev_timer)); - if (m_pPeriodicTaskWatcher == NULL) - { - LOG4_ERROR("new timeout_watcher error!"); - return(false); - } - ev_timer_init (m_pPeriodicTaskWatcher, PeriodicTaskCallback, NODE_BEAT + ev_time() - ev_now(m_loop), 0.); - m_pPeriodicTaskWatcher->data = (void*)this; - ev_timer_start (m_loop, m_pPeriodicTaskWatcher); - return(true); -} - -bool Manager::AddIoReadEvent(std::shared_ptr pChannel) -{ - LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); - if (NULL == io_watcher) - { - return(false); - } - else - { - if (ev_is_active(io_watcher)) - { - ev_io_stop(m_loop, io_watcher); - ev_io_set(io_watcher, io_watcher->fd, io_watcher->events | EV_READ); - ev_io_start (m_loop, io_watcher); - } - else - { - ev_io_init (io_watcher, IoCallback, pChannel->m_pImpl->GetFd(), EV_READ); - ev_io_start (m_loop, io_watcher); - } - return(true); - } -} - -bool Manager::AddIoWriteEvent(std::shared_ptr pChannel) -{ - LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); - if (NULL == io_watcher) - { - return(false); - } - else - { - if (ev_is_active(io_watcher)) - { - ev_io_stop(m_loop, io_watcher); - ev_io_set(io_watcher, io_watcher->fd, io_watcher->events | EV_WRITE); - ev_io_start (m_loop, io_watcher); - } - else - { - ev_io_init (io_watcher, IoCallback, pChannel->m_pImpl->GetFd(), EV_WRITE); - ev_io_start (m_loop, io_watcher); - } - return(true); - } -} - -bool Manager::RemoveIoWriteEvent(std::shared_ptr pChannel) -{ - LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); - if (NULL == io_watcher) - { - return(false); - } - if (EV_WRITE & io_watcher->events) - { - ev_io_stop(m_loop, io_watcher); - ev_io_set(io_watcher, io_watcher->fd, io_watcher->events & (~EV_WRITE)); - ev_io_start (m_loop, io_watcher); - } - return(true); -} - -bool Manager::DelEvents(ev_io* io_watcher) -{ - if (NULL == io_watcher) - { - return(false); - } - LOG4_TRACE("fd %d", io_watcher->fd); - ev_io_stop (m_loop, io_watcher); - return(true); -} -bool Manager::DelEvents(ev_timer* timer_watcher) -{ - if (NULL == timer_watcher) - { - return(false); - } - ev_timer_stop (m_loop, timer_watcher); return(true); } @@ -1177,23 +354,22 @@ void Manager::CreateWorker() LOG4_TRACE(" "); int iPid = 0; - for (unsigned int i = 1; i <= m_stManagerInfo.uiWorkerNum; ++i) + for (unsigned int i = 1; i <= m_stNodeInfo.uiWorkerNum; ++i) { int iControlFds[2]; int iDataFds[2]; if (socketpair(PF_UNIX, SOCK_STREAM, 0, iControlFds) < 0) { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_szErrBuff, 1024)); + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); } if (socketpair(PF_UNIX, SOCK_STREAM, 0, iDataFds) < 0) { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_szErrBuff, 1024)); + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); } iPid = fork(); if (iPid == 0) // 子进程 { - ev_loop_destroy(m_loop); close(m_stManagerInfo.iS2SListenFd); if (m_stManagerInfo.iC2SListenFd > 2) { @@ -1203,7 +379,7 @@ void Manager::CreateWorker() close(iDataFds[0]); x_sock_set_block(iControlFds[1], 0); x_sock_set_block(iDataFds[1], 0); - Worker oWorker(m_stManagerInfo.strWorkPath, iControlFds[1], iDataFds[1], i, m_oCurrentConf); + Worker oWorker(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], i, m_oCurrentConf); oWorker.Run(); exit(-2); } @@ -1213,102 +389,42 @@ void Manager::CreateWorker() close(iDataFds[1]); x_sock_set_block(iControlFds[0], 0); x_sock_set_block(iDataFds[0], 0); - tagWorkerAttr stWorkerAttr; - stWorkerAttr.iWorkerIndex = i; - stWorkerAttr.iControlFd = iControlFds[0]; - stWorkerAttr.iDataFd = iDataFds[0]; - stWorkerAttr.dBeatTime = ev_now(m_loop); - m_mapWorker.insert(std::pair(iPid, stWorkerAttr)); - m_mapWorkerFdPid.insert(std::pair(iControlFds[0], iPid)); - m_mapWorkerFdPid.insert(std::pair(iDataFds[0], iPid)); - std::shared_ptr pChannelData = CreateChannel(iControlFds[0], CODEC_NEBULA); - std::shared_ptr pChannelControl = CreateChannel(iDataFds[0], CODEC_NEBULA); - pChannelData->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - pChannelControl->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - AddIoReadEvent(pChannelData); - AddIoReadEvent(pChannelControl); + m_pSessionManager->AddWorkerInfo(i, iPid, iControlFds[0], iDataFds[0]); + std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); + std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); + m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->AddIoReadEvent(pChannelData); + m_pDispatcher->AddIoReadEvent(pChannelControl); } else { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_szErrBuff, 1024)); - } - } -} - -bool Manager::CheckWorker() -{ - LOG4_TRACE(" "); - - for (auto worker_iter = m_mapWorker.begin(); - worker_iter != m_mapWorker.end(); ++worker_iter) - { - LOG4_TRACE("now %lf, worker_beat_time %lf, worker_beat %d", - ev_now(m_loop), worker_iter->second.dBeatTime, m_stManagerInfo.iWorkerBeat); - if ((ev_now(m_loop) - worker_iter->second.dBeatTime) > m_stManagerInfo.iWorkerBeat) - { - LOG4_INFO("worker_%d pid %d is unresponsive, " - "terminate it.", worker_iter->second.iWorkerIndex, worker_iter->first); - kill(worker_iter->first, SIGKILL); //SIGINT); -// RestartWorker(worker_iter->first); + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); } } - return(true); } bool Manager::RestartWorker(int iDeathPid) { LOG4_DEBUG("%d", iDeathPid); + int iWorkerIndex = 0; int iNewPid = 0; - char errMsg[1024] = {0}; - auto worker_iter = m_mapWorker.find(iDeathPid); - if (worker_iter != m_mapWorker.end()) + if (m_pSessionManager->WorkerDeath(iDeathPid, iWorkerIndex)) { - LOG4_TRACE("restart worker %d, close control fd %d and data fd %d first.", - worker_iter->second.iWorkerIndex, worker_iter->second.iControlFd, worker_iter->second.iDataFd); - int iWorkerIndex = worker_iter->second.iWorkerIndex; - auto fd_iter = m_mapWorkerFdPid.find(worker_iter->second.iControlFd); - if (fd_iter != m_mapWorkerFdPid.end()) - { - m_mapWorkerFdPid.erase(fd_iter); - } - fd_iter = m_mapWorkerFdPid.find(worker_iter->second.iDataFd); - if (fd_iter != m_mapWorkerFdPid.end()) - { - m_mapWorkerFdPid.erase(fd_iter); - } - auto channel_iter = m_mapSocketChannel.find(worker_iter->second.iControlFd); - if (channel_iter != m_mapSocketChannel.end()) - { - DiscardSocketChannel(channel_iter->second); - } - channel_iter = m_mapSocketChannel.find(worker_iter->second.iDataFd); - if (channel_iter != m_mapSocketChannel.end()) - { - DiscardSocketChannel(channel_iter->second); - } - m_mapWorker.erase(worker_iter); - - auto restart_num_iter = m_mapWorkerRestartNum.find(iWorkerIndex); - if (restart_num_iter != m_mapWorkerRestartNum.end()) - { - LOG4_INFO("worker %d had been restarted %d times!", iWorkerIndex, restart_num_iter->second); - } - int iControlFds[2]; int iDataFds[2]; if (socketpair(PF_UNIX, SOCK_STREAM, 0, iControlFds) < 0) { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, errMsg, 1024)); + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); } if (socketpair(PF_UNIX, SOCK_STREAM, 0, iDataFds) < 0) { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, errMsg, 1024)); + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); } iNewPid = fork(); if (iNewPid == 0) // 子进程 { - ev_loop_destroy(m_loop); close(m_stManagerInfo.iS2SListenFd); if (m_stManagerInfo.iC2SListenFd > 2) { @@ -1318,113 +434,34 @@ bool Manager::RestartWorker(int iDeathPid) close(iDataFds[0]); x_sock_set_block(iControlFds[1], 0); x_sock_set_block(iDataFds[1], 0); - Worker oWorker(m_stManagerInfo.strWorkPath, iControlFds[1], iDataFds[1], iWorkerIndex, m_oCurrentConf); + Worker oWorker(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], iWorkerIndex, m_oCurrentConf); oWorker.Run(); exit(-2); // 子进程worker没有正常运行 } else if (iNewPid > 0) // 父进程 { LOG4_INFO("worker %d restart successfully", iWorkerIndex); - ev_loop_fork(m_loop); close(iControlFds[1]); close(iDataFds[1]); x_sock_set_block(iControlFds[0], 0); x_sock_set_block(iDataFds[0], 0); - tagWorkerAttr stWorkerAttr; - stWorkerAttr.iWorkerIndex = iWorkerIndex; - stWorkerAttr.iControlFd = iControlFds[0]; - stWorkerAttr.iDataFd = iDataFds[0]; - stWorkerAttr.dBeatTime = ev_now(m_loop); - LOG4_TRACE("m_mapWorker insert (iNewPid %d, worker_index %d)", iNewPid, iWorkerIndex); - m_mapWorker.insert(std::pair(iNewPid, stWorkerAttr)); - m_mapWorkerFdPid.insert(std::pair(iControlFds[0], iNewPid)); - m_mapWorkerFdPid.insert(std::pair(iDataFds[0], iNewPid)); - std::shared_ptr pChannelData = CreateChannel(iControlFds[0], CODEC_NEBULA); - std::shared_ptr pChannelControl = CreateChannel(iDataFds[0], CODEC_NEBULA); - pChannelData->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - pChannelControl->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - AddIoReadEvent(pChannelData); - AddIoReadEvent(pChannelControl); - restart_num_iter = m_mapWorkerRestartNum.find(iWorkerIndex); - if (restart_num_iter == m_mapWorkerRestartNum.end()) - { - m_mapWorkerRestartNum.insert(std::pair(iWorkerIndex, 1)); - } - else - { - restart_num_iter->second++; - } - // 重启Worker进程后下发其他节点的信息 - - MsgBody oMsgBody; - CJsonObject oSubscribeNode; - oSubscribeNode.Add("add_nodes", CJsonObject("[]")); - for (auto it = m_mapOnlineNodes.begin(); it != m_mapOnlineNodes.end(); ++it) - { - oSubscribeNode["add_nodes"].Add(CJsonObject(it->second)); - } - oMsgBody.set_data(oSubscribeNode.ToString()); - SendToWorker(CMD_REQ_NODE_NOTICE, GetSequence(), oMsgBody); - return(true); + m_pSessionManager->AddWorkerInfo(iWorkerIndex, iNewPid, iControlFds[0], iDataFds[0]); + std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); + std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); + m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->AddIoReadEvent(pChannelData); + m_pDispatcher->AddIoReadEvent(pChannelControl); + m_pSessionManager->SendOnlineNodesToWorker(); } else { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, errMsg, 1024)); + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); } } return(false); } -std::pair Manager::GetMinLoadWorkerDataFd() -{ - LOG4_TRACE(" "); - int iMinLoadWorkerFd = 0; - int iMinLoad = -1; - std::pair worker_pid_fd; - for (auto iter = m_mapWorker.begin(); iter != m_mapWorker.end(); ++iter) - { - if (iter == m_mapWorker.begin()) - { - iMinLoadWorkerFd = iter->second.iDataFd; - iMinLoad = iter->second.iLoad; - worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); - } - else if (iter->second.iLoad < iMinLoad) - { - iMinLoadWorkerFd = iter->second.iDataFd; - iMinLoad = iter->second.iLoad; - worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); - } - } - return(worker_pid_fd); -} - -bool Manager::SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) -{ - std::unordered_map>::iterator worker_conn_iter; - for (auto worker_iter = m_mapWorker.begin(); worker_iter != m_mapWorker.end(); ++worker_iter) - { - worker_conn_iter = m_mapSocketChannel.find(worker_iter->second.iControlFd); - if (worker_conn_iter != m_mapSocketChannel.end()) - { - E_CODEC_STATUS eCodecStatus = worker_conn_iter->second->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - if (CODEC_STATUS_OK == eCodecStatus) - { - RemoveIoWriteEvent(worker_conn_iter->second); - } - else if (CODEC_STATUS_PAUSE == eCodecStatus) - { - AddIoWriteEvent(worker_conn_iter->second); - } - else - { - DiscardSocketChannel(worker_conn_iter->second); - } - } - } - return(true); -} - void Manager::RefreshServer() { LOG4_TRACE(" "); @@ -1440,10 +477,8 @@ void Manager::RefreshServer() LogLevel oLogLevel; oLogLevel.set_log_level(iLogLevel); oLogLevel.set_net_log_level(iNetLogLevel); - //oMsgBody.set_data(oLogLevel.SerializeAsString()); - oLogLevel.SerializeToString(&m_strDataString); - oMsgBody.set_data(m_strDataString); - SendToWorker(CMD_REQ_SET_LOG_LEVEL, GetSequence(), oMsgBody); + oMsgBody.set_data(oLogLevel.SerializeAsString()); + m_pSessionManager->SendToWorker(CMD_REQ_SET_LOG_LEVEL, GetSequence(), oMsgBody); } // 更新动态库配置或重新加载动态库 @@ -1451,886 +486,54 @@ void Manager::RefreshServer() { MsgBody oMsgBody; oMsgBody.set_data(m_oCurrentConf["dynamic_loading"].ToString()); - SendToWorker(CMD_REQ_RELOAD_SO, GetSequence(), oMsgBody); + m_pSessionManager->SendToWorker(CMD_REQ_RELOAD_SO, GetSequence(), oMsgBody); } } else { LOG4_ERROR("GetConf() error, please check the config file.", ""); } - m_pSessionLogger->Timeout(); } -bool Manager::RegisterToBeacon() +bool Manager::AddPeriodicTaskEvent() { - if (std::string("BEACON") == m_stManagerInfo.strNodeType) - { - return(true); - } LOG4_TRACE(" "); - int iLoad = 0; - int iConnect = 0; - int iRecvNum = 0; - int iRecvByte = 0; - int iSendNum = 0; - int iSendByte = 0; - int iClientNum = 0; - MsgBody oMsgBody; - CJsonObject oReportData; - CJsonObject oMember; - oReportData.Add("node_type", m_stManagerInfo.strNodeType); - oReportData.Add("node_id", m_stManagerInfo.uiNodeId); - oReportData.Add("node_ip", m_stManagerInfo.strHostForServer); - oReportData.Add("node_port", m_stManagerInfo.iPortForServer); - if (m_stManagerInfo.strGateway.size() > 0) - { - oReportData.Add("access_ip", m_stManagerInfo.strGateway); - } - else - { - oReportData.Add("access_ip", m_stManagerInfo.strHostForClient); - } - if (m_stManagerInfo.iGatewayPort > 0) - { - oReportData.Add("access_port", m_stManagerInfo.iGatewayPort); - } - else - { - oReportData.Add("access_port", m_stManagerInfo.iPortForClient); - } - oReportData.Add("worker_num", (int)m_mapWorker.size()); - oReportData.Add("active_time", ev_now(m_loop)); - oReportData.Add("node", CJsonObject("{}")); - oReportData.Add("worker", CJsonObject("[]")); - auto worker_iter = m_mapWorker.begin(); - for (; worker_iter != m_mapWorker.end(); ++worker_iter) - { - iLoad += worker_iter->second.iLoad; - iConnect += worker_iter->second.iConnect; - iRecvNum += worker_iter->second.iRecvNum; - iRecvByte += worker_iter->second.iRecvByte; - iSendNum += worker_iter->second.iSendNum; - iSendByte += worker_iter->second.iSendByte; - iClientNum += worker_iter->second.iClientNum; - oMember.Clear(); - oMember.Add("load", worker_iter->second.iLoad); - oMember.Add("connect", worker_iter->second.iConnect); - oMember.Add("recv_num", worker_iter->second.iRecvNum); - oMember.Add("recv_byte", worker_iter->second.iRecvByte); - oMember.Add("send_num", worker_iter->second.iSendNum); - oMember.Add("send_byte", worker_iter->second.iSendByte); - oMember.Add("client", worker_iter->second.iClientNum); - oReportData["worker"].Add(oMember); - } - oReportData["node"].Add("load", iLoad); - oReportData["node"].Add("connect", iConnect); - oReportData["node"].Add("recv_num", iRecvNum); - oReportData["node"].Add("recv_byte", iRecvByte); - oReportData["node"].Add("send_num", iSendNum); - oReportData["node"].Add("send_byte", iSendByte); - oReportData["node"].Add("client", iClientNum); - oMsgBody.set_data(oReportData.ToString()); - Broadcast("BEACON", CMD_REQ_NODE_REGISTER, GetSequence(), oMsgBody); - return(true); -} - -/** - * @brief 上报节点状态信息 - * @return 上报是否成功 - * @note 节点状态信息结构如: - * { - * "node_type":"ACCESS", - * "node_ip":"192.168.11.12", - * "node_port":9988, - * "access_ip":"120.234.2.106", - * "access_port":10001, - * "worker_num":10, - * "active_time":16879561651.06, - * "node":{ - * "load":1885792, "connect":495873, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":495870 - * }, - * "worker": - * [ - * {"load":655666, "connect":495873, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":195870}}, - * {"load":655235, "connect":485872, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":195870}}, - * {"load":585696, "connect":415379, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":195870}} - * ] - * } - */ -bool Manager::ReportToBeacon() -{ - if (std::string("BEACON") == m_stManagerInfo.strNodeType) - { - return(true); - } - int iLoad = 0; - int iConnect = 0; - int iRecvNum = 0; - int iRecvByte = 0; - int iSendNum = 0; - int iSendByte = 0; - int iClientNum = 0; - MsgBody oMsgBody; - CJsonObject oReportData; - CJsonObject oMember; - oReportData.Add("node_type", m_stManagerInfo.strNodeType); - oReportData.Add("node_id", m_stManagerInfo.uiNodeId); - oReportData.Add("node_ip", m_stManagerInfo.strHostForServer); - oReportData.Add("node_port", m_stManagerInfo.iPortForServer); - if (m_stManagerInfo.strGateway.size() > 0) - { - oReportData.Add("access_ip", m_stManagerInfo.strGateway); - } - else - { - oReportData.Add("access_ip", m_stManagerInfo.strHostForClient); - } - if (m_stManagerInfo.iGatewayPort > 0) - { - oReportData.Add("access_port", m_stManagerInfo.iGatewayPort); - } - else - { - oReportData.Add("access_port", m_stManagerInfo.iPortForClient); - } - oReportData.Add("worker_num", (int)m_mapWorker.size()); - oReportData.Add("active_time", ev_now(m_loop)); - oReportData.Add("node", CJsonObject("{}")); - oReportData.Add("worker", CJsonObject("[]")); - auto worker_iter = m_mapWorker.begin(); - for (; worker_iter != m_mapWorker.end(); ++worker_iter) - { - iLoad += worker_iter->second.iLoad; - iConnect += worker_iter->second.iConnect; - iRecvNum += worker_iter->second.iRecvNum; - iRecvByte += worker_iter->second.iRecvByte; - iSendNum += worker_iter->second.iSendNum; - iSendByte += worker_iter->second.iSendByte; - iClientNum += worker_iter->second.iClientNum; - oMember.Clear(); - oMember.Add("load", worker_iter->second.iLoad); - oMember.Add("connect", worker_iter->second.iConnect); - oMember.Add("recv_num", worker_iter->second.iRecvNum); - oMember.Add("recv_byte", worker_iter->second.iRecvByte); - oMember.Add("send_num", worker_iter->second.iSendNum); - oMember.Add("send_byte", worker_iter->second.iSendByte); - oMember.Add("client", worker_iter->second.iClientNum); - oReportData["worker"].Add(oMember); - } - oReportData["node"].Add("load", iLoad); - oReportData["node"].Add("connect", iConnect); - oReportData["node"].Add("recv_num", iRecvNum); - oReportData["node"].Add("recv_byte", iRecvByte); - oReportData["node"].Add("send_num", iSendNum); - oReportData["node"].Add("send_byte", iSendByte); - oReportData["node"].Add("client", iClientNum); - oMsgBody.set_data(oReportData.ToString()); - LOG4_TRACE("%s", oReportData.ToString().c_str()); - - Broadcast("BEACON", CMD_REQ_NODE_STATUS_REPORT, GetSequence(), oMsgBody); - return(true); -} - -bool Manager::AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel) -{ - LOG4_TRACE("%s", strIdentify.c_str()); - auto named_iter = m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == m_mapNamedSocketChannel.end()) - { - m_mapNamedSocketChannel.insert(std::make_pair(strIdentify, pChannel)); - } - else - { - return(false); - } - pChannel->m_pImpl->SetIdentify(strIdentify); - return(true); - -} - -void Manager::DelNamedSocketChannel(const std::string& strIdentify) -{ - auto named_iter = m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == m_mapNamedSocketChannel.end()) - { - ; - } - else - { - m_mapNamedSocketChannel.erase(named_iter); - } -} - -void Manager::AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify) -{ - LOG4_TRACE("%s, %s", strNodeType.c_str(), strIdentify.c_str()); - m_pSessionNode->AddNode(strNodeType, strIdentify); - - std::string strOnlineNode; - if (std::string("LOGGER") == strNodeType && m_pSessionNode->GetNode(strNodeType, strOnlineNode)) - { - m_pLogger->EnableNetLogger(true); - } -} - -void Manager::DelNodeIdentify(const std::string& strNodeType, const std::string& strIdentify) -{ - LOG4_TRACE("%s, %s", strNodeType.c_str(), strIdentify.c_str()); - if (std::string("BEACON") == strNodeType) - { - LOG4_WARNING("node_type BEACON should not be deleted!"); - } - - m_pSessionNode->DelNode(strNodeType, strIdentify); - - std::string strOnlineNode; - if (std::string("LOGGER") == strNodeType && !m_pSessionNode->GetNode(strNodeType, strOnlineNode)) - { - m_pLogger->EnableNetLogger(false); - } -} - -bool Manager::AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout) -{ - LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_timer* timer_watcher = pChannel->m_pImpl->MutableTimerWatcher(); - if (NULL == timer_watcher) - { - return(false); - } - else - { - if (ev_is_active(timer_watcher)) - { - ev_timer_stop(m_loop, timer_watcher); - ev_timer_set(timer_watcher, dTimeout + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, timer_watcher); - } - else - { - ev_timer_init (timer_watcher, IoTimeoutCallback, dTimeout + ev_time() - ev_now(m_loop), 0.); - ev_timer_start (m_loop, timer_watcher); - } - return(true); - } -} - -bool Manager::AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout) -{ - LOG4_TRACE(" "); - ev_timer* timeout_watcher = new ev_timer(); - if (timeout_watcher == NULL) + m_pPeriodicTaskWatcher = (ev_timer*)malloc(sizeof(ev_timer)); + if (m_pPeriodicTaskWatcher == NULL) { LOG4_ERROR("new timeout_watcher error!"); return(false); } - tagClientConnWatcherData* pData = new tagClientConnWatcherData(); - if (pData == NULL) - { - LOG4_ERROR("new tagClientConnWatcherData error!"); - delete timeout_watcher; - return(false); - } - ev_timer_init (timeout_watcher, ClientConnFrequencyTimeoutCallback, dTimeout + ev_time() - ev_now(m_loop), 0.); - pData->pLabor = this; - snprintf(pData->pAddr, gc_iAddrLen, "%s", pAddr); - timeout_watcher->data = (void*)pData; - ev_timer_start (m_loop, timeout_watcher); - return(true); -} - -std::shared_ptr Manager::CreateChannel(int iFd, E_CODEC_TYPE eCodecType) -{ - LOG4_DEBUG("iFd", iFd); - auto iter = m_mapSocketChannel.find(iFd); - if (iter == m_mapSocketChannel.end()) - { - std::shared_ptr pChannel = nullptr; - try - { - pChannel = std::make_shared(m_pLogger, iFd, GetSequence()); - } - catch(std::bad_alloc& e) - { - LOG4_ERROR("new channel for fd %d error: %s", e.what()); - return(nullptr); - } - pChannel->m_pImpl->SetLabor(this); - if (pChannel->m_pImpl->Init(eCodecType)) - { - m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); - return(pChannel); - } - else - { - return(nullptr); - } - } - else - { - LOG4_WARNING("fd %d is exist!", iFd); - return(iter->second); - } -} - -bool Manager::DiscardSocketChannel(std::shared_ptr pChannel) -{ - LOG4_TRACE("fd[%d], seq[%u]", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - auto name_channel_iter = m_mapNamedSocketChannel.find(pChannel->m_pImpl->GetIdentify()); - if (name_channel_iter != m_mapNamedSocketChannel.end()) - { - m_mapNamedSocketChannel.erase(name_channel_iter); - } - DelEvents(pChannel->m_pImpl->MutableIoWatcher()); - DelEvents(pChannel->m_pImpl->MutableTimerWatcher()); - auto iter = m_mapSocketChannel.find(pChannel->m_pImpl->GetFd()); - if (iter != m_mapSocketChannel.end()) - { - m_mapSocketChannel.erase(iter); - } - pChannel->m_pImpl->Close(); - return(true); -} - -void Manager::SetWorkerLoad(int iPid, CJsonObject& oJsonLoad) -{ - auto iter = m_mapWorker.find(iPid); - if (iter != m_mapWorker.end()) - { - oJsonLoad.Get("load", iter->second.iLoad); - oJsonLoad.Get("connect", iter->second.iConnect); - oJsonLoad.Get("recv_num", iter->second.iRecvNum); - oJsonLoad.Get("recv_byte", iter->second.iRecvByte); - oJsonLoad.Get("send_num", iter->second.iSendNum); - oJsonLoad.Get("send_byte", iter->second.iSendByte); - oJsonLoad.Get("client", iter->second.iClientNum); - iter->second.dBeatTime = ev_now(m_loop); - } -} - -void Manager::AddWorkerLoad(int iPid, int iLoad) -{ - LOG4_TRACE(" "); - auto iter = m_mapWorker.find(iPid); - if (iter != m_mapWorker.end()) - { - iter->second.iLoad += iLoad; - } -} - -bool Manager::OnWorkerData(std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody) -{ - LOG4_DEBUG("cmd %u, seq %u", oInMsgHead.cmd(), oInMsgHead.seq()); - if (CMD_REQ_UPDATE_WORKER_LOAD == oInMsgHead.cmd()) // 新请求 - { - auto iter = m_mapWorkerFdPid.find(pChannel->m_pImpl->GetFd()); - if (iter != m_mapWorkerFdPid.end()) - { - CJsonObject oJsonLoad; - oJsonLoad.Parse(oInMsgBody.data()); - SetWorkerLoad(iter->second, oJsonLoad); - } - } - else - { - LOG4_WARNING("unknow cmd %d from worker!", oInMsgHead.cmd()); - } - return(true); -} - -bool Manager::OnDataAndTransferFd(std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody) -{ - LOG4_DEBUG("cmd %u, seq %u", oInMsgHead.cmd(), oInMsgHead.seq()); - MsgBody oOutMsgBody; - LOG4_TRACE("oInMsgHead.cmd() = %u, seq() = %u", oInMsgHead.cmd(), oInMsgHead.seq()); - if (oInMsgHead.cmd() == CMD_REQ_CONNECT_TO_WORKER) - { - int iWorkerIndex = std::stoi(oInMsgBody.data()); - std::unordered_map::iterator worker_iter; - for (worker_iter = m_mapWorker.begin(); - worker_iter != m_mapWorker.end(); ++worker_iter) - { - if (iWorkerIndex == worker_iter->second.iWorkerIndex) - { - int iErrno = SocketChannel::SendChannelFd( - worker_iter->second.iDataFd, pChannel->m_pImpl->GetFd(), m_stManagerInfo.iS2SFamily, (int)pChannel->m_pImpl->GetCodecType(), m_pLogger); - if (iErrno != 0) - { - DiscardSocketChannel(pChannel); - return(false); - } - DiscardSocketChannel(pChannel); - return(true); - } - } - if (worker_iter == m_mapWorker.end()) - { - oOutMsgBody.mutable_rsp_result()->set_code(ERR_NO_SUCH_WORKER_INDEX); - oOutMsgBody.mutable_rsp_result()->set_msg("no such worker index!"); - } - } - else - { - oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); - oOutMsgBody.mutable_rsp_result()->set_msg("unknow cmd!"); - LOG4_DEBUG("unknow cmd %d!", oInMsgHead.cmd()); - } - - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); - if (CODEC_STATUS_OK == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - } - else if (CODEC_STATUS_PAUSE == eCodecStatus) - { - AddIoWriteEvent(pChannel); - } - else - { - DiscardSocketChannel(pChannel); - return(false); - } - return(true); -} - -bool Manager::OnBeaconData(std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody) -{ - LOG4_DEBUG("cmd %u, seq %u", oInMsgHead.cmd(), oInMsgHead.seq()); - if (gc_uiCmdReq & oInMsgHead.cmd()) // 新请求,直接转发给Worker,并回复beacon已收到请求 - { - MsgBody oOutMsgBody; - switch (oInMsgHead.cmd()) - { - case CMD_REQ_BEAT: - OnBeat(oInMsgBody, oOutMsgBody); - break; - case CMD_REQ_TELL_WORKER: - OnTellWorker(pChannel, oInMsgBody, oOutMsgBody); - break; - case CMD_REQ_NODE_NOTICE: - OnNodeNotify(oInMsgBody, oOutMsgBody); - break; - case CMD_REQ_SET_NODE_CONFIG: - OnSetNodeConf(oInMsgBody, oOutMsgBody); - break; - case CMD_REQ_GET_NODE_CONFIG: - OnGetNodeConf(oInMsgBody, oOutMsgBody); - break; - case CMD_REQ_SET_NODE_CUSTOM_CONFIG: - OnSetNodeCustomConf(oInMsgBody, oOutMsgBody); - break; - case CMD_REQ_GET_NODE_CUSTOM_CONFIG: - OnGetNodeCustomConf(oInMsgBody, oOutMsgBody); - break; - case CMD_REQ_SET_CUSTOM_CONFIG: - OnSetCustomConf(oInMsgBody, oOutMsgBody); - break; - case CMD_REQ_GET_CUSTOM_CONFIG: - OnGetCustomConf(oInMsgBody, oOutMsgBody); - break; - default: - oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); - oOutMsgBody.mutable_rsp_result()->set_msg("unknow cmd!"); - } - - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); - if (CODEC_STATUS_OK == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - else if (CODEC_STATUS_PAUSE == eCodecStatus) - { - AddIoWriteEvent(pChannel); - return(true); - } - else - { - DiscardSocketChannel(pChannel); - return(false); - } - } - else // 回调 - { - if (CMD_RSP_NODE_REGISTER == oInMsgHead.cmd()) //Manager这层只有向beacon注册会收到回调,上报状态不收回调或者收到回调不必处理 - { - if (ERR_OK == oInMsgBody.rsp_result().code()) - { - CJsonObject oNode(oInMsgBody.data()); - oNode.Get("node_id", m_stManagerInfo.uiNodeId); - SendToWorker(CMD_REQ_REFRESH_NODE_ID, oInMsgHead.seq(), oInMsgBody); - RemoveIoWriteEvent(pChannel); - } - else - { - LOG4_ERROR("register to beacon error %d: %s!", oInMsgBody.rsp_result().code(), oInMsgBody.rsp_result().msg().c_str()); - } - } - else if (CMD_RSP_CONNECT_TO_WORKER == oInMsgHead.cmd()) // 连接beacon时的回调 TODO delete - { - MsgBody oOutMsgBody; - TargetWorker oTargetWorker; - char szWorkerIdentify[64] = {0}; - snprintf(szWorkerIdentify, 64, "%s:%d", m_stManagerInfo.strHostForServer.c_str(), m_stManagerInfo.iPortForServer); - oTargetWorker.set_worker_identify(szWorkerIdentify); - oTargetWorker.set_node_type(m_stManagerInfo.strNodeType); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("OK"); - //oOutMsgBody.set_data(oTargetWorker.SerializeAsString()); - oTargetWorker.SerializeToString(&m_strDataString); - oOutMsgBody.set_data(m_strDataString); - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(CMD_REQ_TELL_WORKER, GetSequence(), oOutMsgBody); - if (CODEC_STATUS_OK == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - } - else if (CODEC_STATUS_PAUSE == eCodecStatus) - { - AddIoWriteEvent(pChannel); - } - else - { - DiscardSocketChannel(pChannel); - return(false); - } - return(true); - } - else if (CMD_RSP_TELL_WORKER == oInMsgHead.cmd()) // 连接beacon时的回调 - { - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(); - if (CODEC_STATUS_OK == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - } - else if (CODEC_STATUS_PAUSE == eCodecStatus) - { - AddIoWriteEvent(pChannel); - } - else - { - DiscardSocketChannel(pChannel); - return(false); - } - return(true); - } - } - return(true); -} - -bool Manager::OnBeat(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) -{ - oOutMsgBody = oInMsgBody; - return(true); -} - -bool Manager::OnTellWorker(std::shared_ptr pChannel, const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) -{ - TargetWorker oInTargetWorker; - TargetWorker oOutTargetWorker; - if (oInTargetWorker.ParseFromString(oInMsgBody.data())) - { - LOG4_DEBUG("AddNodeIdentify(%s, %s)!", oInTargetWorker.node_type().c_str(), - oInTargetWorker.worker_identify().c_str()); - AddNamedSocketChannel(oInTargetWorker.worker_identify(), pChannel); - AddNodeIdentify(oInTargetWorker.node_type(), oInTargetWorker.worker_identify()); - oOutTargetWorker.set_worker_identify(GetNodeIdentify()); - oOutTargetWorker.set_node_type(m_stManagerInfo.strNodeType); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("OK"); - //oOutMsgBody.set_data(oOutTargetWorker.SerializeAsString()); - oOutTargetWorker.SerializeToString(&m_strDataString); - oOutMsgBody.set_data(m_strDataString); - return(true); - } - else - { - LOG4_ERROR("error %d: WorkerLoad ParseFromString error!", ERR_PARASE_PROTOBUF); - oOutTargetWorker.set_worker_identify("unknow"); - oOutTargetWorker.set_node_type(m_stManagerInfo.strNodeType); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); - oOutMsgBody.mutable_rsp_result()->set_msg("WorkerLoad ParseFromString error!"); - return(false); - } -} - -bool Manager::OnNodeNotify(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) -{ - CJsonObject oJson; - if (!oJson.Parse(oInMsgBody.data())) - { - LOG4_ERROR("failed to parse msgbody content!"); - return(false); - } - SendToWorker(CMD_REQ_NODE_NOTICE, GetSequence(), oInMsgBody); - std::ostringstream oss; - std::string strIdentify; - std::string strNodeType; - std::string strNodeHost; - int iNodePort = 0; - int iWorkerNum = 0; - for (int i = 0; i < oJson["add_nodes"].GetArraySize(); ++i) - { - if (oJson["add_nodes"][i].Get("node_type",strNodeType) - && oJson["add_nodes"][i].Get("node_ip",strNodeHost) - && oJson["add_nodes"][i].Get("node_port",iNodePort) - && oJson["add_nodes"][i].Get("worker_num",iWorkerNum)) - { - oss << strNodeHost << ":" << iNodePort; - strIdentify = std::move(oss.str()); - auto n_iter = m_mapOnlineNodes.find(strIdentify); - if (n_iter == m_mapOnlineNodes.end()) - { - m_mapOnlineNodes.insert( - std::make_pair(strIdentify, oJson["add_nodes"][i].ToString())); - } - else - { - n_iter->second = oJson["add_nodes"][i].ToString(); - } - if (std::string("LOGGER") == strNodeType) - { - for(int j = 1; j <= iWorkerNum; ++j) - { - oss << strNodeHost << ":" << iNodePort << "." << j; - strIdentify = std::move(oss.str()); - std::shared_ptr pChannel = std::make_shared(m_pLogger, 0, 0); - AddNodeIdentify(strNodeType, strIdentify); - LOG4_DEBUG("AddNodeIdentify(%s,%s)", strNodeType.c_str(), strIdentify.c_str()); - } - } - } - } - - for (int i = 0; i < oJson["del_nodes"].GetArraySize(); ++i) - { - if (oJson["del_nodes"][i].Get("node_type",strNodeType) - && oJson["del_nodes"][i].Get("node_ip",strNodeHost) - && oJson["del_nodes"][i].Get("node_port",iNodePort) - && oJson["del_nodes"][i].Get("worker_num",iWorkerNum)) - { - oss << strNodeHost << ":" << iNodePort; - strIdentify = std::move(oss.str()); - auto n_iter = m_mapOnlineNodes.find(strIdentify); - if (n_iter != m_mapOnlineNodes.end()) - { - m_mapOnlineNodes.erase(n_iter); - } - if (std::string("LOGGER") == strNodeType) - { - for(int j = 1; j <= iWorkerNum; ++j) - { - oss << strNodeHost << ":" << iNodePort << "." << j; - strIdentify = std::move(oss.str()); - if (std::string("LOGGER") == strNodeType) - { - DelNodeIdentify(strNodeType, strIdentify); - LOG4_DEBUG("DelNodeIdentify(%s,%s)", strNodeType.c_str(), strIdentify.c_str()); - } - } - } - } - } + m_pPeriodicTaskWatcher->data = (void*)this; + m_pDispatcher->AddEvent(m_pPeriodicTaskWatcher, Dispatcher::PeriodicTaskCallback, NODE_BEAT); + std::shared_ptr pReportToBeacon = m_pActorBuilder->MakeSharedStep( + nullptr, "neb::StepReportToBeacon", NODE_BEAT); return(true); } -bool Manager::OnSetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +const CJsonObject& Manager::GetNodeConf() const { - ConfigInfo oConfigInfo; - if (oConfigInfo.ParseFromString(oInMsgBody.data())) - { - CJsonObject oJsonData; - if (oJsonData.Parse(oConfigInfo.file_content())) - { - // some data can not be set by beacon. - oJsonData.Replace("node_type", m_oCurrentConf("node_type")); - oJsonData.Replace("access_host", m_oCurrentConf("access_host")); - oJsonData.Replace("access_port", m_oCurrentConf("access_port")); - oJsonData.Replace("access_codec", m_oCurrentConf("access_codec")); - oJsonData.Replace("host", m_oCurrentConf("host")); - oJsonData.Replace("port", m_oCurrentConf("port")); - oJsonData.Replace("server_name", m_oCurrentConf("server_name")); - oJsonData.Replace("worker_num", m_oCurrentConf("worker_num")); - std::ofstream fout(m_stManagerInfo.strConfFile.c_str()); - if (fout.good()) - { - std::string strNewConfData = oJsonData.ToFormattedString(); - fout.write(strNewConfData.c_str(), strNewConfData.size()); - fout.close(); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("success"); - SendToWorker(CMD_REQ_SET_NODE_CONFIG, GetSequence(), oInMsgBody); - return(true); - } - return(false); - } - else - { - oOutMsgBody.mutable_rsp_result()->set_code(ERR_BODY_JSON); - oOutMsgBody.mutable_rsp_result()->set_msg("the server config must be in json format!"); - return(false); - } - } - else - { - oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); - oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); - return(false); - } + return(m_oCurrentConf); } -bool Manager::OnGetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +void Manager::SetNodeConf(const CJsonObject& oNodeConf) { - ConfigInfo oConfigInfo; - oConfigInfo.set_file_name(m_stManagerInfo.strConfFile); - oConfigInfo.set_file_content(m_oCurrentConf.ToFormattedString()); - //oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); - oConfigInfo.SerializeToString(&m_strDataString); - oOutMsgBody.set_data(m_strDataString); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("success"); - return(true); + m_oCurrentConf = oNodeConf; } -bool Manager::OnSetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +const NodeInfo& Manager::GetNodeInfo() const { - ConfigInfo oConfigInfo; - if (oConfigInfo.ParseFromString(oInMsgBody.data())) - { - CJsonObject oJsonData; - if (oJsonData.Parse(oConfigInfo.file_content())) - { - m_oCurrentConf.Replace("custom", oJsonData); - std::ofstream fout(m_stManagerInfo.strConfFile.c_str()); - if (fout.good()) - { - std::string strNewConfData = m_oCurrentConf.ToFormattedString(); - fout.write(strNewConfData.c_str(), strNewConfData.size()); - fout.close(); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("success"); - SendToWorker(CMD_REQ_SET_NODE_CUSTOM_CONFIG, GetSequence(), oInMsgBody); - return(true); - } - return(false); - } - else - { - oOutMsgBody.mutable_rsp_result()->set_code(ERR_BODY_JSON); - oOutMsgBody.mutable_rsp_result()->set_msg("the server custom config must be in json format!"); - return(false); - } - } - else - { - oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); - oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); - return(false); - } + return(m_stNodeInfo); } -bool Manager::OnGetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +void Manager::SetNodeId(uint32 uiNodeId) { - ConfigInfo oConfigInfo; - oConfigInfo.set_file_name(m_stManagerInfo.strConfFile); - oConfigInfo.set_file_content(m_oCurrentConf["custom"].ToFormattedString()); - //oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); - oConfigInfo.SerializeToString(&m_strDataString); - oOutMsgBody.set_data(m_strDataString); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("success"); - return(true); + m_stNodeInfo.uiNodeId = uiNodeId; } -bool Manager::OnSetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) +const Manager::tagManagerInfo& Manager::GetManagerInfo() const { - ConfigInfo oConfigInfo; - if (oConfigInfo.ParseFromString(oInMsgBody.data())) - { - std::stringstream ssConfFile; - if (oConfigInfo.file_path().size() > 0) - { - ssConfFile << m_stManagerInfo.strWorkPath - << "/conf/" << oConfigInfo.file_path() - << "/" << oConfigInfo.file_name(); - } - else - { - ssConfFile << m_stManagerInfo.strWorkPath - << "/conf/" << oConfigInfo.file_name(); - } - - std::ofstream fout(ssConfFile.str().c_str()); - if (fout.good()) - { - fout.write(oConfigInfo.file_content().c_str(), oConfigInfo.file_content().size()); - fout.close(); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("success"); - SendToWorker(CMD_REQ_SET_CUSTOM_CONFIG, GetSequence(), oInMsgBody); - SendToWorker(CMD_REQ_RELOAD_CUSTOM_CONFIG, GetSequence(), oInMsgBody); - return(true); - } - else - { - oOutMsgBody.mutable_rsp_result()->set_code(ERR_FILE_NOT_EXIST); - oOutMsgBody.mutable_rsp_result()->set_msg("file \"" + ssConfFile.str() + "\" not exist!"); - return(false); - } - } - else - { - oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); - oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); - return(false); - } -} - -bool Manager::OnGetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody) -{ - ConfigInfo oConfigInfo; - if (oConfigInfo.ParseFromString(oInMsgBody.data())) - { - std::stringstream ssConfFile; - if (oConfigInfo.file_path().size() > 0) - { - ssConfFile << m_stManagerInfo.strWorkPath - << "/conf/" << oConfigInfo.file_path() - << "/" << oConfigInfo.file_name(); - } - else - { - ssConfFile << m_stManagerInfo.strWorkPath - << "/conf/" << oConfigInfo.file_name(); - } - - std::ifstream fin(ssConfFile.str().c_str()); - if (fin.good()) - { - std::stringstream ssContent; - ssContent << fin.rdbuf(); - oConfigInfo.set_file_content(ssContent.str()); - //oOutMsgBody.set_data(oConfigInfo.SerializeAsString()); - oConfigInfo.SerializeToString(&m_strDataString); - oOutMsgBody.set_data(m_strDataString); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("success"); - fin.close(); - return(true); - } - else - { - oOutMsgBody.mutable_rsp_result()->set_code(ERR_FILE_NOT_EXIST); - oOutMsgBody.mutable_rsp_result()->set_msg("file \"" + ssConfFile.str() + "\" not exist!"); - return(false); - } - } - else - { - oOutMsgBody.mutable_rsp_result()->set_code(ERR_PARASE_PROTOBUF); - oOutMsgBody.mutable_rsp_result()->set_msg("ConfigInfo.ParseFromString() failed."); - return(false); - } + return(m_stManagerInfo); } } /* namespace neb */ diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index db9cfdb9..8e8874fc 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -1,43 +1,14 @@ /******************************************************************************* * Project: Nebula * @file Manager.hpp - * @brief 管理者 + * @brief 管理进程 * @author Bwar * @date: 2016年8月13日 * @note * Modify history: ******************************************************************************/ -#ifndef SRC_LABOR_CHIEF_MANAGER_HPP_ -#define SRC_LABOR_CHIEF_MANAGER_HPP_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif -#include "ev.h" -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -#ifdef __cplusplus -} -#endif +#ifndef SRC_LABOR_MANAGER_HPP_ +#define SRC_LABOR_MANAGER_HPP_ #include #include @@ -49,287 +20,115 @@ extern "C" { #include #include "util/json/CJsonObject.hpp" +#include "logger/NetLogger.hpp" #include "util/CBuffer.hpp" #include "pb/msg.pb.h" #include "pb/neb_sys.pb.h" #include "Error.hpp" #include "Definition.hpp" -#include "logger/NetLogger.hpp" +#include "NodeInfo.hpp" #include "Labor.hpp" #include "channel/Channel.hpp" -#include "actor/cmd/Cmd.hpp" -#include "actor/session/sys_session/SessionNode.hpp" -#include "actor/session/sys_session/SessionManagerLogger.hpp" namespace neb { class Worker; -class Actor; +class Dispatcher; +class ActorBuilder; +class Step; +class SessionManager; class Manager: public Labor { public: - struct tagClientConnWatcherData - { - char* pAddr; - Labor* pLabor; // 不在结构体析构时回收 - - tagClientConnWatcherData() : pAddr(NULL), pLabor(NULL) - { - pAddr = (char*)malloc(gc_iAddrLen); - } - - ~tagClientConnWatcherData() - { - free(pAddr); - pAddr = NULL; - } - }; - struct tagManagerInfo { - E_CODEC_TYPE eCodec; ///< 接入端编解码器 - uint32 uiNodeId; ///< 节点ID(由center分配) - int32 iPortForServer; ///< Server间通信监听端口,对应 iS2SListenFd - int32 iPortForClient; ///< 对Client通信监听端口,对应 iC2SListenFd - int32 iGatewayPort; ///< 对Client服务的真实端口 - uint32 uiWorkerNum; ///< Worker子进程数量 - int32 iAddrPermitNum; ///< IP地址统计时间内允许连接次数 - int iWorkerBeat; ///< worker进程心跳,若大于此心跳未收到worker进程上报,则重启worker进程 - int iS2SListenFd; ///< Server to Server监听文件描述符(Server与Server之间的连接较少,但每个Server的每个Worker均与其他Server的每个Worker相连) - int iS2SFamily; ///< - int iC2SListenFd; ///< Client to Server监听文件描述符(Client与Server之间的连接较多,但每个Client只需连接某个Server的某个Worker) - int iC2SFamily; ///< - ev_tstamp dIoTimeout; ///< IO超时配置 - ev_tstamp dAddrStatInterval; ///< IP地址数据统计时间间隔 - std::string strWorkPath; ///< 工作路径 - std::string strConfFile; ///< 配置文件 - std::string strNodeType; ///< 节点类型 - std::string strHostForServer; ///< 对其他Server服务的IP地址,对应 m_iS2SListenFd - std::string strHostForClient; ///< 对Client服务的IP地址,对应 m_iC2SListenFd - std::string strGateway; ///< 对Client服务的真实IP地址(此ip转发给m_strHostForClient) - std::string strNodeIdentify; - - tagManagerInfo() - : eCodec(CODEC_HTTP), uiNodeId(0), iPortForServer(0), iPortForClient(0), iGatewayPort(0), - uiWorkerNum(0), iAddrPermitNum(0), - iWorkerBeat(7), iS2SListenFd(-1), iC2SListenFd(-1), dIoTimeout(300.0), dAddrStatInterval(60.0) - { - } - }; - - /** - * @brief 工作进程属性 - */ - struct tagWorkerAttr - { - int iWorkerIndex; ///< 工作进程序号 - int iControlFd; ///< 与Manager进程通信的文件描述符(控制流) - int iDataFd; ///< 与Manager进程通信的文件描述符(数据流) - int32 iLoad; ///< 负载 - int32 iConnect; ///< 连接数量 - int32 iRecvNum; ///< 接收数据包数量 - int32 iRecvByte; ///< 接收字节数 - int32 iSendNum; ///< 发送数据包数量 - int32 iSendByte; ///< 发送字节数 - int32 iClientNum; ///< 客户端数量 - ev_tstamp dBeatTime; ///< 心跳时间 - - tagWorkerAttr() - { - iWorkerIndex = 0; - iControlFd = -1; - iDataFd = -1; - iLoad = 0; - iConnect = 0; - iRecvNum = 0; - iRecvByte = 0; - iSendNum = 0; - iSendByte = 0; - iClientNum = 0; - dBeatTime = 0.0; - } - - tagWorkerAttr(const tagWorkerAttr& stAttr) - { - iWorkerIndex = stAttr.iWorkerIndex; - iControlFd = stAttr.iControlFd; - iDataFd = stAttr.iDataFd; - iLoad = stAttr.iLoad; - iConnect = stAttr.iConnect; - iRecvNum = stAttr.iRecvNum; - iRecvByte = stAttr.iRecvByte; - iSendNum = stAttr.iSendNum; - iSendByte = stAttr.iSendByte; - iClientNum = stAttr.iClientNum; - dBeatTime = stAttr.dBeatTime; - } - - tagWorkerAttr& operator=(const tagWorkerAttr& stAttr) - { - iWorkerIndex = stAttr.iWorkerIndex; - iControlFd = stAttr.iControlFd; - iDataFd = stAttr.iDataFd; - iLoad = stAttr.iLoad; - iConnect = stAttr.iConnect; - iRecvNum = stAttr.iRecvNum; - iRecvByte = stAttr.iRecvByte; - iSendNum = stAttr.iSendNum; - iSendByte = stAttr.iSendByte; - iClientNum = stAttr.iClientNum; - dBeatTime = stAttr.dBeatTime; - return(*this); - } + int iWorkerBeat = 7; ///< worker进程心跳,若大于此心跳未收到worker进程上报,则重启worker进程 + int iS2SListenFd = -1; ///< Server to Server监听文件描述符(Server与Server之间的连接较少,但每个Server的每个Worker均与其他Server的每个Worker相连) + int iS2SFamily = 0; ///< + int iC2SListenFd = -1; ///< Client to Server监听文件描述符(Client与Server之间的连接较多,但每个Client只需连接某个Server的某个Worker) + int iC2SFamily = 0; ///< }; public: Manager(const std::string& strConfFile); virtual ~Manager(); - static void SignalCallback(struct ev_loop* loop, struct ev_signal* watcher, int revents); - static void IdleCallback(struct ev_loop* loop, struct ev_idle* watcher, int revents); - static void IoCallback(struct ev_loop* loop, struct ev_io* watcher, int revents); - static void IoTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); - static void PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, int revents); - static void ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); - bool OnManagerTerminated(struct ev_signal* watcher); - bool OnChildTerminated(struct ev_signal* watcher); - bool OnIoRead(std::shared_ptr pChannel); - bool OnIoWrite(std::shared_ptr pChannel); - bool OnIoError(std::shared_ptr pChannel); - bool OnIoTimeout(std::shared_ptr pChannel); - bool OnClientConnFrequencyTimeout(tagClientConnWatcherData* pData, ev_timer* watcher); - void Run(); + void OnTerminated(struct ev_signal* watcher); + void OnChildTerminated(struct ev_signal* watcher); template - void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); + void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); public: - bool InitLogger(const CJsonObject& oJsonConf); - virtual bool SetProcessName(const CJsonObject& oJsonConf); - virtual bool AddNetLogMsg(const MsgBody& oMsgBody); - virtual bool SendTo(std::shared_ptr pChannel); - virtual bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - virtual void SetNodeId(uint32 uiNodeId) {m_stManagerInfo.uiNodeId = uiNodeId;} - uint32 GetSequence() const + virtual uint32 GetSequence() const { return(m_uiSequence++); } - virtual uint32 GetNodeId() const - { - return(m_stManagerInfo.uiNodeId); - } - - virtual time_t GetNowTime() const + virtual Dispatcher* GetDispatcher() { - return((time_t)ev_now(m_loop)); + return(m_pDispatcher); } - virtual const std::string& GetNodeIdentify() const + virtual ActorBuilder* GetActorBuilder() { - return(m_stManagerInfo.strNodeIdentify); + return(m_pActorBuilder); } - virtual const std::string& GetNodeType() const + std::shared_ptr GetSessionManager() { - return(m_stManagerInfo.strNodeType); + return m_pSessionManager; } - virtual void AddInnerChannel(std::shared_ptr pChannel){}; + virtual time_t GetNowTime() const; + virtual const CJsonObject& GetNodeConf() const; + virtual void SetNodeConf(const CJsonObject& oNodeConf); + virtual const NodeInfo& GetNodeInfo() const; + virtual void SetNodeId(uint32 uiNodeId); + const tagManagerInfo& GetManagerInfo() const; - void SetWorkerLoad(int iPid, CJsonObject& oJsonLoad); - void AddWorkerLoad(int iPid, int iLoad = 1); + virtual bool AddNetLogMsg(const MsgBody& oMsgBody); + void RefreshServer(); protected: bool GetConf(); bool Init(); + bool InitLogger(const CJsonObject& oJsonConf); + bool InitDispatcher(); + bool InitActorBuilder(); void Destroy(); bool CreateEvents(); - bool AddPeriodicTaskEvent(); - bool AddIoReadEvent(std::shared_ptr pChannel); - bool AddIoWriteEvent(std::shared_ptr pChannel); - bool RemoveIoWriteEvent(std::shared_ptr pChannel); - bool DelEvents(ev_io* io_watcher); - bool DelEvents(ev_timer* timer_watcher); - void CreateWorker(); - bool CheckWorker(); bool RestartWorker(int iDeathPid); - std::pair GetMinLoadWorkerDataFd(); - bool SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); // 向Worker发送数据 - void RefreshServer(); - - bool RegisterToBeacon(); - bool ReportToBeacon(); - - bool AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel); - void DelNamedSocketChannel(const std::string& strIdentify); - void AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); - void DelNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); - bool AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout = 1.0); - bool AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout = 60.0); - std::shared_ptr CreateChannel(int iFd, E_CODEC_TYPE eCodecType); - bool DiscardSocketChannel(std::shared_ptr pChannel); - bool FdTransfer(int iFd, int iFamily = AF_INET); - bool AcceptServerConn(int iFd); - bool DataRecvAndHandle(std::shared_ptr pChannel); - bool OnWorkerData(std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody); - bool OnDataAndTransferFd(std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody); - bool OnBeaconData(std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody); - bool OnBeat(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnTellWorker(std::shared_ptr pChannel, const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnNodeNotify(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnSetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnGetNodeConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnSetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnGetNodeCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnSetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); - bool OnGetCustomConf(const MsgBody& oInMsgBody, MsgBody& oOutMsgBody); + bool AddPeriodicTaskEvent(); private: - mutable uint32 m_uiSequence; + char* m_pErrBuff = NULL; + mutable uint32 m_uiSequence = 0; + Dispatcher* m_pDispatcher = nullptr; + ActorBuilder* m_pActorBuilder = nullptr; + CJsonObject m_oLastConf; ///< 上次加载的配置 CJsonObject m_oCurrentConf; ///< 当前加载的配置 - std::string m_strDataString; - - char m_szErrBuff[256]; - std::shared_ptr m_pLogger = nullptr; - struct ev_loop* m_loop; - ev_timer* m_pPeriodicTaskWatcher; ///< 周期任务定时器 - + NodeInfo m_stNodeInfo; tagManagerInfo m_stManagerInfo; - std::unique_ptr m_pSessionNode = nullptr; - std::unique_ptr m_pSessionLogger = nullptr; - - std::unordered_map m_mapWorker; ///< 业务逻辑工作进程及进程属性,key为pid - std::unordered_map m_mapWorkerRestartNum; ///< 进程被重启次数,key为WorkerIdx - std::unordered_map m_mapWorkerFdPid; ///< 工作进程通信FD对应的进程号 - std::unordered_map m_mapOnlineNodes; ///< 订阅的节点在线信息 - - std::unordered_map > m_mapNamedSocketChannel; - std::unordered_map > m_mapSocketChannel; ///< 通信通道 - std::unordered_map m_mapClientConnFrequency; ///< 客户端连接频率 - - std::vector m_vecFreeWorkerIdx; ///< 空闲进程编号 + ev_timer* m_pPeriodicTaskWatcher = NULL; ///< 进程周期任务定时器 + std::shared_ptr m_pLogger = nullptr; + std::shared_ptr m_pSessionManager = nullptr; + std::shared_ptr m_pReportStep = nullptr; }; template -void Manager::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) +void Manager::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } } /* namespace neb */ -#endif /* SRC_LABOR_CHIEF_MANAGER_HPP_ */ +#endif /* SRC_LABOR_MANAGER_HPP_ */ diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp new file mode 100644 index 00000000..2928d1db --- /dev/null +++ b/src/labor/NodeInfo.hpp @@ -0,0 +1,73 @@ +/******************************************************************************* + * Project: Nebula + * @file NodeInfo.hpp + * @brief + * @author Bwar + * @date: 2019年9月1日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_LABOR_NODEINFO_HPP_ +#define SRC_LABOR_NODEINFO_HPP_ + +#include +#include "Definition.hpp" +#include "codec/Codec.hpp" + +namespace neb +{ + +struct NodeInfo +{ + NodeInfo(){} + NodeInfo(const NodeInfo& stAttr) = delete; + NodeInfo& operator=(const NodeInfo& stAttr) = delete; + E_CODEC_TYPE eCodec = CODEC_UNKNOW; ///< 接入端编解码器 + uint32 uiNodeId = 0; ///< 节点ID(由beacon分配) + uint32 uiWorkerNum = 0; ///< Worker子进程数量 + int32 iAddrPermitNum = 0; ///< IP地址统计时间内允许连接次数 + int32 iMsgPermitNum = 0; ///< 客户端统计时间内允许发送消息数量 + int32 iPortForServer = 0; ///< Server间通信监听端口,对应 iS2SListenFd + int32 iPortForClient = 0; ///< 对Client通信监听端口,对应 iC2SListenFd + int32 iGatewayPort = 0; ///< 对Client服务的真实端口 + bool bIsAccess = false; ///< 是否接入Server + ev_tstamp dMsgStatInterval = 0.0; ///< 客户端连接发送数据包统计时间间隔 + ev_tstamp dAddrStatInterval = 0.0; ///< IP地址数据统计时间间隔 + ev_tstamp dIoTimeout = 0.0; ///< IO(连接)超时配置 + ev_tstamp dStepTimeout = 0.0; ///< 步骤超时 + std::string strWorkPath; ///< 工作路径 + std::string strConfFile; ///< 配置文件 + std::string strNodeType; ///< 节点类型 + std::string strHostForServer; ///< 对其他Server服务的IP地址,对应 m_iS2SListenFd + std::string strHostForClient; ///< 对Client服务的IP地址,对应 m_iC2SListenFd + std::string strGateway; ///< 对Client服务的真实IP地址(此ip转发给m_strHostForClient) + std::string strNodeIdentify; +}; + +/** + * @brief 工作进程属性 + */ +struct WorkerInfo +{ + int32 iWorkerPid = 0; + int iWorkerIndex = 0; ///< 工作进程序号 + int iControlFd = -1; ///< 与Manager进程通信的文件描述符(控制流) + int iDataFd = -1; ///< 与Manager进程通信的文件描述符(数据流) + int32 iLoad = 0; ///< 负载 + int32 iConnect = 0; ///< 连接数量 + int32 iRecvNum = 0; ///< 接收数据包数量 + int32 iRecvByte = 0; ///< 接收字节数 + int32 iSendNum = 0; ///< 发送数据包数量 + int32 iSendByte = 0; ///< 发送字节数 + int32 iClientNum = 0; ///< 客户端数量 + ev_tstamp dBeatTime = 0.0; ///< 心跳时间 + + WorkerInfo(){} + WorkerInfo(const WorkerInfo& stAttr) = delete; + WorkerInfo& operator=(const WorkerInfo& stAttr) = delete; +}; + +} /* namespace neb */ + +#endif /* SRC_LABOR_NODEINFO_HPP_ */ + diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index e1358d96..fceda171 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -7,181 +7,373 @@ * @note * Modify history: ******************************************************************************/ -#include "labor/Worker.hpp" -#include "labor/WorkerImpl.hpp" +#include +#include +#ifdef __cplusplus +extern "C" { +#endif +#include "util/process_helper.h" +#include "util/proctitle_helper.h" +#ifdef __cplusplus +} +#endif +#include "Worker.hpp" +#include "ios/Dispatcher.hpp" +#include "actor/ActorBuilder.hpp" namespace neb { -/*****************************************************************************/ -/*****************************************************************************/ -/*****************************************************************************/ - Worker::Worker(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex, CJsonObject& oJsonConf) - : m_pImpl(nullptr) + : Labor(LABOR_WORKER) { - // C++14: m_Impl = std::make_unique(strWorkPath, iControlFd, iDataFd, iWorkerIndex, oJsonConf); - m_pImpl = new WorkerImpl(this, strWorkPath, iControlFd, iDataFd, iWorkerIndex, oJsonConf); + m_stWorkerInfo.iControlFd = iControlFd; + m_stWorkerInfo.iDataFd = iDataFd; + m_stWorkerInfo.iWorkerIndex = iWorkerIndex; + m_stWorkerInfo.iWorkerPid = getpid(); + m_stNodeInfo.strWorkPath = strWorkPath; + m_pErrBuff = (char*)malloc(gc_iErrBuffLen); + if (!Init(oJsonConf)) + { + exit(3); + } + m_oNodeConf = oJsonConf; } Worker::~Worker() { - DELETE(m_pImpl); + Destroy(); } void Worker::Run() { - m_pImpl->Run(); -} - -uint32 Worker::GetSequence() const -{ - return(m_pImpl->GetSequence()); -} - -std::shared_ptr Worker::GetSession(uint64 ullSessionId) -{ - return(m_pImpl->GetSession(ullSessionId)); -} - -std::shared_ptr Worker::GetSession(const std::string& strSessionId) -{ - return(m_pImpl->GetSession(strSessionId)); -} - -bool Worker::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg, void* data) -{ - return(m_pImpl->ExecStep(uiStepSeq, iErrno, strErrMsg, data)); -} - -std::shared_ptr Worker::GetModel(const std::string& strModelName) -{ - return(m_pImpl->GetModel(strModelName)); -} - -void Worker::AddAssemblyLine(std::shared_ptr pSession) -{ - m_pImpl->AddAssemblyLine(pSession); -} - -uint32 Worker::GetNodeId() const -{ - return(m_pImpl->GetWorkerInfo().uiNodeId); -} - -int Worker::GetWorkerIndex() const -{ - return(m_pImpl->GetWorkerInfo().iWorkerIndex); -} - -ev_tstamp Worker::GetDefaultTimeout() const -{ - return((m_pImpl->GetWorkerInfo().dStepTimeout)); -} - -int Worker::GetClientBeatTime() const -{ - return((int)(m_pImpl->GetWorkerInfo().dIoTimeout)); -} - -const std::string& Worker::GetNodeType() const -{ - return(m_pImpl->GetWorkerInfo().strNodeType); -} - -int Worker::GetPortForServer() const -{ - return(m_pImpl->GetWorkerInfo().iPortForServer); -} - -const std::string& Worker::GetHostForServer() const -{ - return(m_pImpl->GetWorkerInfo().strHostForServer); -} - -const std::string& Worker::GetWorkPath() const -{ - return(m_pImpl->GetWorkerInfo().strWorkPath); -} - -const std::string& Worker::GetNodeIdentify() const -{ - return(m_pImpl->GetWorkerInfo().strWorkerIdentify); -} - -const CJsonObject& Worker::GetCustomConf() const -{ - return(m_pImpl->GetCustomConf()); + LOG4_DEBUG("%s:%d", __FILE__, __LINE__); + + if (!CreateEvents()) + { + Destroy(); + exit(-2); + } + + m_pDispatcher->EeventRun(); +} + +void Worker::OnTerminated(struct ev_signal* watcher) +{ + int iSignum = watcher->signum; + delete watcher; + LOG4_FATAL("terminated by signal %d!", iSignum); + m_pDispatcher->EvBreak(); + Destroy(); + exit(iSignum); +} + +bool Worker::CheckParent() +{ + pid_t iParentPid = getppid(); + if (iParentPid == 1) // manager进程已不存在 + { + LOG4_INFO("no manager process exist, worker %d exit.", m_stWorkerInfo.iWorkerIndex); + Destroy(); + exit(0); + } + MsgBody oMsgBody; + CJsonObject oJsonLoad; + m_stWorkerInfo.iConnect = m_pDispatcher->GetConnectionNum(); + m_stWorkerInfo.iClientNum = m_pDispatcher->GetClientNum(); + oJsonLoad.Add("load", int32(m_stWorkerInfo.iConnect + m_pActorBuilder->GetStepNum())); + oJsonLoad.Add("connect", m_stWorkerInfo.iConnect); + oJsonLoad.Add("recv_num", m_stWorkerInfo.iRecvNum); + oJsonLoad.Add("recv_byte", m_stWorkerInfo.iRecvByte); + oJsonLoad.Add("send_num", m_stWorkerInfo.iSendNum); + oJsonLoad.Add("send_byte", m_stWorkerInfo.iSendByte); + oJsonLoad.Add("client", m_stWorkerInfo.iClientNum); + oMsgBody.set_data(oJsonLoad.ToString()); + LOG4_TRACE("%s", oJsonLoad.ToString().c_str()); + m_pDispatcher->SendTo(m_pManagerControlChannel, CMD_REQ_UPDATE_WORKER_LOAD, GetSequence(), oMsgBody); + m_stWorkerInfo.iRecvNum = 0; + m_stWorkerInfo.iRecvByte = 0; + m_stWorkerInfo.iSendNum = 0; + m_stWorkerInfo.iSendByte = 0; + return(true); +} + +bool Worker::Init(CJsonObject& oJsonConf) +{ + char szProcessName[64] = {0}; + snprintf(szProcessName, sizeof(szProcessName), "%s_W%d", oJsonConf("server_name").c_str(), m_stWorkerInfo.iWorkerIndex); + ngx_setproctitle(szProcessName); + oJsonConf.Get("io_timeout", m_stNodeInfo.dIoTimeout); + if (!oJsonConf.Get("step_timeout", m_stNodeInfo.dStepTimeout)) + { + m_stNodeInfo.dStepTimeout = 0.5; + } + oJsonConf.Get("node_type", m_stNodeInfo.strNodeType); + oJsonConf.Get("host", m_stNodeInfo.strHostForServer); + oJsonConf.Get("port", m_stNodeInfo.iPortForServer); + m_oCustomConf = oJsonConf["custom"]; + std::ostringstream oss; + oss << m_stNodeInfo.strHostForServer << ":" << m_stNodeInfo.iPortForServer << "." << m_stWorkerInfo.iWorkerIndex; + m_stNodeInfo.strNodeIdentify = std::move(oss.str()); + + if (oJsonConf("access_host").size() > 0 && oJsonConf("access_port").size() > 0) + { + m_stNodeInfo.bIsAccess = true; + oJsonConf["permission"]["uin_permit"].Get("stat_interval", m_stNodeInfo.dMsgStatInterval); + oJsonConf["permission"]["uin_permit"].Get("permit_num", m_stNodeInfo.iMsgPermitNum); + } + if (!InitLogger(oJsonConf)) + { + return(false); + } + + bool bCpuAffinity = false; + oJsonConf.Get("cpu_affinity", bCpuAffinity); + if (bCpuAffinity) + { +#ifndef __CYGWIN__ + /* get logical cpu number */ + int iCpuNum = sysconf(_SC_NPROCESSORS_CONF);; ///< cpu数量 + cpu_set_t stCpuMask; ///< cpu set + CPU_ZERO(&stCpuMask); + CPU_SET(m_stWorkerInfo.iWorkerIndex % iCpuNum, &stCpuMask); + if (sched_setaffinity(0, sizeof(cpu_set_t), &stCpuMask) == -1) + { + LOG4_WARNING("sched_setaffinity failed."); + } +#endif + } + + if (oJsonConf["with_ssl"]("config_path").length() > 0) + { +#ifdef WITH_OPENSSL + if (ERR_OK != SocketChannelSslImpl::SslInit(m_pLogger)) + { + LOG4_FATAL("SslInit() failed!"); + return(false); + } + if (ERR_OK != SocketChannelSslImpl::SslServerCtxCreate(m_pLogger)) + { + LOG4_FATAL("SslServerCtxCreate() failed!"); + return(false); + } + std::string strCertFile = m_stNodeInfo.strWorkPath + "/" + oJsonConf["with_ssl"]("config_path") + "/" + oJsonConf["with_ssl"]("cert_file"); + std::string strKeyFile = m_stNodeInfo.strWorkPath + "/" + oJsonConf["with_ssl"]("config_path") + "/" + oJsonConf["with_ssl"]("key_file"); + if (ERR_OK != SocketChannelSslImpl::SslServerCertificate(m_pLogger, strCertFile, strKeyFile)) + { + LOG4_FATAL("SslServerCertificate() failed!"); + return(false); + } +#endif + } + + if (!InitDispatcher() || !InitActorBuilder()) + { + return(false); + } + + std::string strChainKey; + while (oJsonConf["runtime"]["chains"].GetKey(strChainKey)) + { + std::queue > queChainBlocks; + for (int i = 0; i < oJsonConf["runtime"]["chains"][strChainKey].GetArraySize(); ++i) + { + std::vector vecBlock; + if (oJsonConf["runtime"]["chains"][strChainKey][i].IsArray()) + { + for (int j = 0; j < oJsonConf["runtime"]["chains"][strChainKey][i].GetArraySize(); ++j) + { + vecBlock.push_back(std::move(oJsonConf["runtime"]["chains"][strChainKey][i](j))); + } + } + else + { + vecBlock.push_back(std::move(oJsonConf["runtime"]["chains"][strChainKey](i))); + } + queChainBlocks.push(std::move(vecBlock)); + } + m_pActorBuilder->AddChainConf(strChainKey, std::move(queChainBlocks)); + } + return(true); +} + +bool Worker::InitLogger(const CJsonObject& oJsonConf) +{ + if (nullptr != m_pLogger) // 已经被初始化过,只修改日志级别 + { + int32 iLogLevel = 0; + int32 iNetLogLevel = 0; + oJsonConf.Get("log_level", iLogLevel); + oJsonConf.Get("net_log_level", iNetLogLevel); + m_pLogger->SetLogLevel(iLogLevel); + m_pLogger->SetNetLogLevel(iNetLogLevel); + return(true); + } + else + { + int32 iMaxLogFileSize = 0; + int32 iMaxLogFileNum = 0; + int32 iLogLevel = 0; + int32 iNetLogLevel = 0; + std::string strLogname = m_stNodeInfo.strWorkPath + std::string("/") + oJsonConf("log_path") + + std::string("/") + getproctitle() + std::string(".log"); + std::string strParttern = "[%D,%d{%q}][%p] [%l] %m%n"; + oJsonConf.Get("max_log_file_size", iMaxLogFileSize); + oJsonConf.Get("max_log_file_num", iMaxLogFileNum); + oJsonConf.Get("net_log_level", iNetLogLevel); + oJsonConf.Get("log_level", iLogLevel); + m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, this); + m_pLogger->SetNetLogLevel(iNetLogLevel); + LOG4_NOTICE("%s program begin...", getproctitle()); + return(true); + } +} + +bool Worker::InitDispatcher() +{ + try + { + m_pDispatcher = new Dispatcher(this, m_pLogger); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("new Dispatcher error: %s", e.what()); + return(false); + } + return(m_pDispatcher->Init()); +} + +bool Worker::InitActorBuilder() +{ + try + { + m_pActorBuilder = new ActorBuilder(this, m_pLogger); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("new ActorBuilder error: %s", e.what()); + return(false); + } + if (!m_pActorBuilder->Init(m_oNodeConf["boot_load"], m_oNodeConf["dynamic_loading"])) + { + LOG4_ERROR("ActorBuilder->Init() failed!"); + return(false); + } + return(true); +} + +bool Worker::CreateEvents() +{ + signal(SIGPIPE, SIG_IGN); + // 注册信号事件 + ev_signal* signal_watcher = (ev_signal*)malloc(sizeof(ev_signal)); + signal_watcher->data = (void*)this; + m_pDispatcher->AddEvent(signal_watcher, Dispatcher::SignalCallback, SIGINT); + AddPeriodicTaskEvent(); + + // 注册网络IO事件 + m_pManagerDataChannel = m_pDispatcher->CreateSocketChannel(m_stWorkerInfo.iDataFd, CODEC_NEBULA); + m_pDispatcher->SetChannelStatus(m_pManagerDataChannel, CHANNEL_STATUS_ESTABLISHED); + m_pManagerControlChannel = m_pDispatcher->CreateSocketChannel(m_stWorkerInfo.iControlFd, CODEC_NEBULA); + m_pDispatcher->SetChannelStatus(m_pManagerControlChannel, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->AddIoReadEvent(m_pManagerDataChannel); + m_pDispatcher->AddIoReadEvent(m_pManagerControlChannel); + return(true); +} + +void Worker::Destroy() +{ + LOG4_TRACE(" "); + +#ifdef WITH_OPENSSL + SocketChannelSslImpl::SslFree(); +#endif + if (m_pDispatcher != nullptr) + { + delete m_pDispatcher; + m_pDispatcher = nullptr; + } + if (m_pActorBuilder != nullptr) + { + delete m_pActorBuilder; + m_pActorBuilder = nullptr; + } + if (m_pErrBuff != nullptr) + { + free(m_pErrBuff); + m_pErrBuff = nullptr; + } +} + +bool Worker::AddPeriodicTaskEvent() +{ + LOG4_TRACE(" "); + ev_timer* timeout_watcher = (ev_timer*)malloc(sizeof(ev_timer)); + if (timeout_watcher == NULL) + { + LOG4_ERROR("malloc timeout_watcher error!"); + return(false); + } + timeout_watcher->data = (void*)this; + m_pDispatcher->AddEvent(timeout_watcher, Dispatcher::PeriodicTaskCallback, NODE_BEAT); + return(true); } time_t Worker::GetNowTime() const { - return(m_pImpl->GetNowTime()); -} - -bool Worker::AddNetLogMsg(const MsgBody& oMsgBody) -{ - return(m_pImpl->AddNetLogMsg(oMsgBody)); -} - -bool Worker::SendTo(std::shared_ptr pChannel) -{ - return(m_pImpl->SendTo(pChannel)); + return(m_pDispatcher->GetNowTime()); } -bool Worker::SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +const CJsonObject& Worker::GetNodeConf() const { - return(m_pImpl->SendTo(pChannel, iCmd, uiSeq, oMsgBody, pSender)); + return(m_oNodeConf); } -bool Worker::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +void Worker::SetNodeConf(const CJsonObject& oJsonConf) { - return(m_pImpl->SendTo(strIdentify, iCmd, uiSeq, oMsgBody, pSender)); + m_oNodeConf = oJsonConf; } -bool Worker::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +const NodeInfo& Worker::GetNodeInfo() const { - return(m_pImpl->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); + return(m_stNodeInfo); } -bool Worker::SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +void Worker::SetNodeId(uint32 uiNodeId) { - return(m_pImpl->SendOriented(strNodeType, uiFactor, iCmd, uiSeq, oMsgBody, pSender)); + m_stNodeInfo.uiNodeId = uiNodeId; } -bool Worker::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Worker::AddNetLogMsg(const MsgBody& oMsgBody) { - return(m_pImpl->SendOriented(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); + // 此函数不能写日志,不然可能会导致写日志函数与此函数无限递归 + m_pActorBuilder->AddNetLogMsg(oMsgBody); + return(true); } -bool Worker::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +const WorkerInfo& Worker::GetWorkerInfo() const { - return(m_pImpl->Broadcast(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); + return(m_stWorkerInfo); } -bool Worker::SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) -{ - return(m_pImpl->SendTo(pChannel, oHttpMsg, uiHttpStepSeq)); -} - -bool Worker::SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) -{ - return(m_pImpl->SendTo(strHost, iPort, strUrlPath, oHttpMsg, uiHttpStepSeq)); -} - -bool Worker::SendTo(std::shared_ptr pChannel, Actor* pSender) +const CJsonObject& Worker::GetCustomConf() const { - return(m_pImpl->SendTo(pChannel, pSender)); + return(m_oCustomConf); } -bool Worker::SendTo(const std::string& strIdentify, Actor* pSender) +bool Worker::SetCustomConf(const CJsonObject& oJsonConf) { - return(m_pImpl->SendTo(strIdentify, pSender)); + m_oCustomConf = oJsonConf; + return(m_oNodeConf.Replace("custom", oJsonConf)); } -bool Worker::SendTo(const std::string& strHost, int iPort, Actor* pSender) +bool Worker::WithSsl() { - return(m_pImpl->SendTo(strHost, iPort, pSender)); + if (m_oNodeConf["with_ssl"]("config_path").length() > 0) + { + return(true); + } + return(false); } } /* namespace neb */ diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index cc06e0e7..fd66b264 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -10,15 +10,35 @@ #ifndef SRC_LABOR_WORKER_HPP_ #define SRC_LABOR_WORKER_HPP_ -#include "Labor.hpp" -#include "WorkerImpl.hpp" +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +} +#endif + +#include + +#include "util/CBuffer.hpp" +#include "labor/Labor.hpp" +#include "channel/SocketChannel.hpp" +#include "channel/RedisChannel.hpp" +#include "codec/Codec.hpp" +#include "logger/NetLogger.hpp" +#include "NodeInfo.hpp" namespace neb { -class Manager; -class WorkerImpl; -class Actor; +class Dispatcher; +class ActorBuilder; class Worker: public Labor { @@ -28,106 +48,78 @@ class Worker: public Labor Worker& operator=(const Worker&) = delete; virtual ~Worker(); - // actor操作相关方法 - template - void Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); - - template - std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args); - - template - std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args); - - template - std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args); + // timeout,worker进程无响应或与Manager通信通道异常,被manager进程终止时返回 + void OnTerminated(struct ev_signal* watcher); + bool CheckParent(); - template - std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args); + void Run(); - template - std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args); - - virtual uint32 GetSequence() const; - virtual std::shared_ptr GetSession(uint64 ullSessionId); - virtual std::shared_ptr GetSession(const std::string& strSessionId); - virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); - virtual std::shared_ptr GetModel(const std::string& strModelName); - virtual void AddAssemblyLine(std::shared_ptr pSession); - - // 获取worker信息相关方法 - virtual uint32 GetNodeId() const; - virtual int GetWorkerIndex() const; - virtual ev_tstamp GetDefaultTimeout() const; - virtual int GetClientBeatTime() const; - virtual const std::string& GetNodeType() const; - virtual int GetPortForServer() const; - virtual const std::string& GetHostForServer() const; - virtual const std::string& GetWorkPath() const; - virtual const std::string& GetNodeIdentify() const; - virtual const CJsonObject& GetCustomConf() const; +public: // about worker + virtual Dispatcher* GetDispatcher() + { + return(m_pDispatcher); + } + + virtual ActorBuilder* GetActorBuilder() + { + return(m_pActorBuilder); + } + + virtual uint32 GetSequence() const + { + ++m_ulSequence; + if (0 == m_ulSequence) + { + ++m_ulSequence; + } + return(m_ulSequence); + } virtual time_t GetNowTime() const; - - // 网络IO相关方法 + virtual const CJsonObject& GetNodeConf() const; + virtual void SetNodeConf(const CJsonObject& oJsonConf); + virtual const NodeInfo& GetNodeInfo() const; + virtual void SetNodeId(uint32 uiNodeId); virtual bool AddNetLogMsg(const MsgBody& oMsgBody); - virtual bool SendTo(std::shared_ptr pChannel); - virtual bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); - virtual bool SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); - virtual bool SendTo(std::shared_ptr pChannel, Actor* pSender); - virtual bool SendTo(const std::string& strIdentify, Actor* pSender); - virtual bool SendTo(const std::string& strHost, int iPort, Actor* pSender); + const WorkerInfo& GetWorkerInfo() const; + const CJsonObject& GetCustomConf() const; + bool SetCustomConf(const CJsonObject& oJsonConf); + bool WithSsl(); -private: - void Run(); + template + void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); + +protected: + bool Init(CJsonObject& oJsonConf); + bool InitLogger(const CJsonObject& oJsonConf); + bool InitDispatcher(); + bool InitActorBuilder(); + bool CreateEvents(); + void Destroy(); + bool AddPeriodicTaskEvent(); private: - WorkerImpl* m_pImpl; - friend class WorkerImpl; - friend class WorkerFriend; - friend class Manager; + char* m_pErrBuff = NULL; + mutable uint32 m_ulSequence = 0; + Dispatcher* m_pDispatcher = nullptr; + ActorBuilder* m_pActorBuilder = nullptr; + + CJsonObject m_oNodeConf; + CJsonObject m_oCustomConf; ///< 自定义配置 + NodeInfo m_stNodeInfo; + WorkerInfo m_stWorkerInfo; + + std::shared_ptr m_pLogger = nullptr; + std::shared_ptr m_pManagerControlChannel = nullptr; + std::shared_ptr m_pManagerDataChannel = nullptr; }; template -void Worker::Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) +void Worker::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { - m_pImpl->Logger(strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); + m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } -template -std::shared_ptr Worker::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args) -{ - return(m_pImpl->MakeSharedActor(pCreator, strActorName, std::forward(args)...)); -} - -template -std::shared_ptr Worker::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args) -{ - return(m_pImpl->MakeSharedStep(pCreator, strStepName, std::forward(args)...)); -} - -template -std::shared_ptr Worker::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args) -{ - return(m_pImpl->MakeSharedSession(pCreator, strSessionName, std::forward(args)...)); -} - -template -std::shared_ptr Worker::MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args) -{ - return(m_pImpl->MakeSharedContext(pCreator, strContextName, std::forward(args)...)); -} - -template -std::shared_ptr Worker::MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args) -{ - return(m_pImpl->MakeSharedChain(pCreator, strChainName, std::forward(args)...)); -} } /* namespace neb */ diff --git a/src/labor/WorkerFriend.hpp b/src/labor/WorkerFriend.hpp deleted file mode 100644 index a8404ec1..00000000 --- a/src/labor/WorkerFriend.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* -* Project: Nebula -* @file ActorSys.hpp -* @brief -* @author bwar -* @date: Mar 21, 2018 -* @note -* Modify history: -******************************************************************************/ -#ifndef SRC_ACTOR_WORKERFRIEND_HPP_ -#define SRC_ACTOR_WORKERFRIEND_HPP_ - -#include "Worker.hpp" -#include "actor/Actor.hpp" - -namespace neb -{ - -class WorkerFriend -{ -public: - WorkerImpl* GetWorkerImpl(Actor* pActor) - { - return(pActor->m_pWorker->m_pImpl); - } -}; - -} - -#endif /* SRC_ACTOR_WORKERFRIEND_HPP_ */ diff --git a/src/labor/WorkerImpl.cpp b/src/labor/WorkerImpl.cpp deleted file mode 100644 index 308d7490..00000000 --- a/src/labor/WorkerImpl.cpp +++ /dev/null @@ -1,2927 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file WorkerImpl.cpp - * @brief - * @author Bwar - * @date: 2018年3月27日 - * @note - * Modify history: - ******************************************************************************/ - -//#include -#include -#include -#ifdef __cplusplus -extern "C" { -#endif -#include "util/process_helper.h" -#include "util/proctitle_helper.h" -#ifdef __cplusplus -} -#endif -#include "labor/WorkerImpl.hpp" -#include "labor/Worker.hpp" -#include "actor/Actor.hpp" -#include "actor/cmd/Cmd.hpp" -#include "actor/cmd/Module.hpp" -#include "actor/session/Session.hpp" -#include "actor/step/HttpStep.hpp" -#include "actor/step/PbStep.hpp" -#include "actor/step/RedisStep.hpp" -#include "actor/step/Step.hpp" -#include "actor/model/Model.hpp" -#include "actor/chain/Chain.hpp" -#include "actor/session/sys_session/SessionNode.hpp" -#include "actor/session/sys_session/SessionLogger.hpp" - -namespace neb -{ - -void WorkerImpl::TerminatedCallback(struct ev_loop* loop, struct ev_signal* watcher, int revents) -{ - if (watcher->data != NULL) - { - Worker* pWorker = (Worker*)watcher->data; - pWorker->m_pImpl->Terminated(watcher); // timeout,worker进程无响应或与Manager通信通道异常,被manager进程终止时返回 - } -} - -void WorkerImpl::IdleCallback(struct ev_loop* loop, struct ev_idle* watcher, int revents) -{ - if (watcher->data != NULL) - { - Worker* pWorker = (Worker*)watcher->data; - pWorker->m_pImpl->CheckParent(); - } -} - -void WorkerImpl::IoCallback(struct ev_loop* loop, struct ev_io* watcher, int revents) -{ - if (watcher->data != NULL) - { - SocketChannel* pChannel = static_cast(watcher->data); - Worker* pWorker = (Worker*)pChannel->m_pImpl->m_pLabor; - std::shared_ptr pSharedChannel = pChannel->shared_from_this(); - if (revents & EV_READ) - { - pWorker->m_pImpl->OnIoRead(pSharedChannel); - } - if (revents & EV_WRITE && CHANNEL_STATUS_CLOSED != pChannel->m_pImpl->GetChannelStatus()) // the channel maybe closed by OnIoRead() - { - pWorker->m_pImpl->OnIoWrite(pSharedChannel); - } - } -} - -void WorkerImpl::IoTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) -{ - if (watcher->data != NULL) - { - SocketChannel* pChannel = static_cast(watcher->data); - Worker* pWorker = (Worker*)(pChannel->m_pImpl->m_pLabor); - if (pChannel->m_pImpl->GetFd() < 3) // TODO 这个判断是不得已的做法,需查找fd为0回调到这里的原因 - { - return; - } - pWorker->m_pImpl->OnIoTimeout(pChannel->shared_from_this()); - } -} - -void WorkerImpl::PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, int revents) -{ - if (watcher->data != NULL) - { - WorkerImpl* pWorkerImpl = (WorkerImpl*)(watcher->data); - pWorkerImpl->CheckParent(); - } - ev_timer_stop (loop, watcher); - ev_timer_set (watcher, NODE_BEAT + ev_time() - ev_now(loop), 0); - ev_timer_start (loop, watcher); -} - -void WorkerImpl::StepTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) -{ - if (watcher->data != NULL) - { - Step* pStep = (Step*)watcher->data; - ((Worker*)(pStep->m_pWorker))->m_pImpl->OnStepTimeout(std::dynamic_pointer_cast(pStep->shared_from_this())); - } -} - -void WorkerImpl::SessionTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) -{ - if (watcher->data != NULL) - { - Session* pSession = (Session*)watcher->data; - ((Worker*)pSession->m_pWorker)->m_pImpl->OnSessionTimeout(std::dynamic_pointer_cast(pSession->shared_from_this())); - } -} - -void WorkerImpl::ChainTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) -{ - if (watcher->data != NULL) - { - Chain* pChain = (Chain*)watcher->data; - ((Worker*)pChain->m_pWorker)->m_pImpl->OnChainTimeout(std::dynamic_pointer_cast(pChain->shared_from_this())); - } -} - -void WorkerImpl::RedisConnectCallback(const redisAsyncContext *c, int status) -{ - if (c->data != NULL) - { - Worker* pWorker = (Worker*)c->data; - pWorker->m_pImpl->OnRedisConnected(c, status); - } -} - -void WorkerImpl::RedisDisconnectCallback(const redisAsyncContext *c, int status) -{ - if (c->data != NULL) - { - Worker* pWorker = (Worker*)c->data; - pWorker->m_pImpl->OnRedisDisconnected(c, status); - } -} - -void WorkerImpl::RedisCmdCallback(redisAsyncContext *c, void *reply, void *privdata) -{ - if (c->data != NULL) - { - Worker* pWorker = (Worker*)c->data; - pWorker->m_pImpl->OnRedisCmdResult(c, reply, privdata); - } -} - -WorkerImpl::WorkerImpl(Worker* pWorker, const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex, CJsonObject& oJsonConf) - : m_pErrBuff(nullptr), m_pWorker(pWorker), m_loop(NULL), m_pSessionNode(nullptr) -{ - m_stWorkerInfo.iManagerControlFd = iControlFd; - m_stWorkerInfo.iManagerDataFd = iDataFd; - m_stWorkerInfo.iWorkerIndex = iWorkerIndex; - m_stWorkerInfo.iWorkerPid = getpid(); - m_stWorkerInfo.strWorkPath = strWorkPath; - m_pErrBuff = (char*)malloc(gc_iErrBuffLen); - if (!Init(oJsonConf)) - { - exit(-1); - } - m_oWorkerConf = oJsonConf; -} - -WorkerImpl::~WorkerImpl() -{ - Destroy(); -} - -void WorkerImpl::Run() -{ - LOG4_DEBUG("%s:%d", __FILE__, __LINE__); - - if (!CreateEvents()) - { - Destroy(); - exit(-2); - } - LoadSysCmd(); - BootLoadCmd(m_oWorkerConf["boot_load"]); - DynamicLoad(m_oWorkerConf["dynamic_loading"]); - - //mtrace(); - ev_run (m_loop, 0); - //muntrace(); -} - -void WorkerImpl::Terminated(struct ev_signal* watcher) -{ - LOG4_TRACE(" "); - int iSignum = watcher->signum; - delete watcher; - //Destroy(); - LOG4_FATAL("terminated by signal %d!", iSignum); - Destroy(); - exit(iSignum); -} - -bool WorkerImpl::CheckParent() -{ - LOG4_TRACE("m_mapCallbackStep.size() = %u, m_mapCallbackSession.size() = %u", - m_mapCallbackStep.size(), m_mapCallbackSession.size()); - pid_t iParentPid = getppid(); - if (iParentPid == 1) // manager进程已不存在 - { - LOG4_INFO("no manager process exist, worker %d exit.", m_stWorkerInfo.iWorkerIndex); - Destroy(); - exit(0); - } - MsgBody oMsgBody; - CJsonObject oJsonLoad; - oJsonLoad.Add("load", int32(m_stWorkerInfo.iConnectionNum + m_mapCallbackStep.size() + m_mapCallbackSession.size())); - oJsonLoad.Add("connect", m_stWorkerInfo.iConnectionNum); - oJsonLoad.Add("recv_num", m_stWorkerInfo.iRecvNum); - oJsonLoad.Add("recv_byte", m_stWorkerInfo.iRecvByte); - oJsonLoad.Add("send_num", m_stWorkerInfo.iSendNum); - oJsonLoad.Add("send_byte", m_stWorkerInfo.iSendByte); - oJsonLoad.Add("client", m_stWorkerInfo.iClientNum); - oMsgBody.set_data(oJsonLoad.ToString()); - LOG4_TRACE("%s", oJsonLoad.ToString().c_str()); - m_pManagerControlChannel->m_pImpl->Send(CMD_REQ_UPDATE_WORKER_LOAD, GetSequence(), oMsgBody); - m_stWorkerInfo.iRecvNum = 0; - m_stWorkerInfo.iRecvByte = 0; - m_stWorkerInfo.iSendNum = 0; - m_stWorkerInfo.iSendByte = 0; - return(true); -} - -bool WorkerImpl::OnIoRead(std::shared_ptr pChannel) -{ - LOG4_TRACE("fd[%d]", pChannel->m_pImpl->GetFd()); - if (pChannel->m_pImpl->GetFd() == m_stWorkerInfo.iManagerDataFd) - { - return(FdTransfer()); - } - else - { - return(RecvDataAndHandle(pChannel)); - } -} - -bool WorkerImpl::RecvDataAndHandle(std::shared_ptr pChannel) -{ - LOG4_TRACE(" "); - E_CODEC_STATUS eCodecStatus; - if (CODEC_HTTP == pChannel->m_pImpl->GetCodecType()) - { - for (int i = 0; ; ++i) - { - HttpMsg oHttpMsg; - if (0 == i) - { - eCodecStatus = pChannel->m_pImpl->Recv(oHttpMsg); - } - else - { - eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); - } - - if (CODEC_STATUS_OK == eCodecStatus) - { - Handle(pChannel, oHttpMsg); - } - else if (CODEC_STATUS_WANT_WRITE == eCodecStatus) - { - return(SendTo(pChannel)); - } - else - { - break; - } - } - } - else - { - MsgHead oMsgHead; - MsgBody oMsgBody; - eCodecStatus = pChannel->m_pImpl->Recv(oMsgHead, oMsgBody); - while (CODEC_STATUS_OK == eCodecStatus) - { - if (m_stWorkerInfo.bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) - { - if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 - { - LOG4_DEBUG("invalid request, please login first!"); - DiscardSocketChannel(pChannel); - return(false); - } - } - Handle(pChannel, oMsgHead, oMsgBody); - oMsgHead.Clear(); // 注意protobuf的Clear()使用不当容易造成内存泄露 - oMsgBody.Clear(); - eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); - } - } - - if (CODEC_STATUS_PAUSE == eCodecStatus) - { - return(true); - } - else if (CODEC_STATUS_WANT_WRITE == eCodecStatus) - { - return(SendTo(pChannel)); - } - else if (CODEC_STATUS_WANT_READ == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - else // 编解码出错或连接关闭或连接中断 - { - LOG4_DEBUG("codec error or connection closed!"); - DiscardSocketChannel(pChannel); - return(false); - } -} - -bool WorkerImpl::FdTransfer() -{ - LOG4_TRACE(" "); - int iAcceptFd = -1; - int iAiFamily = AF_INET; - int iCodec = 0; - int iErrno = SocketChannel::RecvChannelFd(m_stWorkerInfo.iManagerDataFd, iAcceptFd, iAiFamily, iCodec, m_pLogger); - if (iErrno != ERR_OK) - { - if (iErrno == ERR_CHANNEL_EOF) - { - LOG4_WARNING("recv_fd from m_iManagerDataFd %d error %d", m_stWorkerInfo.iManagerDataFd, errno); - Destroy(); - exit(2); // manager与worker通信fd已关闭,worker进程退出 - } - } - else - { - int iKeepAlive = 1; - int iKeepIdle = 60; - int iKeepInterval = 5; - int iKeepCount = 3; - int iTcpNoDelay = 1; - if (setsockopt(iAcceptFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)) < 0) - { - LOG4_WARNING("fail to set SO_KEEPALIVE"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPIDLE"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPINTVL"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPCNT"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)) < 0) - { - LOG4_WARNING("fail to set TCP_NODELAY"); - } - std::shared_ptr pChannel = nullptr; - LOG4_TRACE("fd[%d] transfer successfully.", iAcceptFd); - if (CODEC_NEBULA != iCodec && m_oWorkerConf["with_ssl"]("config_path").length() > 0) - { - pChannel = CreateSocketChannel(iAcceptFd, E_CODEC_TYPE(iCodec), false, true); - } - else - { - pChannel = CreateSocketChannel(iAcceptFd, E_CODEC_TYPE(iCodec), false, false); - } - if (nullptr != pChannel) - { - if (AF_INET == iAiFamily) - { - char szClientAddr[64] = {0}; - int z; /* status return code */ - struct sockaddr_in stClientAddr; - socklen_t iClientAddrSize = sizeof(stClientAddr); - z = getpeername(iAcceptFd, (struct sockaddr *)&stClientAddr, &iClientAddrSize); - if (z == 0) - { - inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientAddr, sizeof(szClientAddr)); - LOG4_TRACE("set fd %d's remote addr \"%s\"", iAcceptFd, szClientAddr); - pChannel->m_pImpl->SetRemoteAddr(std::string(szClientAddr)); - } - else - { - LOG4_ERROR("getpeername error %d", errno); - } - } - else // AF_INET6 - { - char szClientAddr[64] = {0}; - int z; /* status return code */ - struct sockaddr_in6 stClientAddr; - socklen_t iClientAddrSize = sizeof(stClientAddr); - z = getpeername(iAcceptFd, (struct sockaddr *)&stClientAddr, &iClientAddrSize); - if (z == 0) - { - inet_ntop(AF_INET6, &stClientAddr.sin6_addr, szClientAddr, sizeof(szClientAddr)); - LOG4_TRACE("set fd %d's remote addr \"%s\"", iAcceptFd, szClientAddr); - pChannel->m_pImpl->SetRemoteAddr(std::string(szClientAddr)); - } - else - { - LOG4_ERROR("getpeername error %d", errno); - } - } - AddIoReadEvent(pChannel); - if (CODEC_NEBULA == iCodec) - { - AddIoTimeout(pChannel, m_stWorkerInfo.dIoTimeout); - std::shared_ptr pStepTellWorker = MakeSharedStep(nullptr, "neb::StepTellWorker", pChannel); - if (nullptr == pStepTellWorker) - { - return(false); - } - pStepTellWorker->Emit(ERR_OK); - } - else - { - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - AddIoTimeout(pChannel, 1.0); // 为了防止大量连接攻击,初始化连接只有一秒即超时,在正常发送第一个数据包之后才采用正常配置的网络IO超时检查 - } - return(true); - } - else // 没有足够资源分配给新连接,直接close掉 - { - close(iAcceptFd); - } - } - return(false); -} - -bool WorkerImpl::OnIoWrite(std::shared_ptr pChannel) -{ - if (CODEC_NEBULA == pChannel->m_pImpl->GetCodecType()) // 系统内部Server间通信 - { - if (CHANNEL_STATUS_TRY_CONNECT == pChannel->m_pImpl->GetChannelStatus()) // connect之后的第一个写事件 - { - std::shared_ptr pStepConnectWorker = MakeSharedStep(nullptr, "neb::StepConnectWorker", pChannel, pChannel->m_pImpl->m_unRemoteWorkerIdx); - if (nullptr == pStepConnectWorker) - { - LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); - return(false); - } - if (CMD_STATUS_RUNNING != pStepConnectWorker->Emit(ERR_OK)) - { - RemoveStep(pStepConnectWorker); - } - return(true); - } - } - else - { - if (CHANNEL_STATUS_CLOSED != pChannel->m_pImpl->GetChannelStatus()) - { - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - } - } - - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(); - if (CODEC_STATUS_OK == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - } - else if (CODEC_STATUS_PAUSE == eCodecStatus || CODEC_STATUS_WANT_WRITE == eCodecStatus) - { - AddIoWriteEvent(pChannel); - } - else if (CODEC_STATUS_WANT_READ == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - } - else - { - DiscardSocketChannel(pChannel); - } - return(true); -} - -bool WorkerImpl::OnIoTimeout(std::shared_ptr pChannel) -{ - ev_tstamp after = pChannel->m_pImpl->GetActiveTime() - ev_now(m_loop) + m_stWorkerInfo.dIoTimeout; - if (after > 0) // IO在定时时间内被重新刷新过,重新设置定时器 - { - ev_timer_stop (m_loop, pChannel->m_pImpl->MutableTimerWatcher()); - ev_timer_set (pChannel->m_pImpl->MutableTimerWatcher(), after + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, pChannel->m_pImpl->MutableTimerWatcher()); - return(true); - } - - LOG4_TRACE("fd %d, seq %u:", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - if (pChannel->m_pImpl->NeedAliveCheck()) // 需要发送心跳检查 - { - std::shared_ptr pStepIoTimeout = MakeSharedStep(nullptr, "neb::StepIoTimeout", pChannel); - if (nullptr == pStepIoTimeout) - { - LOG4_ERROR("new StepIoTimeout error!"); - DiscardSocketChannel(pChannel); - } - E_CMD_STATUS eStatus = pStepIoTimeout->Emit(ERR_OK); - if (CMD_STATUS_RUNNING != eStatus) - { - // 若返回非running状态,则表明发包时已出错, - // 销毁连接过程在SendTo里已经完成,这里不需要再销毁连接 - RemoveStep(pStepIoTimeout); - } - } - else // 关闭文件描述符并清理相关资源 - { - if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType()) // 非内部服务器间的连接才会在超时中关闭 - { - LOG4_TRACE("io timeout!"); - DiscardSocketChannel(pChannel); - } - } - return(true); -} - -bool WorkerImpl::OnStepTimeout(std::shared_ptr pStep) -{ - ev_timer* watcher = pStep->MutableTimerWatcher(); - ev_tstamp after = pStep->GetActiveTime() - ev_now(m_loop) + pStep->GetTimeout(); - if (after > 0) // 在定时时间内被重新刷新过,重新设置定时器 - { - ev_timer_stop (m_loop, watcher); - ev_timer_set (watcher, after + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, watcher); - return(true); - } - else // 步骤已超时 - { - LOG4_TRACE("seq %u: active_time %lf, now_time %lf, lifetime %lf", - pStep->GetSequence(), pStep->GetActiveTime(), ev_now(m_loop), pStep->GetTimeout()); - E_CMD_STATUS eResult = pStep->Timeout(); - if (CMD_STATUS_RUNNING == eResult) - { - ev_tstamp after = pStep->GetTimeout(); - ev_timer_stop (m_loop, watcher); - ev_timer_set (watcher, after + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, watcher); - return(true); - } - else - { - RemoveChain(pStep->GetChainId()); - RemoveStep(pStep); - return(true); - } - } -} - -bool WorkerImpl::OnSessionTimeout(std::shared_ptr pSession) -{ - ev_timer* watcher = pSession->MutableTimerWatcher(); - LOG4_TRACE("CHECK watchar = 0x%x", watcher); - ev_tstamp after = pSession->GetActiveTime() - ev_now(m_loop) + pSession->GetTimeout(); - if (after > 0) // 定时时间内被重新刷新过,重新设置定时器 - { - ev_timer_stop (m_loop, watcher); - ev_timer_set (watcher, after + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, watcher); - return(true); - } - else // 会话已超时 - { - LOG4_TRACE("session_id: %s", pSession->GetSessionId().c_str()); - if (CMD_STATUS_RUNNING == pSession->Timeout()) - { - ev_timer_stop (m_loop, watcher); - ev_timer_set (watcher, pSession->GetTimeout() + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, watcher); - return(true); - } - else - { - RemoveSession(pSession); - return(true); - } - } -} - -bool WorkerImpl::OnChainTimeout(std::shared_ptr pChain) -{ - ev_timer* watcher = pChain->MutableTimerWatcher(); - LOG4_TRACE("CHECK watchar = 0x%x", watcher); - ev_tstamp after = pChain->GetActiveTime() - ev_now(m_loop) + pChain->GetTimeout(); - if (after > 0) // 定时时间内被重新刷新过,重新设置定时器 - { - ev_timer_stop (m_loop, watcher); - ev_timer_set (watcher, after + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, watcher); - return(true); - } - else // 会话已超时 - { - if (CMD_STATUS_RUNNING == pChain->Timeout()) - { - ev_timer_stop (m_loop, watcher); - ev_timer_set (watcher, pChain->GetTimeout() + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, watcher); - return(true); - } - else - { - RemoveChain(pChain->GetSequence()); - return(true); - } - } -} - -bool WorkerImpl::OnRedisConnected(const redisAsyncContext *c, int status) -{ - LOG4_TRACE(" "); - auto channel_iter = m_mapRedisChannel.find((redisAsyncContext*)c); - if (channel_iter != m_mapRedisChannel.end()) - { - if (status == REDIS_OK) - { - channel_iter->second->bIsReady = true; - int iCmdStatus; - for (auto step_iter = channel_iter->second->listPipelineStep.begin(); - step_iter != channel_iter->second->listPipelineStep.end(); ) - { - size_t args_size = (*step_iter)->GetCmdArguments().size() + 1; - const char* argv[args_size]; - size_t arglen[args_size]; - argv[0] = (*step_iter)->GetCmd().c_str(); - arglen[0] = (*step_iter)->GetCmd().size(); - std::vector >::const_iterator c_iter = (*step_iter)->GetCmdArguments().begin(); - for (size_t i = 1; c_iter != (*step_iter)->GetCmdArguments().end(); ++c_iter, ++i) - { - argv[i] = c_iter->first.c_str(); - arglen[i] = c_iter->first.size(); - } - iCmdStatus = redisAsyncCommandArgv((redisAsyncContext*)c, RedisCmdCallback, nullptr, args_size, argv, arglen); - if (iCmdStatus == REDIS_OK) - { - LOG4_DEBUG("succeed in sending redis cmd: %s", (*step_iter)->CmdToString().c_str()); - } - else - { - E_CMD_STATUS eResult; - uint32 uiChainId; - auto interrupt_step_iter = step_iter; - for (; step_iter != channel_iter->second->listPipelineStep.end(); ++step_iter) - { - eResult = (*step_iter)->Callback(c, status, nullptr); - uiChainId = (*step_iter)->GetChainId(); - RemoveStep(*step_iter); - if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) - { - if (0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - else - { - RemoveChain(uiChainId); - } - } - for (auto erase_step_iter = interrupt_step_iter; - erase_step_iter != channel_iter->second->listPipelineStep.end();) - { - RemoveChain((*erase_step_iter)->GetChainId()); - } - channel_iter->second->listPipelineStep.clear(); - DelNamedRedisChannel(channel_iter->second->GetIdentify()); - m_mapRedisChannel.erase(channel_iter); - } - } - } - else - { - for (auto step_iter = channel_iter->second->listPipelineStep.begin(); - step_iter != channel_iter->second->listPipelineStep.end(); ++step_iter) - { - (*step_iter)->Callback(c, status, nullptr); - RemoveChain((*step_iter)->GetChainId()); - RemoveStep(*step_iter); - } - channel_iter->second->listPipelineStep.clear(); - DelNamedRedisChannel(channel_iter->second->GetIdentify()); - m_mapRedisChannel.erase(channel_iter); - } - } - return(true); -} - -bool WorkerImpl::OnRedisDisconnected(const redisAsyncContext *c, int status) -{ - LOG4_TRACE(" "); - auto channel_iter = m_mapRedisChannel.find((redisAsyncContext*)c); - if (channel_iter != m_mapRedisChannel.end()) - { - for (auto step_iter = channel_iter->second->listPipelineStep.begin(); - step_iter != channel_iter->second->listPipelineStep.end(); ++step_iter) - { - LOG4_ERROR("RedisDisconnect callback error %d of redis cmd: %s", - c->err, (*step_iter)->CmdToString().c_str()); - (*step_iter)->Callback(c, c->err, nullptr); - RemoveChain((*step_iter)->GetChainId()); - RemoveStep(std::dynamic_pointer_cast(*step_iter)); - } - channel_iter->second->listPipelineStep.clear(); - - DelNamedRedisChannel(channel_iter->second->GetIdentify()); - m_mapRedisChannel.erase(channel_iter); - } - redisAsyncFree(const_cast(c)); - return(true); -} - -bool WorkerImpl::OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privdata) -{ - LOG4_TRACE(" "); - auto channel_iter = m_mapRedisChannel.find((redisAsyncContext*)c); - if (channel_iter != m_mapRedisChannel.end()) - { - if (channel_iter->second->listPipelineStep.empty()) - { - LOG4_ERROR("no redis step!"); - return(false); - } - - auto step_iter = channel_iter->second->listPipelineStep.begin(); - if (nullptr == reply) - { - LOG4_ERROR("redis %s error %d: %s", channel_iter->second->GetIdentify().c_str(), c->err, c->errstr); - for ( ; step_iter != channel_iter->second->listPipelineStep.end(); ++step_iter) - { - LOG4_ERROR("callback error %d of redis cmd: %s", c->err, (*step_iter)->CmdToString().c_str()); - (*step_iter)->Callback(c, c->err, (redisReply*)reply); - RemoveChain((*step_iter)->GetChainId()); - RemoveStep(*step_iter); - } - channel_iter->second->listPipelineStep.clear(); - - DelNamedRedisChannel(channel_iter->second->GetIdentify()); - m_mapRedisChannel.erase(channel_iter); - } - else - { - if (step_iter != channel_iter->second->listPipelineStep.end()) - { - LOG4_TRACE("callback of redis cmd: %s", (*step_iter)->CmdToString().c_str()); - E_CMD_STATUS eResult = (*step_iter)->Callback(c, REDIS_OK, (redisReply*)reply); - channel_iter->second->listPipelineStep.erase(step_iter); - if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) - { - uint32 uiChainId = (*step_iter)->GetChainId(); - if (0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - //freeReplyObject(reply); - } - else - { - LOG4_ERROR("no redis callback data found!"); - } - } - - RemoveStep(std::dynamic_pointer_cast(*step_iter)); - } - - return(true); -} - -std::shared_ptr WorkerImpl::InitializeSharedActor(Actor* pCreator, std::shared_ptr pSharedActor, const std::string& strActorName) -{ - pSharedActor->SetWorker(m_pWorker); - pSharedActor->SetActiveTime(ev_now(m_loop)); - pSharedActor->SetActorName(strActorName); - if (nullptr != pCreator) - { - pSharedActor->SetContext(pCreator->GetContext()); - } - switch (pSharedActor->GetActorType()) - { - case Actor::ACT_PB_STEP: - case Actor::ACT_HTTP_STEP: - case Actor::ACT_REDIS_STEP: - if (TransformToSharedStep(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - case Actor::ACT_SESSION: - case Actor::ACT_TIMER: - if (TransformToSharedSession(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - case Actor::ACT_CONTEXT: - return(pSharedActor); - break; - case Actor::ACT_CMD: - if (TransformToSharedCmd(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - case Actor::ACT_MODULE: - if (TransformToSharedModule(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - case Actor::ACT_MODEL: - if (TransformToSharedModel(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - case Actor::ACT_CHAIN: - if (TransformToSharedChain(pCreator, pSharedActor)) - { - return(pSharedActor); - } - break; - default: - LOG4_ERROR("\"%s\" must be a Step, a Session, a Model, a Cmd or a Module.", - strActorName.c_str()); - return(nullptr); - } - return(nullptr); -} - -bool WorkerImpl::TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor) -{ - pSharedActor->m_dTimeout = (0 == pSharedActor->m_dTimeout) ? m_stWorkerInfo.dStepTimeout : pSharedActor->m_dTimeout; - ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); - if (NULL == timer_watcher) - { - return(false); - } - - if (nullptr != pCreator) - { - pSharedActor->SetTraceId(pCreator->GetTraceId()); - } - - std::shared_ptr pSharedStep = std::dynamic_pointer_cast(pSharedActor); - for (auto iter = pSharedStep->m_setNextStepSeq.begin(); iter != pSharedStep->m_setNextStepSeq.end(); ++iter) - { - auto callback_iter = m_mapCallbackStep.find(*iter); - if (callback_iter != m_mapCallbackStep.end()) - { - callback_iter->second->m_setPreStepSeq.insert(pSharedStep->GetSequence()); - } - } - - auto ret = m_mapCallbackStep.insert(std::make_pair(pSharedStep->GetSequence(), pSharedStep)); - if (ret.second) - { - if (gc_dNoTimeout != pSharedStep->m_dTimeout) - { - ev_timer_init (timer_watcher, StepTimeoutCallback, pSharedStep->m_dTimeout + ev_time() - ev_now(m_loop), 0.); - ev_timer_start (m_loop, timer_watcher); - } - LOG4_TRACE("Step(seq %u, active_time %lf, lifetime %lf) register successful.", - pSharedStep->GetSequence(), pSharedStep->GetActiveTime(), pSharedStep->GetTimeout()); - auto step_class_iter = m_mapLoadedStep.find(pSharedStep->GetActorName()); - if (step_class_iter != m_mapLoadedStep.end()) - { - step_class_iter->second.insert(pSharedStep->GetSequence()); - } - return(true); - } - else - { - return(false); - } -} - -bool WorkerImpl::TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor) -{ - ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); - if (NULL == timer_watcher) - { - return(false); - } - - if (nullptr != pCreator) - { - std::ostringstream oss; - oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << pSharedActor->GetSequence(); - pSharedActor->SetTraceId(oss.str()); - } - std::shared_ptr pSharedSession = std::dynamic_pointer_cast(pSharedActor); - auto ret = m_mapCallbackSession.insert(std::make_pair(pSharedSession->GetSessionId(), pSharedSession)); - if (ret.second) - { - if (gc_dNoTimeout != pSharedSession->m_dTimeout) - { - ev_timer_init (timer_watcher, SessionTimeoutCallback, pSharedSession->m_dTimeout + ev_time() - ev_now(m_loop), 0.); - ev_timer_start (m_loop, timer_watcher); - } - auto session_class_iter = m_mapLoadedSession.find(pSharedSession->GetActorName()); - if (session_class_iter != m_mapLoadedSession.end()) - { - session_class_iter->second.insert(pSharedSession->GetSessionId()); - } - return(true); - } - else - { - return(false); - } -} - -bool WorkerImpl::TransformToSharedCmd(Actor* pCreator, std::shared_ptr pSharedActor) -{ - if (nullptr != pCreator) - { - pSharedActor->SetTraceId(pCreator->GetTraceId()); - } - std::shared_ptr pSharedCmd = std::dynamic_pointer_cast(pSharedActor); - auto ret = m_mapCmd.insert(std::make_pair(pSharedCmd->GetCmd(), pSharedCmd)); - if (ret.second) - { - if (pSharedCmd->Init()) - { - auto cmd_class_iter = m_mapLoadedCmd.find(pSharedCmd->GetActorName()); - if (cmd_class_iter != m_mapLoadedCmd.end()) - { - cmd_class_iter->second.insert(pSharedCmd->GetCmd()); - } - return(true); - } - } - return(false); -} - -bool WorkerImpl::TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor) -{ - if (nullptr != pCreator) - { - pSharedActor->SetTraceId(pCreator->GetTraceId()); - } - - std::shared_ptr pSharedModule = std::dynamic_pointer_cast(pSharedActor); - auto ret = m_mapModule.insert(std::make_pair(pSharedModule->GetModulePath(), pSharedModule)); - if (ret.second) - { - if (pSharedModule->Init()) - { - auto module_class_iter = m_mapLoadedModule.find(pSharedModule->GetActorName()); - if (module_class_iter != m_mapLoadedModule.end()) - { - module_class_iter->second.insert(pSharedModule->GetModulePath()); - } - return(true); - } - } - return(false); -} - -bool WorkerImpl::TransformToSharedModel(Actor* pCreator, std::shared_ptr pSharedActor) -{ - std::shared_ptr pSharedModel = std::dynamic_pointer_cast(pSharedActor); - auto ret = m_mapModel.insert(std::make_pair(pSharedModel->GetActorName(), pSharedModel)); - if (ret.second) - { - if (pSharedModel->Init()) - { - return(true); - } - } - return(false); -} - -bool WorkerImpl::TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor) -{ - ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); - if (NULL == timer_watcher) - { - return(false); - } - - if (nullptr != pCreator) - { - pSharedActor->SetTraceId(pCreator->GetTraceId()); - } - - std::shared_ptr pSharedChain = std::dynamic_pointer_cast(pSharedActor); - auto chain_conf_iter = m_mapChainConf.find(pSharedChain->GetChainFlag()); - if (chain_conf_iter == m_mapChainConf.end()) - { - LOG4_ERROR("no chain block config for \"%s\"", pSharedChain->GetChainFlag().c_str()); - return(false); - } - else - { - pSharedChain->Init(chain_conf_iter->second); - } - auto ret = m_mapChain.insert(std::make_pair(pSharedChain->GetSequence(), pSharedChain)); - if (ret.second) - { - if (gc_dNoTimeout != pSharedChain->m_dTimeout) - { - ev_timer_init (timer_watcher, ChainTimeoutCallback, pSharedChain->m_dTimeout + ev_time() - ev_now(m_loop), 0.); - ev_timer_start (m_loop, timer_watcher); - } - return(true); - } - return(false); -} - -time_t WorkerImpl::GetNowTime() const -{ - return((time_t)ev_now(m_loop)); -} - -bool WorkerImpl::ResetTimeout(std::shared_ptr pSharedActor) -{ - ev_timer* watcher = pSharedActor->MutableTimerWatcher(); - ev_tstamp after = ev_now(m_loop) + pSharedActor->GetTimeout(); - ev_timer_stop (m_loop, watcher); - ev_timer_set (watcher, after + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, watcher); - return(true); -} - -bool WorkerImpl::SetProcessName(const CJsonObject& oJsonConf) -{ - char szProcessName[64] = {0}; - snprintf(szProcessName, sizeof(szProcessName), "%s_W%d", oJsonConf("server_name").c_str(), m_stWorkerInfo.iWorkerIndex); - ngx_setproctitle(szProcessName); - return(true); -} - -bool WorkerImpl::Init(CJsonObject& oJsonConf) -{ - char szProcessName[64] = {0}; - snprintf(szProcessName, sizeof(szProcessName), "%s_W%d", oJsonConf("server_name").c_str(), m_stWorkerInfo.iWorkerIndex); - ngx_setproctitle(szProcessName); - oJsonConf.Get("io_timeout", m_stWorkerInfo.dIoTimeout); - if (!oJsonConf.Get("step_timeout", m_stWorkerInfo.dStepTimeout)) - { - m_stWorkerInfo.dStepTimeout = 0.5; - } - oJsonConf.Get("node_type", m_stWorkerInfo.strNodeType); - oJsonConf.Get("host", m_stWorkerInfo.strHostForServer); - oJsonConf.Get("port", m_stWorkerInfo.iPortForServer); - m_oCustomConf = oJsonConf["custom"]; - std::ostringstream oss; - oss << m_stWorkerInfo.strHostForServer << ":" << m_stWorkerInfo.iPortForServer << "." << m_stWorkerInfo.iWorkerIndex; - m_stWorkerInfo.strWorkerIdentify = std::move(oss.str()); - - if (oJsonConf("access_host").size() > 0 && oJsonConf("access_port").size() > 0) - { - m_stWorkerInfo.bIsAccess = true; - oJsonConf["permission"]["uin_permit"].Get("stat_interval", m_stWorkerInfo.dMsgStatInterval); - oJsonConf["permission"]["uin_permit"].Get("permit_num", m_stWorkerInfo.iMsgPermitNum); - } - if (!InitLogger(oJsonConf)) - { - return(false); - } - - bool bCpuAffinity = false; - oJsonConf.Get("cpu_affinity", bCpuAffinity); - if (bCpuAffinity) - { -#ifndef __CYGWIN__ - /* get logical cpu number */ - int iCpuNum = sysconf(_SC_NPROCESSORS_CONF);; ///< cpu数量 - cpu_set_t stCpuMask; ///< cpu set - CPU_ZERO(&stCpuMask); - CPU_SET(m_stWorkerInfo.iWorkerIndex % iCpuNum, &stCpuMask); - if (sched_setaffinity(0, sizeof(cpu_set_t), &stCpuMask) == -1) - { - LOG4_WARNING("sched_setaffinity failed."); - } -#endif - } - - if (oJsonConf["with_ssl"]("config_path").length() > 0) - { -#ifdef WITH_OPENSSL - if (ERR_OK != SocketChannelSslImpl::SslInit(m_pLogger)) - { - LOG4_FATAL("SslInit() failed!"); - return(false); - } - if (ERR_OK != SocketChannelSslImpl::SslServerCtxCreate(m_pLogger)) - { - LOG4_FATAL("SslServerCtxCreate() failed!"); - return(false); - } - std::string strCertFile = m_stWorkerInfo.strWorkPath + "/" + oJsonConf["with_ssl"]("config_path") + "/" + oJsonConf["with_ssl"]("cert_file"); - std::string strKeyFile = m_stWorkerInfo.strWorkPath + "/" + oJsonConf["with_ssl"]("config_path") + "/" + oJsonConf["with_ssl"]("key_file"); - if (ERR_OK != SocketChannelSslImpl::SslServerCertificate(m_pLogger, strCertFile, strKeyFile)) - { - LOG4_FATAL("SslServerCertificate() failed!"); - return(false); - } -#endif - } - - std::string strChainKey; - while (oJsonConf["runtime"]["chains"].GetKey(strChainKey)) - { - std::queue > queChainBlocks; - for (int i = 0; i < oJsonConf["runtime"]["chains"][strChainKey].GetArraySize(); ++i) - { - std::vector vecBlock; - if (oJsonConf["runtime"]["chains"][strChainKey][i].IsArray()) - { - for (int j = 0; j < oJsonConf["runtime"]["chains"][strChainKey][i].GetArraySize(); ++j) - { - vecBlock.push_back(std::move(oJsonConf["runtime"]["chains"][strChainKey][i](j))); - } - } - else - { - vecBlock.push_back(std::move(oJsonConf["runtime"]["chains"][strChainKey](i))); - } - queChainBlocks.push(std::move(vecBlock)); - } - m_mapChainConf.insert(std::make_pair(strChainKey, std::move(queChainBlocks))); - } - return(true); -} - -bool WorkerImpl::InitLogger(const CJsonObject& oJsonConf) -{ - if (nullptr != m_pLogger) // 已经被初始化过,只修改日志级别 - { - int32 iLogLevel = 0; - int32 iNetLogLevel = 0; - oJsonConf.Get("log_level", iLogLevel); - oJsonConf.Get("net_log_level", iNetLogLevel); - m_pLogger->SetLogLevel(iLogLevel); - m_pLogger->SetNetLogLevel(iNetLogLevel); - return(true); - } - else - { - int32 iMaxLogFileSize = 0; - int32 iMaxLogFileNum = 0; - int32 iLogLevel = 0; - int32 iNetLogLevel = 0; - std::string strLogname = m_stWorkerInfo.strWorkPath + std::string("/") + oJsonConf("log_path") - + std::string("/") + getproctitle() + std::string(".log"); - std::string strParttern = "[%D,%d{%q}][%p] [%l] %m%n"; - std::ostringstream ssServerName; - ssServerName << getproctitle() << " " << m_stWorkerInfo.strWorkerIdentify; - oJsonConf.Get("max_log_file_size", iMaxLogFileSize); - oJsonConf.Get("max_log_file_num", iMaxLogFileNum); - oJsonConf.Get("net_log_level", iNetLogLevel); - oJsonConf.Get("log_level", iLogLevel); - m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, m_pWorker); - m_pLogger->SetNetLogLevel(iNetLogLevel); - LOG4_NOTICE("%s program begin...", getproctitle()); - return(true); - } -} - -bool WorkerImpl::CreateEvents() -{ - m_loop = ev_loop_new(EVFLAG_AUTO); - if (m_loop == nullptr) - { - return(false); - } - - signal(SIGPIPE, SIG_IGN); - // 注册信号事件 - ev_signal* signal_watcher = (ev_signal*)malloc(sizeof(ev_signal)); - ev_signal_init (signal_watcher, TerminatedCallback, SIGINT); - signal_watcher->data = (void*)this; - ev_signal_start (m_loop, signal_watcher); - - AddPeriodicTaskEvent(); - - // 注册网络IO事件 - m_pManagerDataChannel = std::make_shared(m_pLogger, m_stWorkerInfo.iManagerDataFd, GetSequence()); - m_pManagerDataChannel->m_pImpl->SetLabor(m_pWorker); - m_pManagerDataChannel->m_pImpl->Init(CODEC_NEBULA); - m_pManagerDataChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - m_pManagerControlChannel = std::make_shared(m_pLogger, m_stWorkerInfo.iManagerControlFd, GetSequence()); - m_pManagerControlChannel->m_pImpl->SetLabor(m_pWorker); - m_pManagerControlChannel->m_pImpl->Init(CODEC_NEBULA); - m_pManagerControlChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - AddIoReadEvent(m_pManagerDataChannel); - // AddIoTimeout(pChannelData, 60.0); // TODO 需要优化 - AddIoReadEvent(m_pManagerControlChannel); - // AddIoTimeout(pChannelControl, 60.0); //TODO 需要优化 - return(true); -} - -void WorkerImpl::LoadSysCmd() -{ - // 调用MakeSharedCmd等系列函数必须严格匹配参数类型,类型不符需显式转换,如 (int)CMD_REQ_CONNECT_TO_WORKER - MakeSharedCmd(nullptr, "neb::CmdToldWorker", (int)CMD_REQ_TELL_WORKER); - MakeSharedCmd(nullptr, "neb::CmdUpdateNodeId", (int)CMD_REQ_REFRESH_NODE_ID); - MakeSharedCmd(nullptr, "neb::CmdNodeNotice", (int)CMD_REQ_NODE_NOTICE); - MakeSharedCmd(nullptr, "neb::CmdBeat", (int)CMD_REQ_BEAT); - MakeSharedCmd(nullptr, "neb::CmdSetNodeConf", (int)CMD_REQ_SET_NODE_CONFIG); - MakeSharedCmd(nullptr, "neb::CmdSetNodeCustomConf", (int)CMD_REQ_SET_NODE_CUSTOM_CONFIG); - MakeSharedCmd(nullptr, "neb::CmdReloadCustomConf", (int)CMD_REQ_RELOAD_CUSTOM_CONFIG); -#if __cplusplus >= 201401L - m_pSessionNode = std::make_unique(); -#else - m_pSessionNode = std::unique_ptr(new SessionNode()); -#endif - m_pSessionLogger = std::dynamic_pointer_cast(MakeSharedSession(nullptr, "neb::SessionLogger")); -} - -void WorkerImpl::Destroy() -{ - LOG4_TRACE(" "); - - m_mapCmd.clear(); - m_mapCallbackStep.clear(); - m_mapCallbackSession.clear(); - m_mapSocketChannel.clear(); - m_mapRedisChannel.clear(); - m_mapNamedSocketChannel.clear(); - m_mapNamedRedisChannel.clear(); - - for (auto so_iter = m_mapLoadedSo.begin(); - so_iter != m_mapLoadedSo.end(); ++so_iter) - { - DELETE(so_iter->second); - } - m_mapLoadedSo.clear(); - -#ifdef WITH_OPENSSL - SocketChannelSslImpl::SslFree(); -#endif - // TODO 待补充完整 - - if (m_loop != NULL) - { - ev_loop_destroy(m_loop); - m_loop = NULL; - } - - if (m_pErrBuff != nullptr) - { - delete[] m_pErrBuff; - m_pErrBuff = nullptr; - } -} - -bool WorkerImpl::AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel) -{ - LOG4_TRACE("%s", strIdentify.c_str()); - auto named_iter = m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == m_mapNamedSocketChannel.end()) - { - std::list> listChannel; - listChannel.push_back(pChannel); - m_mapNamedSocketChannel.insert(std::make_pair(strIdentify, listChannel)); - } - else - { - named_iter->second.push_back(pChannel); - } - pChannel->m_pImpl->SetIdentify(strIdentify); - return(true); - -} - -void WorkerImpl::DelNamedSocketChannel(const std::string& strIdentify) -{ - auto named_iter = m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == m_mapNamedSocketChannel.end()) - { - ; - } - else - { - m_mapNamedSocketChannel.erase(named_iter); - } -} - -void WorkerImpl::SetChannelIdentify(std::shared_ptr pChannel, const std::string& strIdentify) -{ - pChannel->m_pImpl->SetIdentify(strIdentify); -} - -bool WorkerImpl::AddNamedRedisChannel(const std::string& strIdentify, std::shared_ptr pChannel) -{ - auto named_iter = m_mapNamedRedisChannel.find(strIdentify); - if (named_iter == m_mapNamedRedisChannel.end()) - { - std::list > listChannel; - listChannel.push_back(pChannel); - m_mapNamedRedisChannel.insert(std::make_pair(strIdentify, listChannel)); - return(true); - } - else - { - named_iter->second.push_back(pChannel); - } - pChannel->SetIdentify(strIdentify); - return(true); -} - -void WorkerImpl::DelNamedRedisChannel(const std::string& strIdentify) -{ - auto named_iter = m_mapNamedRedisChannel.find(strIdentify); - if (named_iter == m_mapNamedRedisChannel.end()) - { - ; - } - else - { - m_mapNamedRedisChannel.erase(named_iter); - } -} - -void WorkerImpl::AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify) -{ - LOG4_TRACE("%s, %s", strNodeType.c_str(), strIdentify.c_str()); - m_pSessionNode->AddNode(strNodeType, strIdentify); - - if (std::string("BEACON") != m_stWorkerInfo.strNodeType - && std::string("LOGGER") != m_stWorkerInfo.strNodeType) - { - std::string strOnlineNode; - if (std::string("LOGGER") == strNodeType && m_pSessionNode->GetNode(strNodeType, strOnlineNode)) - { - m_pLogger->EnableNetLogger(true); - } - } -} - -void WorkerImpl::DelNodeIdentify(const std::string& strNodeType, const std::string& strIdentify) -{ - LOG4_TRACE("%s, %s", strNodeType.c_str(), strIdentify.c_str()); - m_pSessionNode->DelNode(strNodeType, strIdentify); - - std::string strOnlineNode; - if (std::string("LOGGER") == strNodeType - && !m_pSessionNode->GetNode(strNodeType, strOnlineNode)) - { - m_pLogger->EnableNetLogger(false); - } -} - -bool WorkerImpl::AddNetLogMsg(const MsgBody& oMsgBody) -{ - // 此函数不能写日志,不然可能会导致写日志函数与此函数无限递归 - m_pSessionLogger->AddMsg(oMsgBody); - return(true); -} - -bool WorkerImpl::SendTo(std::shared_ptr pChannel) -{ - LOG4_TRACE("channel %d", pChannel->m_pImpl->GetFd()); - E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(); - if (CODEC_STATUS_OK == eStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) - { - AddIoWriteEvent(pChannel); - return(true); - } - else if (CODEC_STATUS_WANT_READ == eStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - return(false); -} - -bool WorkerImpl::SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) -{ - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - if (CODEC_STATUS_OK == eStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) - { - AddIoWriteEvent(pChannel); - return(true); - } - else if (CODEC_STATUS_WANT_READ == eStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - return(false); -} - -bool WorkerImpl::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) -{ - LOG4_TRACE("identify: %s", strIdentify.c_str()); - auto named_iter = m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == m_mapNamedSocketChannel.end()) - { - LOG4_TRACE("no channel match %s.", strIdentify.c_str()); - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - return(AutoSend(strIdentify, iCmd, uiSeq, oMsgBody)); - } - else - { - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - E_CODEC_STATUS eStatus = (*named_iter->second.begin())->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - if (CODEC_STATUS_OK == eStatus) - { - RemoveIoWriteEvent(*named_iter->second.begin()); - return(true); - } - else if (CODEC_STATUS_PAUSE == eStatus) // || CODEC_STATUS_WANT_WRITE == eCodecStatus) - { - AddIoWriteEvent(*named_iter->second.begin()); - return(true); - } - else if (CODEC_STATUS_WANT_READ == eStatus) - { - RemoveIoWriteEvent(*named_iter->second.begin()); - return(true); - } - return(false); - } -} - -bool WorkerImpl::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) -{ - LOG4_TRACE("node_type: %s", strNodeType.c_str()); - std::string strOnlineNode; - if (m_pSessionNode->GetNode(strNodeType, strOnlineNode)) - { - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); - } - else - { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } -} - -bool WorkerImpl::SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) -{ - LOG4_TRACE("nody_type: %s, factor: %d", strNodeType.c_str(), uiFactor); - std::string strOnlineNode; - if (m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) - { - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); - } - else - { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } -} - -bool WorkerImpl::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) -{ - LOG4_TRACE("nody_type: %s", strNodeType.c_str()); - if (oMsgBody.has_req_target()) - { - if (0 != oMsgBody.req_target().route_id()) - { - return(SendOriented(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, pSender)); - } - else if (oMsgBody.req_target().route().length() > 0) - { - std::string strOnlineNode; - if (m_pSessionNode->GetNode(strNodeType, oMsgBody.req_target().route(), strOnlineNode)) - { - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); - } - else - { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } - } - else - { - return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); - } - } - else - { - LOG4_ERROR("MsgBody is not a request message!"); - return(false); - } -}; - -bool WorkerImpl::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) -{ - LOG4_TRACE("node_type: %s", strNodeType.c_str()); - std::unordered_set setOnlineNodes; - if (m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) - { - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - bool bSendResult = false; - for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) - { - bSendResult |= SendTo(*node_iter, iCmd, uiSeq, oMsgBody); - } - return(bSendResult); - } - else - { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } -} - -bool WorkerImpl::SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) -{ - E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); - switch (eStatus) - { - case CODEC_STATUS_OK: - return(true); - case CODEC_STATUS_PAUSE: - case CODEC_STATUS_WANT_WRITE: - AddIoWriteEvent(pChannel); - return(true); - case CODEC_STATUS_WANT_READ: - RemoveIoWriteEvent(pChannel); - return(true); - case CODEC_STATUS_EOF: // http1.0 respone and close - DiscardSocketChannel(pChannel); - return(true); - default: - DiscardSocketChannel(pChannel); - return(false); - } -} - -bool WorkerImpl::SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) -{ - char szIdentify[256] = {0}; - snprintf(szIdentify, sizeof(szIdentify), "%s:%d%s", strHost.c_str(), iPort, strUrlPath.c_str()); - LOG4_TRACE("identify: %s", szIdentify); - auto named_iter = m_mapNamedSocketChannel.find(szIdentify); - if (named_iter == m_mapNamedSocketChannel.end()) - { - LOG4_TRACE("no channel match %s.", szIdentify); - return(AutoSend(strHost, iPort, strUrlPath, oHttpMsg, uiHttpStepSeq)); - } - else - { - if (named_iter->second.empty()) - { - return(AutoSend(strHost, iPort, strUrlPath, oHttpMsg, uiHttpStepSeq)); - } - else - { - auto channel_iter = named_iter->second.begin(); - E_CODEC_STATUS eStatus = (*channel_iter)->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); - if (CODEC_STATUS_OK == eStatus) - { - named_iter->second.erase(channel_iter); // erase from named channel pool, the channel remain in m_mapSocketChannel. - return(true); - } - else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) - { - AddIoWriteEvent(*channel_iter); - return(true); - } - else if (CODEC_STATUS_WANT_READ == eStatus) - { - RemoveIoWriteEvent(*channel_iter); - return(true); - } - return(false); - } - } -} - -bool WorkerImpl::SendTo(std::shared_ptr pRedisChannel, Actor* pSender) -{ - LOG4_TRACE(" "); - if (nullptr == pSender) - { - LOG4_ERROR("pSender can't be nullptr!"); - return(false); - } - if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) - { - LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); - return(false); - } - std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - if (pRedisChannel->bIsReady) - { - int status; - size_t args_size = pRedisStep->GetCmdArguments().size() + 1; - const char* argv[args_size]; - size_t arglen[args_size]; - argv[0] = pRedisStep->GetCmd().c_str(); - arglen[0] = pRedisStep->GetCmd().size(); - std::vector >::const_iterator c_iter = pRedisStep->GetCmdArguments().begin(); - for (size_t i = 1; c_iter != pRedisStep->GetCmdArguments().end(); ++c_iter, ++i) - { - argv[i] = c_iter->first.c_str(); - arglen[i] = c_iter->first.size(); - } - status = redisAsyncCommandArgv((redisAsyncContext*)pRedisChannel->RedisContext(), RedisCmdCallback, nullptr, args_size, argv, arglen); - if (status == REDIS_OK) - { - LOG4_DEBUG("succeed in sending redis cmd: %s", pRedisStep->CmdToString().c_str()); - pRedisChannel->listPipelineStep.push_back(pRedisStep); - return(true); - } - else - { - LOG4_ERROR("redis status %d!", status); - return(false); - } - } - else - { - pRedisChannel->listPipelineStep.push_back(pRedisStep); - return(true); - } -} - -bool WorkerImpl::SendTo(const std::string& strIdentify, Actor* pSender) -{ - LOG4_TRACE("%s", strIdentify.c_str()); - if (nullptr == pSender) - { - LOG4_ERROR("pSender can't be nullptr!"); - return(false); - } - auto ctx_iter = m_mapNamedRedisChannel.find(strIdentify); - if (ctx_iter != m_mapNamedRedisChannel.end()) - { - return(SendTo(*(ctx_iter->second.begin()), pSender)); - } - else - { - size_t iPosIpPortSeparator = strIdentify.rfind(':'); - if (iPosIpPortSeparator == std::string::npos) - { - LOG4_ERROR("invalid node identify \"%s\"", strIdentify.c_str()); - return(false); - } - std::string strHost = strIdentify.substr(0, iPosIpPortSeparator); - std::string strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); - int iPort = atoi(strPort.c_str()); - if (iPort == 0) - { - LOG4_ERROR("invalid node identify \"%s\"", strIdentify.c_str()); - return(false); - } - if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) - { - LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); - return(false); - } - std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep)); - } -} - -bool WorkerImpl::SendTo(const std::string& strHost, int iPort, Actor* pSender) -{ - LOG4_TRACE("%s, %d", strHost.c_str(), iPort); - if (nullptr == pSender) - { - LOG4_ERROR("pSender can't be nullptr!"); - return(false); - } - std::ostringstream oss; - oss << strHost << ":" << iPort; - std::string strIdentify = std::move(oss.str()); - auto ctx_iter = m_mapNamedRedisChannel.find(strIdentify); - if (ctx_iter != m_mapNamedRedisChannel.end()) - { - return(SendTo(*(ctx_iter->second.begin()), pSender)); - } - else - { - if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) - { - LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); - return(false); - } - std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep)); - } -} - -bool WorkerImpl::AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) -{ - LOG4_TRACE("%s", strIdentify.c_str()); - size_t iPosIpPortSeparator = strIdentify.rfind(':'); - if (iPosIpPortSeparator == std::string::npos) - { - return(false); - } - size_t iPosPortWorkerIndexSeparator = strIdentify.rfind('.'); - std::string strHost = strIdentify.substr(0, iPosIpPortSeparator); - std::string strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); - std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); - int iPort = atoi(strPort.c_str()); - if (iPort == 0) - { - return(false); - } - int iWorkerIndex = atoi(strWorkerIndex.c_str()); - if (iWorkerIndex > 200) - { - return(false); - } - - struct addrinfo stAddrHints; - struct addrinfo* pAddrResult; - struct addrinfo* pAddrCurrent; - memset(&stAddrHints, 0, sizeof(struct addrinfo)); - stAddrHints.ai_family = AF_UNSPEC; - stAddrHints.ai_socktype = SOCK_STREAM; - stAddrHints.ai_protocol = IPPROTO_IP; - int iCode = getaddrinfo(strHost.c_str(), strPort.c_str(), &stAddrHints, &pAddrResult); - if (0 != iCode) - { - LOG4_ERROR("getaddrinfo(\"%s\", \"%s\") error %d: %s", - strHost.c_str(), strPort.c_str(), iCode, gai_strerror(iCode)); - return(false); - } - int iFd = -1; - for (pAddrCurrent = pAddrResult; - pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) - { - iFd = socket(pAddrCurrent->ai_family, - pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); - if (iFd == -1) - { - continue; - } - - break; - } - - /* No address succeeded */ - if (pAddrCurrent == NULL) - { - LOG4_ERROR("Could not connect to \"%s:%s\"", strHost.c_str(), strPort.c_str()); - freeaddrinfo(pAddrResult); /* No longer needed */ - return(false); - } - - x_sock_set_block(iFd, 0); - int nREUSEADDR = 1; - setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); - std::shared_ptr pChannel = CreateSocketChannel(iFd, CODEC_NEBULA); - if (nullptr != pChannel) - { - connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); - freeaddrinfo(pAddrResult); /* No longer needed */ - AddIoTimeout(pChannel, 1.5); - AddIoReadEvent(pChannel); - AddIoWriteEvent(pChannel); - pChannel->m_pImpl->SetIdentify(strIdentify); - pChannel->m_pImpl->SetRemoteAddr(strHost); - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - if (CODEC_STATUS_OK != eCodecStatus - && CODEC_STATUS_PAUSE != eCodecStatus - && CODEC_STATUS_WANT_WRITE != eCodecStatus - && CODEC_STATUS_WANT_READ != eCodecStatus) - { - DiscardSocketChannel(pChannel); - } - - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); - pChannel->m_pImpl->m_unRemoteWorkerIdx = iWorkerIndex; - AddNamedSocketChannel(strIdentify, pChannel); - return(true); - } - else // 没有足够资源分配给新连接,直接close掉 - { - freeaddrinfo(pAddrResult); /* No longer needed */ - close(iFd); - return(false); - } -} - -bool WorkerImpl::AutoSend(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) -{ - LOG4_TRACE("%s, %d, %s", strHost.c_str(), iPort, strUrlPath.c_str()); - struct addrinfo stAddrHints; - struct addrinfo* pAddrResult; - struct addrinfo* pAddrCurrent; - memset(&stAddrHints, 0, sizeof(struct addrinfo)); - stAddrHints.ai_family = AF_UNSPEC; - stAddrHints.ai_socktype = SOCK_STREAM; - stAddrHints.ai_protocol = IPPROTO_IP; - int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); - if (0 != iCode) - { - LOG4_ERROR("getaddrinfo(\"%s\", \"%s\") error %d: %s", - strHost.c_str(), iPort, iCode, gai_strerror(iCode)); - return(false); - } - int iFd = -1; - for (pAddrCurrent = pAddrResult; - pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) - { - iFd = socket(pAddrCurrent->ai_family, - pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); - if (iFd == -1) - { - continue; - } - - break; - } - - /* No address succeeded */ - if (pAddrCurrent == NULL) - { - LOG4_ERROR("Could not connect to \"%s:%d\"", strHost.c_str(), iPort); - freeaddrinfo(pAddrResult); /* No longer needed */ - return(false); - } - - x_sock_set_block(iFd, 0); - int nREUSEADDR = 1; - setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); - std::shared_ptr pChannel = nullptr; - std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(':')); - std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c) -> unsigned char { return std::tolower(c); }); - if (strSchema == std::string("https")) - { - pChannel = CreateSocketChannel(iFd, CODEC_HTTP, true, true); - } - else - { - pChannel = CreateSocketChannel(iFd, CODEC_HTTP, true, false); - } - if (nullptr != pChannel) - { - connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); - freeaddrinfo(pAddrResult); /* No longer needed */ - char szIdentify[32] = {0}; - AddIoTimeout(pChannel, 1.5); - AddIoReadEvent(pChannel); - AddIoWriteEvent(pChannel); - snprintf(szIdentify, sizeof(szIdentify), "%s:%d", strHost.c_str(), iPort); - pChannel->m_pImpl->SetIdentify(szIdentify); - pChannel->m_pImpl->SetRemoteAddr(strHost); - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); - if (CODEC_STATUS_OK != eCodecStatus - && CODEC_STATUS_PAUSE != eCodecStatus - && CODEC_STATUS_WANT_WRITE != eCodecStatus - && CODEC_STATUS_WANT_READ != eCodecStatus) - { - DiscardSocketChannel(pChannel); - } - - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT, uiHttpStepSeq); - // AddNamedSocketChannel(szIdentify, pChannel); the channel should not add to named connection pool, because there is an uncompleted http request. - return(true); - } - else // 没有足够资源分配给新连接,直接close掉 - { - freeaddrinfo(pAddrResult); /* No longer needed */ - close(iFd); - return(false); - } -} - -bool WorkerImpl::AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep) -{ - LOG4_TRACE("redisAsyncConnect(%s, %d)", strHost.c_str(), iPort); - redisAsyncContext *c = redisAsyncConnect(strHost.c_str(), iPort); - if (c->err) - { - LOG4_ERROR("error: %s", c->errstr); - redisAsyncFree(c); - return(false); - } - c->data = m_pWorker; - std::shared_ptr pRedisChannel = nullptr; - try - { - pRedisChannel = std::make_shared(c); - } - catch(std::bad_alloc& e) - { - LOG4_ERROR("new RedisChannel error: %s", e.what()); - return(false); - } - pRedisChannel->listPipelineStep.push_back(pRedisStep); - pRedisStep->SetWorker(m_pWorker); - m_mapRedisChannel.insert(std::make_pair(c, pRedisChannel)); - redisLibevAttach(m_loop, c); - redisAsyncSetConnectCallback(c, RedisConnectCallback); - redisAsyncSetDisconnectCallback(c, RedisDisconnectCallback); - - std::ostringstream oss; - oss << strHost << ":" << iPort; - std::string strIdentify = std::move(oss.str()); - AddNamedRedisChannel(strIdentify, pRedisChannel); - return(true); -} - -bool WorkerImpl::Disconnect(std::shared_ptr pChannel, bool bChannelNotice) -{ - return(DiscardSocketChannel(pChannel, bChannelNotice)); -} - -bool WorkerImpl::Disconnect(const std::string& strIdentify, bool bChannelNotice) -{ - auto named_iter = m_mapNamedSocketChannel.find(strIdentify); - if (named_iter != m_mapNamedSocketChannel.end()) - { - std::list>::iterator channel_iter; - while (named_iter->second.size() > 1) - { - channel_iter = named_iter->second.begin(); - DiscardSocketChannel(*channel_iter, bChannelNotice); - } - channel_iter = named_iter->second.begin(); - return(DiscardSocketChannel(*channel_iter, bChannelNotice)); - } - return(false); -} - -bool WorkerImpl::DiscardNamedChannel(const std::string& strIdentify) -{ - LOG4_TRACE("identify: %s", strIdentify.c_str()); - auto named_iter = m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == m_mapNamedSocketChannel.end()) - { - LOG4_DEBUG("no channel match %s.", strIdentify.c_str()); - return(false); - } - else - { - for (auto channel_iter = named_iter->second.begin(); - channel_iter != named_iter->second.end(); ++channel_iter) - { - (*channel_iter)->m_pImpl->SetIdentify(""); - (*channel_iter)->m_pImpl->SetClientData(""); - } - named_iter->second.clear(); - m_mapNamedSocketChannel.erase(named_iter); - return(true); - } -} - -bool WorkerImpl::SwitchCodec(std::shared_ptr pChannel, E_CODEC_TYPE eCodecType) -{ - return(pChannel->m_pImpl->SwitchCodec(eCodecType, m_stWorkerInfo.dIoTimeout)); -} - -void WorkerImpl::BootLoadCmd(CJsonObject& oCmdConf) -{ - LOG4_TRACE(" "); - int iCmd = 0; - std::string strUrlPath; - for (int i = 0; i < oCmdConf["cmd"].GetArraySize(); ++i) - { - oCmdConf["cmd"][i].Get("cmd", iCmd); - MakeSharedCmd(nullptr, oCmdConf["cmd"][i]("class"), iCmd); - } - for (int j = 0; j < oCmdConf["module"].GetArraySize(); ++j) - { - oCmdConf["module"][j].Get("path", strUrlPath); - MakeSharedModule(nullptr, oCmdConf["module"][j]("class"), strUrlPath); - } -} - -void WorkerImpl::DynamicLoad(CJsonObject& oDynamicLoadingConf) -{ - LOG4_TRACE(" "); - std::string strVersion = "1.0"; - bool bIsload = false; - std::string strSoPath; - std::unordered_map::iterator so_iter; - tagSo* pSo = nullptr; - for (int i = 0; i < oDynamicLoadingConf.GetArraySize(); ++i) - { - oDynamicLoadingConf[i].Get("load", bIsload); - if (bIsload) - { - if (oDynamicLoadingConf[i].Get("so_path", strSoPath) && oDynamicLoadingConf[i].Get("version", strVersion)) - { - so_iter = m_mapLoadedSo.find(strSoPath); - if (so_iter == m_mapLoadedSo.end()) - { - std::string strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") - + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); - if (0 != access(strSoFile.c_str(), F_OK)) - { - strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); - if (0 != access(strSoFile.c_str(), F_OK)) - { - LOG4_WARNING("%s not exist!", strSoFile.c_str()); - continue; - } - } - pSo = LoadSo(strSoFile, strVersion); - if (pSo != nullptr) - { - LOG4_INFO("succeed in loading %s", strSoFile.c_str()); - m_mapLoadedSo.insert(std::make_pair(strSoPath, pSo)); - LoadDynamicSymbol(oDynamicLoadingConf[i]); - } - } - else - { - if (strVersion != so_iter->second->strVersion) // 版本升级,先卸载再加载 - { - std::string strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") - + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); - if (0 != access(strSoFile.c_str(), F_OK)) - { - strSoFile = m_stWorkerInfo.strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); - if (0 != access(strSoFile.c_str(), F_OK)) - { - LOG4_WARNING("%s not exist!", strSoFile.c_str()); - continue; - } - } - UnloadDynamicSymbol(oDynamicLoadingConf[i]); - dlclose(so_iter->second->pSoHandle); - delete so_iter->second; - pSo = LoadSo(strSoFile, strVersion); - if (pSo != nullptr) - { - LOG4_INFO("succeed in loading %s", strSoFile.c_str()); - so_iter->second = pSo; - LoadDynamicSymbol(oDynamicLoadingConf[i]); - } - else - { - m_mapLoadedSo.erase(so_iter); - } - } - } - } - } - else // 卸载动态库 - { - if (oDynamicLoadingConf[i].Get("so_path", strSoPath)) - { - so_iter = m_mapLoadedSo.find(strSoPath); - if (so_iter != m_mapLoadedSo.end()) - { - UnloadDynamicSymbol(oDynamicLoadingConf[i]); - dlclose(so_iter->second->pSoHandle); - delete so_iter->second; - m_mapLoadedSo.erase(so_iter); - LOG4_INFO("unload %s.%s", strSoPath.c_str(), - oDynamicLoadingConf[i]("version").c_str()); - } - } - } - } -} - -WorkerImpl::tagSo* WorkerImpl::LoadSo(const std::string& strSoPath, std::string strVersion) -{ - LOG4_TRACE(" "); - tagSo* pSo = new tagSo(); - if (NULL == pSo) - { - return(nullptr); - } - void* pHandle = NULL; - pHandle = dlopen(strSoPath.c_str(), RTLD_NOW); - char* dlsym_error = dlerror(); - if (dlsym_error) - { - LOG4_FATAL("cannot load dynamic lib %s!" , dlsym_error); - if (pHandle != NULL) - { - dlclose(pHandle); - } - delete pSo; - return(nullptr); - } - pSo->pSoHandle = pHandle; - pSo->strVersion = strVersion; - return(pSo); -} - -void WorkerImpl::LoadDynamicSymbol(CJsonObject& oOneSoConf) -{ - int32 iCmd = 0; - std::string strUrlPath; - for (int i = 0; i < oOneSoConf["cmd"].GetArraySize(); ++i) - { - std::unordered_set setCmd; - m_mapLoadedCmd.insert(std::make_pair(oOneSoConf["cmd"][i]("class"), setCmd)); - oOneSoConf["cmd"][i].Get("cmd", iCmd); - MakeSharedCmd(nullptr, oOneSoConf["cmd"][i]("class"), iCmd); - } - for (int j = 0; j < oOneSoConf["module"].GetArraySize(); ++j) - { - std::unordered_set setModule; - m_mapLoadedModule.insert(std::make_pair(oOneSoConf["module"][j]("class"), setModule)); - oOneSoConf["module"][j].Get("path", strUrlPath); - MakeSharedModule(nullptr, oOneSoConf["module"][j]("class"), strUrlPath); - } - for (int k = 0; k < oOneSoConf["session"].GetArraySize(); ++k) - { - std::unordered_set setSession; - m_mapLoadedSession.insert(std::make_pair(oOneSoConf["session"](k), setSession)); - } - for (int l = 0; l < oOneSoConf["step"].GetArraySize(); ++l) - { - std::unordered_set setStep; - m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); - } -} - -void WorkerImpl::UnloadDynamicSymbol(CJsonObject& oOneSoConf) -{ - for (int i = 0; i < oOneSoConf["cmd"].GetArraySize(); ++i) - { - auto class_iter = m_mapLoadedCmd.find(oOneSoConf["cmd"][i]("class")); - if (class_iter != m_mapLoadedCmd.end()) - { - for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) - { - auto cmd_iter = m_mapCmd.find(*id_iter); - if (cmd_iter != m_mapCmd.end()) - { - m_mapCmd.erase(cmd_iter); - } - } - class_iter->second.clear(); - m_mapLoadedCmd.erase(class_iter); - } - } - for (int j = 0; j < oOneSoConf["module"].GetArraySize(); ++j) - { - auto class_iter = m_mapLoadedModule.find(oOneSoConf["module"][j]("class")); - if (class_iter != m_mapLoadedModule.end()) - { - for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) - { - auto module_iter = m_mapModule.find(*id_iter); - if (module_iter != m_mapModule.end()) - { - m_mapModule.erase(module_iter); - } - } - class_iter->second.clear(); - m_mapLoadedModule.erase(class_iter); - } - } - for (int k = 0; k < oOneSoConf["session"].GetArraySize(); ++k) - { - auto class_iter = m_mapLoadedSession.find(oOneSoConf["session"](k)); - if (class_iter != m_mapLoadedSession.end()) - { - for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) - { - auto session_iter = m_mapCallbackSession.find(*id_iter); - if (session_iter != m_mapCallbackSession.end()) - { - m_mapCallbackSession.erase(session_iter); - } - } - class_iter->second.clear(); - m_mapLoadedSession.erase(class_iter); - } - } - for (int l = 0; l < oOneSoConf["step"].GetArraySize(); ++l) - { - auto class_iter = m_mapLoadedStep.find(oOneSoConf["step"](l)); - if (class_iter != m_mapLoadedStep.end()) - { - for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) - { - auto step_iter = m_mapCallbackStep.find(*id_iter); - if (step_iter != m_mapCallbackStep.end()) - { - step_iter->second->Timeout(); - m_mapCallbackStep.erase(step_iter); - } - } - class_iter->second.clear(); - m_mapLoadedStep.erase(class_iter); - } - std::unordered_set setStep; - m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); - } - for (int k = 0; k < oOneSoConf["model"].GetArraySize(); ++k) - { - auto class_iter = m_mapModel.find(oOneSoConf["model"](k)); - if (class_iter != m_mapModel.end()) - { - m_mapModel.erase(class_iter); - } - } -} - -bool WorkerImpl::AddPeriodicTaskEvent() -{ - LOG4_TRACE(" "); - ev_timer* timeout_watcher = (ev_timer*)malloc(sizeof(ev_timer)); - if (timeout_watcher == NULL) - { - LOG4_ERROR("malloc timeout_watcher error!"); - return(false); - } - ev_timer_init (timeout_watcher, PeriodicTaskCallback, NODE_BEAT + ev_time() - ev_now(m_loop), 0.); - timeout_watcher->data = (void*)this; - ev_timer_start (m_loop, timeout_watcher); - return(true); -} - -bool WorkerImpl::AddIoReadEvent(std::shared_ptr pChannel) -{ - LOG4_TRACE("fd[%d], seq[%u]", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); - if (NULL == io_watcher) - { - return(false); - } - else - { - if (ev_is_active(io_watcher)) - { - ev_io_stop(m_loop, io_watcher); - ev_io_set(io_watcher, io_watcher->fd, io_watcher->events | EV_READ); - ev_io_start (m_loop, io_watcher); - } - else - { - ev_io_init (io_watcher, IoCallback, pChannel->m_pImpl->GetFd(), EV_READ); - ev_io_start (m_loop, io_watcher); - } - return(true); - } -} - -bool WorkerImpl::AddIoWriteEvent(std::shared_ptr pChannel) -{ - LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); - if (NULL == io_watcher) - { - return(false); - } - else - { - if (ev_is_active(io_watcher)) - { - ev_io_stop(m_loop, io_watcher); - ev_io_set(io_watcher, io_watcher->fd, io_watcher->events | EV_WRITE); - ev_io_start (m_loop, io_watcher); - } - else - { - ev_io_init (io_watcher, IoCallback, pChannel->m_pImpl->GetFd(), EV_WRITE); - ev_io_start (m_loop, io_watcher); - } - return(true); - } -} - -bool WorkerImpl::RemoveIoWriteEvent(std::shared_ptr pChannel) -{ - LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); - if (NULL == io_watcher) - { - return(false); - } - if (EV_WRITE & io_watcher->events) - { - ev_io_stop(m_loop, io_watcher); - ev_io_set(io_watcher, io_watcher->fd, io_watcher->events & (~EV_WRITE)); - ev_io_start (m_loop, io_watcher); - } - return(true); -} - -void WorkerImpl::SetClientData(std::shared_ptr pChannel, const std::string& strClientData) -{ - pChannel->m_pImpl->SetClientData(strClientData); -} - -bool WorkerImpl::AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout) -{ - LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_timer* timer_watcher = pChannel->m_pImpl->MutableTimerWatcher(); - if (NULL == timer_watcher) - { - return(false); - } - else - { - if (ev_is_active(timer_watcher)) - { - ev_timer_stop(m_loop, timer_watcher); - ev_timer_set(timer_watcher, dTimeout + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, timer_watcher); - } - else - { - ev_timer_init (timer_watcher, IoTimeoutCallback, dTimeout + ev_time() - ev_now(m_loop), 0.); - ev_timer_start (m_loop, timer_watcher); - } - return(true); - } -} - -void WorkerImpl::AddAssemblyLine(std::shared_ptr pSession) -{ - m_setAssemblyLine.insert(pSession); -} - -bool WorkerImpl::SetWorkerConf(const CJsonObject& oJsonConf) -{ - m_oWorkerConf = oJsonConf; - return(true); -} - -bool WorkerImpl::SetCustomConf(const CJsonObject& oJsonConf) -{ - m_oCustomConf = oJsonConf; - return(m_oWorkerConf.Replace("custom", oJsonConf)); -} - -bool WorkerImpl::ReloadCmdConf() -{ - for (auto cmd_iter = m_mapCmd.begin(); cmd_iter != m_mapCmd.end(); ++cmd_iter) - { - cmd_iter->second->Init(); - } - for (auto module_iter = m_mapModule.begin(); module_iter != m_mapModule.end(); ++module_iter) - { - module_iter->second->Init(); - } - return(true); -} - -std::shared_ptr WorkerImpl::GetSession(uint64 ullSessionId) -{ - std::ostringstream oss; - oss << ullSessionId; - auto id_iter = m_mapCallbackSession.find(oss.str()); - if (id_iter == m_mapCallbackSession.end()) - { - return(nullptr); - } - else - { - id_iter->second->SetActiveTime(ev_now(m_loop)); - return(id_iter->second); - } -} - -std::shared_ptr WorkerImpl::GetSession(const std::string& strSessionId) -{ - auto id_iter = m_mapCallbackSession.find(strSessionId); - if (id_iter == m_mapCallbackSession.end()) - { - return(nullptr); - } - else - { - id_iter->second->SetActiveTime(ev_now(m_loop)); - return(id_iter->second); - } -} - -bool WorkerImpl::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg, void* data) -{ - auto iter = m_mapCallbackStep.find(uiStepSeq); - if (iter == m_mapCallbackStep.end()) - { - return(false); - } - else - { - iter->second->Emit(iErrno, strErrMsg, data); - return(true); - } -} - -std::shared_ptr WorkerImpl::GetModel(const std::string& strModelName) -{ - auto iter = m_mapModel.find(strModelName); - if (iter == m_mapModel.end()) - { - return(nullptr); - } - else - { - return(iter->second); - } -} - -std::shared_ptr WorkerImpl::CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient, bool bWithSsl) -{ - LOG4_DEBUG("iFd %d, codec_type %d, with_ssl = %d", iFd, eCodecType, bWithSsl); - - std::shared_ptr pChannel = nullptr; - try - { - pChannel = std::make_shared(m_pLogger, iFd, GetSequence(), bWithSsl); - } - catch(std::bad_alloc& e) - { - LOG4_ERROR("new channel for fd %d error: %s", e.what()); - return(nullptr); - } - pChannel->m_pImpl->SetLabor(m_pWorker); - bool bInitResult = pChannel->m_pImpl->Init(eCodecType, bIsClient); - if (bInitResult) - { - m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); - ++m_stWorkerInfo.iConnectionNum; - if (CODEC_NEBULA != eCodecType) - { - ++m_stWorkerInfo.iClientNum; - } - return(pChannel); - } - else - { - return(nullptr); - } -} - -bool WorkerImpl::DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice) -{ - LOG4_DEBUG("%s disconnect, fd %d, identify %s", pChannel->m_pImpl->GetRemoteAddr().c_str(), pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetIdentify().c_str()); - if (bChannelNotice) - { - ChannelNotice(pChannel, pChannel->m_pImpl->GetIdentify(), pChannel->m_pImpl->GetClientData()); - } - bool bCloseResult = pChannel->m_pImpl->Close(); - if (bCloseResult) - { - ev_io_stop (m_loop, pChannel->m_pImpl->MutableIoWatcher()); - if (nullptr != pChannel->m_pImpl->MutableTimerWatcher()) - { - ev_timer_stop (m_loop, pChannel->m_pImpl->MutableTimerWatcher()); - } - - auto named_iter = m_mapNamedSocketChannel.find(pChannel->m_pImpl->GetIdentify()); - if (named_iter != m_mapNamedSocketChannel.end()) - { - for (auto it = named_iter->second.begin(); - it != named_iter->second.end(); ++it) - { - if ((*it)->m_pImpl->GetSequence() == pChannel->m_pImpl->GetSequence()) - { - named_iter->second.erase(it); - LOG4_TRACE("erase channel %d from m_mapNamedSocketChannel.", pChannel->m_pImpl->GetFd()); - break; - } - } - if (0 == named_iter->second.size()) - { - m_mapNamedSocketChannel.erase(named_iter); - } - } - - auto channel_iter = m_mapSocketChannel.find(pChannel->m_pImpl->GetFd()); - if (channel_iter != m_mapSocketChannel.end()) - { - m_mapSocketChannel.erase(channel_iter); - --m_stWorkerInfo.iConnectionNum; - if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType()) - { - --m_stWorkerInfo.iClientNum; - } - LOG4_TRACE("erase channel %d from m_mapSocketChannel.", pChannel->m_pImpl->GetFd()); - } - return(true); - } - else - { - return(bCloseResult); - } -} - -void WorkerImpl::RemoveStep(std::shared_ptr pStep) -{ - if (nullptr == pStep) - { - return; - } - std::unordered_map >::iterator callback_iter; - for (auto step_seq_iter = pStep->m_setPreStepSeq.begin(); - step_seq_iter != pStep->m_setPreStepSeq.end(); ) - { - callback_iter = m_mapCallbackStep.find(*step_seq_iter); - if (callback_iter == m_mapCallbackStep.end()) - { - pStep->m_setPreStepSeq.erase(step_seq_iter++); - } - else - { - LOG4_DEBUG("step %u had pre step %u running, delay delete callback.", pStep->GetSequence(), *step_seq_iter); - ResetTimeout(std::dynamic_pointer_cast(pStep)); - return; - } - } - auto class_iter = m_mapLoadedStep.find(pStep->GetActorName()); - if (class_iter != m_mapLoadedStep.end()) - { - auto id_iter = class_iter->second.find(pStep->GetSequence()); - if (id_iter != class_iter->second.end()) - { - class_iter->second.erase(id_iter); - } - } - if (pStep->MutableTimerWatcher() != NULL) - { - ev_timer_stop (m_loop, pStep->MutableTimerWatcher()); - } - callback_iter = m_mapCallbackStep.find(pStep->GetSequence()); - if (callback_iter != m_mapCallbackStep.end()) - { - LOG4_TRACE("erase step(seq %u)", pStep->GetSequence()); - m_mapCallbackStep.erase(callback_iter); - } -} - -void WorkerImpl::RemoveSession(std::shared_ptr pSession) -{ - if (nullptr == pSession) - { - return; - } - auto class_iter = m_mapLoadedSession.find(pSession->GetActorName()); - if (class_iter != m_mapLoadedSession.end()) - { - auto id_iter = class_iter->second.find(pSession->GetSessionId()); - if (id_iter != class_iter->second.end()) - { - class_iter->second.erase(id_iter); - } - } - if (pSession->MutableTimerWatcher() != NULL) - { - ev_timer_stop (m_loop, pSession->MutableTimerWatcher()); - } - auto iter = m_mapCallbackSession.find(pSession->GetSessionId()); - if (iter != m_mapCallbackSession.end()) - { - LOG4_TRACE("erase session(session_id %s)", pSession->GetSessionId().c_str()); - m_mapCallbackSession.erase(iter); - } -} - -void WorkerImpl::RemoveChain(uint32 uiChainId) -{ - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - std::shared_ptr pChain = chain_iter->second; - if (pChain->MutableTimerWatcher() != NULL) - { - ev_timer_stop (m_loop, pChain->MutableTimerWatcher()); - } - m_mapChain.erase(chain_iter); - } -} - -void WorkerImpl::ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData) -{ - LOG4_TRACE(" "); - auto cmd_iter = m_mapCmd.find(CMD_REQ_DISCONNECT); - if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) - { - MsgHead oMsgHead; - MsgBody oMsgBody; - oMsgBody.mutable_req_target()->set_route_id(0); - oMsgBody.set_data(strIdentify); - oMsgBody.set_add_on(strClientData); - oMsgHead.set_cmd(CMD_REQ_DISCONNECT); - oMsgHead.set_seq(GetSequence()); - oMsgHead.set_len(oMsgBody.ByteSize()); - std::ostringstream oss; - oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - cmd_iter->second->SetTraceId(oss.str()); - cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); - } -} - -bool WorkerImpl::Handle(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) -{ - LOG4_DEBUG("cmd %u, seq %u", oMsgHead.cmd(), oMsgHead.seq()); - if (gc_uiCmdReq & oMsgHead.cmd()) // 新请求 - { - MsgHead oOutMsgHead; - MsgBody oOutMsgBody; - auto cmd_iter = m_mapCmd.find(gc_uiCmdBit & oMsgHead.cmd()); - if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) - { - if (oMsgBody.trace_id().length() > 10) - { - cmd_iter->second->SetTraceId(oMsgBody.trace_id()); - } - else - { - std::ostringstream oss; - oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - cmd_iter->second->SetTraceId(oss.str()); - } - cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); - } - else // 没有对应的cmd,是需由接入层转发的请求 - { - if (CMD_REQ_SET_LOG_LEVEL == oMsgHead.cmd()) - { - LogLevel oLogLevel; - oLogLevel.ParseFromString(oMsgBody.data()); - LOG4_INFO("log level set to %d", oLogLevel.log_level()); - m_pLogger->SetLogLevel(oLogLevel.log_level()); - } - else if (CMD_REQ_RELOAD_SO == oMsgHead.cmd()) - { - CJsonObject oSoConfJson; - if (oSoConfJson.Parse(oMsgBody.data())) - { - DynamicLoad(oSoConfJson); - } - else - { - LOG4_ERROR("json parse string error: \"%s\""); - } - } - else - { - if (CODEC_NEBULA == pChannel->m_pImpl->GetCodecType()) // 内部服务往客户端发送 if (std::string("0.0.0.0") == strFromIp) - { - cmd_iter = m_mapCmd.find(CMD_REQ_TO_CLIENT); - if (cmd_iter != m_mapCmd.end()) - { - if (oMsgBody.trace_id().length() > 10) - { - cmd_iter->second->SetTraceId(oMsgBody.trace_id()); - } - else - { - std::ostringstream oss; - oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - cmd_iter->second->SetTraceId(oss.str()); - } - cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); - } - else - { - snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); - LOG4_ERROR(m_pErrBuff); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); - oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); - oOutMsgHead.set_cmd(CMD_RSP_SYS_ERROR); - oOutMsgHead.set_seq(oMsgHead.seq()); - oOutMsgHead.set_len(oOutMsgBody.ByteSize()); - } - } - else - { - cmd_iter = m_mapCmd.find(CMD_REQ_FROM_CLIENT); - if (cmd_iter != m_mapCmd.end()) - { - if (oMsgBody.trace_id().length() > 10) - { - cmd_iter->second->SetTraceId(oMsgBody.trace_id()); - } - else - { - std::ostringstream oss; - oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - cmd_iter->second->SetTraceId(oss.str()); - } - cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); - } - else - { - snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); - LOG4_ERROR(m_pErrBuff); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); - oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); - oOutMsgHead.set_cmd(CMD_RSP_SYS_ERROR); - oOutMsgHead.set_seq(oMsgHead.seq()); - oOutMsgHead.set_len(oOutMsgBody.ByteSize()); - } - } -//#else - snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); - LOG4_ERROR(m_pErrBuff); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); - oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); - oOutMsgHead.set_cmd(CMD_RSP_SYS_ERROR); - oOutMsgHead.set_seq(oMsgHead.seq()); - oOutMsgHead.set_len(oOutMsgBody.ByteSize()); -//#endif - } - } - if (CMD_RSP_SYS_ERROR == oOutMsgHead.cmd()) - { - LOG4_TRACE("no cmd handler."); - SendTo(pChannel, oOutMsgHead.cmd(), oOutMsgHead.seq(), oOutMsgBody); - return(false); - } - } - else // 回调 - { - auto step_iter = m_mapCallbackStep.find(oMsgHead.seq()); - if (step_iter != m_mapCallbackStep.end()) // 步骤回调 - { - LOG4_TRACE("receive message, cmd = %d", - oMsgHead.cmd()); - if (step_iter->second != nullptr) - { - E_CMD_STATUS eResult; - step_iter->second->SetActiveTime(ev_now(m_loop)); - LOG4_TRACE("cmd %u, seq %u, step_seq %u, active_time %lf", - oMsgHead.cmd(), oMsgHead.seq(), step_iter->second->GetSequence(), - step_iter->second->GetActiveTime()); - eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); - if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) - { - uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); - if (0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - } - ExecAssemblyLine(pChannel, oMsgHead, oMsgBody); - } - else - { - snprintf(m_pErrBuff, gc_iErrBuffLen, "no callback or the callback for seq %u had been timeout!", oMsgHead.seq()); - LOG4_WARNING(m_pErrBuff); - return(false); - } - } - return(true); -} - -bool WorkerImpl::Handle(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) -{ - LOG4_DEBUG("oInHttpMsg.type() = %d, oInHttpMsg.path() = %s", - oHttpMsg.type(), oHttpMsg.path().c_str()); - if (HTTP_REQUEST == oHttpMsg.type()) // 新请求 - { - auto module_iter = m_mapModule.find(oHttpMsg.path()); - if (module_iter == m_mapModule.end()) - { - module_iter = m_mapModule.find("/switch"); - if (module_iter == m_mapModule.end()) - { - HttpMsg oOutHttpMsg; - snprintf(m_pErrBuff, gc_iErrBuffLen, "no module to dispose %s!", oHttpMsg.path().c_str()); - LOG4_ERROR(m_pErrBuff); - oOutHttpMsg.set_type(HTTP_RESPONSE); - oOutHttpMsg.set_status_code(404); - oOutHttpMsg.set_http_major(oHttpMsg.http_major()); - oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); - pChannel->m_pImpl->Send(oOutHttpMsg, 0); - } - else - { - std::ostringstream oss; - oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - module_iter->second->SetTraceId(oss.str()); - module_iter->second->AnyMessage(pChannel, oHttpMsg); - } - } - else - { - std::ostringstream oss; - oss << m_stWorkerInfo.uiNodeId << "." << GetNowTime() << "." << GetSequence(); - module_iter->second->SetTraceId(oss.str()); - module_iter->second->AnyMessage(pChannel, oHttpMsg); - } - } - else - { - auto http_step_iter = m_mapCallbackStep.find(pChannel->m_pImpl->GetStepSeq()); - if (http_step_iter == m_mapCallbackStep.end()) - { - LOG4_ERROR("no callback for http response from %s!", oHttpMsg.url().c_str()); - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED, 0); - AddNamedSocketChannel(pChannel->m_pImpl->GetIdentify(), pChannel); // push back to named socket channel pool. - } - else - { - E_CMD_STATUS eResult; - http_step_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = (std::dynamic_pointer_cast(http_step_iter->second))->Callback(pChannel, oHttpMsg); - if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) - { - uint32 uiChainId = http_step_iter->second->GetChainId(); - RemoveStep(http_step_iter->second); - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED, 0); - AddNamedSocketChannel(pChannel->m_pImpl->GetIdentify(), pChannel); // push back to named socket channel pool. - if (0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - } - } - return(true); -} - -void WorkerImpl::ExecAssemblyLine(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) -{ - for (auto session_iter = m_setAssemblyLine.begin(); session_iter != m_setAssemblyLine.end(); ++session_iter) - { - uint32 uiStepSeq = 0; - do - { - uiStepSeq = (*session_iter)->PopWaitingStep(); - auto step_iter = m_mapCallbackStep.find(uiStepSeq); - if (step_iter != m_mapCallbackStep.end()) - { - E_CMD_STATUS eResult; - step_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); - if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) - { - uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); - if (0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(ev_now(m_loop)); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - } - } - while(uiStepSeq > 0); - } - m_setAssemblyLine.clear(); -} - -} /* namespace neb */ diff --git a/src/labor/WorkerImpl.hpp b/src/labor/WorkerImpl.hpp deleted file mode 100644 index dc69a986..00000000 --- a/src/labor/WorkerImpl.hpp +++ /dev/null @@ -1,398 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file Worker.hpp - * @brief 工作者 - * @author Bwar - * @date: 2018年2月27日 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_LABOR_WORKERIMPL_HPP_ -#define SRC_LABOR_WORKERIMPL_HPP_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#pragma GCC diagnostic ignored "-Wunused-function" -#endif -#include "ev.h" -#include "hiredis/hiredis.h" -#include "hiredis/async.h" -#include "hiredis/adapters/libev.h" -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -#ifdef __cplusplus -} -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "pb/msg.pb.h" -#include "pb/http.pb.h" -#include "pb/neb_sys.pb.h" -#include "util/CBuffer.hpp" -#include "labor/Labor.hpp" -#include "channel/SocketChannel.hpp" -#include "channel/RedisChannel.hpp" -#include "codec/Codec.hpp" -#include "logger/NetLogger.hpp" - -#include "actor/ActorFactory.hpp" - -namespace neb -{ - -class Worker; - -class Actor; -class Cmd; -class Module; -class Session; -class Timer; -class Context; -class Step; -class RedisStep; -class HttpStep; -class Model; -class Chain; - -class SessionNode; -class SessionLogger; - -class WorkerImpl -{ -public: - struct tagWorkerInfo - { - uint32 uiNodeId; ///< 节点ID - int32 iManagerControlFd; ///< 与Manager父进程通信fd(控制流) - int32 iManagerDataFd; ///< 与Manager父进程通信fd(数据流) - int32 iWorkerIndex; - int32 iWorkerPid; - int32 iConnectionNum; - int32 iClientNum; - int32 iRecvNum; ///< 接收数据包(head+body)数量 - int32 iRecvByte; ///< 接收字节数(已到达应用层缓冲区) - int32 iSendNum; ///< 发送数据包(head+body)数量(只到达应用层缓冲区,不一定都已发送出去) - int32 iSendByte; ///< 发送字节数(已到达系统发送缓冲区,可认为已发送出去) - int32 iMsgPermitNum; ///< 客户端统计时间内允许发送消息数量 - bool bIsAccess; ///< 是否接入Server - ev_tstamp dMsgStatInterval; ///< 客户端连接发送数据包统计时间间隔 - ev_tstamp dIoTimeout; ///< IO(连接)超时配置 - ev_tstamp dStepTimeout; ///< 步骤超时 - int iPortForServer; ///< Server间通信监听端口(用于生成当前Worker标识) - std::string strHostForServer; ///< 对其他Server服务的IP地址(用于生成当前Worker标识) - std::string strNodeType; ///< 节点类型 - std::string strWorkPath; ///< 工作路径 - std::string strWorkerIdentify; ///< 进程标识 - - tagWorkerInfo() - : uiNodeId(0), iManagerControlFd(0), iManagerDataFd(0), iWorkerIndex(0), iWorkerPid(0), - iConnectionNum(0), iClientNum(0), - iRecvNum(0), iRecvByte(0), iSendNum(0), iSendByte(0), iMsgPermitNum(60), bIsAccess(false), - dMsgStatInterval(60.0), dIoTimeout(480.0), dStepTimeout(3.0), iPortForServer(15000) - { - } - }; - - struct tagSo - { - void* pSoHandle; - std::string strVersion; - std::vector vecCmd; - std::vector vecPath; - tagSo(){}; - ~tagSo(){}; - }; - - // callback - static void TerminatedCallback(struct ev_loop* loop, struct ev_signal* watcher, int revents); - static void IdleCallback(struct ev_loop* loop, struct ev_idle* watcher, int revents); - static void IoCallback(struct ev_loop* loop, struct ev_io* watcher, int revents); - static void IoTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); - static void PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, int revents); - static void StepTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); - static void SessionTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); - static void ChainTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); - static void RedisConnectCallback(const redisAsyncContext *c, int status); - static void RedisDisconnectCallback(const redisAsyncContext *c, int status); - static void RedisCmdCallback(redisAsyncContext *c, void *reply, void *privdata); - -public: - WorkerImpl(Worker* pWorker, const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex, CJsonObject& oJsonConf); - WorkerImpl(const WorkerImpl&) = delete; - WorkerImpl& operator=(const WorkerImpl&) = delete; - virtual ~WorkerImpl(); - - void Terminated(struct ev_signal* watcher); - bool CheckParent(); - bool OnIoRead(std::shared_ptr pChannel); - bool RecvDataAndHandle(std::shared_ptr pChannel); - bool FdTransfer(); - bool OnIoWrite(std::shared_ptr pChannel); - bool OnIoTimeout(std::shared_ptr pChannel); - bool OnStepTimeout(std::shared_ptr pStep); - bool OnSessionTimeout(std::shared_ptr pSession); - bool OnChainTimeout(std::shared_ptr pChain); - bool OnRedisConnected(const redisAsyncContext *c, int status); - bool OnRedisDisconnected(const redisAsyncContext *c, int status); - bool OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privdata); - - void Run(); - -public: // about worker - virtual uint32 GetSequence() const - { - ++m_ulSequence; - if (0 == m_ulSequence) - { - ++m_ulSequence; - } - return(m_ulSequence); - } - - const tagWorkerInfo& GetWorkerInfo() const - { - return(m_stWorkerInfo); - } - - const CJsonObject& GetCustomConf() const - { - return(m_oCustomConf); - } - - virtual time_t GetNowTime() const; - virtual bool ResetTimeout(std::shared_ptr pSharedActor); - - template - void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); - template - void Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); - - template - std::shared_ptr MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args); - - template - std::shared_ptr MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs&&... args); - - template - std::shared_ptr MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs&&... args); - - template - std::shared_ptr MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args); - - template - std::shared_ptr MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args); - - template - std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args); - - template - std::shared_ptr MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs&&... args); - - template - std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args); - -protected: - template Actor* NewActor(const std::string& strActorName, Targs... args); - std::shared_ptr InitializeSharedActor(Actor* pCreator, std::shared_ptr pSharedActor, const std::string& strActorName); - bool TransformToSharedCmd(Actor* pCreator, std::shared_ptr pSharedActor); - bool TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor); - bool TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor); - bool TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor); - bool TransformToSharedModel(Actor* pCreator, std::shared_ptr pSharedActor); - bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); - -public: // about channel - virtual bool AddNetLogMsg(const MsgBody& oMsgBody); - - // SendTo() for nebula socket - virtual bool SendTo(std::shared_ptr pChannel); - virtual bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - virtual bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - - // SendTo() for http - virtual bool SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); - virtual bool SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); - virtual bool AutoSend(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); - - // SendTo() for redis - virtual bool SendTo(std::shared_ptr pRedisChannel, Actor* pSender); - virtual bool SendTo(const std::string& strIdentify, Actor* pSender); - virtual bool SendTo(const std::string& strHost, int iPort, Actor* pSender); - virtual bool AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep); - - virtual bool Disconnect(std::shared_ptr pChannel, bool bChannelNotice = true); - virtual bool Disconnect(const std::string& strIdentify, bool bChannelNotice = true); - virtual bool DiscardNamedChannel(const std::string& strIdentify); - virtual bool SwitchCodec(std::shared_ptr pChannel, E_CODEC_TYPE eCodecType); - -public: - // about session - virtual std::shared_ptr GetSession(uint64 ullSessionId); - virtual std::shared_ptr GetSession(const std::string& strSessionId); - - // about step - virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); - - // about model - virtual std::shared_ptr GetModel(const std::string& strModelName); - -public: // Worker相关设置(由专用Cmd类调用这些方法完成Worker自身的初始化和更新) - virtual bool SetProcessName(const CJsonObject& oJsonConf); - virtual bool AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel); - virtual void DelNamedSocketChannel(const std::string& strIdentify); - virtual void SetChannelIdentify(std::shared_ptr pChannel, const std::string& strIdentify); - virtual bool AddNamedRedisChannel(const std::string& strIdentify, std::shared_ptr pChannel); - virtual void DelNamedRedisChannel(const std::string& strIdentify); - virtual void AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); - virtual void DelNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); - virtual void SetNodeId(uint32 uiNodeId) {m_stWorkerInfo.uiNodeId = uiNodeId;} - virtual void SetClientData(std::shared_ptr pChannel, const std::string& strClientData); - - bool AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout = 1.0); - void AddAssemblyLine(std::shared_ptr pSession); - - bool SetWorkerConf(const CJsonObject& oJsonConf); - const CJsonObject& GetWorkerConf() const - { - return(m_oWorkerConf); - } - bool SetCustomConf(const CJsonObject& oJsonConf); - bool ReloadCmdConf(); - -protected: - bool Init(CJsonObject& oJsonConf); - bool InitLogger(const CJsonObject& oJsonConf); - bool CreateEvents(); - void LoadSysCmd(); - void Destroy(); - bool AddPeriodicTaskEvent(); - bool AddIoReadEvent(std::shared_ptr pChannel); - bool AddIoWriteEvent(std::shared_ptr pChannel); - bool RemoveIoWriteEvent(std::shared_ptr pChannel); - std::shared_ptr CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient = false, bool bWithSsl = false); - bool DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice = true); - void RemoveStep(std::shared_ptr pStep); - void RemoveSession(std::shared_ptr pSession); - void RemoveChain(uint32 uiChainId); - void ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData); - - /** - * @brief 收到完整数据包后处理 - * @note - *

-     * 框架层接收并解析到完整的数据包后调用此函数做数据处理。数据处理可能有多重不同情况出现:
-     * 1. 处理成功,仍需继续解析数据包;
-     * 2. 处理成功,但无需继续解析数据包;
-     * 3. 处理失败,仍需继续解析数据包;
-     * 4. 处理失败,但无需继续解析数据包。
-     * 是否需要退出解析数据包循环体取决于Dispose()的返回值。返回true就应继续解析数据包,返回
-     * false则应退出解析数据包循环体。处理过程或处理完成后,如需回复对端,则直接使用pSendBuff
-     * 回复数据即可。
-     * 
- * @param[in] pChannel 数据包来源消息通道 - * @param[in] oMsgHead 接收的数据包头 - * @param[in] oMsgBody 接收的数据包体 - * @return 是否正常处理 - */ - bool Handle(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); - - /** - * @brief 收到完整的hhtp包后处理 - * @param pChannel 数据包来源消息通道 - * @param oHttpMsg 接收的HTTP包 - * @return 是否正常处理 - */ - bool Handle(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); - - void ExecAssemblyLine(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); - - void BootLoadCmd(CJsonObject& oCmdConf); - void DynamicLoad(CJsonObject& oSoConf); - tagSo* LoadSo(const std::string& strSoPath, std::string strVersion); - void LoadDynamicSymbol(CJsonObject& oOneSoConf); - void UnloadDynamicSymbol(CJsonObject& oOneSoConf); - -private: - mutable uint32 m_ulSequence = 0; - char* m_pErrBuff; - Worker* m_pWorker; - std::shared_ptr m_pLogger = nullptr; - - tagWorkerInfo m_stWorkerInfo; - CJsonObject m_oWorkerConf; - CJsonObject m_oCustomConf; ///< 自定义配置 - - struct ev_loop* m_loop; - std::shared_ptr m_pManagerControlChannel; // std::unique_ptr is perfect - std::shared_ptr m_pManagerDataChannel; - std::unique_ptr m_pSessionNode; - std::shared_ptr m_pSessionLogger; - - // dynamic load,use for load and unload. - std::unordered_map m_mapLoadedSo; - std::unordered_map > m_mapLoadedCmd; //key为CmdClassName,value为iCmd集合 - std::unordered_map > m_mapLoadedModule; //key为ModuleClassName,value为strModulePath集合 - std::unordered_map > m_mapLoadedStep; //key为StepClassName,value为uiSeq集合 - std::unordered_map > m_mapLoadedSession; //key为SessionClassName,value为strSessionId集合 - - // Cmd and Module - std::unordered_map > m_mapCmd; - std::unordered_map > m_mapModule; - - // Chain and Model - std::unordered_map > > m_mapChainConf; //key为Chain的配置名(ChainFlag),value为由Model类名和Step类名构成的ChainBlock链 - std::unordered_map > m_mapChain; //key为Chain的Sequence,称为ChainId - std::unordered_map > m_mapModel; //key为Model类名 - - // Step and Session - std::unordered_map > m_mapCallbackStep; - std::unordered_map > m_mapCallbackSession; - std::unordered_set > m_setAssemblyLine; ///< 资源就绪后执行队列 - - // Channel - std::unordered_map > m_mapSocketChannel; - std::unordered_map > m_mapRedisChannel; - - /* named Channel */ - std::unordered_map > > m_mapNamedSocketChannel; ///< key为Identify,连接存在时,if(http连接)list.size()>=1;else list.size()==1; - std::unordered_map > > m_mapNamedRedisChannel; ///< key为identify,list.size()==1 -}; - - -} /* namespace neb */ - - -#include "WorkerImpl.inl" - -#endif /* SRC_LABOR_WORKERIMPL_HPP_ */ diff --git a/src/labor/WorkerImpl.inl b/src/labor/WorkerImpl.inl deleted file mode 100644 index 836599fd..00000000 --- a/src/labor/WorkerImpl.inl +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************************************* -* Project: Nebula -* @file WorkerImpl.inl -* @brief -* @author bwar -* @date: Feb 26, 2018 -* @note -* Modify history: -******************************************************************************/ -#ifndef LABOR_CHIEF_WORKERIMPL_INL_ -#define LABOR_CHIEF_WORKERIMPL_INL_ - -namespace neb -{ - -template -void WorkerImpl::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) -{ - m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); -} - -template -void WorkerImpl::Logger(const std::string& strTraceId, int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) -{ - m_pLogger->WriteLog(strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); -} - -template -std::shared_ptr WorkerImpl::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs... args) -{ - Actor* pActor = ActorFactory::Instance()->Create(strActorName, std::forward(args)...); - if (nullptr == pActor) - { - /** - * @brief 为兼容&&参数推导差异导致ActorFactory未Regist进而导致 - * ActorFactory::Instance()->Create()调用不到对应的创建函数而增加。 - * NewActor()参数将按值传递,如果调用到NewActor()才new成功,代价会相对大些。 - */ - pActor = NewActor(strActorName, std::forward(args)...); - if (nullptr == pActor) - { - LOG4_ERROR("failed to make shared actor \"%s\"", strActorName.c_str()); - return(nullptr); - } - } - std::shared_ptr pSharedActor; - pSharedActor.reset(pActor); - pActor = nullptr; - return(InitializeSharedActor(pCreator, pSharedActor, strActorName)); -} - -template -std::shared_ptr WorkerImpl::MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs&&... args) -{ - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strCmdName, std::forward(args)...))); -} - -template -std::shared_ptr WorkerImpl::MakeSharedModule(Actor* pCreator, const std::string& strModuleName, Targs&&... args) -{ - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModuleName, std::forward(args)...))); -} - -template -std::shared_ptr WorkerImpl::MakeSharedStep(Actor* pCreator, const std::string& strStepName, Targs&&... args) -{ - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strStepName, std::forward(args)...))); -} - -template -std::shared_ptr WorkerImpl::MakeSharedSession(Actor* pCreator, const std::string& strSessionName, Targs&&... args) -{ - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strSessionName, std::forward(args)...))); -} - -template -std::shared_ptr WorkerImpl::MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args) -{ - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strContextName, std::forward(args)...))); -} - -template -std::shared_ptr WorkerImpl::MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs&&... args) -{ - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModelName, std::forward(args)...))); -} - -template -std::shared_ptr WorkerImpl::MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args) -{ - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strChainName, std::forward(args)...))); -} - -template -Actor* WorkerImpl::NewActor(const std::string& strActorName, Targs... args) -{ - LOG4_TRACE("%s() called by MakeSharedActor().", __FUNCTION__); - return(ActorFactory::Instance()->Create(strActorName, std::forward(args)...)); -} - -} - -#endif /* LABOR_WORKERIMPL_INL_ */ diff --git a/src/logger/NetLogger.cpp b/src/logger/NetLogger.cpp index df835148..5e8ec047 100644 --- a/src/logger/NetLogger.cpp +++ b/src/logger/NetLogger.cpp @@ -12,6 +12,7 @@ #include #include "pb/msg.pb.h" #include "pb/neb_sys.pb.h" +#include "labor/NodeInfo.hpp" #include "labor/Labor.hpp" #include "actor/cmd/CW.hpp" #include "NetLogger.hpp" @@ -52,8 +53,8 @@ int NetLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLin MsgBody oMsgBody; TraceLog oTraceLog; //oTraceLog.set_log_time(); - oTraceLog.set_node_type(m_pLabor->GetNodeType()); - oTraceLog.set_node_identify(m_pLabor->GetNodeIdentify()); + oTraceLog.set_node_type(m_pLabor->GetNodeInfo().strNodeType); + oTraceLog.set_node_identify(m_pLabor->GetNodeInfo().strNodeIdentify); oTraceLog.set_log_level(LogLevMsg[iLev]); oTraceLog.set_code_file_name(szFileName); oTraceLog.set_code_file_line(uiFileLine); @@ -62,7 +63,7 @@ int NetLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLin //oMsgBody.set_data(oTraceLog.SerializeAsString()); oTraceLog.SerializeToString(&m_strLogData); oMsgBody.set_data(m_strLogData); - oMsgBody.mutable_req_target()->set_route(m_pLabor->GetNodeIdentify()); + oMsgBody.mutable_req_target()->set_route(m_pLabor->GetNodeInfo().strNodeIdentify); m_pLabor->AddNetLogMsg(oMsgBody); } @@ -76,7 +77,7 @@ int NetLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szF vsnprintf(m_pLogBuff, gc_uiMaxLogLineLen, szLogStr, ap); va_end(ap); - if (strTraceId == m_pLabor->GetNodeIdentify()) + if (strTraceId == m_pLabor->GetNodeInfo().strNodeIdentify) { m_pLog->WriteLog(iLev, szFileName, uiFileLine, szFunction, m_pLogBuff); } @@ -88,8 +89,8 @@ int NetLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szF { MsgBody oMsgBody; TraceLog oTraceLog; - oTraceLog.set_node_type(m_pLabor->GetNodeType()); - oTraceLog.set_node_identify(m_pLabor->GetNodeIdentify()); + oTraceLog.set_node_type(m_pLabor->GetNodeInfo().strNodeType); + oTraceLog.set_node_identify(m_pLabor->GetNodeInfo().strNodeIdentify); oTraceLog.set_log_level(LogLevMsg[iLev]); oTraceLog.set_code_file_name(szFileName); oTraceLog.set_code_file_line(uiFileLine); @@ -105,7 +106,7 @@ int NetLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szF } else { - oMsgBody.mutable_req_target()->set_route(m_pLabor->GetNodeIdentify()); + oMsgBody.mutable_req_target()->set_route(m_pLabor->GetNodeInfo().strNodeIdentify); } m_pLabor->AddNetLogMsg(oMsgBody); } From b037ffdfea9bb9d6cbb1282c43556e0f691e8dc3 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 12 Oct 2019 22:28:15 +0800 Subject: [PATCH 061/176] add loader --- src/actor/ActorBuilder.cpp | 2 +- src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp | 1 + .../cmd/sys_cmd/manager/CmdOnNodeNotice.cpp | 7 + .../cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp | 10 + .../sys_session/manager/SessionManager.cpp | 181 +++++++++--- .../sys_session/manager/SessionManager.hpp | 10 +- src/actor/step/sys_step/StepTellWorker.cpp | 2 - src/channel/SocketChannelImpl.cpp | 4 + src/codec/Codec.hpp | 1 + src/ios/Dispatcher.cpp | 278 ++++++++++++++++-- src/ios/Dispatcher.hpp | 3 + src/ios/Nodes.cpp | 2 +- src/ios/Nodes.hpp | 13 +- src/labor/Labor.hpp | 2 +- src/labor/Loader.cpp | 38 +++ src/labor/Loader.hpp | 31 ++ src/labor/Manager.cpp | 109 ++++++- src/labor/Manager.hpp | 1 + src/labor/Worker.cpp | 41 ++- src/labor/Worker.hpp | 11 +- 20 files changed, 639 insertions(+), 108 deletions(-) create mode 100644 src/labor/Loader.cpp create mode 100644 src/labor/Loader.hpp diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index f3fd1957..fa5335ac 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -712,7 +712,7 @@ void ActorBuilder::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdBeat", (int)CMD_REQ_BEAT); if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) { - MakeSharedCmd(nullptr, "neb::CmdUpdateWorkerLoad", (int)CMD_REQ_UPDATE_WORKER_LOAD); + MakeSharedCmd(nullptr, "neb::CmdWorkerLoad", (int)CMD_REQ_UPDATE_WORKER_LOAD); MakeSharedCmd(nullptr, "neb::CmdOnTellWorker", (int)CMD_REQ_TELL_WORKER); MakeSharedCmd(nullptr, "neb::CmdOnOrientationFdTransfer", (int)CMD_REQ_CONNECT_TO_WORKER); MakeSharedCmd(nullptr, "neb::CmdOnNodeNotice", (int)CMD_REQ_NODE_NOTICE); diff --git a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp index 898be2ef..3075d77b 100644 --- a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp +++ b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp @@ -8,6 +8,7 @@ * Modify history: ******************************************************************************/ #include "actor/cmd/sys_cmd/CmdReloadCustomConf.hpp" +#include "actor/ActorBuilder.hpp" namespace neb { diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp index 450a262b..5f5fb456 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp @@ -30,10 +30,14 @@ bool CmdOnNodeNotice::AnyMessage( const MsgHead& oInMsgHead, const MsgBody& oInMsgBody) { + MsgBody oOutMsgBody; CJsonObject oJson; if (!oJson.Parse(oInMsgBody.data())) { LOG4_ERROR("failed to parse msgbody content!"); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_BODY_JSON); + oOutMsgBody.mutable_rsp_result()->set_msg("failed to pase msgbody content!"); + SendTo(pChannel, CMD_RSP_NODE_NOTICE, oInMsgHead.seq(), oOutMsgBody); return(false); } if (m_pSessionManager == nullptr) @@ -46,6 +50,9 @@ bool CmdOnNodeNotice::AnyMessage( return(false); } } + oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); + oOutMsgBody.mutable_rsp_result()->set_msg("success"); + SendTo(pChannel, CMD_RSP_NODE_NOTICE, oInMsgHead.seq(), oOutMsgBody); m_pSessionManager->SendToWorker(CMD_REQ_NODE_NOTICE, GetSequence(), oInMsgBody); std::ostringstream oss; std::string strIdentify; diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp index 5978172a..27c04a45 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp @@ -31,6 +31,16 @@ bool CmdOnWorkerLoad::AnyMessage( const MsgHead& oInMsgHead, const MsgBody& oInMsgBody) { + if (m_pSessionManager == nullptr) + { + m_pSessionManager = std::dynamic_pointer_cast( + GetSession("neb::SessionManager")); + if (m_pSessionManager == nullptr) + { + LOG4_ERROR("no session named \"neb::SessionManager\"!"); + return(false); + } + } CJsonObject oJsonLoad; if (oJsonLoad.Parse(oInMsgBody.data())) { diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 050e128f..bd584d50 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -9,6 +9,9 @@ ******************************************************************************/ #include "SessionManager.hpp" +#include +#include +#include "util/process_helper.h" #include "util/json/CJsonObject.hpp" #include "labor/NodeInfo.hpp" #include "labor/Manager.hpp" @@ -34,7 +37,6 @@ SessionManager::~SessionManager() m_mapWorkerStartNum.clear(); m_mapWorkerFdPid.clear(); m_mapOnlineNodes.clear(); - m_vecFreeWorkerIdx.clear(); } E_CMD_STATUS SessionManager::Timeout() @@ -67,37 +69,61 @@ void SessionManager::DelOnlineNode(const std::string& strNodeIdentify) bool SessionManager::SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - for (auto worker_iter = m_mapWorker.begin(); - worker_iter != m_mapWorker.end(); ++worker_iter) + for (auto worker_iter = m_mapWorker.begin(); worker_iter != m_mapWorker.end(); ++worker_iter) { GetLabor(this)->GetDispatcher()->SendTo(worker_iter->second->iControlFd); } return(true); } +bool SessionManager::SendToWorkerWithoutLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +{ + for (auto worker_iter = m_mapWorker.begin(); worker_iter != m_mapWorker.end(); ++worker_iter) + { + if (m_iLoaderDataFd == worker_iter->second->iDataFd) + { + continue; + } + GetLabor(this)->GetDispatcher()->SendTo(worker_iter->second->iControlFd); + } + return(true); +} + +bool SessionManager::SendToLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +{ + for (auto worker_iter = m_mapWorker.begin(); worker_iter != m_mapWorker.end(); ++worker_iter) + { + if (m_iLoaderDataFd == worker_iter->second->iDataFd) + { + GetLabor(this)->GetDispatcher()->SendTo(worker_iter->second->iControlFd); + } + } + return(true); +} + void SessionManager::AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd) { - WorkerInfo* pWorkerInfo = nullptr; + WorkerInfo* pWorkerAttr = nullptr; try { - pWorkerInfo = new WorkerInfo(); + pWorkerAttr = new WorkerInfo(); } - catch (std::bad_alloc& e) + catch(std::bad_alloc& e) { LOG4_ERROR("new WorkerInfo error: %s", e.what()); return; } - pWorkerInfo->iWorkerIndex = iWorkerIndex; - pWorkerInfo->iControlFd = iControlFd; - pWorkerInfo->iDataFd = iDataFd; - pWorkerInfo->dBeatTime = GetNowTime(); - m_mapWorker.insert(std::make_pair(iPid, pWorkerInfo)); - m_mapWorkerFdPid.insert(std::make_pair(iControlFd, iPid)); - m_mapWorkerFdPid.insert(std::make_pair(iDataFd, iPid)); + pWorkerAttr->iWorkerIndex = iWorkerIndex; + pWorkerAttr->iControlFd = iControlFd; + pWorkerAttr->iDataFd = iDataFd; + pWorkerAttr->dBeatTime = GetNowTime(); + m_mapWorker.insert(std::make_pair(iPid, pWorkerAttr)); + m_mapWorkerFdPid.insert(std::pair(iControlFd, iPid)); + m_mapWorkerFdPid.insert(std::pair(iDataFd, iPid)); auto start_num_iter = m_mapWorkerStartNum.find(iWorkerIndex); if (start_num_iter == m_mapWorkerStartNum.end()) { - m_mapWorkerStartNum.insert(std::make_pair(iWorkerIndex, 1)); + m_mapWorkerStartNum.insert(std::pair(iWorkerIndex, 1)); } else { @@ -105,10 +131,16 @@ void SessionManager::AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, i } } +void SessionManager::AddLoaderInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd) +{ + m_iLoaderDataFd = iDataFd; + AddWorkerInfo(iWorkerIndex, iPid, iControlFd, iDataFd); +} + const WorkerInfo* SessionManager::GetWorkerInfo(int32 iWorkerIndex) const { for (auto worker_iter = m_mapWorker.begin(); - worker_iter != m_mapWorker.end(); ++worker_iter) + worker_iter != m_mapWorker.end(); ++worker_iter) { if (iWorkerIndex == worker_iter->second->iWorkerIndex) { @@ -152,51 +184,62 @@ bool SessionManager::SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad) std::pair SessionManager::GetMinLoadWorkerDataFd() { + LOG4_TRACE(" "); int iMinLoadWorkerFd = 0; int iMinLoad = -1; std::pair worker_pid_fd; for (auto iter = m_mapWorker.begin(); iter != m_mapWorker.end(); ++iter) { - if (iter == m_mapWorker.begin()) - { - iMinLoadWorkerFd = iter->second->iDataFd; - iMinLoad = iter->second->iLoad; - worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); - } - else if (iter->second->iLoad < iMinLoad) - { - iMinLoadWorkerFd = iter->second->iDataFd; - iMinLoad = iter->second->iLoad; - worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); - } + if (iter == m_mapWorker.begin() && iter->second->iDataFd != m_iLoaderDataFd) + { + iMinLoadWorkerFd = iter->second->iDataFd; + iMinLoad = iter->second->iLoad; + worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); + } + else if (iter->second->iLoad < iMinLoad && iter->second->iDataFd != m_iLoaderDataFd) + { + iMinLoadWorkerFd = iter->second->iDataFd; + iMinLoad = iter->second->iLoad; + worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); + } } return(worker_pid_fd); } bool SessionManager::CheckWorker() { + LOG4_TRACE(" "); for (auto worker_iter = m_mapWorker.begin(); - worker_iter != m_mapWorker.end(); ++worker_iter) + worker_iter != m_mapWorker.end(); ++worker_iter) { - if (GetNowTime() - worker_iter->second->dBeatTime - > ((Manager*)GetLabor(this))->GetManagerInfo().iWorkerBeat) + LOG4_TRACE("now %lf, worker_beat_time %lf, worker_beat %d", + GetNowTime(), worker_iter->second->dBeatTime, ((Manager*)GetLabor(this))->GetManagerInfo().iWorkerBeat); + if (GetNowTime() - worker_iter->second->dBeatTime > ((Manager*)GetLabor(this))->GetManagerInfo().iWorkerBeat) { - LOG4_INFO("worker_%d pid %d is unresponsive, terminate it.", - worker_iter->second->iWorkerIndex, worker_iter->first); - kill(worker_iter->first, SIGKILL); + LOG4_INFO("worker_%d pid %d is unresponsive, " + "terminate it.", worker_iter->second->iWorkerIndex, worker_iter->first); + kill(worker_iter->first, SIGKILL); //SIGINT); } } return(true); } -bool SessionManager::WorkerDeath(int iPid, int& iWorkerIndex) +bool SessionManager::WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& eLaborType) { auto worker_iter = m_mapWorker.find(iPid); if (worker_iter != m_mapWorker.end()) { LOG4_TRACE("restart worker %d, close control fd %d and data fd %d first.", worker_iter->second->iWorkerIndex, worker_iter->second->iControlFd, worker_iter->second->iDataFd); - int iWorkerIndex = worker_iter->second->iWorkerIndex; + iWorkerIndex = worker_iter->second->iWorkerIndex; + if (m_iLoaderDataFd == worker_iter->second->iDataFd) + { + eLaborType = Labor::LABOR_LOADER; + } + else + { + eLaborType = Labor::LABOR_WORKER; + } auto fd_iter = m_mapWorkerFdPid.find(worker_iter->second->iControlFd); if (fd_iter != m_mapWorkerFdPid.end()) { @@ -264,7 +307,7 @@ void SessionManager::SendOnlineNodesToWorker() * ] * } */ -void SessionManager::MakeReportData(CJsonObject& oReportJson) +void SessionManager::MakeReportData(CJsonObject& oReportData) { int iLoad = 0; int iConnect = 0; @@ -273,7 +316,6 @@ void SessionManager::MakeReportData(CJsonObject& oReportJson) int iSendNum = 0; int iSendByte = 0; int iClientNum = 0; - CJsonObject oReportData; CJsonObject oMember; oReportData.Add("node_type", GetLabor(this)->GetNodeInfo().strNodeType); oReportData.Add("node_id", GetLabor(this)->GetNodeInfo().uiNodeId); @@ -295,13 +337,24 @@ void SessionManager::MakeReportData(CJsonObject& oReportJson) { oReportData.Add("access_port", GetLabor(this)->GetNodeInfo().iPortForClient); } - oReportData.Add("worker_num", (int)m_mapWorker.size()); + if (m_iLoaderDataFd == -1) + { + oReportData.Add("worker_num", (int)m_mapWorker.size()); + } + else + { + oReportData.Add("worker_num", (int)m_mapWorker.size() - 1); + } oReportData.Add("active_time", (uint64)GetNowTime()); oReportData.Add("node", CJsonObject("{}")); oReportData.Add("worker", CJsonObject("[]")); auto worker_iter = m_mapWorker.begin(); for (; worker_iter != m_mapWorker.end(); ++worker_iter) { + if (m_iLoaderDataFd == worker_iter->second->iDataFd) + { + continue; + } iLoad += worker_iter->second->iLoad; iConnect += worker_iter->second->iConnect; iRecvNum += worker_iter->second->iRecvNum; @@ -328,4 +381,56 @@ void SessionManager::MakeReportData(CJsonObject& oReportJson) oReportData["node"].Add("client", iClientNum); } +int SessionManager::GetLoaderDataFd() const +{ + return(m_iLoaderDataFd); +} + +bool SessionManager::NewSocketWhenWorkerCreated(int iWorkerDataFd) +{ + if (m_iLoaderDataFd == -1) + { + LOG4_TRACE("no Loader process."); + return(true); + } + int iFds[2]; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, iFds) < 0) + { + LOG4_ERROR("socketpair error %d!", errno); + return(false); + } + x_sock_set_block(iFds[0], 0); + x_sock_set_block(iFds[1], 0); + GetLabor(this)->GetDispatcher()->SendFd(m_iLoaderDataFd, iFds[0], PF_UNIX, CODEC_NEBULA_IN_NODE); + GetLabor(this)->GetDispatcher()->SendFd(iWorkerDataFd, iFds[1], PF_UNIX, CODEC_NEBULA_IN_NODE); + return(true); +} + +bool SessionManager::NewSocketWhenLoaderCreated() +{ + if (m_iLoaderDataFd == -1) + { + LOG4_TRACE("no Loader process."); + return(true); + } + for (auto iter = m_mapWorker.begin(); iter != m_mapWorker.end(); ++iter) + { + if (m_iLoaderDataFd == iter->second->iDataFd) + { + continue; + } + int iFds[2]; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, iFds) < 0) + { + LOG4_ERROR("socketpair error %d!", errno); + return(false); + } + x_sock_set_block(iFds[0], 0); + x_sock_set_block(iFds[1], 0); + GetLabor(this)->GetDispatcher()->SendFd(m_iLoaderDataFd, iFds[0], PF_UNIX, CODEC_NEBULA_IN_NODE); + GetLabor(this)->GetDispatcher()->SendFd(iter->second->iDataFd, iFds[1], PF_UNIX, CODEC_NEBULA_IN_NODE); + } + return(true); +} + } /* namespace neb */ diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index fea10c3f..e917f9f2 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -30,21 +30,27 @@ class SessionManager : public Session, void AddOnlineNode(const std::string& strNodeIdentify, const std::string& strNodeInfo); void DelOnlineNode(const std::string& strNodeIdentify); bool SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); // 向Worker发送数据 + bool SendToWorkerWithoutLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendToLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); void AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); + void AddLoaderInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); const WorkerInfo* GetWorkerInfo(int32 iWorkerIndex) const; bool SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad); std::pair GetMinLoadWorkerDataFd(); bool CheckWorker(); - bool WorkerDeath(int iPid, int& iWorkerIndex); + bool WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& eLaborType); void SendOnlineNodesToWorker(); void MakeReportData(CJsonObject& oReportJson); + int GetLoaderDataFd() const; + bool NewSocketWhenWorkerCreated(int iWorkerDataFd); + bool NewSocketWhenLoaderCreated(); private: + int m_iLoaderDataFd = -1; std::unordered_map m_mapWorker; ///< 业务逻辑工作进程及进程属性,key为pid std::unordered_map m_mapWorkerStartNum; ///< 进程被启动次数,key为WorkerIdx std::unordered_map m_mapWorkerFdPid; ///< 工作进程通信FD对应的进程号 std::unordered_map m_mapOnlineNodes; ///< 订阅的节点在线信息 - std::vector m_vecFreeWorkerIdx; ///< 空闲进程编号 }; } /* namespace neb */ diff --git a/src/actor/step/sys_step/StepTellWorker.cpp b/src/actor/step/sys_step/StepTellWorker.cpp index 3cff2f85..a86769bb 100644 --- a/src/actor/step/sys_step/StepTellWorker.cpp +++ b/src/actor/step/sys_step/StepTellWorker.cpp @@ -31,8 +31,6 @@ E_CMD_STATUS StepTellWorker::Emit( TargetWorker oTargetWorker; oTargetWorker.set_worker_identify(GetNodeIdentify()); oTargetWorker.set_node_type(GetNodeType()); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("OK"); oOutMsgBody.set_data(oTargetWorker.SerializeAsString()); Step::SendTo(m_pChannel, CMD_REQ_TELL_WORKER, GetSequence(), oOutMsgBody); return(CMD_STATUS_RUNNING); diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index eb6537b9..3980f186 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -55,6 +55,8 @@ bool SocketChannelImpl::Init(E_CODEC_TYPE eCodecType, bool bIsClient) switch (eCodecType) { case CODEC_NEBULA: + case CODEC_PROTO: + case CODEC_NEBULA_IN_NODE: m_pCodec = new CodecProto(m_pLogger, eCodecType); m_pCodec->SetKey(m_strKey); break; @@ -653,6 +655,8 @@ bool SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAliv switch (eCodecType) { case CODEC_NEBULA: + case CODEC_PROTO: + case CODEC_NEBULA_IN_NODE: pNewCodec = new CodecProto(m_pLogger, eCodecType); pNewCodec->SetKey(m_strKey); break; diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index c2329641..6871736e 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -39,6 +39,7 @@ enum E_CODEC_TYPE CODEC_PRIVATE = 5, ///< 私有协议编解码 CODEC_WS_EXTEND_JSON = 6, ///< 带Extension data的websocket协议扩展,Application data为json CODEC_WS_EXTEND_PB = 7, ///< 带Extension data的websocket协议扩展,Application data为pb + CODEC_NEBULA_IN_NODE = 8, ///< 节点各进程间通信协议,与CODEC_NEBULA协议相同,使用的编解码类也相同,只为区别节点内部连接与外部连接 }; /** diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index f556b772..4c598d63 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -282,30 +282,33 @@ bool Dispatcher::FdTransfer(int iFd) } else { - int iKeepAlive = 1; - int iKeepIdle = 60; - int iKeepInterval = 5; - int iKeepCount = 3; - int iTcpNoDelay = 1; - if (setsockopt(iAcceptFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)) < 0) - { - LOG4_WARNING("fail to set SO_KEEPALIVE"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPIDLE"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPINTVL"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPCNT"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)) < 0) - { - LOG4_WARNING("fail to set TCP_NODELAY"); + if (iAiFamily != PF_UNIX) + { + int iKeepAlive = 1; + int iKeepIdle = 60; + int iKeepInterval = 5; + int iKeepCount = 3; + int iTcpNoDelay = 1; + if (setsockopt(iAcceptFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPALIVE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)) < 0) + { + LOG4_WARNING("fail to set TCP_KEEPIDLE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)) < 0) + { + LOG4_WARNING("fail to set TCP_KEEPINTVL"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)) < 0) + { + LOG4_WARNING("fail to set TCP_KEEPCNT"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)) < 0) + { + LOG4_WARNING("fail to set TCP_NODELAY"); + } } std::shared_ptr pChannel = nullptr; LOG4_TRACE("fd[%d] transfer successfully.", iAcceptFd); @@ -337,7 +340,7 @@ bool Dispatcher::FdTransfer(int iFd) LOG4_ERROR("getpeername error %d", errno); } } - else // AF_INET6 + else if (AF_INET6 == iAiFamily) // AF_INET6 { char szClientAddr[64] = {0}; int z; /* status return code */ @@ -367,6 +370,12 @@ bool Dispatcher::FdTransfer(int iFd) } pStepTellWorker->Emit(ERR_OK); } + else if (CODEC_NEBULA_IN_NODE == iCodec) + { + pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + m_mapLoaderAndWorkerChannel.insert(std::make_pair(pChannel->GetFd(), pChannel)); + m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); + } else { pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); @@ -1171,6 +1180,46 @@ bool Dispatcher::AutoRedisCmd(const std::string& strHost, int iPort, std::shared return(true); } +bool Dispatcher::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +{ + if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) + { + LOG4_ERROR("this function can not be called by manager process!"); + return(false); + } + if (nullptr != pSender) + { + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); + } + if (m_iterLoaderAndWorkerChannel == m_mapLoaderAndWorkerChannel.end()) + { + m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); + if (m_iterLoaderAndWorkerChannel == m_mapLoaderAndWorkerChannel.end()) + { + LOG4_ERROR("no channel for loader!"); + return(false); + } + } + E_CODEC_STATUS eStatus = m_iterLoaderAndWorkerChannel->second->m_pImpl->Send(iCmd, uiSeq, oMsgBody); + m_iterLoaderAndWorkerChannel++; + if (CODEC_STATUS_OK == eStatus) + { + RemoveIoWriteEvent(m_iterLoaderAndWorkerChannel->second); + return(true); + } + else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) + { + AddIoWriteEvent(m_iterLoaderAndWorkerChannel->second); + return(true); + } + else if (CODEC_STATUS_WANT_READ == eStatus) + { + RemoveIoWriteEvent(m_iterLoaderAndWorkerChannel->second); + return(true); + } + return(false); +} + bool Dispatcher::SendTo(int iFd) { auto iter = m_mapSocketChannel.find(iFd); @@ -1577,6 +1626,13 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b ev_timer_stop (m_loop, pChannel->m_pImpl->MutableTimerWatcher()); } + if (CODEC_NEBULA_IN_NODE == pChannel->m_pImpl->GetCodecType()) + { + auto inner_channel_iter = m_mapLoaderAndWorkerChannel.find(pChannel->GetFd()); + m_mapLoaderAndWorkerChannel.erase(inner_channel_iter); + m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); + } + auto named_iter = m_mapNamedSocketChannel.find(pChannel->m_pImpl->GetIdentify()); if (named_iter != m_mapNamedSocketChannel.end()) { @@ -1663,5 +1719,177 @@ bool Dispatcher::AddIoWriteEvent(std::shared_ptr pChannel) return(true); } } +bool Dispatcher::RemoveIoWriteEvent(std::shared_ptr pChannel) +{ + LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); + ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); + if (NULL == io_watcher) + { + return(false); + } + if (EV_WRITE & io_watcher->events) + { + ev_io_stop(m_loop, io_watcher); + ev_io_set(io_watcher, io_watcher->fd, io_watcher->events & (~EV_WRITE)); + ev_io_start (m_loop, io_watcher); + } + return(true); +} + +void Dispatcher::SetChannelStatus(std::shared_ptr pChannel, E_CHANNEL_STATUS eStatus) +{ + pChannel->m_pImpl->SetChannelStatus(eStatus); +} + +void Dispatcher::SetChannelStatus(std::shared_ptr pChannel, E_CHANNEL_STATUS eStatus, uint32 ulStepSeq) +{ + pChannel->m_pImpl->SetChannelStatus(eStatus, ulStepSeq); +} + +bool Dispatcher::AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout) +{ + LOG4_TRACE(" "); + ev_timer* timeout_watcher = new ev_timer(); + if (timeout_watcher == NULL) + { + LOG4_ERROR("new timeout_watcher error!"); + return(false); + } + tagClientConnWatcherData* pData = new tagClientConnWatcherData(); + if (pData == NULL) + { + LOG4_ERROR("new tagClientConnWatcherData error!"); + delete timeout_watcher; + return(false); + } + ev_timer_init (timeout_watcher, ClientConnFrequencyTimeoutCallback, dTimeout + ev_time() - ev_now(m_loop), 0.); + pData->pDispatcher = this; + snprintf(pData->pAddr, gc_iAddrLen, "%s", pAddr); + timeout_watcher->data = (void*)pData; + ev_timer_start (m_loop, timeout_watcher); + return(true); +} + +bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) +{ + char szClientAddr[64] = {0}; + int iAcceptFd = -1; + if (AF_INET == iFamily) + { + struct sockaddr_in stClientAddr; + socklen_t clientAddrSize = sizeof(stClientAddr); + iAcceptFd = accept(iFd, (struct sockaddr*) &stClientAddr, &clientAddrSize); + if (iAcceptFd < 0) + { + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); + return(false); + } + inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientAddr, sizeof(szClientAddr)); + LOG4_TRACE("accept connect from \"%s\"", szClientAddr); + } + else // AF_INET6 + { + struct sockaddr_in6 stClientAddr; + socklen_t clientAddrSize = sizeof(stClientAddr); + iAcceptFd = accept(iFd, (struct sockaddr*) &stClientAddr, &clientAddrSize); + if (iAcceptFd < 0) + { + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); + return(false); + } + inet_ntop(AF_INET6, &stClientAddr.sin6_addr, szClientAddr, sizeof(szClientAddr)); + LOG4_TRACE("accept connect from \"%s\"", szClientAddr); + } + + auto iter = m_mapClientConnFrequency.find(std::string(szClientAddr)); + if (iter == m_mapClientConnFrequency.end()) + { + m_mapClientConnFrequency.insert(std::make_pair(std::string(szClientAddr), 1)); + AddClientConnFrequencyTimeout(szClientAddr, m_pLabor->GetNodeInfo().dAddrStatInterval); + } + else + { + iter->second++; + if (((Manager*)m_pLabor)->GetManagerInfo().iC2SListenFd > 2 && iter->second > (uint32)m_pLabor->GetNodeInfo().iAddrPermitNum) + { + LOG4_WARNING("client addr %s had been connected more than %u times in %f seconds, it's not permitted", + szClientAddr, m_pLabor->GetNodeInfo().iAddrPermitNum, m_pLabor->GetNodeInfo().dAddrStatInterval); + close(iAcceptFd); + return(false); + } + } + + std::pair worker_pid_fd = ((Manager*)m_pLabor)->GetSessionManager()->GetMinLoadWorkerDataFd(); + if (worker_pid_fd.second > 0) + { + LOG4_DEBUG("send new fd %d to worker communication fd %d", + iAcceptFd, worker_pid_fd.second); + int iCodec = m_pLabor->GetNodeInfo().eCodec; + int iErrno = SocketChannel::SendChannelFd(worker_pid_fd.second, iAcceptFd, iFamily, iCodec, m_pLogger); + if (iErrno != ERR_OK) + { + LOG4_ERROR("error %d: %s", iErrno, strerror_r(iErrno, m_pErrBuff, gc_iErrBuffLen)); + } + close(iAcceptFd); + return(true); + } + LOG4_WARNING("GetMinLoadWorkerDataFd() found worker_pid_fd.second = %d", worker_pid_fd.second); + close(iAcceptFd); + return(false); +} + +bool Dispatcher::AcceptServerConn(int iFd) +{ + struct sockaddr_in stClientAddr; + socklen_t clientAddrSize = sizeof(stClientAddr); + int iAcceptFd = accept(iFd, (struct sockaddr*) &stClientAddr, &clientAddrSize); + if (iAcceptFd < 0) + { + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); + return(false); + } + else + { + /* tcp连接检测 */ + int iKeepAlive = 1; + int iKeepIdle = 60; + int iKeepInterval = 5; + int iKeepCount = 3; + int iTcpNoDelay = 1; + if (setsockopt(iAcceptFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPALIVE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPIDLE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPINTVL"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPALIVE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)) < 0) + { + LOG4_WARNING("fail to set TCP_NODELAY"); + } + x_sock_set_block(iAcceptFd, 0); + std::shared_ptr pChannel = CreateSocketChannel(iAcceptFd, CODEC_NEBULA); + if (NULL != pChannel) + { + AddIoTimeout(pChannel, 1.0); // 为了防止大量连接攻击,初始化连接只有一秒即超时,在正常发送第一个数据包之后才采用正常配置的网络IO超时检查 + AddIoReadEvent(pChannel); + } + } + return(false); +} + +void Dispatcher::EvBreak() +{ + ev_break (m_loop, EVBREAK_ALL); +} } diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 389c68ff..37313918 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -137,6 +137,7 @@ class Dispatcher bool AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep); // SendTo() for unix domain socket + bool SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender); bool SendTo(int iFd); bool Disconnect(std::shared_ptr pChannel, bool bChannelNotice = true); @@ -200,6 +201,8 @@ class Dispatcher /* named Channel */ std::unordered_map > > m_mapNamedSocketChannel; ///< key为Identify,连接存在时,if(http连接)list.size()>=1;else list.size()==1; std::unordered_map > > m_mapNamedRedisChannel; ///< key为identify,list.size()==1 + std::unordered_map > m_mapLoaderAndWorkerChannel; ///< Loader和Worker之间通信通道 + std::unordered_map >::iterator m_iterLoaderAndWorkerChannel; std::unordered_map m_mapClientConnFrequency; ///< 客户端连接频率 diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index 9c3c6045..986ed28d 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -16,7 +16,7 @@ namespace neb { -Nodes::Nodes(int iHashAlgorithm, int iVirtualNodeNum, ev_tstamp dSessionTimeout) +Nodes::Nodes(int iHashAlgorithm, int iVirtualNodeNum) : m_iHashAlgorithm(iHashAlgorithm), m_iVirtualNodeNum(iVirtualNodeNum) { } diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index 77582f8a..d620b486 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -34,11 +34,7 @@ enum E_HASH_ALGORITHM }; /** - * @brief 节点Session - * @note 节点Session常驻内存,永不过期,构造函数中的dSessionTimeout传入0表示 - * 不做超时检查。Timeout()方法实现直接返回Running,即便dSessionTimeout设置了超时, - * 此Session也只是成为了一个定时器,不会真正超时。 - * SessionNode不从Session派生,因为不会被应用层actor用到。 + * @brief 节点管理 */ class Nodes { @@ -48,14 +44,9 @@ class Nodes * @param iVirtualNodeNum 每个实体节点对应的虚拟节点数量 * @param dSessionTimeout 超时时间,0表示永不超时 */ - Nodes(int iHashAlgorithm = HASH_fnv1a_64, int iVirtualNodeNum = 200, ev_tstamp dSessionTimeout = 0.0); + Nodes(int iHashAlgorithm = HASH_fnv1a_64, int iVirtualNodeNum = 200); virtual ~Nodes(); - virtual E_CMD_STATUS Timeout() - { - return(CMD_STATUS_RUNNING); - } - /* 实体节点hash信息 * key为Identify字符串 * value为hash(Property001#0) hash(Property001#1) hash(Property001#2) 组成的vector */ diff --git a/src/labor/Labor.hpp b/src/labor/Labor.hpp index 9fdfc3d9..016181c1 100644 --- a/src/labor/Labor.hpp +++ b/src/labor/Labor.hpp @@ -34,7 +34,7 @@ class Labor LABOR_UNDEFINE = 0, LABOR_MANAGER = 1, LABOR_WORKER = 2, - LABOR_HOLDER = 3, + LABOR_LOADER = 3, }; public: Labor(LABOR_TYPE eLaborType) diff --git a/src/labor/Loader.cpp b/src/labor/Loader.cpp new file mode 100644 index 00000000..c2266074 --- /dev/null +++ b/src/labor/Loader.cpp @@ -0,0 +1,38 @@ +/******************************************************************************* + * Project: Nebula + * @file Loader.hpp + * @brief 装载者,持有者 + * @author Bwar + * @date: 2019年10月4日 + * @note + * Modify history: + ******************************************************************************/ + +#include "Loader.hpp" +#include "actor/ActorBuilder.hpp" + +namespace neb +{ + +Loader::Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex) + : Worker(strWorkPath, iControlFd, iDataFd, iWorkerIndex, Labor::LABOR_LOADER) +{ +} + +Loader::~Loader() +{ +} + +bool Loader::InitActorBuilder() +{ + LOG4_TRACE(""); + if (NewActorBuilder()) + { + return(GetActorBuilder()->Init( + (const_cast(GetNodeConf()))["load_config"]["loader"]["boot_load"], + (const_cast(GetNodeConf()))["load_config"]["loader"]["dynamic_loading"])); + } + return(false); +} + +} /* namespace neb */ diff --git a/src/labor/Loader.hpp b/src/labor/Loader.hpp new file mode 100644 index 00000000..117a47ae --- /dev/null +++ b/src/labor/Loader.hpp @@ -0,0 +1,31 @@ +/******************************************************************************* + * Project: Nebula + * @file Loader.hpp + * @brief 装载者,持有者 + * @author Bwar + * @date: 2019年10月4日 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_LABOR_LOADER_HPP_ +#define SRC_LABOR_LOADER_HPP_ + +#include "Worker.hpp" + +namespace neb +{ + +class Loader: public Worker +{ +public: + Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex); + virtual ~Loader(); + +protected: + virtual bool InitActorBuilder(); +}; + +} /* namespace neb */ + +#endif /* SRC_LABOR_LOADER_HPP_ */ diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index af332c5e..4cfac1d3 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -21,6 +21,7 @@ extern "C" { #include "Manager.hpp" #include "Worker.hpp" +#include "Loader.hpp" #include "channel/SocketChannel.hpp" #include "ios/Dispatcher.hpp" #include "actor/ActorBuilder.hpp" @@ -55,6 +56,7 @@ Manager::Manager(const std::string& strConfFile) } m_stManagerInfo.iWorkerBeat = (gc_iBeatInterval * 2) + 1; CreateEvents(); + CreateLoader(); CreateWorker(); } @@ -164,7 +166,9 @@ bool Manager::InitActorBuilder() LOG4_ERROR("new ActorBuilder error: %s", e.what()); return(false); } - if (!m_pActorBuilder->Init(m_oCurrentConf["boot_load"], m_oCurrentConf["dynamic_loading"])) + if (!m_pActorBuilder->Init( + m_oCurrentConf["load_conf"]["manager"]["boot_load"], + m_oCurrentConf["load_conf"]["manager"]["dynamic_loading"])) { LOG4_ERROR("ActorBuilder->Init() failed!"); return(false); @@ -349,6 +353,67 @@ bool Manager::CreateEvents() return(true); } +void Manager::CreateLoader() +{ + bool bWithLoader = false; + m_oCurrentConf.Get("with_loader", bWithLoader); + if (!bWithLoader) + { + return; + } + LOG4_TRACE(" "); + int iControlFds[2]; + int iDataFds[2]; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, iControlFds) < 0) + { + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); + } + if (socketpair(PF_UNIX, SOCK_STREAM, 0, iDataFds) < 0) + { + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); + } + + int iPid = fork(); + if (iPid == 0) // 子进程 + { + close(m_stManagerInfo.iS2SListenFd); + if (m_stManagerInfo.iC2SListenFd > 2) + { + close(m_stManagerInfo.iC2SListenFd); + } + close(iControlFds[0]); + close(iDataFds[0]); + x_sock_set_block(iControlFds[1], 0); + x_sock_set_block(iDataFds[1], 0); + Loader oLoader(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], 0); + if (!oLoader.Init(m_oCurrentConf)) + { + exit(3); + } + oLoader.Run(); + exit(-2); + } + else if (iPid > 0) // 父进程 + { + close(iControlFds[1]); + close(iDataFds[1]); + x_sock_set_block(iControlFds[0], 0); + x_sock_set_block(iDataFds[0], 0); + m_pSessionManager->AddLoaderInfo(0, iPid, iControlFds[0], iDataFds[0]); + std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); + std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); + m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->AddIoReadEvent(pChannelData); + m_pDispatcher->AddIoReadEvent(pChannelControl); + m_pSessionManager->SendOnlineNodesToWorker(); + } + else + { + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); + } +} + void Manager::CreateWorker() { LOG4_TRACE(" "); @@ -379,7 +444,11 @@ void Manager::CreateWorker() close(iDataFds[0]); x_sock_set_block(iControlFds[1], 0); x_sock_set_block(iDataFds[1], 0); - Worker oWorker(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], i, m_oCurrentConf); + Worker oWorker(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], i); + if (!oWorker.Init(m_oCurrentConf)) + { + exit(3); + } oWorker.Run(); exit(-2); } @@ -396,6 +465,7 @@ void Manager::CreateWorker() m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->AddIoReadEvent(pChannelData); m_pDispatcher->AddIoReadEvent(pChannelControl); + m_pSessionManager->NewSocketWhenWorkerCreated(iDataFds[0]); } else { @@ -409,7 +479,8 @@ bool Manager::RestartWorker(int iDeathPid) LOG4_DEBUG("%d", iDeathPid); int iWorkerIndex = 0; int iNewPid = 0; - if (m_pSessionManager->WorkerDeath(iDeathPid, iWorkerIndex)) + Labor::LABOR_TYPE eLaborType; + if (m_pSessionManager->WorkerDeath(iDeathPid, iWorkerIndex, eLaborType)) { int iControlFds[2]; int iDataFds[2]; @@ -434,7 +505,11 @@ bool Manager::RestartWorker(int iDeathPid) close(iDataFds[0]); x_sock_set_block(iControlFds[1], 0); x_sock_set_block(iDataFds[1], 0); - Worker oWorker(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], iWorkerIndex, m_oCurrentConf); + Worker oWorker(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], iWorkerIndex); + if (!oWorker.Init(m_oCurrentConf)) + { + exit(-1); + } oWorker.Run(); exit(-2); // 子进程worker没有正常运行 } @@ -453,6 +528,16 @@ bool Manager::RestartWorker(int iDeathPid) m_pDispatcher->AddIoReadEvent(pChannelData); m_pDispatcher->AddIoReadEvent(pChannelControl); m_pSessionManager->SendOnlineNodesToWorker(); + if (Labor::LABOR_LOADER == eLaborType) + { + m_pSessionManager->AddLoaderInfo(iWorkerIndex, iNewPid, iControlFds[0], iDataFds[0]); + m_pSessionManager->NewSocketWhenLoaderCreated(); + } + else + { + m_pSessionManager->AddWorkerInfo(iWorkerIndex, iNewPid, iControlFds[0], iDataFds[0]); + m_pSessionManager->NewSocketWhenWorkerCreated(iDataFds[0]); + } } else { @@ -482,11 +567,19 @@ void Manager::RefreshServer() } // 更新动态库配置或重新加载动态库 - if (m_oLastConf["dynamic_loading"].ToString() != m_oCurrentConf["dynamic_loading"].ToString()) + if (m_oLastConf["load_config"]["worker"]["dynamic_loading"].ToString() + != m_oCurrentConf["load_config"]["worker"]["dynamic_loading"].ToString()) + { + MsgBody oMsgBody; + oMsgBody.set_data(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"].ToString()); + m_pSessionManager->SendToWorkerWithoutLoader(CMD_REQ_RELOAD_SO, GetSequence(), oMsgBody); + } + if (m_oLastConf["load_config"]["loader"]["dynamic_loading"].ToString() + != m_oCurrentConf["load_config"]["loader"]["dynamic_loading"].ToString()) { MsgBody oMsgBody; - oMsgBody.set_data(m_oCurrentConf["dynamic_loading"].ToString()); - m_pSessionManager->SendToWorker(CMD_REQ_RELOAD_SO, GetSequence(), oMsgBody); + oMsgBody.set_data(m_oCurrentConf["load_config"]["loader"]["dynamic_loading"].ToString()); + m_pSessionManager->SendToLoader(CMD_REQ_RELOAD_SO, GetSequence(), oMsgBody); } } else @@ -504,7 +597,7 @@ bool Manager::AddPeriodicTaskEvent() LOG4_ERROR("new timeout_watcher error!"); return(false); } - m_pPeriodicTaskWatcher->data = (void*)this; + m_pPeriodicTaskWatcher->data = (void*)this->GetDispatcher(); m_pDispatcher->AddEvent(m_pPeriodicTaskWatcher, Dispatcher::PeriodicTaskCallback, NODE_BEAT); std::shared_ptr pReportToBeacon = m_pActorBuilder->MakeSharedStep( nullptr, "neb::StepReportToBeacon", NODE_BEAT); diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index 8e8874fc..80007674 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -103,6 +103,7 @@ class Manager: public Labor void Destroy(); bool CreateEvents(); + void CreateLoader(); void CreateWorker(); bool RestartWorker(int iDeathPid); bool AddPeriodicTaskEvent(); diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index fceda171..f69c6eba 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -24,8 +24,8 @@ extern "C" { namespace neb { -Worker::Worker(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex, CJsonObject& oJsonConf) - : Labor(LABOR_WORKER) +Worker::Worker(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex, Labor::LABOR_TYPE eLaborType) + : Labor(eLaborType) { m_stWorkerInfo.iControlFd = iControlFd; m_stWorkerInfo.iDataFd = iDataFd; @@ -33,11 +33,6 @@ Worker::Worker(const std::string& strWorkPath, int iControlFd, int iDataFd, int m_stWorkerInfo.iWorkerPid = getpid(); m_stNodeInfo.strWorkPath = strWorkPath; m_pErrBuff = (char*)malloc(gc_iErrBuffLen); - if (!Init(oJsonConf)) - { - exit(3); - } - m_oNodeConf = oJsonConf; } Worker::~Worker() @@ -111,6 +106,7 @@ bool Worker::Init(CJsonObject& oJsonConf) oJsonConf.Get("node_type", m_stNodeInfo.strNodeType); oJsonConf.Get("host", m_stNodeInfo.strHostForServer); oJsonConf.Get("port", m_stNodeInfo.iPortForServer); + m_oNodeConf = oJsonConf; m_oCustomConf = oJsonConf["custom"]; std::ostringstream oss; oss << m_stNodeInfo.strHostForServer << ":" << m_stNodeInfo.iPortForServer << "." << m_stWorkerInfo.iWorkerIndex; @@ -230,6 +226,26 @@ bool Worker::InitLogger(const CJsonObject& oJsonConf) } bool Worker::InitDispatcher() +{ + if (NewDispatcher()) + { + return(m_pDispatcher->Init()); + } + return(false); +} + +bool Worker::InitActorBuilder() +{ + if (NewActorBuilder()) + { + return(m_pActorBuilder->Init( + m_oNodeConf["load_config"]["worker"]["boot_load"], + m_oNodeConf["load_config"]["worker"]["dynamic_loading"])); + } + return(false); +} + +bool Worker::NewDispatcher() { try { @@ -240,10 +256,10 @@ bool Worker::InitDispatcher() LOG4_ERROR("new Dispatcher error: %s", e.what()); return(false); } - return(m_pDispatcher->Init()); + return(true); } -bool Worker::InitActorBuilder() +bool Worker::NewActorBuilder() { try { @@ -254,11 +270,6 @@ bool Worker::InitActorBuilder() LOG4_ERROR("new ActorBuilder error: %s", e.what()); return(false); } - if (!m_pActorBuilder->Init(m_oNodeConf["boot_load"], m_oNodeConf["dynamic_loading"])) - { - LOG4_ERROR("ActorBuilder->Init() failed!"); - return(false); - } return(true); } @@ -314,7 +325,7 @@ bool Worker::AddPeriodicTaskEvent() LOG4_ERROR("malloc timeout_watcher error!"); return(false); } - timeout_watcher->data = (void*)this; + timeout_watcher->data = (void*)this->GetDispatcher(); m_pDispatcher->AddEvent(timeout_watcher, Dispatcher::PeriodicTaskCallback, NODE_BEAT); return(true); } diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index fd66b264..0bcba3d1 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -43,7 +43,8 @@ class ActorBuilder; class Worker: public Labor { public: - Worker(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex, CJsonObject& oJsonConf); + Worker(const std::string& strWorkPath, int iControlFd, int iDataFd, + int iWorkerIndex, Labor::LABOR_TYPE eLaborType = Labor::LABOR_WORKER); Worker(const Worker&) = delete; Worker& operator=(const Worker&) = delete; virtual ~Worker(); @@ -52,6 +53,7 @@ class Worker: public Labor void OnTerminated(struct ev_signal* watcher); bool CheckParent(); + virtual bool Init(CJsonObject& oJsonConf); void Run(); public: // about worker @@ -90,10 +92,11 @@ class Worker: public Labor void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); protected: - bool Init(CJsonObject& oJsonConf); bool InitLogger(const CJsonObject& oJsonConf); - bool InitDispatcher(); - bool InitActorBuilder(); + virtual bool InitDispatcher(); + virtual bool InitActorBuilder(); + bool NewDispatcher(); + bool NewActorBuilder(); bool CreateEvents(); void Destroy(); bool AddPeriodicTaskEvent(); From 60bf27959f7eda424d541c456764075050ae8433 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 12 Oct 2019 22:40:01 +0800 Subject: [PATCH 062/176] add loader config --- conf/nebula.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conf/nebula.json b/conf/nebula.json index 13411f66..8ebe9039 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -17,6 +17,8 @@ "server_name": "AsyncServer", "//worker_num": "进程数量", "worker_num": 10, + "//with_loader":"是否启动loader进程", + "with_loader":false, "//cpu_affinity":"是否设置进程CPU亲和度(绑定CPU)", "cpu_affinity":false, "//worker_capacity": "子进程最大工作负荷", From 9f910ab40a457a93275d609842271e4a6606a73f Mon Sep 17 00:00:00 2001 From: nebim Date: Sun, 13 Oct 2019 21:05:22 +0800 Subject: [PATCH 063/176] add loader config to configuration document. --- docs/cn/configuration.md | 128 ++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 57 deletions(-) diff --git a/docs/cn/configuration.md b/docs/cn/configuration.md index 288bfc88..11a4aeda 100644 --- a/docs/cn/configuration.md +++ b/docs/cn/configuration.md @@ -8,10 +8,6 @@ { "//node_type": "节点类型:ACCESS,LOGIC,PROXY,CENTER等,由业务层定义", "node_type": "ACCESS", - "//host": "系统内各Server之间通信绑定的IP(Server to Server)", - "host": "192.168.18.81", - "//port": "系统内各Server之间通信监听的端口", - "port": 9987, "//access_host": "对系统外提供服务绑定的IP(Client to Server),若不提供对外服务,则无需配置", "access_host": "192.168.18.81", "//access_port": "对系统外提供服务监听的端口", @@ -20,25 +16,33 @@ "access_codec": 4, "gateway": "113.102.157.188", "gateway_port": 9988, + "//host": "系统内各Server之间通信绑定的IP(Server to Server)", + "host": "192.168.18.81", + "//port": "系统内各Server之间通信监听的端口", + "port": 9987, "//server_name": "异步事件驱动Server", "server_name": "AsyncServer", "//worker_num": "进程数量", "worker_num": 10, - "//worker_capacity": "子进程最大工作负荷", - "worker_capacity": 1000000, + "//with_loader":"是否启动loader进程", + "with_loader":false, "//cpu_affinity":"是否设置进程CPU亲和度(绑定CPU)", "cpu_affinity":false, + "//worker_capacity": "子进程最大工作负荷", + "worker_capacity": 1000000, "//config_path": "配置文件路径(相对路径)", "config_path": "conf/", "//log_path": "日志文件路径(相对路径)", "log_path": "log/", + "//beacon": "控制中心", + "beacon": [ + { "host": "192.168.1.11", "port": 16000 }, + { "host": "192.168.1.12", "port": 16000 } + ], "//max_log_file_num": "最大日志文件数量,用于日志文件滚动", "max_log_file_num": 5, "//max_log_file_size": "单个日志文件大小限制", "max_log_file_size": 20480000, - "log_levels": { "FATAL": 0, "CRITICAL": 1, "ERROR": 2, "NOTICE": 3, "WARNING": 4, "INFO": 5, "DEBUG": 6, "TRACE": 7 }, - "log_level": 7, - "net_log_level": 6, "//permission": "限制。addr_permit为连接限制,限制每个IP在统计时间内连接次数;uin_permit为消息数量限制,限制每个用户在单位统计时间内发送消息数量。", "permission": { "addr_permit": { "stat_interval": 60.0, "permit_num": 1000000000 }, @@ -48,62 +52,70 @@ "io_timeout": 300.0, "//step_timeout": "步骤超时设置(单位:秒)小数点后面至少保留一位", "step_timeout": 1.5, - "boot_load": { - "cmd": [ - { "cmd": 1001, "class": "neb::CmdHello" }, - { "cmd": 1003, "class": "neb::CmdDbAgent" } - ], - "module": [ - { "path": "neb/switch", "class": "neb::ModuleSwitch" }, - { "path": "neb/hello", "class": "neb::ModuleHello" } - ] - }, + "log_levels": { "FATAL": 0, "CRITICAL": 1, "ERROR": 2, "NOTICE": 3, "WARNING": 4, "INFO": 5, "DEBUG": 6, "TRACE": 7 }, + "log_level": 7, + "net_log_level": 6, "//with_ssl": "SSL配置(可为空),路径为相对${WorkPath}的相对路径,公钥文件和私钥文件均为PEM格式", "with_ssl": { "config_path": "conf/ssl", "cert_file": "20180623143147.pem", "key_file": "20180623143147.key" }, - "//refresh_interval": "刷新Server配置,检查、加载插件动态库时间周期", + "//refresh_interval": "刷新Server配置,检查、加载插件动态库时间周期(周期时间长短视服务器忙闲而定)", "refresh_interval": 60, - "//beacon": "控制中心", - "beacon": [ - { "host": "192.168.1.11", "port": 16000 }, - { "host": "192.168.1.12", "port": 16000 } - ], - "dynamic_loading": [{ - "so_path": "plugins/User.so", - "load": false, - "version": 1, - "cmd": [ - { "cmd": 2001, "class": "neb::CmdUserLogin" }, - { "cmd": 2003, "class": "neb::CmdUserLogout" } - ], - "module": [ - { "path": "im/user/login", "class": "neb::ModuleLogin" }, - { "path": "im/user/logout", "class": "neb::ModuleLogout" } - ], - "session":["im::SessionUser", "im::SessionGroup"], - "step":["im::StepLogin", "im::StepLogout", "im::StepP2pChat"], - "model":[] + "load_config":{ + "manager":{ }, - { - "so_path": "plugins/ChatMsg.so", - "load": false, - "version": 1, - "cmd": [ - { "cmd": 2001, "class": "neb::CmdChat" } + "worker":{ + "boot_load": { + "cmd": [ + { "cmd": 1001, "class": "neb::CmdHello" }, + { "cmd": 1003, "class": "neb::CmdDbAgent" } + ], + "module": [ + { "path": "neb/switch", "class": "neb::ModuleSwitch" }, + { "path": "neb/hello", "class": "neb::ModuleHello" } + ] + }, + "dynamic_loading": [{ + "so_path": "plugins/User.so", + "load": false, + "version": "1.0", + "cmd": [ + { "cmd": 2001, "class": "neb::CmdUserLogin" }, + { "cmd": 2003, "class": "neb::CmdUserLogout" } + ], + "module": [ + { "path": "im/user/login", "class": "neb::ModuleLogin" }, + { "path": "im/user/logout", "class": "neb::ModuleLogout" } + ], + "session":["im::SessionUser", "im::SessionGroup"], + "step":["im::StepLogin", "im::StepLogout", "im::StepP2pChat"], + "matrix":[], + "chain":[] + }, + { + "so_path": "plugins/ChatMsg.so", + "load": false, + "version": "1.0", + "cmd": [ + { "cmd": 2001, "class": "neb::CmdChat" } + ], + "module": [], + "session":[], + "step":[], + "matrix":[], + "chain":[] + } ], - "module": [], - "session":[], - "step":[], - "model":[] - } - ], - "runtime":{ - "chains":{ - "chain_1":["step1", "matrix1", ["step2A", "step2B", "step2C"], "step3", "matrix2"], - "chain_2":[] + "runtime":{ + "chains":{ + "chain_1":["step1", "matrix1", ["step2A", "step2B", "step2C"], "step3", "matrix2"], + "chain_2":[] + } + } + }, + "loader":{ } }, "//custom": "自定义配置,用于通过框架层带给业务", @@ -126,6 +138,7 @@ * port 集群内部通信端口。host:port组成的字符串在集群内唯一标识一个节点,用作管理层面(与Beacon通信)名字服务的节点名字。host:port.workerindex组成的字符串在集群内唯一标识一个节点的一个Worker进程,用作数据层面(也即业务层面,除Beacon之外的节点间通信)名字服务的节点Worker名字。 * server_name 节点Server进程名,方便在服务器中标识和管理,在集群管理和节点通信中都不会用到。通俗一点讲,这是给人用的不是给机器用的。 * worker_num Worker进程数量,每个节点由一个Manager进程和若干个Worker进程构成。通常,如果某台机器只部署了一个Nebula服务并且主要是给这个服务使用的,为了更充分使用机器资源,将worker_num配置成与cpu核数相同。 +* with_loader 是否启动loader进程。Loader进程用于做本地数据存储,大部分IO密集型的应用不会用到,所以默认不会启动Loader进程。 * worker_capacity 进程容量,用于过载保护。进程负载 = Channel数量 * 系数 + Step数量 * 系数。这个计算公式会根据需要和合理性做调整。当进程负载达到进程容量限制时会拒绝新的连接。 * cpu_affinity CPU亲和度,为true时,Worker进程会均匀地绑定到CPU核。默认为false,不绑定。 * config_path 配置文件存储路径,相对于NEBULA_HOME的路径。 @@ -138,9 +151,10 @@ * permission 连接或消息发送频率限制。addr_permit为连接限制,限制每个IP在统计时间内连接次数,超出permit_num次数限制的连接会被直接拒绝;uin_permit为消息数量限制,限制每个用户在单位统计时间内permit_num发送消息数量,超出限制数量的消息会被直接丢弃。stat_interval到了之后重新计算。 * io_timeout 网络IO(连接)超时设置(单位:秒)小数点后面至少保留一位,用于触发连接的有效性检查。如果在到达超时时间前连接有数据收或发过,则从最后一次收发数据时间开始重新计算超时时间;如果超时时间到达却一直没有数据收发过,有三种处理情况:(1) 需要做应用层心跳检查,自动发送心跳包,心跳包得到响应,连接得以保持,重新计算超时时间;(2) 需要做应用层心跳检查,自动发送心跳包,心跳包未得到响应,立即断开连接,回收连接所分配资源;(3) 无须做应用层心跳检查,立即断开连接,回收连接所分配资源。 * step_timeout 步骤超时设置(单位:秒)小数点后面至少保留一位,用于请求发出后等待响应的默认超时等待。在代码层可以为每一个发出的请求设置等待超时,通常为Step类的最后一个参数,如果这个参数为缺省值,则配置文件里的step_timeout会作为这个Step发出请求后等待响应的超时时间。 -* boot_load 服务启动加载的处理模块入口。Nebula框架会被编译成动态库 libnebula.so,基于Nebula框架的Server需要写个main函数并编译链接成一个二进制文件,比如NebulaBeacon,还有一些不需要做动态加载的模块会一起编译链接到这个二进制文件里,而这些模块是Nebula框架未知的(libnebula.so早于任何一个server二进制文件存在),boot_load就是为这些模块加载使用的。boot_load里的配置说明见下文描述动态加载配置说明。 * with_ssl 配置接入服务的对外连接是否需要SSL传输加密,如果需要则配置ssl配置文件路径和相关文件名。 * refresh_interval 刷新Server配置,检查、加载配置或加载卸载插件动态库时间周期。 +* load_config 功能模块加载。功能模块分manager、worker、loader进程的功能模块,分别加载到对应类型的进程里。 +* boot_load 服务启动加载的处理模块入口。Nebula框架会被编译成动态库 libnebula.so,基于Nebula框架的Server需要写个main函数并编译链接成一个二进制文件,比如NebulaBeacon,还有一些不需要做动态加载的模块会一起编译链接到这个二进制文件里,而这些模块是Nebula框架未知的(libnebula.so早于任何一个server二进制文件存在),boot_load就是为这些模块加载使用的。boot_load里的配置说明见下文描述动态加载配置说明。 #### 控制中心配置 * beacon 控制中心地址和端口,有几个控制中心节点就配置几条。如果无须控制中心,则配置为空。 From f4b45bb6791a9b0c1ca25941a9a4546120d71d67 Mon Sep 17 00:00:00 2001 From: nebim Date: Mon, 14 Oct 2019 22:10:15 +0800 Subject: [PATCH 064/176] sys cmd loaded fixed. --- src/actor/ActorBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index fa5335ac..df52555e 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -712,7 +712,7 @@ void ActorBuilder::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdBeat", (int)CMD_REQ_BEAT); if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) { - MakeSharedCmd(nullptr, "neb::CmdWorkerLoad", (int)CMD_REQ_UPDATE_WORKER_LOAD); + MakeSharedCmd(nullptr, "neb::CmdOnWorkerLoad", (int)CMD_REQ_UPDATE_WORKER_LOAD); MakeSharedCmd(nullptr, "neb::CmdOnTellWorker", (int)CMD_REQ_TELL_WORKER); MakeSharedCmd(nullptr, "neb::CmdOnOrientationFdTransfer", (int)CMD_REQ_CONNECT_TO_WORKER); MakeSharedCmd(nullptr, "neb::CmdOnNodeNotice", (int)CMD_REQ_NODE_NOTICE); From 0627fc4f13dad17f1d496924756abb25b4ec5c0d Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 25 Oct 2019 22:06:11 +0800 Subject: [PATCH 065/176] dispatcher bug fixed. --- src/Makefile | 2 +- src/actor/Actor.hpp | 4 ++-- src/actor/{ActorFriend.hpp => ActorSys.hpp} | 10 +++++----- src/actor/cmd/sys_cmd/CmdBeat.hpp | 4 ++-- src/actor/cmd/sys_cmd/CmdNodeNotice.hpp | 4 ++-- src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp | 4 ++-- src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp | 4 ++-- src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp | 4 ++-- src/actor/cmd/sys_cmd/CmdToldWorker.hpp | 4 ++-- src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp | 4 ++-- src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp | 4 ++-- .../cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp | 4 ++-- .../cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp | 4 ++-- .../sys_cmd/manager/CmdOnGetNodeCustomConf.hpp | 4 ++-- src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp | 2 +- src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp | 4 ++-- .../manager/CmdOnOrientationFdTransfer.hpp | 4 ++-- .../cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp | 4 ++-- .../cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp | 4 ++-- .../cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp | 2 +- .../cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp | 4 ++-- .../sys_cmd/manager/CmdOnSetNodeCustomConf.cpp | 2 +- .../sys_cmd/manager/CmdOnSetNodeCustomConf.hpp | 4 ++-- src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp | 4 ++-- src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp | 4 ++-- .../sys_session/manager/SessionManager.cpp | 15 +++++++++------ .../sys_session/manager/SessionManager.hpp | 8 ++++---- src/actor/step/sys_step/StepIoTimeout.hpp | 4 ++-- src/actor/step/sys_step/StepTellWorker.hpp | 4 ++-- .../step/sys_step/manager/StepReportToBeacon.cpp | 2 +- .../step/sys_step/manager/StepReportToBeacon.hpp | 4 ++-- src/ios/Dispatcher.cpp | 9 +++++++-- src/ios/Dispatcher.hpp | 2 +- src/labor/Manager.cpp | 4 ++-- 34 files changed, 79 insertions(+), 71 deletions(-) rename src/actor/{ActorFriend.hpp => ActorSys.hpp} (72%) diff --git a/src/Makefile b/src/Makefile index c0c37a43..175d4fc4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -39,7 +39,7 @@ LDFLAGS := $(LDFLAGS) -D_LINUX_OS_ \ -L$(LIB3RD_PATH)/lib -lprotobuf \ -L$(SYSTEM_LIB_PATH) -lc -lrt -ldl -SUB_INCLUDE = channel labor codec pb mydis logger +SUB_INCLUDE = channel ios labor codec pb mydis logger DEEP_SUB_INCLUDE = actor util CPP_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cpp)) CC_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cc)) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 3e45fe23..02732a77 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -37,7 +37,7 @@ namespace neb class Labor; class Dispatcher; class ActorBuilder; -class ActorFriend; +class ActorSys; class SocketChannel; class RedisChannel; @@ -262,7 +262,7 @@ class Actor: public std::enable_shared_from_this friend class Dispatcher; friend class ActorBuilder; - friend class ActorFriend; + friend class ActorSys; friend class Chain; }; diff --git a/src/actor/ActorFriend.hpp b/src/actor/ActorSys.hpp similarity index 72% rename from src/actor/ActorFriend.hpp rename to src/actor/ActorSys.hpp index 2aa33dc7..6de7c14f 100644 --- a/src/actor/ActorFriend.hpp +++ b/src/actor/ActorSys.hpp @@ -4,12 +4,12 @@ * @brief Actor友元类,用于访问Actor的保护或私有成员 * @author Bwar * @date: 2019年9月21日 - * @note + * @note 只有系统管理类的Actor子类才需要从ActorSys派生 * Modify history: ******************************************************************************/ -#ifndef SRC_ACTOR_ACTORFRIEND_HPP_ -#define SRC_ACTOR_ACTORFRIEND_HPP_ +#ifndef SRC_ACTOR_ACTORSYS_HPP_ +#define SRC_ACTOR_ACTORSYS_HPP_ #include "labor/Labor.hpp" #include "Actor.hpp" @@ -17,7 +17,7 @@ namespace neb { -class ActorFriend +class ActorSys { public: Labor* GetLabor(Actor* pActor) @@ -28,4 +28,4 @@ class ActorFriend } -#endif /* SRC_ACTOR_ACTORFRIEND_HPP_ */ +#endif /* SRC_ACTOR_ACTORSYS_HPP_ */ diff --git a/src/actor/cmd/sys_cmd/CmdBeat.hpp b/src/actor/cmd/sys_cmd/CmdBeat.hpp index 39bf594d..5cc4a70f 100644 --- a/src/actor/cmd/sys_cmd/CmdBeat.hpp +++ b/src/actor/cmd/sys_cmd/CmdBeat.hpp @@ -10,14 +10,14 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDBEAT_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDBEAT_HPP_ +#include "../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" -#include "actor/ActorFriend.hpp" namespace neb { class CmdBeat: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdBeat(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp b/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp index 98b69d5a..f3bc6dbf 100644 --- a/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp +++ b/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp @@ -10,8 +10,8 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDNODENOTICE_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDNODENOTICE_HPP_ +#include "../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" -#include "actor/ActorFriend.hpp" #include "pb/neb_sys.pb.h" namespace neb @@ -24,7 +24,7 @@ namespace neb * @note 各个模块启动时需要向BEACON进行注册 */ class CmdNodeNotice : public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdNodeNotice(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp index b4a23497..2430debb 100644 --- a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp @@ -10,14 +10,14 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDRELOADCUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDRELOADCUSTOMCONF_HPP_ +#include "../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" -#include "actor/ActorFriend.hpp" namespace neb { class CmdReloadCustomConf: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdReloadCustomConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp b/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp index 4dfefb9f..80da9535 100644 --- a/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp +++ b/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp @@ -10,14 +10,14 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECONF_HPP_ +#include "../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" -#include "actor/ActorFriend.hpp" namespace neb { class CmdSetNodeConf: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdSetNodeConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp index 338f79bd..b2f8eeea 100644 --- a/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp @@ -10,14 +10,14 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECUSTOMCONF_HPP_ +#include "../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" -#include "actor/ActorFriend.hpp" namespace neb { class CmdSetNodeCustomConf: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdSetNodeCustomConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdToldWorker.hpp b/src/actor/cmd/sys_cmd/CmdToldWorker.hpp index 0b89c787..a3b60e76 100644 --- a/src/actor/cmd/sys_cmd/CmdToldWorker.hpp +++ b/src/actor/cmd/sys_cmd/CmdToldWorker.hpp @@ -10,14 +10,14 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDTOLDWORKER_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDTOLDWORKER_HPP_ +#include "../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" -#include "actor/ActorFriend.hpp" namespace neb { class CmdToldWorker: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdToldWorker(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp b/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp index 8ef1ad14..d49a86d8 100644 --- a/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp +++ b/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp @@ -10,14 +10,14 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDUPDATENODEID_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDUPDATENODEID_HPP_ +#include "../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" -#include "actor/ActorFriend.hpp" namespace neb { class CmdUpdateNodeId: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdUpdateNodeId(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp index 1fef38a8..97005230 100644 --- a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp +++ b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp @@ -10,15 +10,15 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MODULEHTTPUPGRADE_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MODULEHTTPUPGRADE_HPP_ +#include "../../ActorSys.hpp" #include "codec/Codec.hpp" #include "actor/cmd/Module.hpp" -#include "actor/ActorFriend.hpp" namespace neb { class ModuleHttpUpgrade: public Module, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: ModuleHttpUpgrade(const std::string& strModulePath); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp index fe2adc16..4c385bda 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp @@ -11,14 +11,14 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETCUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETCUSTOMCONF_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb { class CmdOnGetCustomConf: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdOnGetCustomConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp index 9da5b230..b0b97c91 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp @@ -11,14 +11,14 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECONF_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb { class CmdOnGetNodeConf: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdOnGetNodeConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp index 1a32631e..f2baae79 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECUSTOMCONF_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb @@ -19,7 +19,7 @@ namespace neb class CmdOnGetNodeCustomConf: public Cmd, public DynamicCreator, - public ActorFriend + public ActorSys { public: CmdOnGetNodeCustomConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp index 5f5fb456..871601f6 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp @@ -53,7 +53,7 @@ bool CmdOnNodeNotice::AnyMessage( oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); SendTo(pChannel, CMD_RSP_NODE_NOTICE, oInMsgHead.seq(), oOutMsgBody); - m_pSessionManager->SendToWorker(CMD_REQ_NODE_NOTICE, GetSequence(), oInMsgBody); + m_pSessionManager->SendToChild(CMD_REQ_NODE_NOTICE, GetSequence(), oInMsgBody); std::ostringstream oss; std::string strIdentify; std::string strNodeType; diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp index 33af0cec..0c68eecb 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONNODENOTICE_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONNODENOTICE_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb @@ -21,7 +21,7 @@ class SessionManager; class CmdOnNodeNotice: public Cmd, public DynamicCreator, - public ActorFriend + public ActorSys { public: CmdOnNodeNotice(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp index 2327ab8e..320e4599 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONORIENTATIONFDTRANSFER_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONORIENTATIONFDTRANSFER_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb @@ -21,7 +21,7 @@ class SessionManager; class CmdOnOrientationFdTransfer: public Cmd, public DynamicCreator, - public ActorFriend + public ActorSys { public: CmdOnOrientationFdTransfer(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp index a226b293..c0f6a7f7 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp @@ -62,8 +62,8 @@ bool CmdOnSetCustomConf::AnyMessage( fout.close(); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); - m_pSessionManager->SendToWorker(CMD_REQ_SET_CUSTOM_CONFIG, GetSequence(), oInMsgBody); - m_pSessionManager->SendToWorker(CMD_REQ_RELOAD_CUSTOM_CONFIG, GetSequence(), oInMsgBody); + m_pSessionManager->SendToChild(CMD_REQ_SET_CUSTOM_CONFIG, GetSequence(), oInMsgBody); + m_pSessionManager->SendToChild(CMD_REQ_RELOAD_CUSTOM_CONFIG, GetSequence(), oInMsgBody); SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); return(true); } diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp index 88f9ef7e..1de00919 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETCUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETCUSTOMCONF_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb @@ -20,7 +20,7 @@ namespace neb class SessionManager; class CmdOnSetCustomConf: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdOnSetCustomConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp index 2c32fa5f..3fd7e2e1 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp @@ -65,7 +65,7 @@ bool CmdOnSetNodeConf::AnyMessage( fout.close(); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); - m_pSessionManager->SendToWorker(CMD_REQ_SET_NODE_CONFIG, GetSequence(), oInMsgBody); + m_pSessionManager->SendToChild(CMD_REQ_SET_NODE_CONFIG, GetSequence(), oInMsgBody); GetLabor(this)->SetNodeConf(oCurrentConf); SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); return(true); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp index 0037e726..24d602cf 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECONF_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb @@ -20,7 +20,7 @@ namespace neb class SessionManager; class CmdOnSetNodeConf: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdOnSetNodeConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.cpp index b0afa5a5..ffda45d5 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.cpp @@ -55,7 +55,7 @@ bool CmdOnSetNodeCustomConf::AnyMessage( fout.close(); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); - m_pSessionManager->SendToWorker(CMD_REQ_SET_NODE_CUSTOM_CONFIG, GetSequence(), oInMsgBody); + m_pSessionManager->SendToChild(CMD_REQ_SET_NODE_CUSTOM_CONFIG, GetSequence(), oInMsgBody); GetLabor(this)->SetNodeConf(oCurrentConf); SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); return(true); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp index 27523581..b1cd5a5f 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECUSTOMCONF_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb @@ -20,7 +20,7 @@ namespace neb class SessionManager; class CmdOnSetNodeCustomConf: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdOnSetNodeCustomConf(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp index f333d8a0..a3d49748 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp @@ -11,14 +11,14 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONTELLWORKER_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONTELLWORKER_HPP_ +#include "../../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" -#include "actor/ActorFriend.hpp" namespace neb { class CmdOnTellWorker: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdOnTellWorker(int32 iCmd); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp index 4a6a127e..7b0f53f3 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONWORKERLOAD_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONWORKERLOAD_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../../ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb @@ -20,7 +20,7 @@ namespace neb class SessionManager; class CmdOnWorkerLoad: public Cmd, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: CmdOnWorkerLoad(int32 iCmd); diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index bd584d50..091550b6 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -67,16 +67,17 @@ void SessionManager::DelOnlineNode(const std::string& strNodeIdentify) } } -bool SessionManager::SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +bool SessionManager::SendToChild(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { for (auto worker_iter = m_mapWorker.begin(); worker_iter != m_mapWorker.end(); ++worker_iter) { - GetLabor(this)->GetDispatcher()->SendTo(worker_iter->second->iControlFd); + GetLabor(this)->GetDispatcher()->SendTo( + worker_iter->second->iControlFd, iCmd, uiSeq, oMsgBody); } return(true); } -bool SessionManager::SendToWorkerWithoutLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +bool SessionManager::SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { for (auto worker_iter = m_mapWorker.begin(); worker_iter != m_mapWorker.end(); ++worker_iter) { @@ -84,7 +85,8 @@ bool SessionManager::SendToWorkerWithoutLoader(int32 iCmd, uint32 uiSeq, const M { continue; } - GetLabor(this)->GetDispatcher()->SendTo(worker_iter->second->iControlFd); + GetLabor(this)->GetDispatcher()->SendTo( + worker_iter->second->iControlFd, iCmd, uiSeq, oMsgBody); } return(true); } @@ -95,7 +97,8 @@ bool SessionManager::SendToLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgB { if (m_iLoaderDataFd == worker_iter->second->iDataFd) { - GetLabor(this)->GetDispatcher()->SendTo(worker_iter->second->iControlFd); + GetLabor(this)->GetDispatcher()->SendTo( + worker_iter->second->iControlFd, iCmd, uiSeq, oMsgBody); } } return(true); @@ -281,7 +284,7 @@ void SessionManager::SendOnlineNodesToWorker() oSubscribeNode["add_nodes"].Add(CJsonObject(it->second)); } oMsgBody.set_data(oSubscribeNode.ToString()); - SendToWorker(CMD_REQ_NODE_NOTICE, GetSequence(), oMsgBody); + SendToChild(CMD_REQ_NODE_NOTICE, GetSequence(), oMsgBody); } /** diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index e917f9f2..dae2720b 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -11,15 +11,15 @@ #ifndef SRC_ACTOR_SESSION_SYS_SESSION_MANAGER_SESSIONMANAGER_HPP_ #define SRC_ACTOR_SESSION_SYS_SESSION_MANAGER_SESSIONMANAGER_HPP_ +#include "../../../ActorSys.hpp" #include "labor/NodeInfo.hpp" -#include "actor/ActorFriend.hpp" #include "actor/session/Session.hpp" namespace neb { class SessionManager : public Session, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: SessionManager(); @@ -29,8 +29,8 @@ class SessionManager : public Session, void AddOnlineNode(const std::string& strNodeIdentify, const std::string& strNodeInfo); void DelOnlineNode(const std::string& strNodeIdentify); - bool SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); // 向Worker发送数据 - bool SendToWorkerWithoutLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendToChild(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); // 向Worker和Loader发送数据 + bool SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); bool SendToLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); void AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); void AddLoaderInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); diff --git a/src/actor/step/sys_step/StepIoTimeout.hpp b/src/actor/step/sys_step/StepIoTimeout.hpp index 4b5aa987..7d34ecbb 100644 --- a/src/actor/step/sys_step/StepIoTimeout.hpp +++ b/src/actor/step/sys_step/StepIoTimeout.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_STEP_SYS_STEP_STEPIOTIMEOUT_HPP_ #define SRC_ACTOR_STEP_SYS_STEP_STEPIOTIMEOUT_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../ActorSys.hpp" #include "actor/step/PbStep.hpp" #include "Definition.hpp" @@ -25,7 +25,7 @@ namespace neb */ class StepIoTimeout: public PbStep, public DynamicCreator >, - public ActorFriend + public ActorSys { public: StepIoTimeout(std::shared_ptr pChannel); diff --git a/src/actor/step/sys_step/StepTellWorker.hpp b/src/actor/step/sys_step/StepTellWorker.hpp index 2b0ba35d..4a954d14 100644 --- a/src/actor/step/sys_step/StepTellWorker.hpp +++ b/src/actor/step/sys_step/StepTellWorker.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_STEP_SYS_STEP_STEPTELLWORKER_HPP_ #define SRC_ACTOR_STEP_SYS_STEP_STEPTELLWORKER_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../ActorSys.hpp" #include "actor/step/PbStep.hpp" #include "pb/neb_sys.pb.h" @@ -19,7 +19,7 @@ namespace neb class StepTellWorker: public PbStep, public DynamicCreator >, - public ActorFriend + public ActorSys { public: StepTellWorker(std::shared_ptr pChannel); diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp index affbb281..638e39c6 100644 --- a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp +++ b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp @@ -65,7 +65,7 @@ E_CMD_STATUS StepReportToBeacon::Callback( if (uiNodeId != GetLabor(this)->GetNodeInfo().uiNodeId) { GetLabor(this)->SetNodeId((uiNodeId)); - m_pSessionManager->SendToWorker(CMD_REQ_REFRESH_NODE_ID, oInMsgHead.seq(), oInMsgBody); + m_pSessionManager->SendToChild(CMD_REQ_REFRESH_NODE_ID, oInMsgHead.seq(), oInMsgBody); } return(CMD_STATUS_RUNNING); } diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.hpp b/src/actor/step/sys_step/manager/StepReportToBeacon.hpp index b0f851f7..ec1c547d 100644 --- a/src/actor/step/sys_step/manager/StepReportToBeacon.hpp +++ b/src/actor/step/sys_step/manager/StepReportToBeacon.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_STEP_SYS_STEP_MANAGER_STEPREPORTTOBEACON_HPP_ #define SRC_ACTOR_STEP_SYS_STEP_MANAGER_STEPREPORTTOBEACON_HPP_ -#include "actor/ActorFriend.hpp" +#include "../../../ActorSys.hpp" #include "actor/step/PbStep.hpp" namespace neb @@ -20,7 +20,7 @@ namespace neb class SessionManager; class StepReportToBeacon: public PbStep, - public DynamicCreator, public ActorFriend + public DynamicCreator, public ActorSys { public: StepReportToBeacon(ev_tstamp dTimeout); diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 4c598d63..b92d559c 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -1220,12 +1220,12 @@ bool Dispatcher::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor return(false); } -bool Dispatcher::SendTo(int iFd) +bool Dispatcher::SendTo(int iFd, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { auto iter = m_mapSocketChannel.find(iFd); if (iter != m_mapSocketChannel.end()) { - return(SendTo(iter->second)); + return(SendTo(iter->second, iCmd, uiSeq, oMsgBody)); } return(false); } @@ -1612,6 +1612,11 @@ std::shared_ptr Dispatcher::CreateSocketChannel(int iFd, E_CODEC_ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice) { + if (pChannel == nullptr) + { + LOG4_ERROR("pChannel not exist!"); + return(false); + } LOG4_DEBUG("%s disconnect, fd %d, identify %s", pChannel->m_pImpl->GetRemoteAddr().c_str(), pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetIdentify().c_str()); if (bChannelNotice) { diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 37313918..5b7c7e16 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -138,7 +138,7 @@ class Dispatcher // SendTo() for unix domain socket bool SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender); - bool SendTo(int iFd); + bool SendTo(int iFd, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); bool Disconnect(std::shared_ptr pChannel, bool bChannelNotice = true); bool Disconnect(const std::string& strIdentify, bool bChannelNotice = true); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 4cfac1d3..dc4b8bcc 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -563,7 +563,7 @@ void Manager::RefreshServer() oLogLevel.set_log_level(iLogLevel); oLogLevel.set_net_log_level(iNetLogLevel); oMsgBody.set_data(oLogLevel.SerializeAsString()); - m_pSessionManager->SendToWorker(CMD_REQ_SET_LOG_LEVEL, GetSequence(), oMsgBody); + m_pSessionManager->SendToChild(CMD_REQ_SET_LOG_LEVEL, GetSequence(), oMsgBody); } // 更新动态库配置或重新加载动态库 @@ -572,7 +572,7 @@ void Manager::RefreshServer() { MsgBody oMsgBody; oMsgBody.set_data(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"].ToString()); - m_pSessionManager->SendToWorkerWithoutLoader(CMD_REQ_RELOAD_SO, GetSequence(), oMsgBody); + m_pSessionManager->SendToWorker(CMD_REQ_RELOAD_SO, GetSequence(), oMsgBody); } if (m_oLastConf["load_config"]["loader"]["dynamic_loading"].ToString() != m_oCurrentConf["load_config"]["loader"]["dynamic_loading"].ToString()) From 60234ed9fdc4fe3cc61710697a974980c91177cf Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 27 Oct 2019 17:08:13 +0800 Subject: [PATCH 066/176] 1. add max log line buf config. 2. add Manager transfer fd to Loader. --- conf/nebula.json | 6 ++- .../sys_session/manager/SessionManager.cpp | 45 ++++++++++++------- .../sys_session/manager/SessionManager.hpp | 5 ++- src/labor/Manager.cpp | 8 +++- src/labor/Worker.cpp | 4 +- src/logger/Logger.hpp | 2 +- src/logger/NetLogger.cpp | 10 +++-- src/logger/NetLogger.hpp | 2 + 8 files changed, 56 insertions(+), 26 deletions(-) diff --git a/conf/nebula.json b/conf/nebula.json index 8ebe9039..e9d59284 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -19,6 +19,8 @@ "worker_num": 10, "//with_loader":"是否启动loader进程", "with_loader":false, + "//new_client_to_loader":"集群外部(从access_port端口进来)的新连接直接转发到loader,不转发给worker", + "new_client_to_loader":false, "//cpu_affinity":"是否设置进程CPU亲和度(绑定CPU)", "cpu_affinity":false, "//worker_capacity": "子进程最大工作负荷", @@ -27,6 +29,8 @@ "config_path": "conf/", "//log_path": "日志文件路径(相对路径)", "log_path": "log/", + "//log_max_line_len":"每条日志最大字节数", + "log_max_line_len":4096, "//beacon": "控制中心", "beacon": [ { "host": "192.168.1.11", "port": 16000 }, @@ -54,7 +58,7 @@ "cert_file": "20180623143147.pem", "key_file": "20180623143147.key" }, - "//refresh_interval": "刷新Server配置,检查、加载插件动态库时间周期(周期时间长短视服务器忙闲而定)", + "//refresh_interval": "刷新Server配置,检查、加载插件动态库时间周期", "refresh_interval": 60, "load_config":{ "manager":{ diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 091550b6..4061e698 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -21,8 +21,8 @@ namespace neb { -SessionManager::SessionManager() - : Session("neb::SessionManager", gc_dNoTimeout) +SessionManager::SessionManager(bool bDirectToLoader) + : Session("neb::SessionManager", gc_dNoTimeout), m_bDirectToLoader(bDirectToLoader) { } @@ -191,20 +191,35 @@ std::pair SessionManager::GetMinLoadWorkerDataFd() int iMinLoadWorkerFd = 0; int iMinLoad = -1; std::pair worker_pid_fd; - for (auto iter = m_mapWorker.begin(); iter != m_mapWorker.end(); ++iter) + if (m_bDirectToLoader && m_iLoaderDataFd != -1) { - if (iter == m_mapWorker.begin() && iter->second->iDataFd != m_iLoaderDataFd) - { - iMinLoadWorkerFd = iter->second->iDataFd; - iMinLoad = iter->second->iLoad; - worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); - } - else if (iter->second->iLoad < iMinLoad && iter->second->iDataFd != m_iLoaderDataFd) - { - iMinLoadWorkerFd = iter->second->iDataFd; - iMinLoad = iter->second->iLoad; - worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); - } + auto it = m_mapWorkerFdPid.find(m_iLoaderDataFd); + if (it != m_mapWorkerFdPid.end()) + { + worker_pid_fd = std::pair(it->second, m_iLoaderDataFd); + } + else + { + worker_pid_fd = std::pair(0, m_iLoaderDataFd); + } + } + else + { + for (auto iter = m_mapWorker.begin(); iter != m_mapWorker.end(); ++iter) + { + if (iMinLoad == -1 && iter->second->iDataFd != m_iLoaderDataFd) + { + iMinLoadWorkerFd = iter->second->iDataFd; + iMinLoad = iter->second->iLoad; + worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); + } + else if (iter->second->iLoad < iMinLoad && iter->second->iDataFd != m_iLoaderDataFd) + { + iMinLoadWorkerFd = iter->second->iDataFd; + iMinLoad = iter->second->iLoad; + worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); + } + } } return(worker_pid_fd); } diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index dae2720b..a0d2b97f 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -19,10 +19,10 @@ namespace neb { class SessionManager : public Session, - public DynamicCreator, public ActorSys + public DynamicCreator, public ActorSys { public: - SessionManager(); + SessionManager(bool bDirectToLoader); virtual ~SessionManager(); virtual E_CMD_STATUS Timeout(); @@ -46,6 +46,7 @@ class SessionManager : public Session, bool NewSocketWhenLoaderCreated(); private: + bool m_bDirectToLoader = false; int m_iLoaderDataFd = -1; std::unordered_map m_mapWorker; ///< 业务逻辑工作进程及进程属性,key为pid std::unordered_map m_mapWorkerStartNum; ///< 进程被启动次数,key为WorkerIdx diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index dc4b8bcc..cc0d4e4b 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -125,6 +125,7 @@ bool Manager::InitLogger(const CJsonObject& oJsonConf) { int32 iMaxLogFileSize = 0; int32 iMaxLogFileNum = 0; + int32 iMaxLogLineLen = 1024; std::string strLoggingHost; std::string strLogname = m_stNodeInfo.strWorkPath + std::string("/") + oJsonConf("log_path") + std::string("/") + getproctitle() + std::string(".log"); @@ -133,8 +134,9 @@ bool Manager::InitLogger(const CJsonObject& oJsonConf) ssServerName << getproctitle() << " " << m_stNodeInfo.strHostForServer << ":" << m_stNodeInfo.iPortForServer; oJsonConf.Get("max_log_file_size", iMaxLogFileSize); oJsonConf.Get("max_log_file_num", iMaxLogFileNum); + oJsonConf.Get("log_max_line_len", iMaxLogLineLen); oJsonConf.Get("log_level", iLogLevel); - m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, this); + m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, iMaxLogLineLen, this); m_pLogger->SetNetLogLevel(iNetLogLevel); LOG4_NOTICE("%s program begin, and work path %s...", oJsonConf("server_name").c_str(), m_stNodeInfo.strWorkPath.c_str()); return(true); @@ -346,8 +348,10 @@ bool Manager::CreateEvents() fpe_signal_watcher->data = (void*)this; m_pDispatcher->AddEvent(fpe_signal_watcher, Dispatcher::SignalCallback, SIGFPE); + bool bDirectToLoader = false; + m_oCurrentConf.Get("new_client_to_loader", bDirectToLoader); m_pSessionManager = std::dynamic_pointer_cast( - m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager")); + m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", bDirectToLoader)); AddPeriodicTaskEvent(); return(true); diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index f69c6eba..32165cda 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -209,6 +209,7 @@ bool Worker::InitLogger(const CJsonObject& oJsonConf) { int32 iMaxLogFileSize = 0; int32 iMaxLogFileNum = 0; + int32 iMaxLogLineLen = 1024; int32 iLogLevel = 0; int32 iNetLogLevel = 0; std::string strLogname = m_stNodeInfo.strWorkPath + std::string("/") + oJsonConf("log_path") @@ -216,9 +217,10 @@ bool Worker::InitLogger(const CJsonObject& oJsonConf) std::string strParttern = "[%D,%d{%q}][%p] [%l] %m%n"; oJsonConf.Get("max_log_file_size", iMaxLogFileSize); oJsonConf.Get("max_log_file_num", iMaxLogFileNum); + oJsonConf.Get("log_max_line_len", iMaxLogLineLen); oJsonConf.Get("net_log_level", iNetLogLevel); oJsonConf.Get("log_level", iLogLevel); - m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, this); + m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, iMaxLogLineLen, this); m_pLogger->SetNetLogLevel(iNetLogLevel); LOG4_NOTICE("%s program begin...", getproctitle()); return(true); diff --git a/src/logger/Logger.hpp b/src/logger/Logger.hpp index 3774b3b5..f0042751 100644 --- a/src/logger/Logger.hpp +++ b/src/logger/Logger.hpp @@ -16,7 +16,7 @@ namespace neb { -const unsigned int gc_uiMaxLogLineLen = 1024; +const unsigned int gc_uiMaxLogLineLen = 2048; const unsigned int gc_uiMaxLogFileSize = 2048000; const unsigned int gc_uiMaxRollLogFileIndex = 9; diff --git a/src/logger/NetLogger.cpp b/src/logger/NetLogger.cpp index 5e8ec047..15a55aba 100644 --- a/src/logger/NetLogger.cpp +++ b/src/logger/NetLogger.cpp @@ -20,10 +20,12 @@ namespace neb { -NetLogger::NetLogger(const std::string strLogFile, int iLogLev, unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex, Labor* pLabor) - : m_pLogBuff(NULL), m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), m_bEnableNetLogger(false), m_pLabor(pLabor), m_pLog(nullptr) +NetLogger::NetLogger(const std::string strLogFile, int iLogLev, unsigned int uiMaxFileSize, + unsigned int uiMaxRollFileIndex, unsigned int uiMaxLogLineLen, Labor* pLabor) + : m_pLogBuff(NULL), m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), m_uiMaxLogLineLen(uiMaxFileSize), + m_bEnableNetLogger(false), m_pLabor(pLabor), m_pLog(nullptr) { - m_pLogBuff = (char*)malloc(gc_uiMaxLogLineLen); + m_pLogBuff = (char*)malloc(m_uiMaxLogLineLen); #if __cplusplus >= 201401L m_pLog = std::make_unique(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex); #else @@ -39,7 +41,7 @@ int NetLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLin { va_list ap; va_start(ap, szLogStr); - vsnprintf(m_pLogBuff, gc_uiMaxLogLineLen, szLogStr, ap); + vsnprintf(m_pLogBuff, m_uiMaxLogLineLen, szLogStr, ap); va_end(ap); m_pLog->WriteLog(iLev, szFileName, uiFileLine, szFunction, m_pLogBuff); diff --git a/src/logger/NetLogger.hpp b/src/logger/NetLogger.hpp index 80c9c715..3732f3c3 100644 --- a/src/logger/NetLogger.hpp +++ b/src/logger/NetLogger.hpp @@ -27,6 +27,7 @@ class NetLogger: public Logger int iLogLev = Logger::INFO, unsigned int uiMaxFileSize = gc_uiMaxLogFileSize, unsigned int uiMaxRollFileIndex = gc_uiMaxRollLogFileIndex, + unsigned int uiMaxLogLineLen = gc_uiMaxLogLineLen, Labor* pLabor = nullptr); virtual ~NetLogger(); @@ -56,6 +57,7 @@ class NetLogger: public Logger char* m_pLogBuff; int m_iLogLevel; int m_iNetLogLevel; + unsigned int m_uiMaxLogLineLen; bool m_bEnableNetLogger; Labor* m_pLabor; std::string m_strLogData; ///< 用于提高序列化效率 From deadfa6f438010b90cc81d3fb42cbc611995bebd Mon Sep 17 00:00:00 2001 From: wincsb Date: Tue, 29 Oct 2019 10:35:26 +0800 Subject: [PATCH 067/176] Update Dispatcher.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在构造函数里已经初始化过了。重复初始化! --- src/ios/Dispatcher.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index b92d559c..2763618d 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -34,12 +34,13 @@ Dispatcher::Dispatcher(Labor* pLabor, std::shared_ptr pLogger) m_pLogger(pLogger), m_pSessionNode(nullptr) { m_pErrBuff = (char*)malloc(gc_iErrBuffLen); + m_loop = ev_loop_new(EVFLAG_FORKCHECK | EVFLAG_SIGNALFD); -#if __cplusplus >= 201401L +/*#if __cplusplus >= 201401L m_pSessionNode = std::make_unique(); #else m_pSessionNode = std::unique_ptr(new Nodes()); -#endif +#endif*/ } Dispatcher::~Dispatcher() From 2d941875efcc1be79a43e8df6c10b2ff2ce0dacd Mon Sep 17 00:00:00 2001 From: wincsb Date: Tue, 29 Oct 2019 10:38:06 +0800 Subject: [PATCH 068/176] Update Manager.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 漏了释放 --- src/labor/Manager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index cc0d4e4b..b64a6eb2 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -314,6 +314,12 @@ void Manager::Destroy() delete m_pActorBuilder; m_pActorBuilder = nullptr; } + + if (m_pErrBuff != NULL) + { + free(m_pErrBuff); + m_pErrBuff = NULL; + } } bool Manager::CreateEvents() From f159bf4b8b30f81bb8a6b45dacee5d9da44ed395 Mon Sep 17 00:00:00 2001 From: wincsb Date: Tue, 29 Oct 2019 11:44:53 +0800 Subject: [PATCH 069/176] fix bug --- src/ios/Dispatcher.cpp | 8 ++++---- src/labor/Manager.cpp | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 2763618d..22296516 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -36,11 +36,11 @@ Dispatcher::Dispatcher(Labor* pLabor, std::shared_ptr pLogger) m_pErrBuff = (char*)malloc(gc_iErrBuffLen); m_loop = ev_loop_new(EVFLAG_FORKCHECK | EVFLAG_SIGNALFD); -/*#if __cplusplus >= 201401L +#if __cplusplus >= 201401L m_pSessionNode = std::make_unique(); #else m_pSessionNode = std::unique_ptr(new Nodes()); -#endif*/ +#endif } Dispatcher::~Dispatcher() @@ -1544,11 +1544,11 @@ int32 Dispatcher::GetClientNum() const bool Dispatcher::Init() { -#if __cplusplus >= 201401L +/*#if __cplusplus >= 201401L m_pSessionNode = std::make_shared(); #else m_pSessionNode = std::unique_ptr(new Nodes()); -#endif +#endif*/ return(true); } diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index b64a6eb2..f7e4365a 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -320,6 +320,7 @@ void Manager::Destroy() free(m_pErrBuff); m_pErrBuff = NULL; } + } bool Manager::CreateEvents() From 895534ca098a9f25b544432fdbd9653a4fec99b8 Mon Sep 17 00:00:00 2001 From: wincsb Date: Tue, 29 Oct 2019 11:59:17 +0800 Subject: [PATCH 070/176] Revert "fix bug" This reverts commit f159bf4b8b30f81bb8a6b45dacee5d9da44ed395. --- src/ios/Dispatcher.cpp | 8 ++++---- src/labor/Manager.cpp | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 22296516..2763618d 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -36,11 +36,11 @@ Dispatcher::Dispatcher(Labor* pLabor, std::shared_ptr pLogger) m_pErrBuff = (char*)malloc(gc_iErrBuffLen); m_loop = ev_loop_new(EVFLAG_FORKCHECK | EVFLAG_SIGNALFD); -#if __cplusplus >= 201401L +/*#if __cplusplus >= 201401L m_pSessionNode = std::make_unique(); #else m_pSessionNode = std::unique_ptr(new Nodes()); -#endif +#endif*/ } Dispatcher::~Dispatcher() @@ -1544,11 +1544,11 @@ int32 Dispatcher::GetClientNum() const bool Dispatcher::Init() { -/*#if __cplusplus >= 201401L +#if __cplusplus >= 201401L m_pSessionNode = std::make_shared(); #else m_pSessionNode = std::unique_ptr(new Nodes()); -#endif*/ +#endif return(true); } diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index f7e4365a..b64a6eb2 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -320,7 +320,6 @@ void Manager::Destroy() free(m_pErrBuff); m_pErrBuff = NULL; } - } bool Manager::CreateEvents() From 2f43a510716f88a5d298f817032e379c3a250be1 Mon Sep 17 00:00:00 2001 From: wincsb Date: Sun, 3 Nov 2019 13:21:32 +0800 Subject: [PATCH 071/176] =?UTF-8?q?1=E3=80=81[2019-11-02=2018:45:12,622][E?= =?UTF-8?q?RROR][../src/ios/Dispatcher.cpp:1003][AutoSend]=20getaddrinfo("?= =?UTF-8?q?192.168.137.100:16050192.168.137.100",=20"16050")=20error=20-2:?= =?UTF-8?q?=20Name=20or=20service=20not=20known=20=20=EF=BC=8C=E8=B0=83?= =?UTF-8?q?=E7=94=A8str("")=E5=B0=86=E7=BC=93=E5=86=B2=E5=8C=BA=E6=B8=85?= =?UTF-8?q?=E9=9B=B6=EF=BC=8C=E6=B8=85=E9=99=A4=E8=84=8F=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=202=E3=80=81CMD=5FREQ=5FDISCONNECT=20=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E5=AE=9E=E7=8E=B0=E6=9C=AA=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actor/ActorBuilder.cpp | 1 + src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index df52555e..385e7eba 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -643,6 +643,7 @@ void ActorBuilder::RemoveChain(uint32 uiChainId) void ActorBuilder::ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData) { + //TODO CMD_REQ_DISCONNECT 命令业务实现未定义 LOG4_TRACE(" "); auto cmd_iter = m_mapCmd.find(CMD_REQ_DISCONNECT); if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp index 871601f6..27e1181d 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp @@ -67,6 +67,7 @@ bool CmdOnNodeNotice::AnyMessage( && oJson["add_nodes"][i].Get("node_port",iNodePort) && oJson["add_nodes"][i].Get("worker_num",iWorkerNum)) { + oss.str(""); oss << strNodeHost << ":" << iNodePort; strIdentify = std::move(oss.str()); m_pSessionManager->AddOnlineNode(strIdentify, oJson["add_nodes"][i].ToString()); @@ -74,6 +75,7 @@ bool CmdOnNodeNotice::AnyMessage( { for(int j = 1; j <= iWorkerNum; ++j) { + oss.str(""); oss << strNodeHost << ":" << iNodePort << "." << j; strIdentify = std::move(oss.str()); GetLabor(this)->GetDispatcher()->AddNodeIdentify(strNodeType, strIdentify); From a25673a2feb52103d6348c09f7462cdb15849439 Mon Sep 17 00:00:00 2001 From: wincsb Date: Wed, 6 Nov 2019 10:17:57 +0800 Subject: [PATCH 072/176] =?UTF-8?q?=E8=A7=A3=E5=86=B3ActorFactory::Instance()->Create()=E8=B0=83=E7=94=A8=E4=B8=8D=E5=88=B0?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=EF=BC=8C=E8=80=8C=E8=BF=9B=E5=85=A5NewActor(?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actor/ActorBuilder.cpp | 8 ++++---- src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp | 2 ++ src/ios/Dispatcher.cpp | 4 ++-- src/labor/Manager.cpp | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 385e7eba..d9d4edc1 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -1074,12 +1074,12 @@ void ActorBuilder::BootLoadCmd(CJsonObject& oCmdConf) for (int i = 0; i < oCmdConf["cmd"].GetArraySize(); ++i) { oCmdConf["cmd"][i].Get("cmd", iCmd); - MakeSharedCmd(nullptr, oCmdConf["cmd"][i]("class"), iCmd); + MakeSharedCmd(nullptr, oCmdConf["cmd"][i]("class"), (int32)iCmd); } for (int j = 0; j < oCmdConf["module"].GetArraySize(); ++j) { oCmdConf["module"][j].Get("path", strUrlPath); - MakeSharedModule(nullptr, oCmdConf["module"][j]("class"), strUrlPath); + MakeSharedModule(nullptr, oCmdConf["module"][j]("class"), (std::string)strUrlPath); } } @@ -1207,14 +1207,14 @@ void ActorBuilder::LoadDynamicSymbol(CJsonObject& oOneSoConf) std::unordered_set setCmd; m_mapLoadedCmd.insert(std::make_pair(oOneSoConf["cmd"][i]("class"), setCmd)); oOneSoConf["cmd"][i].Get("cmd", iCmd); - MakeSharedCmd(nullptr, oOneSoConf["cmd"][i]("class"), iCmd); + MakeSharedCmd(nullptr, oOneSoConf["cmd"][i]("class"), (int32)iCmd); } for (int j = 0; j < oOneSoConf["module"].GetArraySize(); ++j) { std::unordered_set setModule; m_mapLoadedModule.insert(std::make_pair(oOneSoConf["module"][j]("class"), setModule)); oOneSoConf["module"][j].Get("path", strUrlPath); - MakeSharedModule(nullptr, oOneSoConf["module"][j]("class"), strUrlPath); + MakeSharedModule(nullptr, oOneSoConf["module"][j]("class"), (std::string)strUrlPath); } for (int k = 0; k < oOneSoConf["session"].GetArraySize(); ++k) { diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp index 27e1181d..76f1c808 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp @@ -92,6 +92,7 @@ bool CmdOnNodeNotice::AnyMessage( && oJson["del_nodes"][i].Get("node_port",iNodePort) && oJson["del_nodes"][i].Get("worker_num",iWorkerNum)) { + oss.str(""); oss << strNodeHost << ":" << iNodePort; strIdentify = std::move(oss.str()); m_pSessionManager->DelOnlineNode(strIdentify); @@ -99,6 +100,7 @@ bool CmdOnNodeNotice::AnyMessage( { for(int j = 1; j <= iWorkerNum; ++j) { + oss.str(""); oss << strNodeHost << ":" << iNodePort << "." << j; strIdentify = std::move(oss.str()); if (std::string("LOGGER") == strNodeType) diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 2763618d..11955ad2 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -409,7 +409,7 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) else { std::shared_ptr pStepConnectWorker = m_pLabor->GetActorBuilder()->MakeSharedStep( - nullptr, "neb::StepConnectWorker", pChannel, pChannel->m_pImpl->m_unRemoteWorkerIdx); + nullptr, "neb::StepConnectWorker", (std::shared_ptr)pChannel, (uint16)pChannel->m_pImpl->m_unRemoteWorkerIdx); if (nullptr == pStepConnectWorker) { LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); @@ -472,7 +472,7 @@ bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) if (pChannel->m_pImpl->NeedAliveCheck()) // 需要发送心跳检查 { std::shared_ptr pStepIoTimeout = m_pLabor->GetActorBuilder()->MakeSharedStep( - nullptr, "neb::StepIoTimeout", pChannel); + nullptr, "neb::StepIoTimeout", (std::shared_ptr)pChannel); if (nullptr == pStepIoTimeout) { LOG4_ERROR("new StepIoTimeout error!"); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index b64a6eb2..0a80498e 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -357,7 +357,7 @@ bool Manager::CreateEvents() bool bDirectToLoader = false; m_oCurrentConf.Get("new_client_to_loader", bDirectToLoader); m_pSessionManager = std::dynamic_pointer_cast( - m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", bDirectToLoader)); + m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", (bool)bDirectToLoader)); AddPeriodicTaskEvent(); return(true); From 8038f970607fbff5b34ef59b5bef0339fb9014c0 Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 8 Nov 2019 11:45:57 +0800 Subject: [PATCH 073/176] =?UTF-8?q?Revert=20"=E8=A7=A3=E5=86=B3ActorFactor?= =?UTF-8?q?y::Instance()->Create()=E8=B0=83=E7=94=A8=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E5=A4=B1=E8=B4=A5=EF=BC=8C=E8=80=8C=E8=BF=9B=E5=85=A5?= =?UTF-8?q?NewActor()"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actor/ActorBuilder.cpp | 8 ++++---- src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp | 2 -- src/ios/Dispatcher.cpp | 4 ++-- src/labor/Manager.cpp | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index d9d4edc1..385e7eba 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -1074,12 +1074,12 @@ void ActorBuilder::BootLoadCmd(CJsonObject& oCmdConf) for (int i = 0; i < oCmdConf["cmd"].GetArraySize(); ++i) { oCmdConf["cmd"][i].Get("cmd", iCmd); - MakeSharedCmd(nullptr, oCmdConf["cmd"][i]("class"), (int32)iCmd); + MakeSharedCmd(nullptr, oCmdConf["cmd"][i]("class"), iCmd); } for (int j = 0; j < oCmdConf["module"].GetArraySize(); ++j) { oCmdConf["module"][j].Get("path", strUrlPath); - MakeSharedModule(nullptr, oCmdConf["module"][j]("class"), (std::string)strUrlPath); + MakeSharedModule(nullptr, oCmdConf["module"][j]("class"), strUrlPath); } } @@ -1207,14 +1207,14 @@ void ActorBuilder::LoadDynamicSymbol(CJsonObject& oOneSoConf) std::unordered_set setCmd; m_mapLoadedCmd.insert(std::make_pair(oOneSoConf["cmd"][i]("class"), setCmd)); oOneSoConf["cmd"][i].Get("cmd", iCmd); - MakeSharedCmd(nullptr, oOneSoConf["cmd"][i]("class"), (int32)iCmd); + MakeSharedCmd(nullptr, oOneSoConf["cmd"][i]("class"), iCmd); } for (int j = 0; j < oOneSoConf["module"].GetArraySize(); ++j) { std::unordered_set setModule; m_mapLoadedModule.insert(std::make_pair(oOneSoConf["module"][j]("class"), setModule)); oOneSoConf["module"][j].Get("path", strUrlPath); - MakeSharedModule(nullptr, oOneSoConf["module"][j]("class"), (std::string)strUrlPath); + MakeSharedModule(nullptr, oOneSoConf["module"][j]("class"), strUrlPath); } for (int k = 0; k < oOneSoConf["session"].GetArraySize(); ++k) { diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp index 76f1c808..27e1181d 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp @@ -92,7 +92,6 @@ bool CmdOnNodeNotice::AnyMessage( && oJson["del_nodes"][i].Get("node_port",iNodePort) && oJson["del_nodes"][i].Get("worker_num",iWorkerNum)) { - oss.str(""); oss << strNodeHost << ":" << iNodePort; strIdentify = std::move(oss.str()); m_pSessionManager->DelOnlineNode(strIdentify); @@ -100,7 +99,6 @@ bool CmdOnNodeNotice::AnyMessage( { for(int j = 1; j <= iWorkerNum; ++j) { - oss.str(""); oss << strNodeHost << ":" << iNodePort << "." << j; strIdentify = std::move(oss.str()); if (std::string("LOGGER") == strNodeType) diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 11955ad2..2763618d 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -409,7 +409,7 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) else { std::shared_ptr pStepConnectWorker = m_pLabor->GetActorBuilder()->MakeSharedStep( - nullptr, "neb::StepConnectWorker", (std::shared_ptr)pChannel, (uint16)pChannel->m_pImpl->m_unRemoteWorkerIdx); + nullptr, "neb::StepConnectWorker", pChannel, pChannel->m_pImpl->m_unRemoteWorkerIdx); if (nullptr == pStepConnectWorker) { LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); @@ -472,7 +472,7 @@ bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) if (pChannel->m_pImpl->NeedAliveCheck()) // 需要发送心跳检查 { std::shared_ptr pStepIoTimeout = m_pLabor->GetActorBuilder()->MakeSharedStep( - nullptr, "neb::StepIoTimeout", (std::shared_ptr)pChannel); + nullptr, "neb::StepIoTimeout", pChannel); if (nullptr == pStepIoTimeout) { LOG4_ERROR("new StepIoTimeout error!"); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 0a80498e..b64a6eb2 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -357,7 +357,7 @@ bool Manager::CreateEvents() bool bDirectToLoader = false; m_oCurrentConf.Get("new_client_to_loader", bDirectToLoader); m_pSessionManager = std::dynamic_pointer_cast( - m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", (bool)bDirectToLoader)); + m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", bDirectToLoader)); AddPeriodicTaskEvent(); return(true); From c262f7fecf794d42e452476649252c5a00c75969 Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 8 Nov 2019 12:24:27 +0800 Subject: [PATCH 074/176] set stringsteam empty to CmdOnNodeNotice and change NewActor comment. --- src/actor/ActorBuilder.hpp | 7 +++++-- src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 63648533..e5a22973 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -216,7 +216,10 @@ std::shared_ptr ActorBuilder::MakeSharedActor(Actor* pCreator, const std: /** * @brief 为兼容&&参数推导差异导致ActorFactory未Regist进而导致 * ActorFactory::Instance()->Create()调用不到对应的创建函数而增加。 - * NewActor()参数将按值传递,如果调用到NewActor()才new成功,代价会相对大些。 + * NewActor()参数将按值传递,如果调用到NewActor()才new成功,代价可能会相对大些。 + * 如果是整型、浮点型等内置类型则正常,如果是std::string等自定义类型,检查一下 + * Actor子类定义时public DynamicCreator传递的参数是否写错,导致本该按引用传递 + * 参数变成了按值传递。 */ pActor = NewActor(strActorName, std::forward(args)...); if (nullptr == pActor) @@ -276,7 +279,7 @@ std::shared_ptr ActorBuilder::MakeSharedChain(Actor* pCreator, const std: template Actor* ActorBuilder::NewActor(const std::string& strActorName, Targs... args) { - LOG4_TRACE("%s() called by MakeSharedActor().", __FUNCTION__); + LOG4_TRACE("\"%s\" created by NewActor().", strActorName.c_str()); return(ActorFactory::Instance()->Create(strActorName, std::forward(args)...)); } diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp index 27e1181d..f01f7098 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp @@ -92,11 +92,13 @@ bool CmdOnNodeNotice::AnyMessage( && oJson["del_nodes"][i].Get("node_port",iNodePort) && oJson["del_nodes"][i].Get("worker_num",iWorkerNum)) { + oss.str(""); oss << strNodeHost << ":" << iNodePort; strIdentify = std::move(oss.str()); m_pSessionManager->DelOnlineNode(strIdentify); if (std::string("LOGGER") == strNodeType) { + oss.str(""); for(int j = 1; j <= iWorkerNum; ++j) { oss << strNodeHost << ":" << iNodePort << "." << j; From fac98ae0c2c27545b3cd54cde7ba53b3e734e0db Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 22 Nov 2019 23:03:46 +0800 Subject: [PATCH 075/176] logger optimization, loader restart and channel notice bug fixed. --- src/actor/Actor.cpp | 8 +++++++- src/actor/Actor.hpp | 9 ++++++++ src/actor/ActorBuilder.cpp | 26 ++++++++++++++++++----- src/actor/chain/Chain.cpp | 33 +++++++++++++++++++++++++++++- src/actor/chain/Chain.hpp | 5 ++++- src/ios/Dispatcher.cpp | 5 ++++- src/ios/Dispatcher.hpp | 2 +- src/labor/Manager.cpp | 20 ++++++++++++++---- src/logger/FileLogger.cpp | 42 ++++++++++++++++++++++---------------- src/logger/FileLogger.hpp | 1 + src/logger/NetLogger.cpp | 4 ++++ 11 files changed, 123 insertions(+), 32 deletions(-) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index ec9f711b..a25318db 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -27,7 +27,8 @@ Actor::Actor(ACTOR_TYPE eActorType, ev_tstamp dTimeout) Actor::~Actor() { FREE(m_pTimerWatcher); - m_pLabor->GetActorBuilder()->Logger(m_strTraceId, Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "eActorType %d, seq %u", m_eActorType, m_uiSequence); + LOG4_TRACE("eActorType %d, seq %u, actor name \"%s\"", + m_eActorType, GetSequence(), m_strActorName.c_str()); } uint32 Actor::GetSequence() @@ -152,6 +153,11 @@ bool Actor::SendTo(const std::string& strHost, int iPort) return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, this)); } +bool Actor::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +{ + return(m_pLabor->GetDispatcher()->SendTo(iCmd, uiSeq, oMsgBody, this)); +} + bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, this)); diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 02732a77..5a8b176b 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -202,6 +202,15 @@ class Actor: public std::enable_shared_from_this */ bool SendTo(const std::string& strHost, int iPort); + /** + * @brief 从worker发送到loader或从loader发送到worker + * @param iCmd 发送的命令字 + * @param uiSeq 发送的数据包seq + * @param oMsgBody 数据包体 + * @return 是否发送成功 + */ + bool SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + /** * @brief 发送到下一个同一类型的节点 * @note 发送到下一个同一类型的节点,适用于对同一类型节点做轮询方式发送以达到简单的负载均衡。 diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 385e7eba..9fd91c69 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -10,6 +10,7 @@ #include #include "ActorBuilder.hpp" +#include "util/json/CJsonObject.hpp" #include "labor/NodeInfo.hpp" #include "Actor.hpp" #include "cmd/Cmd.hpp" @@ -170,7 +171,7 @@ bool ActorBuilder::OnChainTimeout(std::shared_ptr pChain) bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) { - LOG4_DEBUG("cmd %u, seq %lu", oMsgHead.cmd(), oMsgHead.seq()); + LOG4_DEBUG("cmd %u, seq %u", oMsgHead.cmd(), oMsgHead.seq()); if (gc_uiCmdReq & oMsgHead.cmd()) // 新请求 { MsgHead oOutMsgHead; @@ -643,7 +644,6 @@ void ActorBuilder::RemoveChain(uint32 uiChainId) void ActorBuilder::ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData) { - //TODO CMD_REQ_DISCONNECT 命令业务实现未定义 LOG4_TRACE(" "); auto cmd_iter = m_mapCmd.find(CMD_REQ_DISCONNECT); if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) @@ -957,12 +957,28 @@ bool ActorBuilder::TransformToSharedChain(Actor* pCreator, std::shared_ptrGetChainFlag()); if (chain_conf_iter == m_mapChainConf.end()) { - LOG4_ERROR("no chain block config for \"%s\"", pSharedChain->GetChainFlag().c_str()); - return(false); + neb::CJsonObject oChainConf; + if (oChainConf.Parse(pSharedChain->GetChainFlag())) + { + if (!pSharedChain->Init(oChainConf)) + { + LOG4_ERROR("chain \"%s\" init failed!", pSharedChain->GetActorName().c_str()); + return(false); + } + } + else + { + LOG4_ERROR("no chain block config for \"%s\"", pSharedChain->GetChainFlag().c_str()); + return(false); + } } else { - pSharedChain->Init(chain_conf_iter->second); + if (!pSharedChain->Init(chain_conf_iter->second)) + { + LOG4_ERROR("chain \"%s\" init failed!", pSharedChain->GetActorName().c_str()); + return(false); + } } auto ret = m_mapChain.insert(std::make_pair(pSharedChain->GetSequence(), pSharedChain)); if (ret.second) diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index dec5dbbd..4c1173c5 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -9,6 +9,7 @@ ******************************************************************************/ #include "Chain.hpp" +#include "util/json/CJsonObject.hpp" #include "actor/context/Context.hpp" #include "actor/step/Step.hpp" #include "actor/model/Model.hpp" @@ -26,9 +27,39 @@ Chain::~Chain() { } -void Chain::Init(const std::queue >& queChainBlock) +bool Chain::Init(const std::queue >& queChainBlock) { m_queChainBlock = queChainBlock; + return(true); +} + +bool Chain::Init(CJsonObject& oChainBlock) +{ + LOG4_TRACE("actor chain: %s", oChainBlock.ToString().c_str()); + if (oChainBlock.IsArray()) + { + for (int i = 0; i < oChainBlock.GetArraySize(); ++i) + { + std::vector vecBlock; + if (oChainBlock[i].IsArray()) + { + for (int j = 0; j < oChainBlock[i].GetArraySize(); ++j) + { + vecBlock.push_back(oChainBlock[i](j)); + } + } + else + { + vecBlock.push_back(oChainBlock(i)); + } + m_queChainBlock.push(std::move(vecBlock)); + } + return(true); + } + else + { + return(false); + } } E_CMD_STATUS Chain::Next() diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index f47d6f1b..bddb365f 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -19,6 +19,7 @@ namespace neb { class ActorBuilder; +class CJsonObject; class Chain final: public Actor, public neb::DynamicCreator @@ -29,11 +30,13 @@ class Chain final: public Actor, Chain& operator=(const Chain&) = delete; virtual ~Chain(); - void Init(const std::queue >& queChainBlock); + bool Init(const std::queue >& queChainBlock); + bool Init(CJsonObject& oChainBlock); E_CMD_STATUS Next(); virtual E_CMD_STATUS Timeout() { + LOG4_ERROR("chain_id %d timeout, chain flag \"%s\"", GetSequence(), m_strChainFlag.c_str()); return(CMD_STATUS_FAULT); } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 2763618d..4c84acd4 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -1202,22 +1202,25 @@ bool Dispatcher::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor } } E_CODEC_STATUS eStatus = m_iterLoaderAndWorkerChannel->second->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - m_iterLoaderAndWorkerChannel++; if (CODEC_STATUS_OK == eStatus) { RemoveIoWriteEvent(m_iterLoaderAndWorkerChannel->second); + m_iterLoaderAndWorkerChannel++; return(true); } else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) { AddIoWriteEvent(m_iterLoaderAndWorkerChannel->second); + m_iterLoaderAndWorkerChannel++; return(true); } else if (CODEC_STATUS_WANT_READ == eStatus) { RemoveIoWriteEvent(m_iterLoaderAndWorkerChannel->second); + m_iterLoaderAndWorkerChannel++; return(true); } + m_iterLoaderAndWorkerChannel++; return(false); } diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 5b7c7e16..82e01f0e 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -161,7 +161,7 @@ class Dispatcher return((time_t)ev_now(m_loop)); } std::shared_ptr CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient = false, bool bWithSsl = false); - bool DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice = false); + bool DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice = true); bool CreateListenFd(const std::string& strHost, int32 iPort, int& iFd, int& iFamily); std::shared_ptr GetChannel(int iFd); int SendFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index b64a6eb2..a3546f13 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -515,12 +515,24 @@ bool Manager::RestartWorker(int iDeathPid) close(iDataFds[0]); x_sock_set_block(iControlFds[1], 0); x_sock_set_block(iDataFds[1], 0); - Worker oWorker(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], iWorkerIndex); - if (!oWorker.Init(m_oCurrentConf)) + if (Labor::LABOR_LOADER == eLaborType) { - exit(-1); + Loader oLoader(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], 0); + if (!oLoader.Init(m_oCurrentConf)) + { + exit(-1); + } + oLoader.Run(); + } + else + { + Worker oWorker(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], iWorkerIndex); + if (!oWorker.Init(m_oCurrentConf)) + { + exit(-1); + } + oWorker.Run(); } - oWorker.Run(); exit(-2); // 子进程worker没有正常运行 } else if (iNewPid > 0) // 父进程 diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp index 9310c63e..df84af9e 100644 --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +28,7 @@ FileLogger* FileLogger::s_pInstance = nullptr; FileLogger::FileLogger(const std::string& strLogFile, int iLogLev, unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex) - : m_iLogLevel(iLogLev), m_uiMaxFileSize(uiMaxFileSize), + : m_iLogLevel(iLogLev), m_uiLogNum(0), m_uiMaxFileSize(uiMaxFileSize), m_uiMaxRollFileIndex(uiMaxRollFileIndex), m_strLogFileBase(strLogFile) { #if __GNUC__ < 5 @@ -70,6 +71,7 @@ int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLi fprintf(m_fp, "\n"); fflush(m_fp); + ++m_uiLogNum; return 0; } @@ -93,6 +95,7 @@ int FileLogger::WriteLog(const std::string& strTraceId, int iLev, const char* sz va_end(ap); fprintf(m_fp, "\n"); + ++m_uiLogNum; fflush(m_fp); @@ -188,23 +191,26 @@ int FileLogger::Vappend(int iLev, const char* szFileName, unsigned int uiFileLin int FileLogger::Vappend(const std::string& strTraceId, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, va_list ap) { - long file_size = -1; - if (NULL != m_fp) - { - file_size = ftell(m_fp); - } - // if (0 == fstat(m_fd, &sb)) - // { - // file_size = sb.st_size; - // } - if (file_size < 0) - { - ReOpen(); - } - else if (file_size >= m_uiMaxFileSize) - { - RollOver(); - ReOpen(); + if (m_uiLogNum % 10000 == 1) + { + long file_size = -1; + if (NULL != m_fp) + { + file_size = ftell(m_fp); + } + // if (0 == fstat(m_fd, &sb)) + // { + // file_size = sb.st_size; + // } + if (file_size < 0) + { + ReOpen(); + } + else if (file_size >= m_uiMaxFileSize) + { + RollOver(); + ReOpen(); + } } if (NULL == m_fp) { diff --git a/src/logger/FileLogger.hpp b/src/logger/FileLogger.hpp index 60e6cf01..96da4cd4 100644 --- a/src/logger/FileLogger.hpp +++ b/src/logger/FileLogger.hpp @@ -67,6 +67,7 @@ class FileLogger: public Logger #endif FILE* m_fp; int m_iLogLevel; + unsigned int m_uiLogNum; unsigned int m_uiMaxFileSize; // 日志文件大小 unsigned int m_uiMaxRollFileIndex; // 滚动日志文件数量 std::string m_strLogFileBase; // 日志文件基本名(如 log/program_name.log) diff --git a/src/logger/NetLogger.cpp b/src/logger/NetLogger.cpp index 15a55aba..16660848 100644 --- a/src/logger/NetLogger.cpp +++ b/src/logger/NetLogger.cpp @@ -39,6 +39,10 @@ NetLogger::~NetLogger() int NetLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, ...) { + if (iLev > m_iLogLevel && iLev > m_iNetLogLevel) + { + return(0); + } va_list ap; va_start(ap, szLogStr); vsnprintf(m_pLogBuff, m_uiMaxLogLineLen, szLogStr, ap); From 6aea0f99a79ae43c546006d81701ea1b0cbb4d3f Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 23 Nov 2019 19:39:34 +0800 Subject: [PATCH 076/176] move Chain::Timeout() to cpp file. --- src/actor/chain/Chain.cpp | 6 ++++++ src/actor/chain/Chain.hpp | 6 +----- src/actor/cmd/sys_cmd/CmdBeat.hpp | 2 +- src/actor/cmd/sys_cmd/CmdNodeNotice.hpp | 2 +- src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp | 2 +- src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp | 2 +- src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp | 2 +- src/actor/cmd/sys_cmd/CmdToldWorker.hpp | 2 +- src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp | 2 +- src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp | 2 +- src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp | 2 +- src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp | 2 +- src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp | 2 +- src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp | 2 +- .../cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp | 2 +- src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp | 2 +- src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp | 2 +- src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp | 2 +- src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp | 2 +- src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp | 2 +- src/actor/session/sys_session/manager/SessionManager.hpp | 2 +- src/actor/step/sys_step/StepIoTimeout.hpp | 2 +- src/actor/step/sys_step/StepTellWorker.hpp | 2 +- src/actor/step/sys_step/manager/StepReportToBeacon.hpp | 2 +- 24 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 4c1173c5..f8983a7a 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -148,4 +148,10 @@ E_CMD_STATUS Chain::Next() } } +E_CMD_STATUS Chain::Timeout() +{ + LOG4_ERROR("chain_id %d timeout, chain flag \"%s\"", GetSequence(), m_strChainFlag.c_str()); + return(CMD_STATUS_FAULT); +} + } /* namespace neb */ diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index bddb365f..0b5e28a9 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -34,11 +34,7 @@ class Chain final: public Actor, bool Init(CJsonObject& oChainBlock); E_CMD_STATUS Next(); - virtual E_CMD_STATUS Timeout() - { - LOG4_ERROR("chain_id %d timeout, chain flag \"%s\"", GetSequence(), m_strChainFlag.c_str()); - return(CMD_STATUS_FAULT); - } + virtual E_CMD_STATUS Timeout(); const std::string& GetChainFlag() const { diff --git a/src/actor/cmd/sys_cmd/CmdBeat.hpp b/src/actor/cmd/sys_cmd/CmdBeat.hpp index 5cc4a70f..f4c760f2 100644 --- a/src/actor/cmd/sys_cmd/CmdBeat.hpp +++ b/src/actor/cmd/sys_cmd/CmdBeat.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDBEAT_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDBEAT_HPP_ -#include "../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp b/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp index f3bc6dbf..0748de2f 100644 --- a/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp +++ b/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDNODENOTICE_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDNODENOTICE_HPP_ -#include "../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" #include "pb/neb_sys.pb.h" diff --git a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp index 2430debb..46eacee9 100644 --- a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDRELOADCUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDRELOADCUSTOMCONF_HPP_ -#include "../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp b/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp index 80da9535..c6cb1b4b 100644 --- a/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp +++ b/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECONF_HPP_ -#include "../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp index b2f8eeea..6b8b1908 100644 --- a/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDSETNODECUSTOMCONF_HPP_ -#include "../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/CmdToldWorker.hpp b/src/actor/cmd/sys_cmd/CmdToldWorker.hpp index a3b60e76..cd2153ee 100644 --- a/src/actor/cmd/sys_cmd/CmdToldWorker.hpp +++ b/src/actor/cmd/sys_cmd/CmdToldWorker.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDTOLDWORKER_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDTOLDWORKER_HPP_ -#include "../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp b/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp index d49a86d8..7ea9e3f8 100644 --- a/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp +++ b/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_CMDUPDATENODEID_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_CMDUPDATENODEID_HPP_ -#include "../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp index 97005230..36db39db 100644 --- a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp +++ b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MODULEHTTPUPGRADE_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MODULEHTTPUPGRADE_HPP_ -#include "../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "codec/Codec.hpp" #include "actor/cmd/Module.hpp" diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp index 4c385bda..3a436125 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETCUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETCUSTOMCONF_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp index b0b97c91..9c6d3f05 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECONF_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp index f2baae79..9d137f87 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONGETNODECUSTOMCONF_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp index 0c68eecb..fbea61e2 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONNODENOTICE_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONNODENOTICE_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp index 320e4599..ee90c8ef 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONORIENTATIONFDTRANSFER_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONORIENTATIONFDTRANSFER_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp index 1de00919..2eb396d6 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETCUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETCUSTOMCONF_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp index 24d602cf..ab022172 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECONF_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp index b1cd5a5f..01dfc62e 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECUSTOMCONF_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSETNODECUSTOMCONF_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp index a3d49748..457b02d5 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONTELLWORKER_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONTELLWORKER_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp index 7b0f53f3..d05ef947 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONWORKERLOAD_HPP_ #define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONWORKERLOAD_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/cmd/Cmd.hpp" namespace neb diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index a0d2b97f..2d835750 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_SESSION_SYS_SESSION_MANAGER_SESSIONMANAGER_HPP_ #define SRC_ACTOR_SESSION_SYS_SESSION_MANAGER_SESSIONMANAGER_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "labor/NodeInfo.hpp" #include "actor/session/Session.hpp" diff --git a/src/actor/step/sys_step/StepIoTimeout.hpp b/src/actor/step/sys_step/StepIoTimeout.hpp index 7d34ecbb..5fd4d607 100644 --- a/src/actor/step/sys_step/StepIoTimeout.hpp +++ b/src/actor/step/sys_step/StepIoTimeout.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_STEP_SYS_STEP_STEPIOTIMEOUT_HPP_ #define SRC_ACTOR_STEP_SYS_STEP_STEPIOTIMEOUT_HPP_ -#include "../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/step/PbStep.hpp" #include "Definition.hpp" diff --git a/src/actor/step/sys_step/StepTellWorker.hpp b/src/actor/step/sys_step/StepTellWorker.hpp index 4a954d14..d576f637 100644 --- a/src/actor/step/sys_step/StepTellWorker.hpp +++ b/src/actor/step/sys_step/StepTellWorker.hpp @@ -10,7 +10,7 @@ #ifndef SRC_ACTOR_STEP_SYS_STEP_STEPTELLWORKER_HPP_ #define SRC_ACTOR_STEP_SYS_STEP_STEPTELLWORKER_HPP_ -#include "../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/step/PbStep.hpp" #include "pb/neb_sys.pb.h" diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.hpp b/src/actor/step/sys_step/manager/StepReportToBeacon.hpp index ec1c547d..4443519c 100644 --- a/src/actor/step/sys_step/manager/StepReportToBeacon.hpp +++ b/src/actor/step/sys_step/manager/StepReportToBeacon.hpp @@ -11,7 +11,7 @@ #ifndef SRC_ACTOR_STEP_SYS_STEP_MANAGER_STEPREPORTTOBEACON_HPP_ #define SRC_ACTOR_STEP_SYS_STEP_MANAGER_STEPREPORTTOBEACON_HPP_ -#include "../../../ActorSys.hpp" +#include "actor/ActorSys.hpp" #include "actor/step/PbStep.hpp" namespace neb From 0f282fedf857f70322049797d6b50cee07fcdb50 Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 29 Nov 2019 23:12:33 +0800 Subject: [PATCH 077/176] add chain block check and worker beat start. --- src/actor/chain/Chain.cpp | 6 ++++++ .../sys_session/manager/SessionManager.cpp | 16 ++++++++++------ src/ios/Dispatcher.cpp | 2 +- src/labor/Manager.cpp | 1 + src/labor/NodeInfo.hpp | 1 + 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index f8983a7a..9e83a581 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -87,6 +87,12 @@ E_CMD_STATUS Chain::Next() if (pSharedModel == nullptr) { std::shared_ptr pSharedActor = MakeSharedActor(*iter); + if (pSharedActor == nullptr) + { + LOG4_ERROR("failed to new \"%s\", the chain \"%s\" terminated!", + iter->c_str(), m_strChainFlag.c_str()); + break; + } // pSharedModel->SetContext(GetContext()); it had been set in MakeSharedActor(). if (Actor::ACT_MODEL == pSharedActor->GetActorType()) { diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 4061e698..cebb1f8a 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -170,6 +170,7 @@ bool SessionManager::SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad) oJsonLoad.Get("send_byte", it->second->iSendByte); oJsonLoad.Get("client", it->second->iClientNum); it->second->dBeatTime = GetNowTime(); + it->second->bStartBeatCheck = true; return(true); } else @@ -230,13 +231,16 @@ bool SessionManager::CheckWorker() for (auto worker_iter = m_mapWorker.begin(); worker_iter != m_mapWorker.end(); ++worker_iter) { - LOG4_TRACE("now %lf, worker_beat_time %lf, worker_beat %d", - GetNowTime(), worker_iter->second->dBeatTime, ((Manager*)GetLabor(this))->GetManagerInfo().iWorkerBeat); - if (GetNowTime() - worker_iter->second->dBeatTime > ((Manager*)GetLabor(this))->GetManagerInfo().iWorkerBeat) + if (worker_iter->second->bStartBeatCheck) { - LOG4_INFO("worker_%d pid %d is unresponsive, " + LOG4_TRACE("now %lf, worker_beat_time %lf, worker_beat %d", + GetNowTime(), worker_iter->second->dBeatTime, ((Manager*)GetLabor(this))->GetManagerInfo().iWorkerBeat); + if (GetNowTime() - worker_iter->second->dBeatTime > ((Manager*)GetLabor(this))->GetManagerInfo().iWorkerBeat) + { + LOG4_ERROR("worker_%d pid %d is unresponsive, " "terminate it.", worker_iter->second->iWorkerIndex, worker_iter->first); - kill(worker_iter->first, SIGKILL); //SIGINT); + kill(worker_iter->first, SIGKILL); //SIGINT); + } } } return(true); @@ -269,7 +273,7 @@ bool SessionManager::WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& m_mapWorkerFdPid.erase(fd_iter); } auto pControlChannel = GetLabor(this)->GetDispatcher()->GetChannel(worker_iter->second->iControlFd); - GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pControlChannel); + GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pControlChannel); // 在io事件中已关闭连接,这里可以不需要 auto pDataChannel = GetLabor(this)->GetDispatcher()->GetChannel(worker_iter->second->iDataFd); GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pDataChannel); delete worker_iter->second; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 4c84acd4..fbf3da3c 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -1618,7 +1618,7 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b { if (pChannel == nullptr) { - LOG4_ERROR("pChannel not exist!"); + LOG4_DEBUG("pChannel not exist!"); return(false); } LOG4_DEBUG("%s disconnect, fd %d, identify %s", pChannel->m_pImpl->GetRemoteAddr().c_str(), pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetIdentify().c_str()); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index a3546f13..a7739f16 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -623,6 +623,7 @@ bool Manager::AddPeriodicTaskEvent() m_pDispatcher->AddEvent(m_pPeriodicTaskWatcher, Dispatcher::PeriodicTaskCallback, NODE_BEAT); std::shared_ptr pReportToBeacon = m_pActorBuilder->MakeSharedStep( nullptr, "neb::StepReportToBeacon", NODE_BEAT); + //pReportToBeacon->Emit(); return(true); } diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index 2928d1db..f0e41018 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -61,6 +61,7 @@ struct WorkerInfo int32 iSendByte = 0; ///< 发送字节数 int32 iClientNum = 0; ///< 客户端数量 ev_tstamp dBeatTime = 0.0; ///< 心跳时间 + bool bStartBeatCheck = 0.0; ///< 是否需要心跳检查,worker或loader进程启动时可能需要加载数据而处于繁忙状态无法响应Manager的心跳,需等待其就绪之后才开始心跳检查。 WorkerInfo(){} WorkerInfo(const WorkerInfo& stAttr) = delete; From c2d2a2ce1cbe34558ffde32b15a1bf84c8759ecf Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 4 Dec 2019 23:22:15 +0800 Subject: [PATCH 078/176] add GetStepNum() to Actor and Channel close bug fixed. --- src/actor/Actor.cpp | 5 +++++ src/actor/Actor.hpp | 2 ++ src/channel/SocketChannelImpl.cpp | 6 +++--- src/ios/Dispatcher.cpp | 10 +++++++--- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index a25318db..ef18a91b 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -173,6 +173,11 @@ bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSe return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, iCmd, uiSeq, oMsgBody, this)); } +int32 Actor::GetStepNum() const +{ + return(m_pLabor->GetActorBuilder()->GetStepNum()); +} + void Actor::SetLabor(Labor* pLabor) { m_pLabor = pLabor; diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 5a8b176b..af76d4b7 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -236,6 +236,8 @@ class Actor: public std::enable_shared_from_this bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + int32 GetStepNum() const; + protected: virtual void SetActiveTime(ev_tstamp dActiveTime) { diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 3980f186..67a55c1c 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -724,7 +724,7 @@ bool SocketChannelImpl::Close() if (0 == close(m_iFd)) { m_ucChannelStatus = CHANNEL_STATUS_CLOSED; - LOG4_DEBUG("channel[%d] close successfully.", m_iFd); + LOG4_DEBUG("channel[%d], channel_seq[%u] close successfully.", m_iFd, GetSequence()); return(true); } else @@ -735,8 +735,8 @@ bool SocketChannelImpl::Close() } else { - LOG4_WARNING("channel(fd %d) had been closed before.", m_iFd); - return(true); + LOG4_WARNING("channel(fd %d, seq %u) had been closed before.", m_iFd, GetSequence()); + return(false); } } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index fbf3da3c..b3ba28ec 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -832,9 +832,9 @@ bool Dispatcher::SendTo(const std::string& strHost, int iPort, const std::string { auto channel_iter = named_iter->second.begin(); E_CODEC_STATUS eStatus = (*channel_iter)->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); + named_iter->second.erase(channel_iter); // erase from named channel pool, the channel remain in m_mapSocketChannel. if (CODEC_STATUS_OK == eStatus) { - named_iter->second.erase(channel_iter); // erase from named channel pool, the channel remain in m_mapSocketChannel. return(true); } else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) @@ -1621,7 +1621,10 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b LOG4_DEBUG("pChannel not exist!"); return(false); } - LOG4_DEBUG("%s disconnect, fd %d, identify %s", pChannel->m_pImpl->GetRemoteAddr().c_str(), pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetIdentify().c_str()); + LOG4_DEBUG("%s disconnect, fd %d, channel_seq %u, identify %s", + pChannel->m_pImpl->GetRemoteAddr().c_str(), + pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence(), + pChannel->m_pImpl->GetIdentify().c_str()); if (bChannelNotice) { m_pLabor->GetActorBuilder()->ChannelNotice(pChannel, pChannel->m_pImpl->GetIdentify(), pChannel->m_pImpl->GetClientData()); @@ -1669,7 +1672,8 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b { --m_iClientNum; } - LOG4_TRACE("erase channel %d from m_mapSocketChannel.", pChannel->m_pImpl->GetFd()); + LOG4_TRACE("erase channel %d channel_seq %u from m_mapSocketChannel.", + pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); } return(true); } From 8ee9a2f832a55032c4cdfc2cbaee19bd18cbed04 Mon Sep 17 00:00:00 2001 From: wincsb Date: Tue, 10 Dec 2019 11:53:30 +0800 Subject: [PATCH 079/176] =?UTF-8?q?=E8=A7=A3=E5=86=B3contex=E5=86=85?= =?UTF-8?q?=E5=AD=98=E6=B3=84=E9=9C=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actor/ActorBuilder.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 9fd91c69..828eab45 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -741,7 +741,8 @@ std::shared_ptr ActorBuilder::InitializeSharedActor(Actor* pCreator, std: pSharedActor->SetLabor(m_pLabor); pSharedActor->SetActiveTime(m_pLabor->GetNowTime()); pSharedActor->SetActorName(strActorName); - if (nullptr != pCreator) + + if (nullptr != pCreator && pSharedActor->GetActorType() != Actor::ACT_CONTEXT) { pSharedActor->SetContext(pCreator->GetContext()); } From 9597c54b73a4c50096226522ca92f1e95ab30f01 Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 11 Dec 2019 08:48:33 +0800 Subject: [PATCH 080/176] redis channel and NextStep bug fixed, and redis data migration test passing, it's an important testing for mydis. --- src/actor/ActorBuilder.cpp | 4 ++-- src/actor/step/Step.cpp | 2 +- src/channel/RedisChannel.cpp | 9 +++++---- src/codec/CodecHttp.cpp | 6 ++++++ src/ios/Dispatcher.cpp | 8 ++++++-- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 828eab45..51136173 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -434,6 +434,7 @@ bool ActorBuilder::OnRedisConnected(std::shared_ptr pChannel, cons if (iCmdStatus == REDIS_OK) { LOG4_TRACE("succeed in sending redis cmd: %s", (*step_iter)->CmdToString().c_str()); + ++step_iter; } else { @@ -741,8 +742,7 @@ std::shared_ptr ActorBuilder::InitializeSharedActor(Actor* pCreator, std: pSharedActor->SetLabor(m_pLabor); pSharedActor->SetActiveTime(m_pLabor->GetNowTime()); pSharedActor->SetActorName(strActorName); - - if (nullptr != pCreator && pSharedActor->GetActorType() != Actor::ACT_CONTEXT) + if (nullptr != pCreator && pSharedActor->GetActorType() != Actor::ACT_CONTEXT) { pSharedActor->SetContext(pCreator->GetContext()); } diff --git a/src/actor/step/Step.cpp b/src/actor/step/Step.cpp index e2daa165..347d1978 100644 --- a/src/actor/step/Step.cpp +++ b/src/actor/step/Step.cpp @@ -37,7 +37,7 @@ void Step::NextStep(int iErrno, const std::string& strErrMsg, void* data) for (auto it = m_setNextStepSeq.begin(); it != m_setNextStepSeq.end(); ++it) { - ExecStep(*it); + ExecStep(*it, iErrno, strErrMsg, data); } } diff --git a/src/channel/RedisChannel.cpp b/src/channel/RedisChannel.cpp index b4fb156f..90d6ea32 100644 --- a/src/channel/RedisChannel.cpp +++ b/src/channel/RedisChannel.cpp @@ -19,10 +19,11 @@ RedisChannel::RedisChannel(redisAsyncContext *c) RedisChannel::~RedisChannel() { - if (NULL != m_pRedisCtx) - { - redisAsyncFree(m_pRedisCtx); - } + // RedisChannel should not be active closed. + //if (NULL != m_pRedisCtx) + //{ + // redisAsyncFree(m_pRedisCtx); + //} } } /* namespace neb */ diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 47590188..7be50f9a 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -200,6 +200,12 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } + if (stUrl.field_data[UF_PATH].off >= oHttpMsg.url().size()) + { + LOG4_ERROR("invalid url \"%s\"!", oHttpMsg.url().c_str()); + m_mapAddingHttpHeader.clear(); + return(CODEC_STATUS_ERR); + } iWriteSize = pBuff->Printf("%s %s HTTP/%u.%u\r\n", http_method_str((http_method)oHttpMsg.method()), oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off, std::string::npos).c_str(), oHttpMsg.http_major(), oHttpMsg.http_minor()); diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index b3ba28ec..5b07ee85 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -522,7 +522,7 @@ bool Dispatcher::OnRedisDisconnected(const redisAsyncContext *c, int status) DelNamedRedisChannel(channel_iter->second->GetIdentify()); m_mapRedisChannel.erase(channel_iter); } - redisAsyncFree(const_cast(c)); + //redisAsyncDisconnect(const_cast(c)); //被动断开连接不需要 return(true); } @@ -1153,7 +1153,10 @@ bool Dispatcher::AutoRedisCmd(const std::string& strHost, int iPort, std::shared if (c->err) { LOG4_ERROR("error: %s", c->errstr); - redisAsyncFree(c); + // If the onConnect callback is called with REDIS_ERROR the context will + // be disconnected (and the inner context freed) after that callback anyway. + // No need to call it yourself + // redisAsyncFree(c); return(false); } c->data = m_pLabor; @@ -1177,6 +1180,7 @@ bool Dispatcher::AutoRedisCmd(const std::string& strHost, int iPort, std::shared std::ostringstream oss; oss << strHost << ":" << iPort; std::string strIdentify = std::move(oss.str()); + pRedisChannel->SetIdentify(strIdentify); AddNamedRedisChannel(strIdentify, pRedisChannel); return(true); } From bd2b6ff4ded1be398481b6813411784e9b872f7a Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 22 Dec 2019 22:32:24 +0800 Subject: [PATCH 081/176] add data report. http1.0 bug fixed and log level change bug fixed. --- proto/report.proto | 13 + src/Definition.hpp | 2 +- src/actor/Actor.cpp | 10 + src/actor/Actor.hpp | 3 + src/actor/ActorBuilder.cpp | 38 +- src/actor/chain/Chain.hpp | 2 +- src/actor/cmd/CW.hpp | 4 +- src/actor/cmd/sys_cmd/CmdDataReport.cpp | 69 ++ src/actor/cmd/sys_cmd/CmdDataReport.hpp | 37 + .../session/sys_session/SessionDataReport.cpp | 109 +++ .../session/sys_session/SessionDataReport.hpp | 52 ++ .../sys_session/manager/SessionManager.cpp | 25 + .../sys_session/manager/SessionManager.hpp | 2 + src/actor/step/HttpStep.cpp | 20 +- src/actor/step/HttpStep.hpp | 8 +- src/channel/SocketChannel.hpp | 2 +- src/channel/SocketChannelImpl.cpp | 35 +- src/codec/CodecHttp.cpp | 222 +++--- src/codec/CodecHttp.hpp | 7 +- src/codec/CodecProto.cpp | 2 +- src/ios/Dispatcher.cpp | 59 +- src/ios/Dispatcher.hpp | 1 + src/labor/Manager.cpp | 3 + src/labor/NodeInfo.hpp | 3 +- src/labor/Worker.cpp | 6 + src/labor/Worker.hpp | 1 + src/pb/report.pb.cc | 753 ++++++++++++++++++ src/pb/report.pb.h | 357 +++++++++ 28 files changed, 1698 insertions(+), 147 deletions(-) create mode 100644 proto/report.proto create mode 100644 src/actor/cmd/sys_cmd/CmdDataReport.cpp create mode 100644 src/actor/cmd/sys_cmd/CmdDataReport.hpp create mode 100644 src/actor/session/sys_session/SessionDataReport.cpp create mode 100644 src/actor/session/sys_session/SessionDataReport.hpp create mode 100644 src/pb/report.pb.cc create mode 100644 src/pb/report.pb.h diff --git a/proto/report.proto b/proto/report.proto new file mode 100644 index 00000000..6423ebcf --- /dev/null +++ b/proto/report.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; +package neb; + +message ReportRecord +{ + bytes key = 1; + repeated uint64 value = 2; +} + +message Report +{ + repeated ReportRecord records = 1; +} diff --git a/src/Definition.hpp b/src/Definition.hpp index 883317cb..b2340178 100644 --- a/src/Definition.hpp +++ b/src/Definition.hpp @@ -13,7 +13,7 @@ #include #ifndef NODE_BEAT -#define NODE_BEAT 1.0 +#define NODE_BEAT 7.0 #endif /* diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index ef18a91b..e46c5d81 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -69,6 +69,11 @@ const std::string& Actor::GetNodeIdentify() const return(m_pLabor->GetNodeInfo().strNodeIdentify); } +ev_tstamp Actor::GetDataReportInterval() const +{ + return(m_pLabor->GetNodeInfo().dDataReportInterval); +} + time_t Actor::GetNowTime() const { return(m_pLabor->GetNowTime()); @@ -173,6 +178,11 @@ bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSe return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, iCmd, uiSeq, oMsgBody, this)); } +bool Actor::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +{ + return(m_pLabor->GetDispatcher()->SendDataReport(iCmd, uiSeq, oMsgBody, this)); +} + int32 Actor::GetStepNum() const { return(m_pLabor->GetActorBuilder()->GetStepNum()); diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index af76d4b7..bc188459 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -106,6 +106,7 @@ class Actor: public std::enable_shared_from_this const std::string& GetWorkPath() const; const std::string& GetNodeIdentify() const; time_t GetNowTime() const; + ev_tstamp GetDataReportInterval() const; /** * @brief 获取Server自定义配置 @@ -236,6 +237,8 @@ class Actor: public std::enable_shared_from_this bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + int32 GetStepNum() const; protected: diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 51136173..7bf11efa 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -197,8 +197,10 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH { LogLevel oLogLevel; oLogLevel.ParseFromString(oMsgBody.data()); - LOG4_INFO("log level set to %d", oLogLevel.log_level()); + LOG4_INFO("log level set to %d, net_log_level set to %d", + oLogLevel.log_level(), oLogLevel.net_log_level()); m_pLogger->SetLogLevel(oLogLevel.log_level()); + m_pLogger->SetNetLogLevel(oLogLevel.net_log_level()); } else if (CMD_REQ_RELOAD_SO == oMsgHead.cmd()) { @@ -346,14 +348,25 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http module_iter = m_mapModule.find("/switch"); if (module_iter == m_mapModule.end()) { - HttpMsg oOutHttpMsg; - snprintf(m_pErrBuff, gc_iErrBuffLen, "no module to dispose %s!", oHttpMsg.path().c_str()); - LOG4_ERROR(m_pErrBuff); - oOutHttpMsg.set_type(HTTP_RESPONSE); - oOutHttpMsg.set_status_code(404); - oOutHttpMsg.set_http_major(oHttpMsg.http_major()); - oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); - m_pLabor->GetDispatcher()->SendTo(pChannel, oOutHttpMsg, 0); + module_iter = m_mapModule.find("/route"); + if (module_iter == m_mapModule.end()) + { + HttpMsg oOutHttpMsg; + snprintf(m_pErrBuff, gc_iErrBuffLen, "no module to dispose %s!", oHttpMsg.path().c_str()); + LOG4_ERROR(m_pErrBuff); + oOutHttpMsg.set_type(HTTP_RESPONSE); + oOutHttpMsg.set_status_code(404); + oOutHttpMsg.set_http_major(oHttpMsg.http_major()); + oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); + m_pLabor->GetDispatcher()->SendTo(pChannel, oOutHttpMsg, 0); + } + else + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + module_iter->second->SetTraceId(oss.str()); + module_iter->second->AnyMessage(pChannel, oHttpMsg); + } } else { @@ -376,7 +389,7 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http auto http_step_iter = m_mapCallbackStep.find(pChannel->GetStepSeq()); if (http_step_iter == m_mapCallbackStep.end()) { - LOG4_ERROR("no callback for http response from %s!", oHttpMsg.url().c_str()); + LOG4_TRACE("no callback for http response from %s!", oHttpMsg.url().c_str()); m_pLabor->GetDispatcher()->SetChannelStatus(pChannel, CHANNEL_STATUS_ESTABLISHED, 0); m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. } @@ -724,6 +737,7 @@ void ActorBuilder::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdOnGetNodeCustomConf", (int)CMD_REQ_GET_NODE_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdOnSetCustomConf", (int)CMD_REQ_SET_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdOnGetCustomConf", (int)CMD_REQ_GET_CUSTOM_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdDataReport", (int)CMD_REQ_DATA_REPORT); } else { @@ -800,7 +814,7 @@ std::shared_ptr ActorBuilder::InitializeSharedActor(Actor* pCreator, std: bool ActorBuilder::TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor) { - pSharedActor->m_dTimeout = (0 == pSharedActor->m_dTimeout) + pSharedActor->m_dTimeout = (gc_dDefaultTimeout == pSharedActor->m_dTimeout) ? m_pLabor->GetNodeInfo().dStepTimeout : pSharedActor->m_dTimeout; ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); if (NULL == timer_watcher) @@ -863,7 +877,7 @@ bool ActorBuilder::TransformToSharedSession(Actor* pCreator, std::shared_ptrGetSessionId(), pSharedSession)); if (ret.second) { - if (gc_dNoTimeout != pSharedSession->m_dTimeout) + if (pSharedSession->m_dTimeout > 0) { m_pLabor->GetDispatcher()->AddEvent(timer_watcher, SessionTimeoutCallback, pSharedSession->m_dTimeout); } diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index 0b5e28a9..57a01c9e 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -22,7 +22,7 @@ class ActorBuilder; class CJsonObject; class Chain final: public Actor, - public neb::DynamicCreator + public neb::DynamicCreator { public: Chain(const std::string& strChainFlag, ev_tstamp dChainTimeout = 60.0); diff --git a/src/actor/cmd/CW.hpp b/src/actor/cmd/CW.hpp index 1063898e..35683229 100644 --- a/src/actor/cmd/CW.hpp +++ b/src/actor/cmd/CW.hpp @@ -47,8 +47,8 @@ enum E_CMD CMD_RSP_NODE_NOTICE = 106, ///< 节点通知应答通知(Manager应答,Worker无须应答) CMD_REQ_REFRESH_NODE_ID = 107, ///< 更新节点ID请求(Manager发往Worker) CMD_RSP_REFRESH_NODE_ID = 108, ///< 更新节点ID应答(一般无须应答) - CMD_REQ_SERVER_DATA_STATUS_REPORT = 109, ///< 服务器数据状态上报请求 - CMD_RSP_SERVER_DATA_STATUS_REPORT = 110, ///< 服务器数据状态上报应答 + CMD_REQ_DATA_REPORT = 109, ///< 服务器数据状态上报请求 + CMD_RSP_DATA_REPORT = 110, ///< 服务器数据状态上报应答 CMD_REQ_GET_LOAD_MIN_SERVER = 111, ///< 获取低负载服务器请求 CMD_RSP_GET_LOAD_MIN_SERVER = 112, ///< 获取低负载服务器应答 CMD_REQ_LEADER_ELECTION = 113, ///< 分布式leader选举请求 diff --git a/src/actor/cmd/sys_cmd/CmdDataReport.cpp b/src/actor/cmd/sys_cmd/CmdDataReport.cpp new file mode 100644 index 00000000..beda5715 --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdDataReport.cpp @@ -0,0 +1,69 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdDataReport.cpp + * @brief + * @author Bwar + * @date: 2016年8月16日 + * @note + * Modify history: + ******************************************************************************/ +#include "CmdDataReport.hpp" +#include "pb/report.pb.h" +#include "actor/session/sys_session/SessionDataReport.hpp" + +namespace neb +{ + +CmdDataReport::CmdDataReport(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdDataReport::~CmdDataReport() +{ +} + +bool CmdDataReport::Init() +{ + if (pSessionDataReport == nullptr) + { + std::string strReportSessionId = "neb::SessionDataReport"; + auto pSharedSession = MakeSharedSession("neb::SessionReport", + strReportSessionId, GetDataReportInterval()); + if (pSharedSession == nullptr) + { + return(false); + } + pSessionDataReport = std::dynamic_pointer_cast(pSharedSession); + } + return(true); +} + +bool CmdDataReport::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + LOG4_TRACE(""); + Report oReport; + if (!oReport.ParseFromString(oInMsgBody.data())) + { + LOG4_ERROR("Report.ParseFromString() failed!"); + return(false); + } + if (pSessionDataReport == nullptr) + { + std::string strReportSessionId = "neb::SessionDataReport"; + auto pSharedSession = MakeSharedSession("neb::SessionDataReport", + strReportSessionId, GetDataReportInterval()); + if (pSharedSession == nullptr) + { + return(false); + } + pSessionDataReport = std::dynamic_pointer_cast(pSharedSession); + } + pSessionDataReport->AddReport(oReport); + return(true); +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/CmdDataReport.hpp b/src/actor/cmd/sys_cmd/CmdDataReport.hpp new file mode 100644 index 00000000..041bbd63 --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdDataReport.hpp @@ -0,0 +1,37 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdDataReport.hpp + * @brief 心跳包响应 + * @author Bwar + * @date: 2016年8月16日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef ACTOR_CMD_SYS_CMD_CMDDATAREPORT_HPP_ +#define ACTOR_CMD_SYS_CMD_CMDDATAREPORT_HPP_ + +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class SessionDataReport; + +class CmdDataReport: public Cmd, + public DynamicCreator +{ +public: + CmdDataReport(int32 iCmd); + virtual ~CmdDataReport(); + virtual bool Init(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody); +private: + std::shared_ptr pSessionDataReport = nullptr; +}; + +} /* namespace neb */ + +#endif /* ACTOR_CMD_SYS_CMD_CMDDATAREPORT_HPP_ */ diff --git a/src/actor/session/sys_session/SessionDataReport.cpp b/src/actor/session/sys_session/SessionDataReport.cpp new file mode 100644 index 00000000..c9d78c42 --- /dev/null +++ b/src/actor/session/sys_session/SessionDataReport.cpp @@ -0,0 +1,109 @@ +/******************************************************************************* + * Project: Nebula + * @file SessionDataReport.cpp + * @brief 数据上报 + * @author Bwar + * @date: 2019-12-22 + * @note + * Modify history: + ******************************************************************************/ + +#include "SessionDataReport.hpp" +#include "ios/Dispatcher.hpp" + +namespace neb +{ + +SessionDataReport::SessionDataReport(const std::string& strSessionId, ev_tstamp dStatInterval) + : Timer(strSessionId, dStatInterval), m_pReport(nullptr) +{ +} + +SessionDataReport::~SessionDataReport() +{ + if (m_pReport != nullptr) + { + delete m_pReport; + m_pReport = nullptr; + } +} + +E_CMD_STATUS SessionDataReport::Timeout() +{ + if (m_mapData.empty()) + { + return(CMD_STATUS_RUNNING); + } + if (m_pReport != nullptr) + { + delete m_pReport; + m_pReport = nullptr; + } + try + { + m_pReport = new Report(); + } + catch (std::bad_alloc& e) + { + LOG4_ERROR("failed to new Report!"); + return(CMD_STATUS_FAULT); + } + for (auto iter = m_mapData.begin(); iter != m_mapData.end(); ++iter) + { + m_pReport->mutable_records()->AddAllocated(iter->second); + iter->second = nullptr; + } + if (Labor::LABOR_MANAGER == GetLabor(this)->GetLaborType()) + { + MsgBody oMsgBody; + m_pReport->SerializeToString(&m_strReport); + oMsgBody.set_data(m_strReport); + SendDataReport(CMD_REQ_DATA_REPORT, GetSequence(), oMsgBody); + } + m_mapData.clear(); + return(CMD_STATUS_RUNNING); +} + +void SessionDataReport::AddReport(const Report& oReport) +{ + std::unordered_map::iterator iter; + for (int i = 0; i < oReport.records_size(); ++i) + { + iter = m_mapData.find(oReport.records(i).key()); + if (iter == m_mapData.end()) + { + ReportRecord* pRecord = nullptr; + try + { + pRecord = new ReportRecord(); + } + catch (std::bad_alloc& e) + { + return; + } + pRecord->set_key(oReport.records(i).key()); + for (int j = 0; j < oReport.records(i).value_size(); ++j) + { + pRecord->add_value(oReport.records(i).value(j)); + } + m_mapData.insert(std::make_pair(oReport.records(i).key(), pRecord)); + } + else + { + for (int j = 0; j < oReport.records(i).value_size(); ++j) + { + if (j < iter->second->value_size()) + { + iter->second->set_value(j, iter->second->value(j) + oReport.records(i).value(j)); + } + else + { + iter->second->add_value(oReport.records(i).value(j)); + } + } + } + } +} + +} + diff --git a/src/actor/session/sys_session/SessionDataReport.hpp b/src/actor/session/sys_session/SessionDataReport.hpp new file mode 100644 index 00000000..f7426d3b --- /dev/null +++ b/src/actor/session/sys_session/SessionDataReport.hpp @@ -0,0 +1,52 @@ +/******************************************************************************* + * Project: Nebula + * @file SessionDataReport.hpp + * @brief 数据上报 + * @author Bwar + * @date: 2019-12-22 + * @note + * Modify history: + ******************************************************************************/ +#ifndef ACTOR_SESSION_SYS_SESSION_SESSIONDATAREPORT_HPP_ +#define ACTOR_SESSION_SYS_SESSION_SESSIONDATAREPORT_HPP_ + +#include +#include +#include "actor/session/Timer.hpp" +#include "actor/ActorSys.hpp" +#include "pb/report.pb.h" + +namespace neb +{ + +class SessionDataReport: public Timer, + DynamicCreator, + public ActorSys +{ +public: + SessionDataReport(const std::string& strSessionId, ev_tstamp dStatInterval = 60.0); + virtual ~SessionDataReport(); + + virtual E_CMD_STATUS Timeout(); + + void AddReport(const Report& oReport); + + const Report* GetReport() + { + return(m_pReport); + } + + const std::string& GetReportString() + { + return(m_strReport); + } + +private: + Report* m_pReport; ///< final report + std::string m_strReport; ///< m_pReport SerializeToString + std::unordered_map m_mapData; ///< report collector +}; + +} + +#endif /* ACTOR_SESSION_SYS_SESSION_SESSIONDATAREPORT_HPP_ */ diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index cebb1f8a..2f14f618 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -24,6 +24,7 @@ namespace neb SessionManager::SessionManager(bool bDirectToLoader) : Session("neb::SessionManager", gc_dNoTimeout), m_bDirectToLoader(bDirectToLoader) { + m_iterWorker = m_mapWorker.begin(); } SessionManager::~SessionManager() @@ -34,6 +35,7 @@ SessionManager::~SessionManager() it->second = nullptr; } m_mapWorker.clear(); + m_iterWorker = m_mapWorker.begin(); m_mapWorkerStartNum.clear(); m_mapWorkerFdPid.clear(); m_mapOnlineNodes.clear(); @@ -121,6 +123,7 @@ void SessionManager::AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, i pWorkerAttr->iDataFd = iDataFd; pWorkerAttr->dBeatTime = GetNowTime(); m_mapWorker.insert(std::make_pair(iPid, pWorkerAttr)); + m_iterWorker = m_mapWorker.begin(); m_mapWorkerFdPid.insert(std::pair(iControlFd, iPid)); m_mapWorkerFdPid.insert(std::pair(iDataFd, iPid)); auto start_num_iter = m_mapWorkerStartNum.find(iWorkerIndex); @@ -186,6 +189,27 @@ bool SessionManager::SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad) } } +int SessionManager::GetNextWorkerDataFd() +{ + if (m_bDirectToLoader && m_iLoaderDataFd != -1) + { + return(m_iLoaderDataFd); + } + else + { + if (m_mapWorker.empty()) + { + return(-1); + } + ++m_iterWorker; + if (m_iterWorker == m_mapWorker.end()) + { + m_iterWorker = m_mapWorker.begin(); + } + return(m_iterWorker->second->iDataFd); + } +} + std::pair SessionManager::GetMinLoadWorkerDataFd() { LOG4_TRACE(" "); @@ -278,6 +302,7 @@ bool SessionManager::WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pDataChannel); delete worker_iter->second; m_mapWorker.erase(worker_iter); + m_iterWorker = m_mapWorker.begin(); auto restart_num_iter = m_mapWorkerStartNum.find(iWorkerIndex); if (restart_num_iter != m_mapWorkerStartNum.end()) diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index 2d835750..7d16d88f 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -36,6 +36,7 @@ class SessionManager : public Session, void AddLoaderInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); const WorkerInfo* GetWorkerInfo(int32 iWorkerIndex) const; bool SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad); + int GetNextWorkerDataFd(); std::pair GetMinLoadWorkerDataFd(); bool CheckWorker(); bool WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& eLaborType); @@ -49,6 +50,7 @@ class SessionManager : public Session, bool m_bDirectToLoader = false; int m_iLoaderDataFd = -1; std::unordered_map m_mapWorker; ///< 业务逻辑工作进程及进程属性,key为pid + std::unordered_map::iterator m_iterWorker; std::unordered_map m_mapWorkerStartNum; ///< 进程被启动次数,key为WorkerIdx std::unordered_map m_mapWorkerFdPid; ///< 工作进程通信FD对应的进程号 std::unordered_map m_mapOnlineNodes; ///< 订阅的节点在线信息 diff --git a/src/actor/step/HttpStep.cpp b/src/actor/step/HttpStep.cpp index a25bbff8..8e419ba3 100644 --- a/src/actor/step/HttpStep.cpp +++ b/src/actor/step/HttpStep.cpp @@ -22,6 +22,17 @@ HttpStep::~HttpStep() { } +bool HttpStep::HttpGet(const std::string& strUrl) +{ + HttpMsg oHttpMsg; + oHttpMsg.set_http_major(1); + oHttpMsg.set_http_minor(1); + oHttpMsg.set_type(HTTP_REQUEST); + oHttpMsg.set_method(HTTP_GET); + oHttpMsg.set_url(strUrl); + return(HttpRequest(oHttpMsg)); +} + bool HttpStep::HttpPost(const std::string& strUrl, const std::string& strBody, const std::unordered_map& mapHeaders) { HttpMsg oHttpMsg; @@ -38,14 +49,19 @@ bool HttpStep::HttpPost(const std::string& strUrl, const std::string& strBody, c return(HttpRequest(oHttpMsg)); } -bool HttpStep::HttpGet(const std::string& strUrl) +bool HttpStep::HttpPost(const std::string& strUrl, const std::string& strBody, const ::google::protobuf::Map& mapHeaders) { HttpMsg oHttpMsg; oHttpMsg.set_http_major(1); oHttpMsg.set_http_minor(1); oHttpMsg.set_type(HTTP_REQUEST); - oHttpMsg.set_method(HTTP_GET); + oHttpMsg.set_method(HTTP_POST); oHttpMsg.set_url(strUrl); + for (auto c_iter = mapHeaders.begin(); c_iter != mapHeaders.end(); ++c_iter) + { + oHttpMsg.mutable_headers()->insert(google::protobuf::MapPair(c_iter->first, c_iter->second)); + } + oHttpMsg.set_body(strBody); return(HttpRequest(oHttpMsg)); } diff --git a/src/actor/step/HttpStep.hpp b/src/actor/step/HttpStep.hpp index d368915e..db4d2ec6 100644 --- a/src/actor/step/HttpStep.hpp +++ b/src/actor/step/HttpStep.hpp @@ -10,6 +10,7 @@ #ifndef SRC_ACTOR_STEP_HTTPSTEP_HPP_ #define SRC_ACTOR_STEP_HTTPSTEP_HPP_ +#include #include #include "util/http/http_parser.h" @@ -29,10 +30,13 @@ class HttpStep: public Step const HttpMsg& oHttpMsg, void* data = NULL) = 0; - bool HttpPost(const std::string& strUrl, const std::string& strBody, const std::unordered_map& mapHeaders); bool HttpGet(const std::string& strUrl); + bool HttpPost(const std::string& strUrl, const std::string& strBody, + const std::unordered_map& mapHeaders); + bool HttpPost(const std::string& strUrl, const std::string& strBody, + const ::google::protobuf::Map& mapHeaders); -protected: +private: bool HttpRequest(const HttpMsg& oHttpMsg); }; diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index d7acf35b..37739e4e 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -30,7 +30,7 @@ class SocketChannel: public Channel, public std::enable_shared_from_this pLogger, int iFd, uint32 ulSeq, bool bWithSsl = false, ev_tstamp dKeepAlive = 0.0); + SocketChannel(std::shared_ptr pLogger, int iFd, uint32 ulSeq, bool bWithSsl = false, ev_tstamp dKeepAlive = 10.0); virtual ~SocketChannel(); static int SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, std::shared_ptr pLogger); diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 67a55c1c..6bfa0bb2 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -65,7 +65,7 @@ bool SocketChannelImpl::Init(E_CODEC_TYPE eCodecType, bool bIsClient) m_pCodec->SetKey(m_strKey); break; case CODEC_HTTP: - m_pCodec = new CodecHttp(m_pLogger, eCodecType); + m_pCodec = new CodecHttp(m_pLogger, eCodecType, m_dKeepAlive); m_pCodec->SetKey(m_strKey); break; default: @@ -174,7 +174,7 @@ E_CODEC_STATUS SocketChannelImpl::Send() m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } - LOG4_ERROR("send to fd %d error %d: %s", + LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); m_strErrMsg = m_szErrBuff; return(CODEC_STATUS_INT); @@ -237,7 +237,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& } break; default: - LOG4_ERROR("invalid connect status %d!", m_ucChannelStatus); + LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), m_ucChannelStatus); return(CODEC_STATUS_OK); } @@ -286,7 +286,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } - LOG4_ERROR("send to fd %d error %d: %s", + LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); m_strErrMsg = m_szErrBuff; return(CODEC_STATUS_INT); @@ -320,7 +320,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq } break; default: - LOG4_ERROR("invalid connect status %d!", m_ucChannelStatus); + LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), m_ucChannelStatus); return(CODEC_STATUS_OK); } @@ -336,7 +336,8 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq } int iWriteLen = Write(m_pSendBuff, m_iErrno); - LOG4_TRACE("iWriteLen = %d, m_iErrno = %d", iWriteLen, m_iErrno); + LOG4_TRACE("fd[%d], channel_seq[%u] iWriteLen = %d, m_iErrno = %d", + GetFd(), GetSequence(), iWriteLen, m_iErrno); if (iWriteLen >= 0) { if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ @@ -348,10 +349,6 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq m_dActiveTime = m_pLabor->GetNowTime(); if (iNeedWriteLen == iWriteLen) { - if (ulStepSeq == 0 && m_dKeepAlive == 0.0) - { - return(CODEC_STATUS_EOF); - } return(CODEC_STATUS_OK); } else @@ -366,7 +363,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } - LOG4_ERROR("send to fd %d error %d: %s", + LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); m_strErrMsg = m_szErrBuff; return(CODEC_STATUS_INT); @@ -433,7 +430,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) } break; default: - LOG4_ERROR("invalid connect status %d!", m_ucChannelStatus); + LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), m_ucChannelStatus); return(CODEC_STATUS_ERR); } } @@ -454,7 +451,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } - LOG4_ERROR("recv from fd %d error %d: %s", + LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); m_strErrMsg = m_szErrBuff; return(CODEC_STATUS_INT); @@ -506,7 +503,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } - LOG4_ERROR("recv from fd %d error %d: %s", + LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); m_strErrMsg = m_szErrBuff; return(CODEC_STATUS_INT); @@ -566,7 +563,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, Htt m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } - LOG4_ERROR("recv from fd %d error %d: %s", + LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); m_strErrMsg = m_szErrBuff; return(CODEC_STATUS_INT); @@ -596,13 +593,13 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) { - // TODO 当http1.0响应包未带Content-Length头时,以关闭连接表示数据发送完毕。需再处理 LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_ulSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_ulSeq, m_ucChannelStatus); return(CODEC_STATUS_EOF); } + // 当http1.0响应包未带Content-Length头时,m_pRecvBuff可读字节数为0,以关闭连接表示数据发送完毕。 E_CODEC_STATUS eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); return(eCodecStatus); } @@ -665,7 +662,7 @@ bool SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAliv pNewCodec->SetKey(m_strKey); break; case CODEC_HTTP: - pNewCodec = new CodecHttp(m_pLogger, eCodecType); + pNewCodec = new CodecHttp(m_pLogger, eCodecType, dKeepAlive); pNewCodec->SetKey(m_strKey); break; default: @@ -742,13 +739,13 @@ bool SocketChannelImpl::Close() int SocketChannelImpl::Write(CBuffer* pBuff, int& iErrno) { - LOG4_TRACE(""); + LOG4_TRACE("fd[%d], channel_seq[%u]", GetFd(), GetSequence()); return(pBuff->WriteFD(m_iFd, iErrno)); } int SocketChannelImpl::Read(CBuffer* pBuff, int& iErrno) { - LOG4_TRACE(""); + LOG4_TRACE("fd[%d], channel_seq[%u]", GetFd(), GetSequence()); return(pBuff->ReadFD(m_iFd, iErrno)); } diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 7be50f9a..6e0bf34c 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -78,9 +78,10 @@ static const char * status_string(int code) namespace neb { -CodecHttp::CodecHttp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) +CodecHttp::CodecHttp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive) : Codec(pLogger, eCodecType), - m_iHttpMajor(1), m_iHttpMinor(1), m_dKeepAlive(10.0) + m_bChannelIsClient(false), m_uiEncodedNum(0), m_uiDecodedNum(0), + m_iHttpMajor(1), m_iHttpMinor(1), m_dKeepAlive(dKeepAlive) { } @@ -91,6 +92,11 @@ CodecHttp::~CodecHttp() E_CODEC_STATUS CodecHttp::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) { LOG4_TRACE(" "); + ++m_uiEncodedNum; + if (m_uiEncodedNum > m_uiDecodedNum) + { + m_bChannelIsClient = true; + } HttpMsg oHttpMsg; if (oHttpMsg.ParseFromString(oMsgBody.data())) { @@ -108,6 +114,7 @@ E_CODEC_STATUS CodecHttp::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBod E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) { LOG4_TRACE(" "); + ++m_uiDecodedNum; if (pBuff->ReadableBytes() == 0) { LOG4_DEBUG("no data..."); @@ -142,6 +149,11 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { LOG4_TRACE("pBuff->ReadableBytes() = %u, ReadIndex = %u, WriteIndex = %u", pBuff->ReadableBytes(), pBuff->GetReadIndex(), pBuff->GetWriteIndex()); + ++m_uiEncodedNum; + if (m_uiEncodedNum > m_uiDecodedNum) + { + m_bChannelIsClient = true; + } if (0 == oHttpMsg.http_major()) { LOG4_ERROR("miss http version!"); @@ -150,7 +162,7 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) } int iWriteSize = 0; - int iHadWriteSize = 0; + int iHadEncodedSize = 0; if (HTTP_REQUEST == oHttpMsg.type()) { if (oHttpMsg.url().size() == 0) @@ -211,24 +223,24 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) oHttpMsg.http_major(), oHttpMsg.http_minor()); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } iWriteSize = pBuff->Printf("Host: %s:%d\r\n", strHost.c_str(), iPort); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } else if (HTTP_RESPONSE == oHttpMsg.type()) @@ -236,7 +248,7 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) if (0 == oHttpMsg.status_code()) { LOG4_ERROR("miss status code!"); - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } @@ -244,51 +256,37 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) oHttpMsg.status_code(), status_string(oHttpMsg.status_code())); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } - //if (oHttpMsg.http_major() < 1 || (oHttpMsg.http_major() == 1 && oHttpMsg.http_minor() < 1)) - if (m_iHttpMajor < 1 || (m_iHttpMajor == 1 && m_iHttpMinor < 1)) - { - m_mapAddingHttpHeader.insert(std::pair("Connection", "close")); - m_dKeepAlive = 0.0; - } - else + if (!m_bChannelIsClient) { - m_mapAddingHttpHeader.insert(std::pair("Connection", "keep-alive")); - m_dKeepAlive = -1; + if (m_dKeepAlive == 0) + { + m_mapAddingHttpHeader.insert(std::pair("Connection", "close")); + } + else + { + m_mapAddingHttpHeader.insert(std::pair("Connection", "keep-alive")); + } } m_mapAddingHttpHeader.insert(std::make_pair("Server", "NebulaHttp")); - m_mapAddingHttpHeader.insert(std::make_pair("Content-Type", "application/json;charset=UTF-8")); m_mapAddingHttpHeader.insert(std::make_pair("Allow", "POST,GET")); + //m_mapAddingHttpHeader.insert(std::make_pair("Content-Type", "application/json;charset=UTF-8")); } bool bIsChunked = false; bool bIsGzip = false; // 是否用gizp压缩传输包 for (auto h_iter = oHttpMsg.headers().begin(); h_iter != oHttpMsg.headers().end(); ++h_iter) { - iWriteSize = pBuff->Printf("%s: %s\r\n", h_iter->first.c_str(), h_iter->second.c_str()); - if (iWriteSize < 0) + auto h_a_iter = m_mapAddingHttpHeader.find(h_iter->first); + if (h_a_iter == m_mapAddingHttpHeader.end()) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadWriteSize += iWriteSize; - } - if (std::string("Content-Encoding") == h_iter->first && std::string("gzip") == h_iter->second) - { - bIsGzip = true; - } - else if (std::string("Transfer-Encoding") == h_iter->first && std::string("chunked") == h_iter->second) - { - bIsChunked = true; + m_mapAddingHttpHeader.insert(std::make_pair(h_iter->first, h_iter->second)); } } for (auto h_iter = m_mapAddingHttpHeader.begin(); h_iter != m_mapAddingHttpHeader.end(); ++h_iter) @@ -296,13 +294,17 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Printf("%s: %s\r\n", h_iter->first.c_str(), h_iter->second.c_str()); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; + } + if (std::string("Content-Encoding") == h_iter->first && std::string("gzip") == h_iter->second) + { + bIsGzip = true; } if (std::string("Transfer-Encoding") == h_iter->first && std::string("chunked") == h_iter->second) { @@ -317,7 +319,7 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) if (!Gzip(oHttpMsg.body(), strGzipData)) { LOG4_ERROR("gzip error!"); - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } @@ -330,43 +332,43 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Printf("\r\n"); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } else { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); - iHadWriteSize = 0; + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); + iHadEncodedSize = 0; } if (strGzipData.size() > 0) { iWriteSize = pBuff->Printf("%x\r\n", strGzipData.size()); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } iWriteSize = pBuff->Write(strGzipData.c_str(), strGzipData.size()); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } else @@ -374,13 +376,13 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Printf("%x\r\n", oHttpMsg.body().size()); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } if (oHttpMsg.body().size() == 0) { @@ -391,13 +393,13 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Write(oHttpMsg.body().c_str(), oHttpMsg.body().size()); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } } @@ -412,13 +414,13 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Printf("Transfer-Encoding: chunked\r\n\r\n"); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } size_t iChunkLength = 8192; for (size_t iPos = 0; iPos < strGzipData.size(); iPos += iChunkLength) @@ -427,36 +429,36 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Printf("%x\r\n", iChunkLength); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } iWriteSize = pBuff->Printf("%s\r\n", strGzipData.substr(iPos, iChunkLength).c_str(), iChunkLength); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } iWriteSize = pBuff->Printf("0\r\n\r\n"); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } else @@ -464,24 +466,24 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Printf("Content-Length: %u\r\n\r\n", strGzipData.size()); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } iWriteSize = pBuff->Write(strGzipData.c_str(), strGzipData.size()); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } } @@ -493,13 +495,13 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Printf("Transfer-Encoding: chunked\r\n\r\n"); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } size_t iChunkLength = 8192; for (size_t iPos = 0; iPos < oHttpMsg.body().size(); iPos += iChunkLength) @@ -508,36 +510,36 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Printf("%x\r\n", iChunkLength); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } iWriteSize = pBuff->Printf("%s\r\n", oHttpMsg.body().substr(iPos, iChunkLength).c_str(), iChunkLength); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } iWriteSize = pBuff->Printf("0\r\n\r\n"); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } else @@ -545,24 +547,24 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Printf("Content-Length: %u\r\n\r\n", oHttpMsg.body().size()); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } iWriteSize = pBuff->Write(oHttpMsg.body().c_str(), oHttpMsg.body().size()); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } } @@ -577,29 +579,29 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Printf("\r\n"); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } else { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); } iWriteSize = pBuff->Printf("0\r\n\r\n"); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } else @@ -607,13 +609,13 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iWriteSize = pBuff->Printf("Content-Length: 0\r\n\r\n"); if (iWriteSize < 0) { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteSize); + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } else { - iHadWriteSize += iWriteSize; + iHadEncodedSize += iWriteSize; } } } @@ -621,8 +623,8 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) size_t iWriteIndex = pBuff->GetWriteIndex(); LOG4_TRACE("%s", pBuff->GetRawReadBuffer()); pBuff->SetWriteIndex(iWriteIndex - iWriteSize); - LOG4_TRACE("pBuff->ReadableBytes() = %u, ReadIndex = %u, WriteIndex = %u, iHadWriteSize = %d", - pBuff->ReadableBytes(), pBuff->GetReadIndex(), pBuff->GetWriteIndex(), iHadWriteSize); + LOG4_TRACE("pBuff->ReadableBytes() = %u, ReadIndex = %u, WriteIndex = %u, iHadEncodedSize = %d", + pBuff->ReadableBytes(), pBuff->GetReadIndex(), pBuff->GetWriteIndex(), iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_OK); } @@ -630,9 +632,13 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) { LOG4_TRACE(" "); + ++m_uiDecodedNum; if (pBuff->ReadableBytes() == 0) { - // LOG4_DEBUG("no data..."); + if (CloseRightAway()) + { + return(CODEC_STATUS_EOF); + } return(CODEC_STATUS_PAUSE); } m_parser_setting.on_message_begin = OnMessageBegin; @@ -647,9 +653,10 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) m_parser_setting.on_chunk_complete = OnChunkComplete; m_parser.data = &oHttpMsg; http_parser_init(&m_parser, HTTP_BOTH); - size_t uiReadableBytes = pBuff->ReadableBytes(); + const char* pDecodeBuff = pBuff->GetRawReadBuffer(); + size_t uiDecodeBuffLen = pBuff->ReadableBytes(); size_t uiLen = http_parser_execute(&m_parser, &m_parser_setting, - pBuff->GetRawReadBuffer(), uiReadableBytes); + pDecodeBuff, uiDecodeBuffLen); if (!oHttpMsg.is_decoding()) { if(m_parser.http_errno != HPE_OK) @@ -671,6 +678,16 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) m_iHttpMinor = oHttpMsg.http_minor(); m_dKeepAlive = oHttpMsg.keep_alive(); } + else + { + if (m_bChannelIsClient) + { + if (oHttpMsg.keep_alive() == 0.0) + { + m_dKeepAlive = 0.0; + } + } + } auto iter = oHttpMsg.headers().find("Content-Encoding"); if (iter != oHttpMsg.headers().end()) { @@ -740,6 +757,25 @@ const std::string& CodecHttp::ToString(const HttpMsg& oHttpMsg) return(m_strHttpString); } +bool CodecHttp::CloseRightAway() const +{ + if (m_bChannelIsClient) + { + if (m_dKeepAlive == 0.0) + { + return(true); + } + else + { + return(false); + } + } + else + { + return(false); + } +} + int CodecHttp::OnMessageBegin(http_parser *parser) { HttpMsg* pHttpMsg = (HttpMsg*) parser->data; @@ -829,7 +865,7 @@ int CodecHttp::OnHeaderValue(http_parser *parser, const char *at, size_t len) } else if (std::string("close") == strHeadValue) { - ; //pHttpMsg->set_keep_alive(0.0); + pHttpMsg->set_keep_alive(0.0); } if (std::string("Upgrade") == strHeadValue) @@ -884,13 +920,9 @@ int CodecHttp::OnMessageComplete(http_parser *parser) pHttpMsg->set_http_minor(parser->http_minor); pHttpMsg->set_is_decoding(false); - if (http_should_keep_alive(parser)) - { - pHttpMsg->set_keep_alive(-1); ; - } - else + if (!http_should_keep_alive(parser)) { - pHttpMsg->set_keep_alive(0); + pHttpMsg->set_keep_alive(0.0); } /* switch ((http_method)pHttpMsg->method()) diff --git a/src/codec/CodecHttp.hpp b/src/codec/CodecHttp.hpp index 5e4bc375..9e2fe6ec 100644 --- a/src/codec/CodecHttp.hpp +++ b/src/codec/CodecHttp.hpp @@ -20,7 +20,7 @@ namespace neb class CodecHttp: public Codec { public: - CodecHttp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + CodecHttp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive = 10.0); virtual ~CodecHttp(); virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); @@ -43,6 +43,8 @@ class CodecHttp: public Codec return(m_dKeepAlive); } + bool CloseRightAway() const; + protected: static int OnMessageBegin(http_parser *parser); static int OnUrl(http_parser *parser, const char *at, size_t len); @@ -56,6 +58,9 @@ class CodecHttp: public Codec static int OnChunkComplete(http_parser *parser); private: + bool m_bChannelIsClient; // 当前编解码器所在channel是作为http客户端还是作为http服务端 + uint32 m_uiEncodedNum; + uint32 m_uiDecodedNum; int32 m_iHttpMajor; int32 m_iHttpMinor; ev_tstamp m_dKeepAlive; diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp index e809c3ef..47d8e105 100644 --- a/src/codec/CodecProto.cpp +++ b/src/codec/CodecProto.cpp @@ -97,7 +97,7 @@ E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oM } else { - LOG4_ERROR("oMsgHead.ParseFromArray() error!"); + LOG4_WARNING("oMsgHead.ParseFromArray() error!"); // maybe port scan from operation and maintenance system. return(CODEC_STATUS_ERR); } } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 5b07ee85..cbbfb023 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -259,7 +259,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) } else // 编解码出错或连接关闭或连接中断 { - LOG4_DEBUG("codec error or connection closed!"); + LOG4_TRACE("codec error or connection closed!"); DiscardSocketChannel(pChannel); return(false); } @@ -788,6 +788,44 @@ bool Dispatcher::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 ui } } +bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +{ + if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) + { + return(Broadcast("BEACON", iCmd, uiSeq, oMsgBody, pSender)); + } + else + { + auto pChannel = ((Worker*)m_pLabor)->GetManagerControlChannel(); + if (pChannel == nullptr) + { + LOG4_ERROR("no connected channel to manager!"); + return(false); + } + if (nullptr != pSender) + { + (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); + } + E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); + if (CODEC_STATUS_OK == eStatus) + { + RemoveIoWriteEvent(pChannel); + return(true); + } + else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) + { + AddIoWriteEvent(pChannel); + return(true); + } + else if (CODEC_STATUS_WANT_READ == eStatus) + { + RemoveIoWriteEvent(pChannel); + return(true); + } + return(false); + } +} + bool Dispatcher::SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) { E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); @@ -814,7 +852,7 @@ bool Dispatcher::SendTo(std::shared_ptr pChannel, const HttpMsg& bool Dispatcher::SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) { char szIdentify[256] = {0}; - snprintf(szIdentify, sizeof(szIdentify), "%s:%d%s", strHost.c_str(), iPort, strUrlPath.c_str()); + snprintf(szIdentify, sizeof(szIdentify), "%s:%d", strHost.c_str(), iPort); LOG4_TRACE("identify: %s", szIdentify); auto named_iter = m_mapNamedSocketChannel.find(szIdentify); if (named_iter == m_mapNamedSocketChannel.end()) @@ -1074,7 +1112,7 @@ bool Dispatcher::AutoSend(const std::string& strHost, int iPort, const std::stri int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); if (0 != iCode) { - LOG4_ERROR("getaddrinfo(\"%s\", \"%s\") error %d: %s", + LOG4_ERROR("getaddrinfo(\"%s\", \"%d\") error %d: %s", strHost.c_str(), iPort, iCode, gai_strerror(iCode)); return(false); } @@ -1552,7 +1590,7 @@ int32 Dispatcher::GetClientNum() const bool Dispatcher::Init() { #if __cplusplus >= 201401L - m_pSessionNode = std::make_shared(); + m_pSessionNode = std::make_unique(); #else m_pSessionNode = std::unique_ptr(new Nodes()); #endif @@ -1836,13 +1874,16 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) } } - std::pair worker_pid_fd = ((Manager*)m_pLabor)->GetSessionManager()->GetMinLoadWorkerDataFd(); - if (worker_pid_fd.second > 0) + int iWorkerDataFd = -1; + //std::pair worker_pid_fd = ((Manager*)m_pLabor)->GetSessionManager()->GetMinLoadWorkerDataFd(); + //iWorkerDataFd = worker_pid_fd.second; + iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(); + if (iWorkerDataFd > 0) { LOG4_DEBUG("send new fd %d to worker communication fd %d", - iAcceptFd, worker_pid_fd.second); + iAcceptFd, iWorkerDataFd); int iCodec = m_pLabor->GetNodeInfo().eCodec; - int iErrno = SocketChannel::SendChannelFd(worker_pid_fd.second, iAcceptFd, iFamily, iCodec, m_pLogger); + int iErrno = SocketChannel::SendChannelFd(iWorkerDataFd, iAcceptFd, iFamily, iCodec, m_pLogger); if (iErrno != ERR_OK) { LOG4_ERROR("error %d: %s", iErrno, strerror_r(iErrno, m_pErrBuff, gc_iErrBuffLen)); @@ -1850,7 +1891,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) close(iAcceptFd); return(true); } - LOG4_WARNING("GetMinLoadWorkerDataFd() found worker_pid_fd.second = %d", worker_pid_fd.second); + LOG4_WARNING("GetMinLoadWorkerDataFd() found worker_pid_fd.second = %d", iWorkerDataFd); close(iAcceptFd); return(false); } diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 82e01f0e..12f437ed 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -124,6 +124,7 @@ class Dispatcher bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender); // SendTo() for http bool SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index a7739f16..53b5d32c 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -235,6 +235,7 @@ bool Manager::GetConf() if (m_oLastConf.ToString() != m_oCurrentConf.ToString()) { m_oCurrentConf.Get("io_timeout", m_stNodeInfo.dIoTimeout); + m_oCurrentConf.Get("data_report", m_stNodeInfo.dDataReportInterval); if (m_oLastConf.ToString().length() == 0) { m_stNodeInfo.uiWorkerNum = strtoul(m_oCurrentConf("worker_num").c_str(), NULL, 10); @@ -578,6 +579,8 @@ void Manager::RefreshServer() { int iLogLevel = Logger::INFO; int iNetLogLevel = Logger::INFO; + m_oCurrentConf.Get("log_level", iLogLevel); + m_oCurrentConf.Get("net_log_level", iNetLogLevel); m_pLogger->SetLogLevel(iLogLevel); m_pLogger->SetNetLogLevel(iNetLogLevel); MsgBody oMsgBody; diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index f0e41018..2cf8543c 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -31,9 +31,10 @@ struct NodeInfo int32 iPortForClient = 0; ///< 对Client通信监听端口,对应 iC2SListenFd int32 iGatewayPort = 0; ///< 对Client服务的真实端口 bool bIsAccess = false; ///< 是否接入Server + ev_tstamp dIoTimeout = 0.0; ///< IO(连接)超时配置 + ev_tstamp dDataReportInterval = 60.0; ///< 统计数据上报时间间隔 ev_tstamp dMsgStatInterval = 0.0; ///< 客户端连接发送数据包统计时间间隔 ev_tstamp dAddrStatInterval = 0.0; ///< IP地址数据统计时间间隔 - ev_tstamp dIoTimeout = 0.0; ///< IO(连接)超时配置 ev_tstamp dStepTimeout = 0.0; ///< 步骤超时 std::string strWorkPath; ///< 工作路径 std::string strConfFile; ///< 配置文件 diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 32165cda..af6c8b81 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -106,6 +106,7 @@ bool Worker::Init(CJsonObject& oJsonConf) oJsonConf.Get("node_type", m_stNodeInfo.strNodeType); oJsonConf.Get("host", m_stNodeInfo.strHostForServer); oJsonConf.Get("port", m_stNodeInfo.iPortForServer); + oJsonConf.Get("data_report", m_stNodeInfo.dDataReportInterval); m_oNodeConf = oJsonConf; m_oCustomConf = oJsonConf["custom"]; std::ostringstream oss; @@ -374,6 +375,11 @@ const CJsonObject& Worker::GetCustomConf() const return(m_oCustomConf); } +std::shared_ptr Worker::GetManagerControlChannel() +{ + return(m_pManagerControlChannel); +} + bool Worker::SetCustomConf(const CJsonObject& oJsonConf) { m_oCustomConf = oJsonConf; diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 0bcba3d1..a360c88a 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -85,6 +85,7 @@ class Worker: public Labor virtual bool AddNetLogMsg(const MsgBody& oMsgBody); const WorkerInfo& GetWorkerInfo() const; const CJsonObject& GetCustomConf() const; + std::shared_ptr GetManagerControlChannel(); bool SetCustomConf(const CJsonObject& oJsonConf); bool WithSsl(); diff --git a/src/pb/report.pb.cc b/src/pb/report.pb.cc new file mode 100644 index 00000000..3dbf9a7f --- /dev/null +++ b/src/pb/report.pb.cc @@ -0,0 +1,753 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: report.proto + +#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION +#include "report.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace neb { + +namespace { + +const ::google::protobuf::Descriptor* ReportRecord_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + ReportRecord_reflection_ = NULL; +const ::google::protobuf::Descriptor* Report_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Report_reflection_ = NULL; + +} // namespace + + +void protobuf_AssignDesc_report_2eproto() GOOGLE_ATTRIBUTE_COLD; +void protobuf_AssignDesc_report_2eproto() { + protobuf_AddDesc_report_2eproto(); + const ::google::protobuf::FileDescriptor* file = + ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( + "report.proto"); + GOOGLE_CHECK(file != NULL); + ReportRecord_descriptor_ = file->message_type(0); + static const int ReportRecord_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ReportRecord, key_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ReportRecord, value_), + }; + ReportRecord_reflection_ = + ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( + ReportRecord_descriptor_, + ReportRecord::default_instance_, + ReportRecord_offsets_, + -1, + -1, + -1, + sizeof(ReportRecord), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ReportRecord, _internal_metadata_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ReportRecord, _is_default_instance_)); + Report_descriptor_ = file->message_type(1); + static const int Report_offsets_[1] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Report, records_), + }; + Report_reflection_ = + ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( + Report_descriptor_, + Report::default_instance_, + Report_offsets_, + -1, + -1, + -1, + sizeof(Report), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Report, _internal_metadata_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Report, _is_default_instance_)); +} + +namespace { + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); +inline void protobuf_AssignDescriptorsOnce() { + ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, + &protobuf_AssignDesc_report_2eproto); +} + +void protobuf_RegisterTypes(const ::std::string&) GOOGLE_ATTRIBUTE_COLD; +void protobuf_RegisterTypes(const ::std::string&) { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + ReportRecord_descriptor_, &ReportRecord::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Report_descriptor_, &Report::default_instance()); +} + +} // namespace + +void protobuf_ShutdownFile_report_2eproto() { + delete ReportRecord::default_instance_; + delete ReportRecord_reflection_; + delete Report::default_instance_; + delete Report_reflection_; +} + +void protobuf_AddDesc_report_2eproto() GOOGLE_ATTRIBUTE_COLD; +void protobuf_AddDesc_report_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + + ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( + "\n\014report.proto\022\003neb\"*\n\014ReportRecord\022\013\n\003k" + "ey\030\001 \001(\014\022\r\n\005value\030\002 \003(\004\",\n\006Report\022\"\n\007rec" + "ords\030\001 \003(\0132\021.neb.ReportRecordb\006proto3", 117); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( + "report.proto", &protobuf_RegisterTypes); + ReportRecord::default_instance_ = new ReportRecord(); + Report::default_instance_ = new Report(); + ReportRecord::default_instance_->InitAsDefaultInstance(); + Report::default_instance_->InitAsDefaultInstance(); + ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_report_2eproto); +} + +// Force AddDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_report_2eproto { + StaticDescriptorInitializer_report_2eproto() { + protobuf_AddDesc_report_2eproto(); + } +} static_descriptor_initializer_report_2eproto_; + +// =================================================================== + +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +const int ReportRecord::kKeyFieldNumber; +const int ReportRecord::kValueFieldNumber; +#endif // !defined(_MSC_VER) || _MSC_VER >= 1900 + +ReportRecord::ReportRecord() + : ::google::protobuf::Message(), _internal_metadata_(NULL) { + SharedCtor(); + // @@protoc_insertion_point(constructor:neb.ReportRecord) +} + +void ReportRecord::InitAsDefaultInstance() { + _is_default_instance_ = true; +} + +ReportRecord::ReportRecord(const ReportRecord& from) + : ::google::protobuf::Message(), + _internal_metadata_(NULL) { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:neb.ReportRecord) +} + +void ReportRecord::SharedCtor() { + _is_default_instance_ = false; + ::google::protobuf::internal::GetEmptyString(); + _cached_size_ = 0; + key_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + +ReportRecord::~ReportRecord() { + // @@protoc_insertion_point(destructor:neb.ReportRecord) + SharedDtor(); +} + +void ReportRecord::SharedDtor() { + key_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + if (this != default_instance_) { + } +} + +void ReportRecord::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* ReportRecord::descriptor() { + protobuf_AssignDescriptorsOnce(); + return ReportRecord_descriptor_; +} + +const ReportRecord& ReportRecord::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_report_2eproto(); + return *default_instance_; +} + +ReportRecord* ReportRecord::default_instance_ = NULL; + +ReportRecord* ReportRecord::New(::google::protobuf::Arena* arena) const { + ReportRecord* n = new ReportRecord; + if (arena != NULL) { + arena->Own(n); + } + return n; +} + +void ReportRecord::Clear() { +// @@protoc_insertion_point(message_clear_start:neb.ReportRecord) + key_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_.Clear(); +} + +bool ReportRecord::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:neb.ReportRecord) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional bytes key = 1; + case 1: { + if (tag == 10) { + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_key())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(18)) goto parse_value; + break; + } + + // repeated uint64 value = 2; + case 2: { + if (tag == 18) { + parse_value: + DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, this->mutable_value()))); + } else if (tag == 16) { + DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + 1, 18, input, this->mutable_value()))); + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag)); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:neb.ReportRecord) + return true; +failure: + // @@protoc_insertion_point(parse_failure:neb.ReportRecord) + return false; +#undef DO_ +} + +void ReportRecord::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:neb.ReportRecord) + // optional bytes key = 1; + if (this->key().size() > 0) { + ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased( + 1, this->key(), output); + } + + // repeated uint64 value = 2; + if (this->value_size() > 0) { + ::google::protobuf::internal::WireFormatLite::WriteTag(2, ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); + output->WriteVarint32(_value_cached_byte_size_); + } + for (int i = 0; i < this->value_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64NoTag( + this->value(i), output); + } + + // @@protoc_insertion_point(serialize_end:neb.ReportRecord) +} + +::google::protobuf::uint8* ReportRecord::InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:neb.ReportRecord) + // optional bytes key = 1; + if (this->key().size() > 0) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 1, this->key(), target); + } + + // repeated uint64 value = 2; + if (this->value_size() > 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteTagToArray( + 2, + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, + target); + target = ::google::protobuf::io::CodedOutputStream::WriteVarint32ToArray( + _value_cached_byte_size_, target); + } + for (int i = 0; i < this->value_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteUInt64NoTagToArray(this->value(i), target); + } + + // @@protoc_insertion_point(serialize_to_array_end:neb.ReportRecord) + return target; +} + +int ReportRecord::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:neb.ReportRecord) + int total_size = 0; + + // optional bytes key = 1; + if (this->key().size() > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->key()); + } + + // repeated uint64 value = 2; + { + int data_size = 0; + for (int i = 0; i < this->value_size(); i++) { + data_size += ::google::protobuf::internal::WireFormatLite:: + UInt64Size(this->value(i)); + } + if (data_size > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size(data_size); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _value_cached_byte_size_ = data_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + total_size += data_size; + } + + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void ReportRecord::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:neb.ReportRecord) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + const ReportRecord* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); + if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:neb.ReportRecord) + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:neb.ReportRecord) + MergeFrom(*source); + } +} + +void ReportRecord::MergeFrom(const ReportRecord& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:neb.ReportRecord) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + value_.MergeFrom(from.value_); + if (from.key().size() > 0) { + + key_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.key_); + } +} + +void ReportRecord::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:neb.ReportRecord) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void ReportRecord::CopyFrom(const ReportRecord& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:neb.ReportRecord) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ReportRecord::IsInitialized() const { + + return true; +} + +void ReportRecord::Swap(ReportRecord* other) { + if (other == this) return; + InternalSwap(other); +} +void ReportRecord::InternalSwap(ReportRecord* other) { + key_.Swap(&other->key_); + value_.UnsafeArenaSwap(&other->value_); + _internal_metadata_.Swap(&other->_internal_metadata_); + std::swap(_cached_size_, other->_cached_size_); +} + +::google::protobuf::Metadata ReportRecord::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = ReportRecord_descriptor_; + metadata.reflection = ReportRecord_reflection_; + return metadata; +} + +#if PROTOBUF_INLINE_NOT_IN_HEADERS +// ReportRecord + +// optional bytes key = 1; +void ReportRecord::clear_key() { + key_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& ReportRecord::key() const { + // @@protoc_insertion_point(field_get:neb.ReportRecord.key) + return key_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void ReportRecord::set_key(const ::std::string& value) { + + key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:neb.ReportRecord.key) +} + void ReportRecord::set_key(const char* value) { + + key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:neb.ReportRecord.key) +} + void ReportRecord::set_key(const void* value, size_t size) { + + key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:neb.ReportRecord.key) +} + ::std::string* ReportRecord::mutable_key() { + + // @@protoc_insertion_point(field_mutable:neb.ReportRecord.key) + return key_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* ReportRecord::release_key() { + // @@protoc_insertion_point(field_release:neb.ReportRecord.key) + + return key_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void ReportRecord::set_allocated_key(::std::string* key) { + if (key != NULL) { + + } else { + + } + key_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), key); + // @@protoc_insertion_point(field_set_allocated:neb.ReportRecord.key) +} + +// repeated uint64 value = 2; +int ReportRecord::value_size() const { + return value_.size(); +} +void ReportRecord::clear_value() { + value_.Clear(); +} + ::google::protobuf::uint64 ReportRecord::value(int index) const { + // @@protoc_insertion_point(field_get:neb.ReportRecord.value) + return value_.Get(index); +} + void ReportRecord::set_value(int index, ::google::protobuf::uint64 value) { + value_.Set(index, value); + // @@protoc_insertion_point(field_set:neb.ReportRecord.value) +} + void ReportRecord::add_value(::google::protobuf::uint64 value) { + value_.Add(value); + // @@protoc_insertion_point(field_add:neb.ReportRecord.value) +} + const ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >& +ReportRecord::value() const { + // @@protoc_insertion_point(field_list:neb.ReportRecord.value) + return value_; +} + ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >* +ReportRecord::mutable_value() { + // @@protoc_insertion_point(field_mutable_list:neb.ReportRecord.value) + return &value_; +} + +#endif // PROTOBUF_INLINE_NOT_IN_HEADERS + +// =================================================================== + +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +const int Report::kRecordsFieldNumber; +#endif // !defined(_MSC_VER) || _MSC_VER >= 1900 + +Report::Report() + : ::google::protobuf::Message(), _internal_metadata_(NULL) { + SharedCtor(); + // @@protoc_insertion_point(constructor:neb.Report) +} + +void Report::InitAsDefaultInstance() { + _is_default_instance_ = true; +} + +Report::Report(const Report& from) + : ::google::protobuf::Message(), + _internal_metadata_(NULL) { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:neb.Report) +} + +void Report::SharedCtor() { + _is_default_instance_ = false; + _cached_size_ = 0; +} + +Report::~Report() { + // @@protoc_insertion_point(destructor:neb.Report) + SharedDtor(); +} + +void Report::SharedDtor() { + if (this != default_instance_) { + } +} + +void Report::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Report::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Report_descriptor_; +} + +const Report& Report::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_report_2eproto(); + return *default_instance_; +} + +Report* Report::default_instance_ = NULL; + +Report* Report::New(::google::protobuf::Arena* arena) const { + Report* n = new Report; + if (arena != NULL) { + arena->Own(n); + } + return n; +} + +void Report::Clear() { +// @@protoc_insertion_point(message_clear_start:neb.Report) + records_.Clear(); +} + +bool Report::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:neb.Report) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // repeated .neb.ReportRecord records = 1; + case 1: { + if (tag == 10) { + DO_(input->IncrementRecursionDepth()); + parse_loop_records: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( + input, add_records())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(10)) goto parse_loop_records; + input->UnsafeDecrementRecursionDepth(); + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag)); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:neb.Report) + return true; +failure: + // @@protoc_insertion_point(parse_failure:neb.Report) + return false; +#undef DO_ +} + +void Report::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:neb.Report) + // repeated .neb.ReportRecord records = 1; + for (unsigned int i = 0, n = this->records_size(); i < n; i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 1, this->records(i), output); + } + + // @@protoc_insertion_point(serialize_end:neb.Report) +} + +::google::protobuf::uint8* Report::InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:neb.Report) + // repeated .neb.ReportRecord records = 1; + for (unsigned int i = 0, n = this->records_size(); i < n; i++) { + target = ::google::protobuf::internal::WireFormatLite:: + InternalWriteMessageNoVirtualToArray( + 1, this->records(i), false, target); + } + + // @@protoc_insertion_point(serialize_to_array_end:neb.Report) + return target; +} + +int Report::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:neb.Report) + int total_size = 0; + + // repeated .neb.ReportRecord records = 1; + total_size += 1 * this->records_size(); + for (int i = 0; i < this->records_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->records(i)); + } + + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Report::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:neb.Report) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + const Report* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); + if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:neb.Report) + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:neb.Report) + MergeFrom(*source); + } +} + +void Report::MergeFrom(const Report& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:neb.Report) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + records_.MergeFrom(from.records_); +} + +void Report::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:neb.Report) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Report::CopyFrom(const Report& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:neb.Report) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Report::IsInitialized() const { + + return true; +} + +void Report::Swap(Report* other) { + if (other == this) return; + InternalSwap(other); +} +void Report::InternalSwap(Report* other) { + records_.UnsafeArenaSwap(&other->records_); + _internal_metadata_.Swap(&other->_internal_metadata_); + std::swap(_cached_size_, other->_cached_size_); +} + +::google::protobuf::Metadata Report::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Report_descriptor_; + metadata.reflection = Report_reflection_; + return metadata; +} + +#if PROTOBUF_INLINE_NOT_IN_HEADERS +// Report + +// repeated .neb.ReportRecord records = 1; +int Report::records_size() const { + return records_.size(); +} +void Report::clear_records() { + records_.Clear(); +} +const ::neb::ReportRecord& Report::records(int index) const { + // @@protoc_insertion_point(field_get:neb.Report.records) + return records_.Get(index); +} +::neb::ReportRecord* Report::mutable_records(int index) { + // @@protoc_insertion_point(field_mutable:neb.Report.records) + return records_.Mutable(index); +} +::neb::ReportRecord* Report::add_records() { + // @@protoc_insertion_point(field_add:neb.Report.records) + return records_.Add(); +} +::google::protobuf::RepeatedPtrField< ::neb::ReportRecord >* +Report::mutable_records() { + // @@protoc_insertion_point(field_mutable_list:neb.Report.records) + return &records_; +} +const ::google::protobuf::RepeatedPtrField< ::neb::ReportRecord >& +Report::records() const { + // @@protoc_insertion_point(field_list:neb.Report.records) + return records_; +} + +#endif // PROTOBUF_INLINE_NOT_IN_HEADERS + +// @@protoc_insertion_point(namespace_scope) + +} // namespace neb + +// @@protoc_insertion_point(global_scope) diff --git a/src/pb/report.pb.h b/src/pb/report.pb.h new file mode 100644 index 00000000..35094e0d --- /dev/null +++ b/src/pb/report.pb.h @@ -0,0 +1,357 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: report.proto + +#ifndef PROTOBUF_report_2eproto__INCLUDED +#define PROTOBUF_report_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 3000000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3000000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace neb { + +// Internal implementation detail -- do not call these. +void protobuf_AddDesc_report_2eproto(); +void protobuf_AssignDesc_report_2eproto(); +void protobuf_ShutdownFile_report_2eproto(); + +class Report; +class ReportRecord; + +// =================================================================== + +class ReportRecord : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:neb.ReportRecord) */ { + public: + ReportRecord(); + virtual ~ReportRecord(); + + ReportRecord(const ReportRecord& from); + + inline ReportRecord& operator=(const ReportRecord& from) { + CopyFrom(from); + return *this; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const ReportRecord& default_instance(); + + void Swap(ReportRecord* other); + + // implements Message ---------------------------------------------- + + inline ReportRecord* New() const { return New(NULL); } + + ReportRecord* New(::google::protobuf::Arena* arena) const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const ReportRecord& from); + void MergeFrom(const ReportRecord& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const { + return InternalSerializeWithCachedSizesToArray(false, output); + } + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(ReportRecord* other); + private: + inline ::google::protobuf::Arena* GetArenaNoVirtual() const { + return _internal_metadata_.arena(); + } + inline void* MaybeArenaPtr() const { + return _internal_metadata_.raw_arena_ptr(); + } + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional bytes key = 1; + void clear_key(); + static const int kKeyFieldNumber = 1; + const ::std::string& key() const; + void set_key(const ::std::string& value); + void set_key(const char* value); + void set_key(const void* value, size_t size); + ::std::string* mutable_key(); + ::std::string* release_key(); + void set_allocated_key(::std::string* key); + + // repeated uint64 value = 2; + int value_size() const; + void clear_value(); + static const int kValueFieldNumber = 2; + ::google::protobuf::uint64 value(int index) const; + void set_value(int index, ::google::protobuf::uint64 value); + void add_value(::google::protobuf::uint64 value); + const ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >& + value() const; + ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >* + mutable_value(); + + // @@protoc_insertion_point(class_scope:neb.ReportRecord) + private: + + ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + bool _is_default_instance_; + ::google::protobuf::internal::ArenaStringPtr key_; + ::google::protobuf::RepeatedField< ::google::protobuf::uint64 > value_; + mutable int _value_cached_byte_size_; + mutable int _cached_size_; + friend void protobuf_AddDesc_report_2eproto(); + friend void protobuf_AssignDesc_report_2eproto(); + friend void protobuf_ShutdownFile_report_2eproto(); + + void InitAsDefaultInstance(); + static ReportRecord* default_instance_; +}; +// ------------------------------------------------------------------- + +class Report : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:neb.Report) */ { + public: + Report(); + virtual ~Report(); + + Report(const Report& from); + + inline Report& operator=(const Report& from) { + CopyFrom(from); + return *this; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Report& default_instance(); + + void Swap(Report* other); + + // implements Message ---------------------------------------------- + + inline Report* New() const { return New(NULL); } + + Report* New(::google::protobuf::Arena* arena) const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Report& from); + void MergeFrom(const Report& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const { + return InternalSerializeWithCachedSizesToArray(false, output); + } + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(Report* other); + private: + inline ::google::protobuf::Arena* GetArenaNoVirtual() const { + return _internal_metadata_.arena(); + } + inline void* MaybeArenaPtr() const { + return _internal_metadata_.raw_arena_ptr(); + } + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // repeated .neb.ReportRecord records = 1; + int records_size() const; + void clear_records(); + static const int kRecordsFieldNumber = 1; + const ::neb::ReportRecord& records(int index) const; + ::neb::ReportRecord* mutable_records(int index); + ::neb::ReportRecord* add_records(); + ::google::protobuf::RepeatedPtrField< ::neb::ReportRecord >* + mutable_records(); + const ::google::protobuf::RepeatedPtrField< ::neb::ReportRecord >& + records() const; + + // @@protoc_insertion_point(class_scope:neb.Report) + private: + + ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + bool _is_default_instance_; + ::google::protobuf::RepeatedPtrField< ::neb::ReportRecord > records_; + mutable int _cached_size_; + friend void protobuf_AddDesc_report_2eproto(); + friend void protobuf_AssignDesc_report_2eproto(); + friend void protobuf_ShutdownFile_report_2eproto(); + + void InitAsDefaultInstance(); + static Report* default_instance_; +}; +// =================================================================== + + +// =================================================================== + +#if !PROTOBUF_INLINE_NOT_IN_HEADERS +// ReportRecord + +// optional bytes key = 1; +inline void ReportRecord::clear_key() { + key_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& ReportRecord::key() const { + // @@protoc_insertion_point(field_get:neb.ReportRecord.key) + return key_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void ReportRecord::set_key(const ::std::string& value) { + + key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:neb.ReportRecord.key) +} +inline void ReportRecord::set_key(const char* value) { + + key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:neb.ReportRecord.key) +} +inline void ReportRecord::set_key(const void* value, size_t size) { + + key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:neb.ReportRecord.key) +} +inline ::std::string* ReportRecord::mutable_key() { + + // @@protoc_insertion_point(field_mutable:neb.ReportRecord.key) + return key_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* ReportRecord::release_key() { + // @@protoc_insertion_point(field_release:neb.ReportRecord.key) + + return key_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void ReportRecord::set_allocated_key(::std::string* key) { + if (key != NULL) { + + } else { + + } + key_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), key); + // @@protoc_insertion_point(field_set_allocated:neb.ReportRecord.key) +} + +// repeated uint64 value = 2; +inline int ReportRecord::value_size() const { + return value_.size(); +} +inline void ReportRecord::clear_value() { + value_.Clear(); +} +inline ::google::protobuf::uint64 ReportRecord::value(int index) const { + // @@protoc_insertion_point(field_get:neb.ReportRecord.value) + return value_.Get(index); +} +inline void ReportRecord::set_value(int index, ::google::protobuf::uint64 value) { + value_.Set(index, value); + // @@protoc_insertion_point(field_set:neb.ReportRecord.value) +} +inline void ReportRecord::add_value(::google::protobuf::uint64 value) { + value_.Add(value); + // @@protoc_insertion_point(field_add:neb.ReportRecord.value) +} +inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >& +ReportRecord::value() const { + // @@protoc_insertion_point(field_list:neb.ReportRecord.value) + return value_; +} +inline ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >* +ReportRecord::mutable_value() { + // @@protoc_insertion_point(field_mutable_list:neb.ReportRecord.value) + return &value_; +} + +// ------------------------------------------------------------------- + +// Report + +// repeated .neb.ReportRecord records = 1; +inline int Report::records_size() const { + return records_.size(); +} +inline void Report::clear_records() { + records_.Clear(); +} +inline const ::neb::ReportRecord& Report::records(int index) const { + // @@protoc_insertion_point(field_get:neb.Report.records) + return records_.Get(index); +} +inline ::neb::ReportRecord* Report::mutable_records(int index) { + // @@protoc_insertion_point(field_mutable:neb.Report.records) + return records_.Mutable(index); +} +inline ::neb::ReportRecord* Report::add_records() { + // @@protoc_insertion_point(field_add:neb.Report.records) + return records_.Add(); +} +inline ::google::protobuf::RepeatedPtrField< ::neb::ReportRecord >* +Report::mutable_records() { + // @@protoc_insertion_point(field_mutable_list:neb.Report.records) + return &records_; +} +inline const ::google::protobuf::RepeatedPtrField< ::neb::ReportRecord >& +Report::records() const { + // @@protoc_insertion_point(field_list:neb.Report.records) + return records_; +} + +#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace neb + +// @@protoc_insertion_point(global_scope) + +#endif // PROTOBUF_report_2eproto__INCLUDED From 27c1525d64e1df235733b2f120dbe0450069838f Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 28 Dec 2019 20:48:38 +0800 Subject: [PATCH 082/176] update CJsonObject and named socket channel bug fixed. --- src/actor/Actor.cpp | 5 + src/actor/Actor.hpp | 1 + src/actor/ActorBuilder.cpp | 2 +- src/actor/cmd/sys_cmd/CmdDataReport.cpp | 16 +- .../cmd/sys_cmd/manager/CmdOnTellWorker.cpp | 1 - .../session/sys_session/SessionDataReport.cpp | 63 ++++-- .../session/sys_session/SessionDataReport.hpp | 12 +- src/codec/CodecHttp.cpp | 16 +- src/ios/Dispatcher.cpp | 53 ++--- src/ios/Dispatcher.hpp | 4 + src/labor/Labor.hpp | 1 + src/labor/Manager.cpp | 5 + src/labor/Manager.hpp | 1 + src/labor/Worker.cpp | 5 + src/labor/Worker.hpp | 1 + src/util/json/CJsonObject.cpp | 193 +++++++++++------- src/util/json/CJsonObject.hpp | 3 +- src/util/json/cJSON.c | 8 +- src/util/json/cJSON.h | 2 +- 19 files changed, 261 insertions(+), 131 deletions(-) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index e46c5d81..73c30478 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -79,6 +79,11 @@ time_t Actor::GetNowTime() const return(m_pLabor->GetNowTime()); } +long Actor::GetNowTimeMs() const +{ + return(m_pLabor->GetNowTimeMs()); +} + const CJsonObject& Actor::GetCustomConf() const { return(((Worker*)m_pLabor)->GetCustomConf()); diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index bc188459..de5b069c 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -106,6 +106,7 @@ class Actor: public std::enable_shared_from_this const std::string& GetWorkPath() const; const std::string& GetNodeIdentify() const; time_t GetNowTime() const; + long GetNowTimeMs() const; ev_tstamp GetDataReportInterval() const; /** diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 7bf11efa..f4dee8a1 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -353,7 +353,7 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http { HttpMsg oOutHttpMsg; snprintf(m_pErrBuff, gc_iErrBuffLen, "no module to dispose %s!", oHttpMsg.path().c_str()); - LOG4_ERROR(m_pErrBuff); + LOG4_WARNING(m_pErrBuff); oOutHttpMsg.set_type(HTTP_RESPONSE); oOutHttpMsg.set_status_code(404); oOutHttpMsg.set_http_major(oHttpMsg.http_major()); diff --git a/src/actor/cmd/sys_cmd/CmdDataReport.cpp b/src/actor/cmd/sys_cmd/CmdDataReport.cpp index beda5715..28d5ef5f 100644 --- a/src/actor/cmd/sys_cmd/CmdDataReport.cpp +++ b/src/actor/cmd/sys_cmd/CmdDataReport.cpp @@ -45,8 +45,13 @@ bool CmdDataReport::AnyMessage( const MsgBody& oInMsgBody) { LOG4_TRACE(""); - Report oReport; - if (!oReport.ParseFromString(oInMsgBody.data())) + std::shared_ptr pReport = std::make_shared(); + if (pReport == nullptr) + { + LOG4_ERROR("failed to new Report!"); + return(CMD_STATUS_FAULT); + } + if (!pReport->ParseFromString(oInMsgBody.data())) { LOG4_ERROR("Report.ParseFromString() failed!"); return(false); @@ -62,7 +67,12 @@ bool CmdDataReport::AnyMessage( } pSessionDataReport = std::dynamic_pointer_cast(pSharedSession); } - pSessionDataReport->AddReport(oReport); + LOG4_TRACE("data report from %s", pChanel->GetIdentify().c_str()); + pSessionDataReport->AddReport(pReport); + if (GetNodeType() == "BEACON") + { + pSessionDataReport->AddReport(pChannel->GetIdentify(), pReport); + } return(true); } diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp index c39ee290..5dd3582d 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp @@ -36,7 +36,6 @@ bool CmdOnTellWorker::AnyMessage( { LOG4_DEBUG("AddNodeIdentify(%s, %s)!", oInTargetWorker.node_type().c_str(), oInTargetWorker.worker_identify().c_str()); - GetLabor(this)->GetDispatcher()->AddNamedSocketChannel(oInTargetWorker.worker_identify(), pChannel); GetLabor(this)->GetDispatcher()->AddNodeIdentify(oInTargetWorker.node_type(), oInTargetWorker.worker_identify()); oOutTargetWorker.set_worker_identify(GetNodeIdentify()); oOutTargetWorker.set_node_type(GetLabor(this)->GetNodeInfo().strNodeType); diff --git a/src/actor/session/sys_session/SessionDataReport.cpp b/src/actor/session/sys_session/SessionDataReport.cpp index c9d78c42..916b45b4 100644 --- a/src/actor/session/sys_session/SessionDataReport.cpp +++ b/src/actor/session/sys_session/SessionDataReport.cpp @@ -15,8 +15,12 @@ namespace neb { SessionDataReport::SessionDataReport(const std::string& strSessionId, ev_tstamp dStatInterval) - : Timer(strSessionId, dStatInterval), m_pReport(nullptr) + : Timer(strSessionId, dStatInterval), m_uiNodeReportUpdatingIndex(0), m_pReport(nullptr) { + std::unordered_map> mapNodeReport1; + std::unordered_map> mapNodeReport2; + m_vecNodeReport.push_back(mapNodeReport1); + m_vecNodeReport.push_back(mapNodeReport2); } SessionDataReport::~SessionDataReport() @@ -26,11 +30,22 @@ SessionDataReport::~SessionDataReport() delete m_pReport; m_pReport = nullptr; } + for (auto iter = m_mapDataCollect.begin(); iter != m_mapDataCollect.end(); ++iter) + { + if (iter->second != nullptr) + { + delete iter->second; + iter->second = nullptr; + } + } + m_mapDataCollect.clear(); + m_vecNodeReport.clear(); } E_CMD_STATUS SessionDataReport::Timeout() { - if (m_mapData.empty()) + LOG4_TRACE(""); + if (m_mapDataCollect.empty()) { return(CMD_STATUS_RUNNING); } @@ -48,7 +63,7 @@ E_CMD_STATUS SessionDataReport::Timeout() LOG4_ERROR("failed to new Report!"); return(CMD_STATUS_FAULT); } - for (auto iter = m_mapData.begin(); iter != m_mapData.end(); ++iter) + for (auto iter = m_mapDataCollect.begin(); iter != m_mapDataCollect.end(); ++iter) { m_pReport->mutable_records()->AddAllocated(iter->second); iter->second = nullptr; @@ -60,17 +75,20 @@ E_CMD_STATUS SessionDataReport::Timeout() oMsgBody.set_data(m_strReport); SendDataReport(CMD_REQ_DATA_REPORT, GetSequence(), oMsgBody); } - m_mapData.clear(); + m_uiNodeReportUpdatingIndex = 1 - m_uiNodeReportUpdatingIndex; + m_vecNodeReport[m_uiNodeReportUpdatingIndex].clear(); + m_mapDataCollect.clear(); return(CMD_STATUS_RUNNING); } -void SessionDataReport::AddReport(const Report& oReport) +void SessionDataReport::AddReport(const std::shared_ptr pReport) { + LOG4_TRACE(""); std::unordered_map::iterator iter; - for (int i = 0; i < oReport.records_size(); ++i) + for (int i = 0; i < pReport->records_size(); ++i) { - iter = m_mapData.find(oReport.records(i).key()); - if (iter == m_mapData.end()) + iter = m_mapDataCollect.find(pReport->records(i).key()); + if (iter == m_mapDataCollect.end()) { ReportRecord* pRecord = nullptr; try @@ -81,29 +99,44 @@ void SessionDataReport::AddReport(const Report& oReport) { return; } - pRecord->set_key(oReport.records(i).key()); - for (int j = 0; j < oReport.records(i).value_size(); ++j) + pRecord->set_key(pReport->records(i).key()); + for (int j = 0; j < pReport->records(i).value_size(); ++j) { - pRecord->add_value(oReport.records(i).value(j)); + pRecord->add_value(pReport.records(i).value(j)); } - m_mapData.insert(std::make_pair(oReport.records(i).key(), pRecord)); + m_mapDataCollect.insert(std::make_pair(oReport.records(i).key(), pRecord)); } else { - for (int j = 0; j < oReport.records(i).value_size(); ++j) + for (int j = 0; j < pReport.records(i).value_size(); ++j) { if (j < iter->second->value_size()) { - iter->second->set_value(j, iter->second->value(j) + oReport.records(i).value(j)); + iter->second->set_value(j, iter->second->value(j) + pReport.records(i).value(j)); } else { - iter->second->add_value(oReport.records(i).value(j)); + iter->second->add_value(pReport.records(i).value(j)); } } } } } +void SendDataReport::AddReport(const std::string& strNodeIdentify, std::shared_ptr pReport) +{ + LOG4_TRACE("report from %s", strNodeIdentify.c_str()); + auto& mapUpdating = m_vecNodeReport[m_uiNodeReportUpdatingIndex]; + auto iter = mapUpdating.find(strNodeIdentify); + if (iter == mapUpdating.end()) + { + mapUpdating.insert(std::make_pair(strNodeIdentify, pReport)); + } + else + { + iter->second = pRecord; + } +} + } diff --git a/src/actor/session/sys_session/SessionDataReport.hpp b/src/actor/session/sys_session/SessionDataReport.hpp index f7426d3b..da8144a5 100644 --- a/src/actor/session/sys_session/SessionDataReport.hpp +++ b/src/actor/session/sys_session/SessionDataReport.hpp @@ -29,7 +29,8 @@ class SessionDataReport: public Timer, virtual E_CMD_STATUS Timeout(); - void AddReport(const Report& oReport); + void AddReport(const std::shared_ptr pReport); + void AddReport(const std::string& strNodeIdentify, std::shared_ptr pReport); const Report* GetReport() { @@ -40,11 +41,18 @@ class SessionDataReport: public Timer, { return(m_strReport); } + + const std::unordered_map>& GetNodeReport() const + { + return(m_vecNodeReport[1 - m_uiNodeReportUpdatingIndex]); + } private: + uint32 m_uiNodeReportUpdatingIndex; Report* m_pReport; ///< final report std::string m_strReport; ///< m_pReport SerializeToString - std::unordered_map m_mapData; ///< report collector + std::unordered_map m_mapDataCollect; ///< report collector + std::vector> > m_vecNodeReport; ///< report data from nebula node }; } diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 6e0bf34c..20ef5829 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -156,7 +156,7 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) } if (0 == oHttpMsg.http_major()) { - LOG4_ERROR("miss http version!"); + LOG4_WARNING("miss http version!"); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } @@ -167,7 +167,7 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { if (oHttpMsg.url().size() == 0) { - LOG4_ERROR("miss url!"); + LOG4_WARNING("miss url!"); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } @@ -208,13 +208,13 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) } else { - LOG4_ERROR("http_parser_parse_url error!"); + LOG4_WARNING("http_parser_parse_url error!"); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } if (stUrl.field_data[UF_PATH].off >= oHttpMsg.url().size()) { - LOG4_ERROR("invalid url \"%s\"!", oHttpMsg.url().c_str()); + LOG4_WARNING("invalid url \"%s\"!", oHttpMsg.url().c_str()); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); } @@ -247,7 +247,7 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { if (0 == oHttpMsg.status_code()) { - LOG4_ERROR("miss status code!"); + LOG4_WARNING("miss status code!"); pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); @@ -318,7 +318,7 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { if (!Gzip(oHttpMsg.body(), strGzipData)) { - LOG4_ERROR("gzip error!"); + LOG4_WARNING("gzip error!"); pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); m_mapAddingHttpHeader.clear(); return(CODEC_STATUS_ERR); @@ -661,7 +661,7 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) { if(m_parser.http_errno != HPE_OK) { - LOG4_ERROR("Failed to parse http message for cause:%s", + LOG4_WARNING("Failed to parse http message for cause:%s", http_errno_name((http_errno)m_parser.http_errno)); return(CODEC_STATUS_ERR); } @@ -700,7 +700,7 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) } else { - LOG4_ERROR("guzip error!"); + LOG4_WARNING("guzip error!"); return(CODEC_STATUS_ERR); } } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index cbbfb023..965e55b2 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -677,6 +677,7 @@ bool Dispatcher::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq RemoveIoWriteEvent(*named_iter->second.begin()); return(true); } + DiscardSocketChannel(*named_iter->second.begin()); return(false); } } @@ -885,6 +886,7 @@ bool Dispatcher::SendTo(const std::string& strHost, int iPort, const std::string RemoveIoWriteEvent(*channel_iter); return(true); } + DiscardSocketChannel(*channel_iter); return(false); } } @@ -1663,17 +1665,37 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b LOG4_DEBUG("pChannel not exist!"); return(false); } - LOG4_DEBUG("%s disconnect, fd %d, channel_seq %u, identify %s", - pChannel->m_pImpl->GetRemoteAddr().c_str(), - pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence(), - pChannel->m_pImpl->GetIdentify().c_str()); - if (bChannelNotice) + + auto named_iter = m_mapNamedSocketChannel.find(pChannel->m_pImpl->GetIdentify()); + if (named_iter != m_mapNamedSocketChannel.end()) { - m_pLabor->GetActorBuilder()->ChannelNotice(pChannel, pChannel->m_pImpl->GetIdentify(), pChannel->m_pImpl->GetClientData()); + for (auto it = named_iter->second.begin(); + it != named_iter->second.end(); ++it) + { + if ((*it)->m_pImpl->GetSequence() == pChannel->m_pImpl->GetSequence()) + { + named_iter->second.erase(it); + LOG4_TRACE("erase channel %d from m_mapNamedSocketChannel.", pChannel->m_pImpl->GetFd()); + break; + } + } + if (0 == named_iter->second.size()) + { + m_mapNamedSocketChannel.erase(named_iter); + } } + bool bCloseResult = pChannel->m_pImpl->Close(); if (bCloseResult) { + LOG4_DEBUG("%s disconnect, fd %d, channel_seq %u, identify %s", + pChannel->m_pImpl->GetRemoteAddr().c_str(), + pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence(), + pChannel->m_pImpl->GetIdentify().c_str()); + if (bChannelNotice) + { + m_pLabor->GetActorBuilder()->ChannelNotice(pChannel, pChannel->m_pImpl->GetIdentify(), pChannel->m_pImpl->GetClientData()); + } ev_io_stop (m_loop, pChannel->m_pImpl->MutableIoWatcher()); if (nullptr != pChannel->m_pImpl->MutableTimerWatcher()) { @@ -1687,25 +1709,6 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); } - auto named_iter = m_mapNamedSocketChannel.find(pChannel->m_pImpl->GetIdentify()); - if (named_iter != m_mapNamedSocketChannel.end()) - { - for (auto it = named_iter->second.begin(); - it != named_iter->second.end(); ++it) - { - if ((*it)->m_pImpl->GetSequence() == pChannel->m_pImpl->GetSequence()) - { - named_iter->second.erase(it); - LOG4_TRACE("erase channel %d from m_mapNamedSocketChannel.", pChannel->m_pImpl->GetFd()); - break; - } - } - if (0 == named_iter->second.size()) - { - m_mapNamedSocketChannel.erase(named_iter); - } - } - auto channel_iter = m_mapSocketChannel.find(pChannel->m_pImpl->GetFd()); if (channel_iter != m_mapSocketChannel.end()) { diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 12f437ed..c3f6eaf9 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -161,6 +161,10 @@ class Dispatcher { return((time_t)ev_now(m_loop)); } + long GetNowTimeMs() const + { + return((long)ev_now(m_loop) * 1000); + } std::shared_ptr CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient = false, bool bWithSsl = false); bool DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice = true); bool CreateListenFd(const std::string& strHost, int32 iPort, int& iFd, int& iFamily); diff --git a/src/labor/Labor.hpp b/src/labor/Labor.hpp index 016181c1..24a4e07f 100644 --- a/src/labor/Labor.hpp +++ b/src/labor/Labor.hpp @@ -55,6 +55,7 @@ class Labor virtual ActorBuilder* GetActorBuilder() = 0; virtual uint32 GetSequence() const = 0; virtual time_t GetNowTime() const = 0; + virtual long GetNowTimeMs() const = 0; virtual const CJsonObject& GetNodeConf() const = 0; virtual void SetNodeConf(const CJsonObject& oNodeConf) = 0; virtual const NodeInfo& GetNodeInfo() const = 0; diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 53b5d32c..14e289ed 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -193,6 +193,11 @@ time_t Manager::GetNowTime() const return(m_pDispatcher->GetNowTime()); } +long Manager::GetNowTimeMs() const +{ + return(m_pDispatcher->GetNowTimeMs()); +} + bool Manager::GetConf() { if (m_stNodeInfo.strWorkPath.length() == 0) diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index 80007674..680b6b65 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -85,6 +85,7 @@ class Manager: public Labor } virtual time_t GetNowTime() const; + virtual long GetNowTimeMs() const; virtual const CJsonObject& GetNodeConf() const; virtual void SetNodeConf(const CJsonObject& oNodeConf); virtual const NodeInfo& GetNodeInfo() const; diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index af6c8b81..c61887ac 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -338,6 +338,11 @@ time_t Worker::GetNowTime() const return(m_pDispatcher->GetNowTime()); } +long Worker::GetNowTimeMs() const +{ + return(m_pDispatcher->GetNowTimeMs()); +} + const CJsonObject& Worker::GetNodeConf() const { return(m_oNodeConf); diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index a360c88a..61f5a520 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -78,6 +78,7 @@ class Worker: public Labor } virtual time_t GetNowTime() const; + virtual long GetNowTimeMs() const; virtual const CJsonObject& GetNodeConf() const; virtual void SetNodeConf(const CJsonObject& oJsonConf); virtual const NodeInfo& GetNodeInfo() const; diff --git a/src/util/json/CJsonObject.cpp b/src/util/json/CJsonObject.cpp index 1a76f468..1f6a2775 100644 --- a/src/util/json/CJsonObject.cpp +++ b/src/util/json/CJsonObject.cpp @@ -14,19 +14,19 @@ namespace neb { CJsonObject::CJsonObject() - : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL), m_pKeyTravers(NULL) { // m_pJsonData = cJSON_CreateObject(); } CJsonObject::CJsonObject(const std::string& strJson) - : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL), m_pKeyTravers(NULL) { Parse(strJson); } CJsonObject::CJsonObject(const CJsonObject* pJsonObject) - : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL), m_pKeyTravers(NULL) { if (pJsonObject) { @@ -35,7 +35,7 @@ CJsonObject::CJsonObject(const CJsonObject* pJsonObject) } CJsonObject::CJsonObject(const CJsonObject& oJsonObject) - : m_pJsonData(NULL), m_pExternJsonDataRef(NULL) + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL), m_pKeyTravers(NULL) { Parse(oJsonObject.ToString()); } @@ -70,6 +70,7 @@ bool CJsonObject::AddEmptySubObject(const std::string& strKey) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -83,6 +84,11 @@ bool CJsonObject::AddEmptySubObject(const std::string& strKey) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_CreateObject(); if (pJsonStruct == NULL) { @@ -90,7 +96,7 @@ bool CJsonObject::AddEmptySubObject(const std::string& strKey) return(false); } cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -108,6 +114,7 @@ bool CJsonObject::AddEmptySubArray(const std::string& strKey) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -121,6 +128,11 @@ bool CJsonObject::AddEmptySubArray(const std::string& strKey) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_CreateArray(); if (pJsonStruct == NULL) { @@ -128,7 +140,7 @@ bool CJsonObject::AddEmptySubArray(const std::string& strKey) return(false); } cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -138,48 +150,50 @@ bool CJsonObject::GetKey(std::string& strKey) { return(false); } - if (m_listKeys.size() == 0) + if (m_pKeyTravers == NULL) { - cJSON* pFocusData = NULL; if (m_pJsonData != NULL) { - pFocusData = m_pJsonData; + m_pKeyTravers = m_pJsonData; } else if (m_pExternJsonDataRef != NULL) { - pFocusData = m_pExternJsonDataRef; + m_pKeyTravers = m_pExternJsonDataRef; } - else + return(false); + } + else if (m_pKeyTravers == m_pJsonData || m_pKeyTravers == m_pExternJsonDataRef) + { + cJSON *c = m_pKeyTravers->child; + if (c) { - return(false); + strKey = c->string; + m_pKeyTravers = c->next; + return(true); } - - cJSON *c = pFocusData->child; - while (c) + else { - m_listKeys.push_back(c->string); - c = c->next; + return(false); } - m_itKey = m_listKeys.begin(); - } - - if (m_itKey == m_listKeys.end()) - { - strKey = ""; - m_itKey = m_listKeys.begin(); - return(false); } else { - strKey = *m_itKey; - ++m_itKey; + strKey = m_pKeyTravers->string; + m_pKeyTravers = m_pKeyTravers->next; return(true); } } void CJsonObject::ResetTraversing() { - m_itKey = m_listKeys.begin(); + if (m_pJsonData != NULL) + { + m_pKeyTravers = m_pJsonData; + } + else + { + m_pKeyTravers = m_pExternJsonDataRef; + } } CJsonObject& CJsonObject::operator[](const std::string& strKey) @@ -292,7 +306,7 @@ std::string CJsonObject::operator()(const std::string& strKey) const char szNumber[128] = {0}; if (pJsonStruct->sign == -1) { - if ((int64)pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) + if (pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) { snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); } @@ -303,7 +317,7 @@ std::string CJsonObject::operator()(const std::string& strKey) const } else { - if (pJsonStruct->valueint <= (uint64)UINT_MAX) + if ((uint64)pJsonStruct->valueint <= (uint64)UINT_MAX) { snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); } @@ -367,7 +381,7 @@ std::string CJsonObject::operator()(unsigned int uiWhich) const char szNumber[128] = {0}; if (pJsonStruct->sign == -1) { - if ((int64)pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) + if (pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) { snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); } @@ -378,7 +392,7 @@ std::string CJsonObject::operator()(unsigned int uiWhich) const } else { - if (pJsonStruct->valueint <= (uint64)UINT_MAX) + if ((uint64)pJsonStruct->valueint <= (uint64)UINT_MAX) { snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); } @@ -416,6 +430,7 @@ bool CJsonObject::Parse(const std::string& strJson) { Clear(); m_pJsonData = cJSON_Parse(strJson.c_str()); + m_pKeyTravers = m_pJsonData; if (m_pJsonData == NULL) { m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); @@ -427,6 +442,7 @@ bool CJsonObject::Parse(const std::string& strJson) void CJsonObject::Clear() { m_pExternJsonDataRef = NULL; + m_pKeyTravers = NULL; if (m_pJsonData != NULL) { cJSON_Delete(m_pJsonData); @@ -452,7 +468,6 @@ void CJsonObject::Clear() } } m_mapJsonObjectRef.clear(); - m_listKeys.clear(); } bool CJsonObject::IsEmpty() const @@ -785,16 +800,11 @@ bool CJsonObject::Get(const std::string& strKey, float& fValue) const { return(false); } - if (pJsonStruct->type == cJSON_Double) + if (pJsonStruct->type == cJSON_Double || pJsonStruct->type == cJSON_Int) { fValue = (float)(pJsonStruct->valuedouble); return(true); } - else if (pJsonStruct->type == cJSON_Int) - { - fValue = (float)(pJsonStruct->valueint); - return(true); - } return(false); } @@ -819,16 +829,11 @@ bool CJsonObject::Get(const std::string& strKey, double& dValue) const { return(false); } - if (pJsonStruct->type == cJSON_Double) + if (pJsonStruct->type == cJSON_Double || pJsonStruct->type == cJSON_Int) { dValue = pJsonStruct->valuedouble; return(true); } - else if (pJsonStruct->type == cJSON_Int) - { - dValue = (double)(pJsonStruct->valueint); - return(true); - } return(false); } @@ -874,6 +879,7 @@ bool CJsonObject::Add(const std::string& strKey, const CJsonObject& oJsonObject) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -887,6 +893,11 @@ bool CJsonObject::Add(const std::string& strKey, const CJsonObject& oJsonObject) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); if (pJsonStruct == NULL) { @@ -908,7 +919,7 @@ bool CJsonObject::Add(const std::string& strKey, const CJsonObject& oJsonObject) } m_mapJsonObjectRef.erase(iter); } - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -926,6 +937,7 @@ bool CJsonObject::Add(const std::string& strKey, const std::string& strValue) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -939,6 +951,11 @@ bool CJsonObject::Add(const std::string& strKey, const std::string& strValue) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); if (pJsonStruct == NULL) { @@ -949,7 +966,7 @@ bool CJsonObject::Add(const std::string& strKey, const std::string& strValue) { return(false); } - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -967,6 +984,7 @@ bool CJsonObject::Add(const std::string& strKey, int32 iValue) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -980,6 +998,11 @@ bool CJsonObject::Add(const std::string& strKey, int32 iValue) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); if (pJsonStruct == NULL) { @@ -990,7 +1013,7 @@ bool CJsonObject::Add(const std::string& strKey, int32 iValue) { return(false); } - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -1008,6 +1031,7 @@ bool CJsonObject::Add(const std::string& strKey, uint32 uiValue) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -1021,6 +1045,11 @@ bool CJsonObject::Add(const std::string& strKey, uint32 uiValue) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); if (pJsonStruct == NULL) { @@ -1031,7 +1060,7 @@ bool CJsonObject::Add(const std::string& strKey, uint32 uiValue) { return(false); } - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -1049,6 +1078,7 @@ bool CJsonObject::Add(const std::string& strKey, int64 llValue) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -1062,6 +1092,11 @@ bool CJsonObject::Add(const std::string& strKey, int64 llValue) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); if (pJsonStruct == NULL) { @@ -1072,7 +1107,7 @@ bool CJsonObject::Add(const std::string& strKey, int64 llValue) { return(false); } - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -1090,6 +1125,7 @@ bool CJsonObject::Add(const std::string& strKey, uint64 ullValue) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -1103,6 +1139,11 @@ bool CJsonObject::Add(const std::string& strKey, uint64 ullValue) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_CreateInt(ullValue, 1); if (pJsonStruct == NULL) { @@ -1113,7 +1154,7 @@ bool CJsonObject::Add(const std::string& strKey, uint64 ullValue) { return(false); } - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -1131,6 +1172,7 @@ bool CJsonObject::Add(const std::string& strKey, bool bValue, bool bValueAgain) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -1144,6 +1186,11 @@ bool CJsonObject::Add(const std::string& strKey, bool bValue, bool bValueAgain) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_CreateBool(bValue); if (pJsonStruct == NULL) { @@ -1154,7 +1201,7 @@ bool CJsonObject::Add(const std::string& strKey, bool bValue, bool bValueAgain) { return(false); } - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -1172,6 +1219,7 @@ bool CJsonObject::Add(const std::string& strKey, float fValue) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -1185,6 +1233,11 @@ bool CJsonObject::Add(const std::string& strKey, float fValue) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); if (pJsonStruct == NULL) { @@ -1195,7 +1248,7 @@ bool CJsonObject::Add(const std::string& strKey, float fValue) { return(false); } - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -1213,6 +1266,7 @@ bool CJsonObject::Add(const std::string& strKey, double dValue) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -1226,6 +1280,11 @@ bool CJsonObject::Add(const std::string& strKey, double dValue) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); if (pJsonStruct == NULL) { @@ -1236,7 +1295,7 @@ bool CJsonObject::Add(const std::string& strKey, double dValue) { return(false); } - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -1254,6 +1313,7 @@ bool CJsonObject::AddNull(const std::string& strKey) else { m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; pFocusData = m_pJsonData; } @@ -1267,6 +1327,11 @@ bool CJsonObject::AddNull(const std::string& strKey) m_strErrMsg = "not a json object! json array?"; return(false); } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } cJSON* pJsonStruct = cJSON_CreateNull(); if (pJsonStruct == NULL) { @@ -1277,7 +1342,7 @@ bool CJsonObject::AddNull(const std::string& strKey) { return(false); } - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -1313,7 +1378,7 @@ bool CJsonObject::Delete(const std::string& strKey) } m_mapJsonObjectRef.erase(iter); } - m_listKeys.clear(); + m_pKeyTravers = pFocusData; return(true); } @@ -2026,16 +2091,11 @@ bool CJsonObject::Get(int iWhich, float& fValue) const { return(false); } - if (pJsonStruct->type == cJSON_Double) + if (pJsonStruct->type == cJSON_Double || pJsonStruct->type == cJSON_Int) { fValue = (float)(pJsonStruct->valuedouble); return(true); } - else if (pJsonStruct->type == cJSON_Int) - { - fValue = (float)(pJsonStruct->valueint); - return(true); - } return(false); } @@ -2060,16 +2120,11 @@ bool CJsonObject::Get(int iWhich, double& dValue) const { return(false); } - if (pJsonStruct->type == cJSON_Double) + if (pJsonStruct->type == cJSON_Double || pJsonStruct->type == cJSON_Int) { dValue = pJsonStruct->valuedouble; return(true); } - else if (pJsonStruct->type == cJSON_Int) - { - dValue = (double)(pJsonStruct->valueint); - return(true); - } return(false); } @@ -3455,7 +3510,7 @@ bool CJsonObject::ReplaceWithNull(int iWhich) } CJsonObject::CJsonObject(cJSON* pJsonData) - : m_pJsonData(NULL), m_pExternJsonDataRef(pJsonData) + : m_pJsonData(NULL), m_pExternJsonDataRef(pJsonData), m_pKeyTravers(pJsonData) { } diff --git a/src/util/json/CJsonObject.hpp b/src/util/json/CJsonObject.hpp index bb25b0e4..164fbaae 100644 --- a/src/util/json/CJsonObject.hpp +++ b/src/util/json/CJsonObject.hpp @@ -145,11 +145,10 @@ class CJsonObject private: cJSON* m_pJsonData; cJSON* m_pExternJsonDataRef; + cJSON* m_pKeyTravers; std::string m_strErrMsg; std::map m_mapJsonArrayRef; std::map m_mapJsonObjectRef; - std::list m_listKeys; - std::list::const_iterator m_itKey; }; } diff --git a/src/util/json/cJSON.c b/src/util/json/cJSON.c index 840e8902..be7f0fa2 100644 --- a/src/util/json/cJSON.c +++ b/src/util/json/cJSON.c @@ -150,14 +150,14 @@ static const char *parse_number(cJSON *item, const char *num) if (scale == 0 && subscale == 0) { item->valuedouble = (double)(item->sign * n); - item->valueint = (uint64)(item->sign * (uint64)n); + item->valueint = item->sign * (int64)n; item->type = cJSON_Int; } else { n = item->sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ item->valuedouble = (double)n; - item->valueint = (uint64)n; + item->valueint = (int64)n; item->type = cJSON_Double; } return num; @@ -985,7 +985,7 @@ cJSON *cJSON_CreateDouble(double num, int sign) { item->type = cJSON_Double; item->valuedouble = num; - item->valueint = (uint64)num; + item->valueint = (int64)num; item->sign = sign; } return item; @@ -997,7 +997,7 @@ cJSON *cJSON_CreateInt(uint64 num, int sign) { item->type = cJSON_Int; item->valuedouble = (double)num; - item->valueint = (uint64)num; + item->valueint = (int64)num; item->sign = sign; } return item; diff --git a/src/util/json/cJSON.h b/src/util/json/cJSON.h index 116a7cc5..f6ea981b 100644 --- a/src/util/json/cJSON.h +++ b/src/util/json/cJSON.h @@ -55,7 +55,7 @@ typedef struct cJSON int type; /* The type of the item, as above. */ char *valuestring; /* The item's string, if type==cJSON_String */ - uint64 valueint; /* The item's number, if type==cJSON_Number */ + int64 valueint; /* The item's number, if type==cJSON_Number */ double valuedouble; /* The item's number, if type==cJSON_Number */ int sign; /* sign of valueint, 1(unsigned), -1(signed) */ From 872469783609900e430865cd84811cb6d8b19779 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 28 Dec 2019 21:49:29 +0800 Subject: [PATCH 083/176] update CJsonObject and named socket channel bug fixed. --- src/actor/cmd/sys_cmd/CmdDataReport.cpp | 2 +- .../session/sys_session/SessionDataReport.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/actor/cmd/sys_cmd/CmdDataReport.cpp b/src/actor/cmd/sys_cmd/CmdDataReport.cpp index 28d5ef5f..f807a783 100644 --- a/src/actor/cmd/sys_cmd/CmdDataReport.cpp +++ b/src/actor/cmd/sys_cmd/CmdDataReport.cpp @@ -67,7 +67,7 @@ bool CmdDataReport::AnyMessage( } pSessionDataReport = std::dynamic_pointer_cast(pSharedSession); } - LOG4_TRACE("data report from %s", pChanel->GetIdentify().c_str()); + LOG4_TRACE("data report from %s", pChannel->GetIdentify().c_str()); pSessionDataReport->AddReport(pReport); if (GetNodeType() == "BEACON") { diff --git a/src/actor/session/sys_session/SessionDataReport.cpp b/src/actor/session/sys_session/SessionDataReport.cpp index 916b45b4..e658cf37 100644 --- a/src/actor/session/sys_session/SessionDataReport.cpp +++ b/src/actor/session/sys_session/SessionDataReport.cpp @@ -102,28 +102,28 @@ void SessionDataReport::AddReport(const std::shared_ptr pReport) pRecord->set_key(pReport->records(i).key()); for (int j = 0; j < pReport->records(i).value_size(); ++j) { - pRecord->add_value(pReport.records(i).value(j)); + pRecord->add_value(pReport->records(i).value(j)); } - m_mapDataCollect.insert(std::make_pair(oReport.records(i).key(), pRecord)); + m_mapDataCollect.insert(std::make_pair(pReport->records(i).key(), pRecord)); } else { - for (int j = 0; j < pReport.records(i).value_size(); ++j) + for (int j = 0; j < pReport->records(i).value_size(); ++j) { if (j < iter->second->value_size()) { - iter->second->set_value(j, iter->second->value(j) + pReport.records(i).value(j)); + iter->second->set_value(j, iter->second->value(j) + pReport->records(i).value(j)); } else { - iter->second->add_value(pReport.records(i).value(j)); + iter->second->add_value(pReport->records(i).value(j)); } } } } } -void SendDataReport::AddReport(const std::string& strNodeIdentify, std::shared_ptr pReport) +void SessionDataReport::AddReport(const std::string& strNodeIdentify, std::shared_ptr pReport) { LOG4_TRACE("report from %s", strNodeIdentify.c_str()); auto& mapUpdating = m_vecNodeReport[m_uiNodeReportUpdatingIndex]; @@ -134,7 +134,7 @@ void SendDataReport::AddReport(const std::string& strNodeIdentify, std::shared_p } else { - iter->second = pRecord; + iter->second = pReport; } } From 70a7baf24d17ae59a0f2788af45887fc2b1092df Mon Sep 17 00:00:00 2001 From: wincsb Date: Tue, 31 Dec 2019 10:50:29 +0800 Subject: [PATCH 084/176] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E5=BA=93=E7=89=88=E6=9C=AC=E6=9B=B4=E6=96=B0=E4=B8=8D=E7=94=9F?= =?UTF-8?q?=E6=95=88=E9=97=AE=E9=A2=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Makefile | 2 +- src/actor/ActorFactory.hpp | 16 ++++++++++++++++ src/actor/DynamicCreator.hpp | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 175d4fc4..5932dfd3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,7 +2,7 @@ CC = gcc CXX = g++ CFLAGS = -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic -D__GUNC__ -fPIC cplusplus_version=$(shell g++ -dumpversion | awk '{if ($$NF > 5.0) print "c++14"; else print "c++11";}') -CXXFLAG = -std=$(cplusplus_version) -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic -D_GNU_SOURCE=1 -D_REENTRANT -D__GUNC__ -fPIC -DNODE_BEAT=10.0 +CXXFLAG = -std=$(cplusplus_version) -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic --no-gnu-unique -D_GNU_SOURCE=1 -D_REENTRANT -D__GUNC__ -fPIC -DNODE_BEAT=10.0 ifeq ($(unit_test),y) CXXFLAG += -DUNIT_TEST diff --git a/src/actor/ActorFactory.hpp b/src/actor/ActorFactory.hpp index 5f9d6a2c..3e64076a 100644 --- a/src/actor/ActorFactory.hpp +++ b/src/actor/ActorFactory.hpp @@ -35,6 +35,7 @@ class ActorFactory virtual ~ActorFactory(){}; bool Regist(const std::string& strTypeName, std::function pFunc); + bool UnRegist(const std::string& strTypeName); Actor* Create(const std::string& strTypeName, Targs&&... args); private: @@ -59,6 +60,21 @@ bool ActorFactory::Regist(const std::string& strTypeName, std::functio return (bReg); } +template +bool ActorFactory::UnRegist(const std::string& strTypeName) +{ + auto iter = m_mapCreateFunction.find(strTypeName); + if (iter == m_mapCreateFunction.end()) + { + return (false); + } + else + { + m_mapCreateFunction.erase(iter); + return (true); + } +} + template Actor* ActorFactory::Create(const std::string& strTypeName, Targs&&... args) { diff --git a/src/actor/DynamicCreator.hpp b/src/actor/DynamicCreator.hpp index 69b77b4b..3e04e3b6 100644 --- a/src/actor/DynamicCreator.hpp +++ b/src/actor/DynamicCreator.hpp @@ -40,6 +40,23 @@ class DynamicCreator } ActorFactory::Instance()->Regist(strTypeName, CreateObject); } + ~Register() + { + char* szDemangleName = NULL; + std::string strTypeName; +#ifdef __GNUC__ + szDemangleName = abi::__cxa_demangle(typeid(T).name(), NULL, NULL, NULL); +#else + szDemangleName = abi::__cxa_demangle(typeid(T).name(), NULL, NULL, NULL); +#endif + if (NULL != szDemangleName) + { + strTypeName = szDemangleName; + free(szDemangleName); + } + ActorFactory::Instance()->UnRegist(strTypeName); + } + inline void do_nothing()const { }; }; From 2bbb2d7a0a74b1581e1d64fd663c152e786e7a79 Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 1 Jan 2020 19:43:59 +0800 Subject: [PATCH 085/176] CMD_REQ_SET_CUSTOM_CONFIG no need in worker --- src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp index c0f6a7f7..13f4bcfb 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp @@ -62,7 +62,6 @@ bool CmdOnSetCustomConf::AnyMessage( fout.close(); oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); oOutMsgBody.mutable_rsp_result()->set_msg("success"); - m_pSessionManager->SendToChild(CMD_REQ_SET_CUSTOM_CONFIG, GetSequence(), oInMsgBody); m_pSessionManager->SendToChild(CMD_REQ_RELOAD_CUSTOM_CONFIG, GetSequence(), oInMsgBody); SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); return(true); From 697624a980f6cb155ff7ff372507f074c54db2ba Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 4 Jan 2020 20:19:59 +0800 Subject: [PATCH 086/176] add http header passthrough to GET, and add version 1.0 description. --- README.md | 7 ++++ README_cn.md | 9 ++++- conf/nebula.json | 2 ++ docs/cn/configuration.md | 68 ++++++++++++++++--------------------- src/actor/step/HttpStep.cpp | 30 ++++++++++++++++ src/actor/step/HttpStep.hpp | 4 +++ src/codec/CodecHttp.cpp | 4 +++ 7 files changed, 84 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 070e30c3..0c25cbe7 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,13 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v1.0 + - separate the network dispatch and actor management from Labor (Manager and Worker) to Dispatcher and ActorBuilder + - add supports of Actor classes to Manager process, and move the manager system management features to Cmd and Step + - add Loader + - optimize Actor dynamic creator + - optimize HTTP connection and HTTP data sending and receiving + - RedisChannel bug fixed #### v0.10 - the plugin version control dynamically unloads and loads the instant validation feature. - optimize reflection dynamic creation of Actor. diff --git a/README_cn.md b/README_cn.md index cc797962..dd279bd8 100644 --- a/README_cn.md +++ b/README_cn.md @@ -58,7 +58,7 @@ ## 开始 -  Nebula是个较大型项目,也是一个你难得一见的依赖很少的项目,并且提供了一键安装脚本,[NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap),让开发者可以快速部署和体验Nebula。相信部署和体验之后,你会对Nebula产生兴趣,这将会是一个可以广泛应用的框架,基于NebulaBootstrap提供的分布式解决方案可以很方便地用C++开发微服务应用。 +  Nebula是个较完备的项目,也是一个你难得一见的依赖很少的项目,提供了许多开箱即用的功能,并提供了一键安装脚本,[NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap),让开发者可以快速部署和体验Nebula。相信部署和体验之后,你会对Nebula产生兴趣,这将会是一个可以广泛应用的框架,基于NebulaBootstrap提供的分布式解决方案可以很方便地用C++开发微服务应用。Nebula的相关项目就是学习和使用Nebula框架最好的例子。 * [高并发单机Server示例](docs/cn/nebula_server_demo.md) * [分布式服务示例](docs/cn/nebula_distributed_demo.md) @@ -114,6 +114,13 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 +#### v1.0 + - 从Manager和Worker类中分离出网络分发功能类Dispatcher和Actor创建及管理类ActorBuilder + - Manager进程支持Actor类的使用,将Manager系统管理功能分离到Cmd类和Step类中 + - 增加Loader进程类型 + - 优化Actor通过反射动态创建实例 + - 优化http短连接和http数据收发 + - RedisChannel bug修复 #### v0.10 - 增加插件so版本控制动态卸载和加载即时生效功能 - 优化反射动态创建Actor diff --git a/conf/nebula.json b/conf/nebula.json index e9d59284..cc508ffc 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -58,6 +58,8 @@ "cert_file": "20180623143147.pem", "key_file": "20180623143147.key" }, + "//data_report": "数据上报时间间隔,无统计数据时不上报", + "data_report": 60, "//refresh_interval": "刷新Server配置,检查、加载插件动态库时间周期", "refresh_interval": 60, "load_config":{ diff --git a/docs/cn/configuration.md b/docs/cn/configuration.md index 11a4aeda..b65d768e 100644 --- a/docs/cn/configuration.md +++ b/docs/cn/configuration.md @@ -8,6 +8,10 @@ { "//node_type": "节点类型:ACCESS,LOGIC,PROXY,CENTER等,由业务层定义", "node_type": "ACCESS", + "//host": "系统内各Server之间通信绑定的IP(Server to Server)", + "host": "192.168.18.81", + "//port": "系统内各Server之间通信监听的端口", + "port": 9987, "//access_host": "对系统外提供服务绑定的IP(Client to Server),若不提供对外服务,则无需配置", "access_host": "192.168.18.81", "//access_port": "对系统外提供服务监听的端口", @@ -16,33 +20,25 @@ "access_codec": 4, "gateway": "113.102.157.188", "gateway_port": 9988, - "//host": "系统内各Server之间通信绑定的IP(Server to Server)", - "host": "192.168.18.81", - "//port": "系统内各Server之间通信监听的端口", - "port": 9987, "//server_name": "异步事件驱动Server", "server_name": "AsyncServer", "//worker_num": "进程数量", "worker_num": 10, - "//with_loader":"是否启动loader进程", - "with_loader":false, - "//cpu_affinity":"是否设置进程CPU亲和度(绑定CPU)", - "cpu_affinity":false, "//worker_capacity": "子进程最大工作负荷", "worker_capacity": 1000000, + "//cpu_affinity":"是否设置进程CPU亲和度(绑定CPU)", + "cpu_affinity":false, "//config_path": "配置文件路径(相对路径)", "config_path": "conf/", "//log_path": "日志文件路径(相对路径)", "log_path": "log/", - "//beacon": "控制中心", - "beacon": [ - { "host": "192.168.1.11", "port": 16000 }, - { "host": "192.168.1.12", "port": 16000 } - ], "//max_log_file_num": "最大日志文件数量,用于日志文件滚动", "max_log_file_num": 5, "//max_log_file_size": "单个日志文件大小限制", "max_log_file_size": 20480000, + "log_levels": { "FATAL": 0, "CRITICAL": 1, "ERROR": 2, "NOTICE": 3, "WARNING": 4, "INFO": 5, "DEBUG": 6, "TRACE": 7 }, + "log_level": 7, + "net_log_level": 6, "//permission": "限制。addr_permit为连接限制,限制每个IP在统计时间内连接次数;uin_permit为消息数量限制,限制每个用户在单位统计时间内发送消息数量。", "permission": { "addr_permit": { "stat_interval": 60.0, "permit_num": 1000000000 }, @@ -52,6 +48,10 @@ "io_timeout": 300.0, "//step_timeout": "步骤超时设置(单位:秒)小数点后面至少保留一位", "step_timeout": 1.5, + "boot_load": { + "cmd": [ + "//step_timeout": "步骤超时设置(单位:秒)小数点后面至少保留一位", + "step_timeout": 1.5, "log_levels": { "FATAL": 0, "CRITICAL": 1, "ERROR": 2, "NOTICE": 3, "WARNING": 4, "INFO": 5, "DEBUG": 6, "TRACE": 7 }, "log_level": 7, "net_log_level": 6, @@ -61,6 +61,8 @@ "cert_file": "20180623143147.pem", "key_file": "20180623143147.key" }, + "//data_report": "数据上报时间间隔,无统计数据时不上报", + "data_report": 60, "//refresh_interval": "刷新Server配置,检查、加载插件动态库时间周期(周期时间长短视服务器忙闲而定)", "refresh_interval": 60, "load_config":{ @@ -116,18 +118,6 @@ } }, "loader":{ - } - }, - "//custom": "自定义配置,用于通过框架层带给业务", - "custom": {} -} -``` - -  配置文件自带了比较详细的注释,因json自身不支持注释,所以注释都是在需注释的key之前增加一个以json_key前加上“//”表示的key。配置文件里的必选配置项和可选配置项之间差异比较模糊,因为配置项本就很少,理解每项配置也很容易,给出必选可选提示反而会让人偷懒。如果不想先理解配置那么麻烦,想先以最快速度搭建服务体验一下,也没问题,[NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap)提供了一键部署,可以很负责任地说Nebula绝对是极为少有依赖那么少部署那么容易的开源项目。配置文件模板所列的所有配置项都会在框架启动时自动读取,如果某个Server不需要这项配置,删掉并不会有任何副作用,如果是必需的配置项,Server启动时就会报错。 - -  配置文件大体可以分为Server参数配置、控制中心配置、动态加载配置、运行时配置和自定义配置五部分。日志级别和动态加载配置、运行时配置、自定义配置都支持运行时修改,一个刷新周期refresh_interval后生效,如果是通过Beacon配置中心修改则修改成功后立即生效。 - -#### Server参数配置 * node_type 节点类型,由业务层定义,框架把节点类型当字符串处理,用于标识若干(数量不限)服务节点,节点类型字符串建议全大写字母。在Beacon配置节点订阅关系就是通过此项节点类型配置来实现。比如,有3个Server的节点类型配置为“ACCESS”,另有6个Server的节点类型配置为“LOGIC”,在Beacon配置了ACCESS类型的节点订阅LOGIC类型节点信息,那么当有任意LOGIC类型上线、下线时,所有ACCESS类型的节点都会收到通知,ACCESS收到通知后会自动更新内存里存储的LOGIC节点信息。 * access_host 对系统外提供接入服务绑定的IP(Client to Server),若当前节点不提供对外接入服务,则可以直接删除这项配置。 * access_port 对系统外提供接入服务监听的端口,若当前节点不提供对外接入服务,则可以直接删除这项配置。 @@ -144,6 +134,19 @@ * config_path 配置文件存储路径,相对于NEBULA_HOME的路径。 * log_path 日志文件存储路径,相对于NEBULA_HOME的路径。 * max_log_file_num 最大日志文件数量,用于日志文件滚动,超出这个数量的日志文件将会被直接删除。 +* access_codec 对外接入服务的编解码器。目前支持CODEC_PRIVATE(4),CODEC_HTTP(3),CODEC_PROTOBUF(2),且只能配置一种编解码器,后续有可能在同一个端口提供多种协议编解码服务。若当前节点不提供对外接入服务,则可以直接删除这项配置。 +* gateway 网关服务地址,用于客户端路由和负载均衡。gateway和gateway_port提供的是预先通过请求某个接口获取接入服务地址的负载均衡方式,因每个节点的负载都可以从Beacon获取到,客户端发业务请求前先通过一个固定接口查询需将正式请求发往哪个(些)接入节点,得到接入节点地址后再发业务请求。为什么不直接返回access_host、access_port?因为这两个是节点实际绑定的地址和监听的端口,外部不一定能直接访问(很可能也是内网IP和端口),比如需要通过交换机或防火墙,而交换机或防火墙是通过端口映射的方式到access_host、access_port的,那么客户端需要获取的是映射前暴露在外网的地址和端口,此时gateway和gateway_port的作用就体现出来了。假设接入服务前端还有一层类似LVS的外部负载均衡服务,只需把access_host、access_port配置到负载均衡服务即可,gateway和gateway_port不会用到。如果access_host和access_port是外部客户端可以直接访问的,则gateway配置的地址与access_host相同,gateway_port与access_port相同。 +* gateway_port 网关服务端口。 +* host 集群内部通信地址。 +* port 集群内部通信端口。host:port组成的字符串在集群内唯一标识一个节点,用作管理层面(与Beacon通信)名字服务的节点名字。host:port.workerindex组成的字符串在集群内唯一标识一个节点的一个Worker进程,用作数据层面(也即业务层面,除Beacon之外的节点间通信)名字服务的节点Worker名字。 +* server_name 节点Server进程名,方便在服务器中标识和管理,在集群管理和节点通信中都不会用到。通俗一点讲,这是给人用的不是给机器用的。 +* worker_num Worker进程数量,每个节点由一个Manager进程和若干个Worker进程构成。通常,如果某台机器只部署了一个Nebula服务并且主要是给这个服务使用的,为了更充分使用机器资源,将worker_num配置成与cpu核数相同。 +* with_loader 是否启动loader进程。Loader进程用于做本地数据存储,大部分IO密集型的应用不会用到,所以默认不会启动Loader进程。 +* worker_capacity 进程容量,用于过载保护。进程负载 = Channel数量 * 系数 + Step数量 * 系数。这个计算公式会根据需要和合理性做调整。当进程负载达到进程容量限制时会拒绝新的连接。 +* cpu_affinity CPU亲和度,为true时,Worker进程会均匀地绑定到CPU核。默认为false,不绑定。 +* config_path 配置文件存储路径,相对于NEBULA_HOME的路径。 +* log_path 日志文件存储路径,相对于NEBULA_HOME的路径。 +* max_log_file_num 最大日志文件数量,用于日志文件滚动,超出这个数量的日志文件将会被直接删除。 * max_log_file_size 单个日志文件大小限制,用于日志文件滚动,超出这个限制将滚动日志,生成一个新的日志文件。 * log_levels 日志级别枚举,仅用于给log_level和net_log_level配置时参考。 * log_level 本地文件日志级别。框架级别的调试日志用的是LOG4_TRACE(日志量很大,部署生产时请不要打开TRACE级别日志),建议业务的调试日志用LOG4_DEBUG。 @@ -152,6 +155,7 @@ * io_timeout 网络IO(连接)超时设置(单位:秒)小数点后面至少保留一位,用于触发连接的有效性检查。如果在到达超时时间前连接有数据收或发过,则从最后一次收发数据时间开始重新计算超时时间;如果超时时间到达却一直没有数据收发过,有三种处理情况:(1) 需要做应用层心跳检查,自动发送心跳包,心跳包得到响应,连接得以保持,重新计算超时时间;(2) 需要做应用层心跳检查,自动发送心跳包,心跳包未得到响应,立即断开连接,回收连接所分配资源;(3) 无须做应用层心跳检查,立即断开连接,回收连接所分配资源。 * step_timeout 步骤超时设置(单位:秒)小数点后面至少保留一位,用于请求发出后等待响应的默认超时等待。在代码层可以为每一个发出的请求设置等待超时,通常为Step类的最后一个参数,如果这个参数为缺省值,则配置文件里的step_timeout会作为这个Step发出请求后等待响应的超时时间。 * with_ssl 配置接入服务的对外连接是否需要SSL传输加密,如果需要则配置ssl配置文件路径和相关文件名。 +* data_report 数据上报时间间隔,无统计数据时不上报。 * refresh_interval 刷新Server配置,检查、加载配置或加载卸载插件动态库时间周期。 * load_config 功能模块加载。功能模块分manager、worker、loader进程的功能模块,分别加载到对应类型的进程里。 * boot_load 服务启动加载的处理模块入口。Nebula框架会被编译成动态库 libnebula.so,基于Nebula框架的Server需要写个main函数并编译链接成一个二进制文件,比如NebulaBeacon,还有一些不需要做动态加载的模块会一起编译链接到这个二进制文件里,而这些模块是Nebula框架未知的(libnebula.so早于任何一个server二进制文件存在),boot_load就是为这些模块加载使用的。boot_load里的配置说明见下文描述动态加载配置说明。 @@ -196,20 +200,6 @@ * model Model类名数组,配置该插件内所有Model类名,类名需带上名字空间。作用同上。 #### 运行时配置 -  运行时配置是为了提供随时调整服务的功能,在许多业务场景里边不会用到。当前的运行时配置只有调用链配置。 - -```json -"runtime":{ - "chains":{ - "chain_1":["step1", "matrix1", ["step2A", "step2B", "step2C"], "step3", "matrix2"], - "chain_2":[] - } - } -``` - -* chains 调用链(调用链的概念和使用方法详见Actor组件说明),调用链的配置是一个类似于数组的配置,可以理解为chains里存储的是一个map,map的key为一个调用链标识,map的value为调用链的链块配置。“chain_1”、“chain_2”只是用于标识一个调用链,名字由开发者自行定义,无特殊含义也无任何限制。chain map的value部分配置的是调用链的实际调用顺序,这是一个一维或二维的json数组,每个数组元素只能是Step类或Model类,第一维的元素是串行关系,第二维的元素是并行关系,并行关系的元素只能是Step类。如示例“chain_1”所示,调用链首先调用step1,在step1完成后调用matrix1,matrix1完成后同时调用step2A、step2B、step2C,在这三个Step都完成后才调用step3,step3完成后整个调用链完成。如果调用链只有串行关系,只配置一维即可。 - -#### 自定义配置   自定义配置是业务层所需的配置,通常会独立定义配置文件存放在conf路径下。这里的自定义配置指的不是那种业务独立定义的配置文件,而是以“custom”为key的json嵌入到Nebula框架配置文件中的自定义配置。这样配置有什么好处?因为所有的业务代码类都是Actor的派生类,把自定义配置嵌入到框架的配置文件中,只需调用GetCustomConf()方法即可获得配置内容,这个方法是Actor类的成员方法,读取配置就跟访问类自己的成员一样随时随地随心所欲。虽然把自定义配置嵌入到Nebula框架配置文件里非常方便,但也不要滥用,如果什么配置都往这里放会使得框架配置文件变得非常大,可读性和可维护性都变差。建议把许多业务功能模块都会用到或使用频率非常高,配置量不是太大的配置嵌入到框架配置文件里,而只有少数模块会用到,又或使用频率低,或配置内容非常庞大的应独立成其他自定义配置文件。非json格式的配置也应独立成自定义配置文件。 ```json diff --git a/src/actor/step/HttpStep.cpp b/src/actor/step/HttpStep.cpp index 8e419ba3..a27a94b0 100644 --- a/src/actor/step/HttpStep.cpp +++ b/src/actor/step/HttpStep.cpp @@ -33,6 +33,36 @@ bool HttpStep::HttpGet(const std::string& strUrl) return(HttpRequest(oHttpMsg)); } +bool HttpStep::HttpGet(const std::string& strUrl, const std::unordered_map& mapHeaders) +{ + HttpMsg oHttpMsg; + oHttpMsg.set_http_major(1); + oHttpMsg.set_http_minor(1); + oHttpMsg.set_type(HTTP_REQUEST); + oHttpMsg.set_method(HTTP_GET); + oHttpMsg.set_url(strUrl); + for (auto c_iter = mapHeaders.begin(); c_iter != mapHeaders.end(); ++c_iter) + { + oHttpMsg.mutable_headers()->insert(google::protobuf::MapPair(c_iter->first, c_iter->second)); + } + return(HttpRequest(oHttpMsg)); +} + +bool HttpStep::HttpGet(const std::string& strUrl, const ::google::protobuf::Map& mapHeaders) +{ + HttpMsg oHttpMsg; + oHttpMsg.set_http_major(1); + oHttpMsg.set_http_minor(1); + oHttpMsg.set_type(HTTP_REQUEST); + oHttpMsg.set_method(HTTP_GET); + oHttpMsg.set_url(strUrl); + for (auto c_iter = mapHeaders.begin(); c_iter != mapHeaders.end(); ++c_iter) + { + oHttpMsg.mutable_headers()->insert(google::protobuf::MapPair(c_iter->first, c_iter->second)); + } + return(HttpRequest(oHttpMsg)); +} + bool HttpStep::HttpPost(const std::string& strUrl, const std::string& strBody, const std::unordered_map& mapHeaders) { HttpMsg oHttpMsg; diff --git a/src/actor/step/HttpStep.hpp b/src/actor/step/HttpStep.hpp index db4d2ec6..5cfa4046 100644 --- a/src/actor/step/HttpStep.hpp +++ b/src/actor/step/HttpStep.hpp @@ -31,6 +31,10 @@ class HttpStep: public Step void* data = NULL) = 0; bool HttpGet(const std::string& strUrl); + bool HttpGet(const std::string& strUrl, + const std::unordered_map& mapHeaders); + bool HttpGet(const std::string& strUrl, + const ::google::protobuf::Map& mapHeaders); bool HttpPost(const std::string& strUrl, const std::string& strBody, const std::unordered_map& mapHeaders); bool HttpPost(const std::string& strUrl, const std::string& strBody, diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 20ef5829..9909b1b4 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -283,6 +283,10 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) bool bIsGzip = false; // 是否用gizp压缩传输包 for (auto h_iter = oHttpMsg.headers().begin(); h_iter != oHttpMsg.headers().end(); ++h_iter) { + if (h_iter->first == "Content-Length" || h_iter->first == "Host") + { + continue; + } auto h_a_iter = m_mapAddingHttpHeader.find(h_iter->first); if (h_a_iter == m_mapAddingHttpHeader.end()) { From 74e190b596fc1789259044407ec094b2daa682f3 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 16 Feb 2020 12:34:48 +0800 Subject: [PATCH 087/176] add bind ip, add load stress, channel timeout adjust. --- docs/cn/why_nebula.md | 3 +- src/actor/Actor.cpp | 16 +- src/actor/Actor.hpp | 12 +- src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp | 1 + src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp | 2 +- src/channel/SocketChannelImpl.cpp | 41 +++-- src/channel/SocketChannelImpl.hpp | 2 +- src/ios/Dispatcher.cpp | 169 ++++++++++++++++---- src/ios/Dispatcher.hpp | 21 ++- src/ios/Nodes.cpp | 24 +++ src/ios/Nodes.hpp | 3 + src/labor/Labor.hpp | 4 + src/labor/Manager.cpp | 47 ++++-- src/labor/NodeInfo.hpp | 8 +- src/labor/Worker.cpp | 2 +- src/labor/Worker.hpp | 2 +- 16 files changed, 278 insertions(+), 79 deletions(-) diff --git a/docs/cn/why_nebula.md b/docs/cn/why_nebula.md index 9235a91a..795e8720 100644 --- a/docs/cn/why_nebula.md +++ b/docs/cn/why_nebula.md @@ -57,5 +57,6 @@   分布式系统在业务需求的功能以外,还需要增加额外很多非功能的需求。这些非功能需求,往往都是为了系统能稳定可靠运行而去设计和实现的,一般都会让你的代码更加复杂,也会耗费大量时间,用Nebula将为你解决业务需求之外的功能。 - +### Nebula生产应用情况 +  Nebula的绝大部分稳定代码和功能于2019年1月到2019年6月之间开发完成,在2018年11月起有Bwar自己开发的一个实时数据采集和分析项目运行于生产环境,2019年底又有一个网关应用(类似基于nginx的openresty,基于Nebula的网关功能定制十分方便,也是用C++,写起来跟Nebula原生功能无区别)在多个千万级DAU的业务生产环境上运行。 diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 73c30478..623f5c9c 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -138,9 +138,9 @@ bool Actor::SendTo(std::shared_ptr pChannel, const HttpMsg& oHttp return(m_pLabor->GetDispatcher()->SendTo(pChannel, oHttpMsg)); } -bool Actor::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +bool Actor::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { - return(m_pLabor->GetDispatcher()->SendTo(strIdentify, iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendTo(strIdentify, iCmd, uiSeq, oMsgBody, eCodecType, this)); } bool Actor::SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg) @@ -168,19 +168,19 @@ bool Actor::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) return(m_pLabor->GetDispatcher()->SendTo(iCmd, uiSeq, oMsgBody, this)); } -bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { - return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType, this)); } -bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { - return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, uiFactor, iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, uiFactor, iCmd, uiSeq, oMsgBody, eCodecType, this)); } -bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { - return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType, this)); } bool Actor::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index de5b069c..a6e2a122 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -29,6 +29,7 @@ #include "util/json/CJsonObject.hpp" #include "channel/Channel.hpp" #include "labor/Labor.hpp" +#include "codec/Codec.hpp" #include "ActorBuilder.hpp" namespace neb @@ -165,9 +166,10 @@ class Actor: public std::enable_shared_from_this * @param iCmd 发送的命令字 * @param uiSeq 发送的数据包seq * @param oMsgBody 数据包体 + * @param oCodecType 编解码方式 * @return 是否发送成功 */ - bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); /** * @brief 发送数据 @@ -220,9 +222,10 @@ class Actor: public std::enable_shared_from_this * @param iCmd 发送的命令字 * @param uiSeq 发送的数据包seq * @param oMsgBody 数据包体 + * @param oCodecType 编解码方式 * @return 是否发送成功 */ - bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); /** * @brief 以取模方式选择发送到同一类型节点 @@ -232,11 +235,12 @@ class Actor: public std::enable_shared_from_this * @param iCmd 发送的命令字 * @param uiSeq 发送的数据包seq * @param oMsgBody 数据包体 + * @param oCodecType 编解码方式 * @return 是否发送成功 */ - bool SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); - bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp index 3999ad17..467a3193 100644 --- a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp +++ b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp @@ -43,6 +43,7 @@ bool ModuleHttpUpgrade::AnyMessage(std::shared_ptr pChannel, cons oOutHttpMsg.set_status_code(400); oOutHttpMsg.set_http_major(oHttpMsg.http_major()); oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); + SendTo(pChannel, oOutHttpMsg); return(false); } diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp index 36db39db..027f9f0d 100644 --- a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp +++ b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp @@ -18,7 +18,7 @@ namespace neb { class ModuleHttpUpgrade: public Module, - public DynamicCreator, public ActorSys + public DynamicCreator, public ActorSys { public: ModuleHttpUpgrade(const std::string& strModulePath); diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 6bfa0bb2..e8554550 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -128,15 +128,8 @@ E_CODEC_STATUS SocketChannelImpl::Send() iNeedWriteLen = m_pWaitForSendBuff->ReadableBytes(); if (0 == iNeedWriteLen) { - if (CODEC_NEBULA != m_pCodec->GetCodecType() && m_dKeepAlive <= 0.0) - { - return(CODEC_STATUS_EOF); - } - else - { - LOG4_DEBUG("no data need to send."); - return(CODEC_STATUS_OK); - } + LOG4_TRACE("no data need to send."); + return(CODEC_STATUS_OK); } else { @@ -212,6 +205,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& { case CMD_RSP_TELL_WORKER: m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; eCodecStatus = m_pCodec->Encode(oMsgHead, oMsgBody, m_pSendBuff); break; case CMD_REQ_TELL_WORKER: @@ -401,6 +395,10 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) ++m_ulUnitTimeMsgNum; ++m_ulMsgNum; oMsgBody.set_add_on(m_strClientData); + if (m_ulMsgNum) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + } } break; case CHANNEL_STATUS_TELL_WORKER: @@ -414,6 +412,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) { case CMD_RSP_TELL_WORKER: m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; break; case CMD_REQ_TELL_WORKER: m_ucChannelStatus = CHANNEL_STATUS_TELL_WORKER; @@ -479,11 +478,20 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) } m_dActiveTime = m_pLabor->GetNowTime(); E_CODEC_STATUS eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); + if (CODEC_STATUS_OK == eCodecStatus) + { + ++m_ulUnitTimeMsgNum; + ++m_ulMsgNum; + if (m_ulMsgNum > 0) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + } + } return(eCodecStatus); } else if (iReadLen == 0) { - LOG4_DEBUG("fd %d closed by peer, error %d %s!", + LOG4_TRACE("fd %d closed by peer, error %d %s!", m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); if (m_pRecvBuff->ReadableBytes() > 0) { @@ -533,6 +541,15 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, Htt if (CODEC_HTTP == m_pCodec->GetCodecType()) { E_CODEC_STATUS eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); + if (CODEC_STATUS_OK == eCodecStatus) + { + ++m_ulUnitTimeMsgNum; + ++m_ulMsgNum; + if (m_ulMsgNum > 0) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + } + } return(eCodecStatus); } else @@ -544,6 +561,10 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, Htt ++m_ulUnitTimeMsgNum; ++m_ulMsgNum; oMsgBody.set_add_on(m_strClientData); + if (m_ulMsgNum >0) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + } LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_ulSeq, oMsgHead.cmd(), oMsgHead.seq()); } return(eCodecStatus); diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 25ad5e07..d35b0f16 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -200,7 +200,7 @@ class SocketChannelImpl: public Channel uint32 m_ulUnitTimeMsgNum; ///< 统计单位时间内接收消息数量 uint32 m_ulMsgNum; ///< 接收消息数量 ev_tstamp m_dActiveTime; ///< 最后一次访问时间 - ev_tstamp m_dKeepAlive; ///< 连接保持时间,默认值0为用心跳保持的长连接,大于0的值不做心跳检查,时间到即断连接,小于0为收完数据立即断开连接(主要用于http连接) + ev_tstamp m_dKeepAlive; ///< 连接保持时间 ev_io* m_pIoWatcher; ///< 不在结构体析构时回收 ev_timer* m_pTimerWatcher; ///< 不在结构体析构时回收 CBuffer* m_pRecvBuff; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 965e55b2..a18cf0c2 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -176,7 +176,7 @@ bool Dispatcher::OnIoRead(std::shared_ptr pChannel) return(DataRecvAndHandle(pChannel)); } } - else + else if (Labor::LABOR_WORKER == m_pLabor->GetLaborType() || Labor::LABOR_LOADER == m_pLabor->GetLaborType()) { if (pChannel->m_pImpl->GetFd() == ((Worker*)m_pLabor)->GetWorkerInfo().iDataFd) { @@ -187,6 +187,10 @@ bool Dispatcher::OnIoRead(std::shared_ptr pChannel) return(DataRecvAndHandle(pChannel)); } } + else + { + return(DataRecvAndHandle(pChannel)); + } } bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) @@ -313,7 +317,7 @@ bool Dispatcher::FdTransfer(int iFd) } std::shared_ptr pChannel = nullptr; LOG4_TRACE("fd[%d] transfer successfully.", iAcceptFd); - if (CODEC_NEBULA != iCodec && ((Worker*)m_pLabor)->WithSsl()) + if (CODEC_NEBULA != iCodec && m_pLabor->WithSsl()) { pChannel = CreateSocketChannel(iAcceptFd, E_CODEC_TYPE(iCodec), false, true); } @@ -459,7 +463,8 @@ bool Dispatcher::OnIoError(std::shared_ptr pChannel) bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) { - ev_tstamp after = pChannel->m_pImpl->GetActiveTime() - ev_now(m_loop) + m_pLabor->GetNodeInfo().dIoTimeout; + //ev_tstamp after = pChannel->m_pImpl->GetActiveTime() - ev_now(m_loop) + m_pLabor->GetNodeInfo().dIoTimeout; + ev_tstamp after = pChannel->m_pImpl->GetActiveTime() - ev_now(m_loop) + pChannel->m_pImpl->GetKeepAlive(); if (after > 0) // IO在定时时间内被重新刷新过,重新设置定时器 { ev_timer_stop (m_loop, pChannel->m_pImpl->MutableTimerWatcher()); @@ -565,7 +570,7 @@ bool Dispatcher::OnClientConnFrequencyTimeout(tagClientConnWatcherData* pData, e return(bRes); } -void Dispatcher::EeventRun() +void Dispatcher::EventRun() { ev_run (m_loop, 0); } @@ -642,7 +647,7 @@ bool Dispatcher::SendTo(std::shared_ptr pChannel, int32 iCmd, uin return(false); } -bool Dispatcher::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Dispatcher::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) { LOG4_TRACE("identify: %s", strIdentify.c_str()); auto named_iter = m_mapNamedSocketChannel.find(strIdentify); @@ -653,7 +658,7 @@ bool Dispatcher::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq { (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); } - return(AutoSend(strIdentify, iCmd, uiSeq, oMsgBody)); + return(AutoSend(strIdentify, iCmd, uiSeq, oMsgBody, eCodecType)); } else { @@ -682,7 +687,7 @@ bool Dispatcher::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq } } -bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; @@ -692,7 +697,7 @@ bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint { (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); } - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); + return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody, eCodecType)); } else { @@ -701,7 +706,7 @@ bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint } } -bool Dispatcher::SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Dispatcher::SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) { LOG4_TRACE("nody_type: %s, factor: %d", strNodeType.c_str(), uiFactor); std::string strOnlineNode; @@ -711,7 +716,7 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, unsigned int uiFac { (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); } - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); + return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody, eCodecType)); } else { @@ -720,14 +725,14 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, unsigned int uiFac } } -bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) { LOG4_TRACE("nody_type: %s", strNodeType.c_str()); if (oMsgBody.has_req_target()) { if (0 != oMsgBody.req_target().route_id()) { - return(SendOriented(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, pSender)); + return(SendOriented(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, eCodecType, pSender)); } else if (oMsgBody.req_target().route().length() > 0) { @@ -738,7 +743,7 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 { (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); } - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody)); + return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody, eCodecType)); } else { @@ -748,7 +753,7 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 } else { - return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, pSender)); + return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType, pSender)); } } else @@ -758,7 +763,7 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 } }; -bool Dispatcher::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Dispatcher::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); std::unordered_set setOnlineNodes; @@ -771,7 +776,7 @@ bool Dispatcher::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 ui bool bSendResult = false; for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) { - bSendResult |= SendTo(*node_iter, iCmd, uiSeq, oMsgBody); + bSendResult |= SendTo(*node_iter, iCmd, uiSeq, oMsgBody, eCodecType); } return(bSendResult); } @@ -793,7 +798,7 @@ bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBod { if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) { - return(Broadcast("BEACON", iCmd, uiSeq, oMsgBody, pSender)); + return(Broadcast("BEACON", iCmd, uiSeq, oMsgBody, CODEC_NEBULA, pSender)); } else { @@ -827,6 +832,88 @@ bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBod } } +std::shared_ptr Dispatcher::StressSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) +{ + LOG4_TRACE("%s", strIdentify.c_str()); + size_t iPosIpPortSeparator = strIdentify.rfind(":"); + if (iPosIpPortSeparator == std::string::npos) + { + return(nullptr); + } + std::string strHost = strIdentify.substr(0, iPosIpPortSeparator); + std::string strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); + int iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + return(nullptr); + } + + struct addrinfo stAddrHints; + struct addrinfo* pAddrResult; + struct addrinfo* pAddrCurrent; + memset(&stAddrHints, 0, sizeof(struct addrinfo)); + stAddrHints.ai_family = AF_UNSPEC; + stAddrHints.ai_socktype = SOCK_STREAM; + stAddrHints.ai_protocol = IPPROTO_IP; + int iCode = getaddrinfo(strHost.c_str(), strPort.c_str(), &stAddrHints, &pAddrResult); + if (0 != iCode) + { + LOG4_ERROR("getaddrinfo(\"%s\", \"%s\") error %d: %s", + strHost.c_str(), strPort.c_str(), iCode, gai_strerror(iCode)); + return(nullptr); + } + int iFd = -1; + for (pAddrCurrent = pAddrResult; + pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) + { + iFd = socket(pAddrCurrent->ai_family, + pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); + if (iFd == -1) + { + continue; + } + break; + } + + /* no address succeeded */ + if (pAddrCurrent == NULL) + { + LOG4_ERROR("Could not connect to \"%s:%s\"", strHost.c_str(), strPort.c_str()); + freeaddrinfo(pAddrResult); + return(nullptr); + } + + x_sock_set_block(iFd, 0); + int nREUSEADDR = 1; + setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); + std::shared_ptr pChannel = CreateSocketChannel(iFd, eCodecType); + if (nullptr != pChannel) + { + connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); + freeaddrinfo(pAddrResult); /* no longer needed */ + AddIoTimeout(pChannel, 1.5); + AddIoReadEvent(pChannel); + AddIoWriteEvent(pChannel); + pChannel->m_pImpl->SetRemoteAddr(strHost); + E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); + if (CODEC_STATUS_OK != eCodecStatus + && CODEC_STATUS_PAUSE != eCodecStatus + && CODEC_STATUS_WANT_WRITE != eCodecStatus + && CODEC_STATUS_WANT_READ != eCodecStatus) + { + DiscardSocketChannel(pChannel); + } + pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); + return(pChannel); + } + else + { + freeaddrinfo(pAddrResult); + close(iFd); + return(nullptr); + } +} + bool Dispatcher::SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) { E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); @@ -1007,27 +1094,42 @@ bool Dispatcher::SendTo(const std::string& strHost, int iPort, Actor* pSender) } } -bool Dispatcher::AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +bool Dispatcher::AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) { LOG4_TRACE("%s", strIdentify.c_str()); size_t iPosIpPortSeparator = strIdentify.rfind(':'); + size_t iPosPortWorkerIndexSeparator = strIdentify.rfind('.'); if (iPosIpPortSeparator == std::string::npos) { return(false); } - size_t iPosPortWorkerIndexSeparator = strIdentify.rfind('.'); std::string strHost = strIdentify.substr(0, iPosIpPortSeparator); - std::string strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); - std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); - int iPort = atoi(strPort.c_str()); - if (iPort == 0) + std::string strPort; + int iPort = 0; + int iWorkerIndex = 0; + if (iPosPortWorkerIndexSeparator != std::string::npos) { - return(false); + strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); + iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + return(false); + } + std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); + int iWorkerIndex = atoi(strWorkerIndex.c_str()); + if (iWorkerIndex > 200) + { + return(false); + } } - int iWorkerIndex = atoi(strWorkerIndex.c_str()); - if (iWorkerIndex > 200) + else { - return(false); + strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); + iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + return(false); + } } struct addrinfo stAddrHints; @@ -1069,7 +1171,7 @@ bool Dispatcher::AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiS x_sock_set_block(iFd, 0); int nREUSEADDR = 1; setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); - std::shared_ptr pChannel = CreateSocketChannel(iFd, CODEC_NEBULA); + std::shared_ptr pChannel = CreateSocketChannel(iFd, eCodecType); if (nullptr != pChannel) { connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); @@ -1456,6 +1558,17 @@ bool Dispatcher::AddEvent(ev_timer* timer_watcher, timer_callback pFunc, ev_tsta return(true); } +bool Dispatcher::AddEvent(ev_idle* idle_watcher, idle_callback pFunc) +{ + if (NULL == idle_watcher) + { + return(false); + } + ev_idle_init (idle_watcher, pFunc); + ev_idle_start (m_loop, idle_watcher); + return(true); +} + bool Dispatcher::RefreshEvent(ev_timer* timer_watcher, ev_tstamp dTimeout) { if (NULL == timer_watcher) diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index c3f6eaf9..b3d0d16a 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -55,6 +55,7 @@ namespace neb class Labor; class Manager; class Worker; +class LoadStress; // not in Nebula project class Actor; class ActorBuilder; class Nodes; @@ -62,6 +63,7 @@ struct tagClientConnWatcherData; typedef void (*signal_callback)(struct ev_loop*,ev_signal*,int); typedef void (*timer_callback)(struct ev_loop*,ev_timer*,int); +typedef void (*idle_callback)(struct ev_loop*,ev_idle*,int); class Dispatcher { @@ -85,6 +87,7 @@ class Dispatcher Dispatcher(Labor* pLabor, std::shared_ptr pLogger); virtual ~Dispatcher(); + bool Init(); public: static void IoCallback(struct ev_loop* loop, struct ev_io* watcher, int revents); @@ -110,7 +113,7 @@ class Dispatcher template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); - void EeventRun(); + void EventRun(); public: bool AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout = 1.0); @@ -118,13 +121,14 @@ class Dispatcher // SendTo() for nebula socket bool SendTo(std::shared_ptr pChannel); bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); + bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); + bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); + bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); + bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); + bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender); + std::shared_ptr StressSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); // SendTo() for http bool SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); @@ -172,13 +176,13 @@ class Dispatcher int SendFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType); protected: - bool Init(); void Destroy(); bool AddIoReadEvent(std::shared_ptr pChannel); bool AddIoWriteEvent(std::shared_ptr pChannel); bool RemoveIoWriteEvent(std::shared_ptr pChannel); bool AddEvent(ev_signal* signal_watcher, signal_callback pFunc, int iSignum); bool AddEvent(ev_timer* timer_watcher, timer_callback pFunc, ev_tstamp dTimeout); + bool AddEvent(ev_idle* idle_watcher, idle_callback pFunc); bool RefreshEvent(ev_timer* timer_watcher, ev_tstamp dTimeout); bool DelEvent(ev_io* io_watcher); bool DelEvent(ev_timer* timer_watcher); @@ -214,6 +218,7 @@ class Dispatcher friend class Manager; friend class Worker; friend class ActorBuilder; + friend class LoadStress; }; template diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index 986ed28d..36b2b403 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -57,6 +57,7 @@ bool Nodes::GetNode(const std::string& strNodeType, const std::string& strHashKe else { strNodeIdentify = c_iter->second; + node_type_iter->second->itHashRing = c_iter; } return(true); } @@ -79,11 +80,31 @@ bool Nodes::GetNode(const std::string& strNodeType, uint32 uiHash, std::string& else { strNodeIdentify = c_iter->second; + node_type_iter->second->itHashRing = c_iter; } return(true); } } +bool Nodes::GetNodeInHashRing(const std::string& strNodeType, std::string& strNodeIdentify) +{ + auto node_type_iter = m_mapNode.find(strNodeType); + if (node_type_iter == m_mapNode.end()) + { + return(false); + } + else + { + node_type_iter->second->itHashRing++; + if (node_type_iter->second->itHashRing == node_type_iter->second->mapHash2Node.end()) + { + node_type_iter->second->itHashRing = node_type_iter->second->mapHash2Node.begin(); + } + strNodeIdentify = node_type_iter->second->itHashRing->first; + return(true); + } +} + bool Nodes::GetNode(const std::string& strNodeType, std::string& strNodeIdentify) { auto node_type_iter = m_mapNode.find(strNodeType); @@ -148,6 +169,7 @@ void Nodes::AddNode(const std::string& strNodeType, const std::string& strNodeId } } pNode->itPollingNode = pNode->mapNode2Hash.begin(); + pNode->itHashRing = pNode->mapHash2Node.begin(); } else { @@ -175,6 +197,7 @@ void Nodes::AddNode(const std::string& strNodeType, const std::string& strNodeId } } node_type_iter->second->itPollingNode = node_type_iter->second->mapNode2Hash.begin(); + node_type_iter->second->itHashRing = node_type_iter->second->mapHash2Node.begin(); } } } @@ -194,6 +217,7 @@ void Nodes::DelNode(const std::string& strNodeType, const std::string& strNodeId } node_type_iter->second->mapNode2Hash.erase(node_iter); node_type_iter->second->itPollingNode = node_type_iter->second->mapNode2Hash.begin(); + node_type_iter->second->itHashRing = node_type_iter->second->mapHash2Node.begin(); } if (node_type_iter->second->mapNode2Hash.empty()) diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index d620b486..1b3bb238 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -57,6 +57,7 @@ class Nodes T_NODE2HASH_MAP mapNode2Hash; T_NODE2HASH_MAP::iterator itPollingNode; std::map mapHash2Node; + std::map::const_iterator itHashRing; tagNode(){} ~tagNode(){} @@ -77,6 +78,8 @@ class Nodes bool GetNode(const std::string& strNodeType, uint32 uiHash, std::string& strNodeIdentify); + bool GetNodeInHashRing(const std::string& strNodeType, std::string& strNodeIdentify); + bool GetNode(const std::string& strNodeType, std::string& strNodeIdentify); bool GetNode(const std::string& strNodeType, std::unordered_set& setNodeIdentify); diff --git a/src/labor/Labor.hpp b/src/labor/Labor.hpp index 24a4e07f..63ef34a1 100644 --- a/src/labor/Labor.hpp +++ b/src/labor/Labor.hpp @@ -62,6 +62,10 @@ class Labor virtual void SetNodeId(uint32 uiNodeId) = 0; virtual bool AddNetLogMsg(const MsgBody& oMsgBody) = 0; virtual void OnTerminated(struct ev_signal* watcher) = 0; + virtual bool WithSsl() + { + return(false); + } private: LABOR_TYPE m_eLaborType; diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 14e289ed..8d889a3e 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -106,7 +106,7 @@ void Manager::OnChildTerminated(struct ev_signal* watcher) void Manager::Run() { LOG4_TRACE(" "); - m_pDispatcher->EeventRun(); + m_pDispatcher->EventRun(); } bool Manager::InitLogger(const CJsonObject& oJsonConf) @@ -271,23 +271,46 @@ bool Manager::Init() return(false); } - if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) + std::string strBindIp; + if (m_oCurrentConf.Get("bind_ip", strBindIp) && strBindIp.length() > 0) { - // 接入节点才需要监听客户端连接 - if (!m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForClient, - m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd, - m_stManagerInfo.iC2SFamily)) + if (!m_pDispatcher->CreateListenFd(strBindIp, + m_stNodeInfo.iPortForServer, m_stManagerInfo.iS2SListenFd, + m_stManagerInfo.iS2SFamily)) { return(false); } - } - - if (!m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForServer, - m_stNodeInfo.iPortForServer, m_stManagerInfo.iS2SListenFd, - m_stManagerInfo.iS2SFamily)) + if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) + { + // 接入节点才需要监听客户端连接 + if (!m_pDispatcher->CreateListenFd(strBindIp, + m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd, + m_stManagerInfo.iC2SFamily)) + { + return(false); + } + } + } + else { - return(false); + if (!m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForServer, + m_stNodeInfo.iPortForServer, m_stManagerInfo.iS2SListenFd, + m_stManagerInfo.iS2SFamily)) + { + return(false); + } + + if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) + { + // 接入节点才需要监听客户端连接 + if (!m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForClient, + m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd, + m_stManagerInfo.iC2SFamily)) + { + return(false); + } + } } // 创建到beacon的连接信息 diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index 2cf8543c..2149dc66 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -31,11 +31,11 @@ struct NodeInfo int32 iPortForClient = 0; ///< 对Client通信监听端口,对应 iC2SListenFd int32 iGatewayPort = 0; ///< 对Client服务的真实端口 bool bIsAccess = false; ///< 是否接入Server - ev_tstamp dIoTimeout = 0.0; ///< IO(连接)超时配置 + ev_tstamp dIoTimeout = 10.0; ///< IO(连接)超时配置 ev_tstamp dDataReportInterval = 60.0; ///< 统计数据上报时间间隔 - ev_tstamp dMsgStatInterval = 0.0; ///< 客户端连接发送数据包统计时间间隔 - ev_tstamp dAddrStatInterval = 0.0; ///< IP地址数据统计时间间隔 - ev_tstamp dStepTimeout = 0.0; ///< 步骤超时 + ev_tstamp dMsgStatInterval = 60.0; ///< 客户端连接发送数据包统计时间间隔 + ev_tstamp dAddrStatInterval = 60.0; ///< IP地址数据统计时间间隔 + ev_tstamp dStepTimeout = 1.5; ///< 步骤超时 std::string strWorkPath; ///< 工作路径 std::string strConfFile; ///< 配置文件 std::string strNodeType; ///< 节点类型 diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index c61887ac..9ffc18e0 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -50,7 +50,7 @@ void Worker::Run() exit(-2); } - m_pDispatcher->EeventRun(); + m_pDispatcher->EventRun(); } void Worker::OnTerminated(struct ev_signal* watcher) diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 61f5a520..2f558f5e 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -84,11 +84,11 @@ class Worker: public Labor virtual const NodeInfo& GetNodeInfo() const; virtual void SetNodeId(uint32 uiNodeId); virtual bool AddNetLogMsg(const MsgBody& oMsgBody); + bool WithSsl(); const WorkerInfo& GetWorkerInfo() const; const CJsonObject& GetCustomConf() const; std::shared_ptr GetManagerControlChannel(); bool SetCustomConf(const CJsonObject& oJsonConf); - bool WithSsl(); template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); From 82d404775caf07112efd4dcbb096f654577c2703 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 23 Feb 2020 15:44:02 +0800 Subject: [PATCH 088/176] adjust socket channel timeout, add http health module. --- conf/nebula.json | 2 + src/actor/Actor.hpp | 4 +- src/actor/ActorBuilder.cpp | 97 +++++++++++++++++++++++-- src/actor/ActorBuilder.hpp | 2 + src/actor/cmd/sys_cmd/CmdDataReport.cpp | 2 +- src/actor/cmd/sys_cmd/ModuleHealth.cpp | 36 +++++++++ src/actor/cmd/sys_cmd/ModuleHealth.hpp | 33 +++++++++ src/actor/step/Step.hpp | 13 ++++ src/channel/SocketChannelImpl.cpp | 46 ++++++------ src/channel/SocketChannelImpl.hpp | 16 ++-- src/codec/CodecHttp.cpp | 6 +- src/ios/Dispatcher.cpp | 41 +++++------ 12 files changed, 237 insertions(+), 61 deletions(-) create mode 100644 src/actor/cmd/sys_cmd/ModuleHealth.cpp create mode 100644 src/actor/cmd/sys_cmd/ModuleHealth.hpp diff --git a/conf/nebula.json b/conf/nebula.json index cc508ffc..528cd009 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -13,6 +13,8 @@ "host": "192.168.18.81", "//port": "系统内各Server之间通信监听的端口", "port": 9987, + "//bind_ip":"绑定IP地址。当有此项配置时bind()会绑定此ip地址,否则绑定host和access_host", + "bind_ip":"0.0.0.0", "//server_name": "异步事件驱动Server", "server_name": "AsyncServer", "//worker_num": "进程数量", diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index a6e2a122..f7e8f873 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -76,7 +76,7 @@ class Actor: public std::enable_shared_from_this Actor& operator=(const Actor&) = delete; virtual ~Actor(); - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) const; template std::shared_ptr MakeSharedStep(const std::string& strStepName, Targs&&... args); template std::shared_ptr MakeSharedSession(const std::string& strSessionName, Targs&&... args); template std::shared_ptr MakeSharedContext(const std::string& strContextName, Targs&&... args); @@ -286,7 +286,7 @@ class Actor: public std::enable_shared_from_this }; template -void Actor::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) +void Actor::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) const { m_pLabor->GetActorBuilder()->Logger(m_strTraceId, iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index f4dee8a1..5f658084 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -305,11 +305,11 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH oMsgHead.cmd(), oMsgHead.seq(), step_iter->second->GetSequence(), step_iter->second->GetActiveTime()); eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); - if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + if (CMD_STATUS_RUNNING != eResult) { uint32 uiChainId = step_iter->second->GetChainId(); RemoveStep(step_iter->second); - if (0 != uiChainId) + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); if (chain_iter != m_mapChain.end()) @@ -398,13 +398,13 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http E_CMD_STATUS eResult; http_step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); eResult = (std::dynamic_pointer_cast(http_step_iter->second))->Callback(pChannel, oHttpMsg); - if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + if (CMD_STATUS_RUNNING != eResult) { uint32 uiChainId = http_step_iter->second->GetChainId(); RemoveStep(http_step_iter->second); m_pLabor->GetDispatcher()->SetChannelStatus(pChannel, CHANNEL_STATUS_ESTABLISHED, 0); m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. - if (0 != uiChainId) + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); if (chain_iter != m_mapChain.end()) @@ -423,6 +423,46 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http return(true); } +bool ActorBuilder::OnError(std::shared_ptr pChannel, uint32 uiStepSeq, int iErrno, const std::string& strErrMsg) +{ + auto step_iter = m_mapCallbackStep.find(uiStepSeq); + if (step_iter != m_mapCallbackStep.end()) + { + if (step_iter->second != nullptr) + { + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = step_iter->second->ErrBack(pChannel, iErrno, strErrMsg); + if (CMD_STATUS_RUNNING != eResult) + { + uint32 uiChainId = step_iter->second->GetChainId(); + RemoveStep(step_iter->second); + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } + } + } + } + } + ExecAssemblyLine(pChannel, iErrno, strErrMsg); + return(true); + } + else + { + snprintf(m_pErrBuff, gc_iErrBuffLen, "no callback or the callback for seq %u had been timeout!", uiStepSeq); + LOG4_WARNING(m_pErrBuff); + return(false); + } +} + bool ActorBuilder::OnRedisConnected(std::shared_ptr pChannel, const redisAsyncContext *c, int status) { if (status == REDIS_OK) @@ -691,11 +731,50 @@ void ActorBuilder::ExecAssemblyLine(std::shared_ptr pChannel, con E_CMD_STATUS eResult; step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); - if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + if (CMD_STATUS_RUNNING != eResult) + { + uint32 uiChainId = step_iter->second->GetChainId(); + RemoveStep(step_iter->second); + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } + } + } + } + } + } + while(uiStepSeq > 0); + } + m_setAssemblyLine.clear(); +} + +void ActorBuilder::ExecAssemblyLine(std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) +{ + for (auto session_iter = m_setAssemblyLine.begin(); session_iter != m_setAssemblyLine.end(); ++session_iter) + { + uint32 uiStepSeq = 0; + do + { + uiStepSeq = (*session_iter)->PopWaitingStep(); + auto step_iter = m_mapCallbackStep.find(uiStepSeq); + if (step_iter != m_mapCallbackStep.end()) + { + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = (std::dynamic_pointer_cast(step_iter->second))->ErrBack(pChannel, iErrno, strErrMsg); + if (CMD_STATUS_RUNNING != eResult) { uint32 uiChainId = step_iter->second->GetChainId(); RemoveStep(step_iter->second); - if (0 != uiChainId) + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); if (chain_iter != m_mapChain.end()) @@ -747,6 +826,12 @@ void ActorBuilder::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdSetNodeConf", (int)CMD_REQ_SET_NODE_CONFIG); MakeSharedCmd(nullptr, "neb::CmdSetNodeCustomConf", (int)CMD_REQ_SET_NODE_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdReloadCustomConf", (int)CMD_REQ_RELOAD_CUSTOM_CONFIG); + std::string strModulePath = "/healthy"; + MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); + strModulePath = "/health"; + MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); + strModulePath = "/status"; + MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); } m_pSessionLogger = std::dynamic_pointer_cast(MakeSharedSession(nullptr, "neb::SessionLogger")); } diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index e5a22973..25728ad4 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -93,6 +93,7 @@ class ActorBuilder bool OnChainTimeout(std::shared_ptr pChain); bool OnMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); bool OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + bool OnError(std::shared_ptr pChannel, uint32 uiStepSeq, int iErrno, const std::string& strErrMsg); bool OnRedisConnected(std::shared_ptr pChannel, const redisAsyncContext *c, int status); void OnRedisDisconnected(std::shared_ptr pChannel, const redisAsyncContext *c, int status); bool OnRedisCmdResult(std::shared_ptr pChannel, redisAsyncContext *c, void *reply, void *privdata); @@ -153,6 +154,7 @@ class ActorBuilder void RemoveChain(uint32 uiChainId); void ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData); void ExecAssemblyLine(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); + void ExecAssemblyLine(std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg); void AddChainConf(const std::string& strChainKey, std::queue >&& queChainBlocks); void LoadSysCmd(); diff --git a/src/actor/cmd/sys_cmd/CmdDataReport.cpp b/src/actor/cmd/sys_cmd/CmdDataReport.cpp index f807a783..b6d62bff 100644 --- a/src/actor/cmd/sys_cmd/CmdDataReport.cpp +++ b/src/actor/cmd/sys_cmd/CmdDataReport.cpp @@ -28,7 +28,7 @@ bool CmdDataReport::Init() if (pSessionDataReport == nullptr) { std::string strReportSessionId = "neb::SessionDataReport"; - auto pSharedSession = MakeSharedSession("neb::SessionReport", + auto pSharedSession = MakeSharedSession("neb::SessionDataReport", strReportSessionId, GetDataReportInterval()); if (pSharedSession == nullptr) { diff --git a/src/actor/cmd/sys_cmd/ModuleHealth.cpp b/src/actor/cmd/sys_cmd/ModuleHealth.cpp new file mode 100644 index 00000000..b9d38aa9 --- /dev/null +++ b/src/actor/cmd/sys_cmd/ModuleHealth.cpp @@ -0,0 +1,36 @@ +/******************************************************************************* + * Project: Nebula + * @file ModuleHealth.cpp + * @brief + * @author Bwar + * @date: 2020-2-16 + * @note + * Modify history: + ******************************************************************************/ + +#include "ModuleHealth.hpp" + +namespace neb +{ + +ModuleHealth::ModuleHealth(const std::string& strModulePath) + : Module(strModulePath) +{ +} + +ModuleHealth::~ModuleHealth() +{ +} + +bool ModuleHealth::AnyMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +{ + HttpMsg oOutHttpMsg; + oOutHttpMsg.set_type(HTTP_RESPONSE); + oOutHttpMsg.set_status_code(200); + oOutHttpMsg.set_http_major(oHttpMsg.http_major()); + oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); + SendTo(pChannel, oOutHttpMsg); + return(true); +} + +} diff --git a/src/actor/cmd/sys_cmd/ModuleHealth.hpp b/src/actor/cmd/sys_cmd/ModuleHealth.hpp new file mode 100644 index 00000000..878f7d74 --- /dev/null +++ b/src/actor/cmd/sys_cmd/ModuleHealth.hpp @@ -0,0 +1,33 @@ +/******************************************************************************* + * Project: Nebula + * @file ModuleHealth.hpp + * @brief + * @author Bwar + * @date: 2020-2-16 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_SYS_CMD_MODULEHEALTH_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MODULEHEALTH_HPP_ + +#include "actor/cmd/Module.hpp" + +namespace neb +{ + +class ModuleHealth: public Module, + public DynamicCreator +{ +public: + ModuleHealth(const std::string& strModulePath); + virtual ~ModuleHealth(); + + virtual bool AnyMessage( + std::shared_ptr pChannel, + const HttpMsg& oHttpMsg); +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MODULEHEALTH_HPP_ */ + diff --git a/src/actor/step/Step.hpp b/src/actor/step/Step.hpp index fe3ea61b..7e9b3dec 100644 --- a/src/actor/step/Step.hpp +++ b/src/actor/step/Step.hpp @@ -38,6 +38,19 @@ class Step: public Actor */ virtual E_CMD_STATUS Timeout() = 0; + /** + * @brief 错误回调 + * @note 由框架层回调,与业务逻辑无关。当框架明确网络错误发生且不可能产生多于 + * 一种结果时回调(比如异步网络发送错误)。为兼容之前版本,该方法设计为非纯虚 + * 方法。 + */ + virtual E_CMD_STATUS ErrBack(std::shared_ptr pChannel, + int iErrno, const std::string& strErrMsg) + { + LOG4_ERROR("error %d: %s", iErrno, strErrMsg.c_str()); + return(CMD_STATUS_FAULT); + } + protected: /** * @brief 执行当前步骤接下来的步骤 diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index e8554550..f71104d3 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -20,7 +20,7 @@ namespace neb { SocketChannelImpl::SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) - : m_ucChannelStatus(CHANNEL_STATUS_INIT), m_unRemoteWorkerIdx(0), m_iFd(iFd), m_ulSeq(ulSeq), m_ulForeignSeq(0), m_ulStepSeq(0), + : m_ucChannelStatus(CHANNEL_STATUS_INIT), m_unRemoteWorkerIdx(0), m_iFd(iFd), m_ulSeq(ulSeq), m_ulForeignSeq(0), m_uiStepSeq(0), m_ulUnitTimeMsgNum(0), m_ulMsgNum(0), m_dActiveTime(0.0), m_dKeepAlive(dKeepAlive), m_pIoWatcher(NULL), m_pTimerWatcher(NULL), m_pRecvBuff(nullptr), m_pSendBuff(nullptr), m_pWaitForSendBuff(nullptr), @@ -32,6 +32,7 @@ SocketChannelImpl::SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ SocketChannelImpl::~SocketChannelImpl() { LOG4_DEBUG("SocketChannelImpl::~SocketChannelImpl() fd %d, seq %u", m_iFd, m_ulSeq); + m_vecStepWaitForConnected.clear(); if (CHANNEL_STATUS_CLOSED != m_ucChannelStatus) { Close(); @@ -145,6 +146,7 @@ E_CODEC_STATUS SocketChannelImpl::Send() LOG4_TRACE("iNeedWriteLen = %d, iWriteLen = %d", iNeedWriteLen, iWriteLen); if (iWriteLen >= 0) { + m_vecStepWaitForConnected.clear(); if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) { @@ -167,9 +169,9 @@ E_CODEC_STATUS SocketChannelImpl::Send() m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); - m_strErrMsg = m_szErrBuff; + m_iFd, m_iErrno, m_strErrMsg.c_str()); return(CODEC_STATUS_INT); } } @@ -225,6 +227,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& if (CODEC_STATUS_OK == eCodecStatus) { eCodecStatus = CODEC_STATUS_PAUSE; + m_vecStepWaitForConnected.push_back(uiSeq); } break; } @@ -280,14 +283,14 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); - m_strErrMsg = m_szErrBuff; + m_iFd, m_iErrno, m_strErrMsg.c_str()); return(CODEC_STATUS_INT); } } -E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq) +E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq) { LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_ulSeq, m_ucChannelStatus); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) @@ -311,6 +314,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq if (CODEC_STATUS_OK == eCodecStatus) { eCodecStatus = CODEC_STATUS_PAUSE; + m_vecStepWaitForConnected.push_back(uiStepSeq); } break; default: @@ -339,7 +343,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq { m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); } - m_ulStepSeq = ulStepSeq; + m_uiStepSeq = uiStepSeq; m_dActiveTime = m_pLabor->GetNowTime(); if (iNeedWriteLen == iWriteLen) { @@ -357,9 +361,9 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); - m_strErrMsg = m_szErrBuff; + m_iFd, m_iErrno, m_strErrMsg.c_str()); return(CODEC_STATUS_INT); } } @@ -438,9 +442,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) } else if (iReadLen == 0) { + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_DEBUG("fd %d closed by peer, error %d %s!", - m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); - m_strErrMsg = m_szErrBuff; + m_iFd, m_iErrno, m_strErrMsg.c_str()); return(CODEC_STATUS_EOF); } else @@ -450,9 +454,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); - m_strErrMsg = m_szErrBuff; + m_iFd, m_iErrno, m_strErrMsg.c_str()); return(CODEC_STATUS_INT); } } @@ -491,8 +495,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) } else if (iReadLen == 0) { + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_TRACE("fd %d closed by peer, error %d %s!", - m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); + m_iFd, m_iErrno, m_strErrMsg.c_str()); if (m_pRecvBuff->ReadableBytes() > 0) { E_CODEC_STATUS eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); @@ -501,7 +506,6 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) oHttpMsg.set_is_decoding(false); } } - m_strErrMsg = m_szErrBuff; return(CODEC_STATUS_EOF); } else @@ -511,9 +515,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); - m_strErrMsg = m_szErrBuff; + m_iFd, m_iErrno, m_strErrMsg.c_str()); return(CODEC_STATUS_INT); } } @@ -572,9 +576,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, Htt } else if (iReadLen == 0) { + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_DEBUG("fd %d closed by peer, error %d %s!", - m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); - m_strErrMsg = m_szErrBuff; + m_iFd, m_iErrno, m_strErrMsg.c_str()); return(CODEC_STATUS_EOF); } else @@ -584,9 +588,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, Htt m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff))); - m_strErrMsg = m_szErrBuff; + m_iFd, m_iErrno, m_strErrMsg.c_str()); return(CODEC_STATUS_INT); } } diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index d35b0f16..ed02f573 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -51,7 +51,7 @@ class SocketChannelImpl: public Channel virtual E_CODEC_STATUS Send(); virtual E_CODEC_STATUS Send(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - virtual E_CODEC_STATUS Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq); + virtual E_CODEC_STATUS Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq); virtual E_CODEC_STATUS Recv(MsgHead& oMsgHead, MsgBody& oMsgBody); virtual E_CODEC_STATUS Recv(HttpMsg& oHttpMsg); virtual E_CODEC_STATUS Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, HttpMsg& oHttpMsg); @@ -74,7 +74,7 @@ class SocketChannelImpl: public Channel uint32 GetStepSeq() const { - return(m_ulStepSeq); + return(m_uiStepSeq); } ev_tstamp GetActiveTime() const @@ -121,6 +121,11 @@ class SocketChannelImpl: public Channel return(m_ulUnitTimeMsgNum); } + const std::vector& GetStepWaitForConnected() const + { + return(m_vecStepWaitForConnected); + } + int GetErrno() const { return(m_iErrno); @@ -154,10 +159,10 @@ class SocketChannelImpl: public Channel m_ucChannelStatus = (uint8)eStatus; } - void SetChannelStatus(E_CHANNEL_STATUS eStatus, uint32 ulStepSeq) + void SetChannelStatus(E_CHANNEL_STATUS eStatus, uint32 uiStepSeq) { m_ucChannelStatus = (uint8)eStatus; - m_ulStepSeq = ulStepSeq; + m_uiStepSeq = uiStepSeq; } void SetClientData(const std::string& strClientData) @@ -196,7 +201,7 @@ class SocketChannelImpl: public Channel int32 m_iFd; ///< 文件描述符 uint32 m_ulSeq; ///< 文件描述符创建时对应的序列号 uint32 m_ulForeignSeq; ///< 外来的seq,每个连接的包都是有序的,用作接入Server数据包检查,防止篡包 - uint32 m_ulStepSeq; ///< 正在等待回调的Step seq(比如发出一个HttpPost或HttpGet请求,m_ulStepSeq即为正在等待响应的HttpStep的seq) + uint32 m_uiStepSeq; ///< 正在等待回调的Step seq(比如发出一个HttpPost或HttpGet请求,m_uiStepSeq即为正在等待响应的HttpStep的seq) uint32 m_ulUnitTimeMsgNum; ///< 统计单位时间内接收消息数量 uint32 m_ulMsgNum; ///< 接收消息数量 ev_tstamp m_dActiveTime; ///< 最后一次访问时间 @@ -213,6 +218,7 @@ class SocketChannelImpl: public Channel std::string m_strErrMsg; std::string m_strIdentify; ///< 连接标识(可以为空,不为空时用于标识业务层与连接的关系) std::string m_strRemoteAddr; ///< 对端IP地址(不是客户端地址,但可能跟客户端地址相同) + std::vector m_vecStepWaitForConnected; ///< 等待连接成功的Step Labor* m_pLabor; SocketChannel* m_pSocketChannel; std::shared_ptr m_pLogger; diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 9909b1b4..044240a4 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -114,12 +114,12 @@ E_CODEC_STATUS CodecHttp::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBod E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) { LOG4_TRACE(" "); - ++m_uiDecodedNum; if (pBuff->ReadableBytes() == 0) { LOG4_DEBUG("no data..."); return(CODEC_STATUS_PAUSE); } + ++m_uiDecodedNum; HttpMsg oHttpMsg; E_CODEC_STATUS eCodecStatus = Decode(pBuff, oHttpMsg); if (CODEC_STATUS_OK == eCodecStatus) @@ -636,15 +636,17 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) { LOG4_TRACE(" "); - ++m_uiDecodedNum; if (pBuff->ReadableBytes() == 0) { + /* if (CloseRightAway()) { return(CODEC_STATUS_EOF); } + */ return(CODEC_STATUS_PAUSE); } + ++m_uiDecodedNum; m_parser_setting.on_message_begin = OnMessageBegin; m_parser_setting.on_url = OnUrl; m_parser_setting.on_status = OnStatus; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index a18cf0c2..52415f23 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -215,10 +215,6 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) { m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); } - else if (CODEC_STATUS_WANT_WRITE == eCodecStatus) - { - return(SendTo(pChannel)); - } else { break; @@ -264,6 +260,13 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) else // 编解码出错或连接关闭或连接中断 { LOG4_TRACE("codec error or connection closed!"); + auto& vecUncompletedStep = pChannel->m_pImpl->GetStepWaitForConnected(); + for (auto it = vecUncompletedStep.begin(); + it != vecUncompletedStep.end(); ++it) + { + m_pLabor->GetActorBuilder()->OnError(pChannel, *it, + pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); + } DiscardSocketChannel(pChannel); return(false); } @@ -402,29 +405,18 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) { if (CHANNEL_STATUS_TRY_CONNECT == pChannel->m_pImpl->GetChannelStatus()) // connect之后的第一个写事件 { - if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) + std::shared_ptr pStepConnectWorker = m_pLabor->GetActorBuilder()->MakeSharedStep( + nullptr, "neb::StepConnectWorker", pChannel, pChannel->m_pImpl->m_unRemoteWorkerIdx); + if (nullptr == pStepConnectWorker) { - MsgBody oMsgBody; - oMsgBody.set_data(std::to_string(pChannel->m_pImpl->m_unRemoteWorkerIdx)); - LOG4_DEBUG("send after connect, oMsgBody.ByteSize() = %d", oMsgBody.ByteSize()); - SendTo(pChannel, CMD_REQ_CONNECT_TO_WORKER, m_pLabor->GetSequence(), oMsgBody); - return(true); + LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); + return(false); } - else + if (CMD_STATUS_RUNNING != pStepConnectWorker->Emit(ERR_OK)) { - std::shared_ptr pStepConnectWorker = m_pLabor->GetActorBuilder()->MakeSharedStep( - nullptr, "neb::StepConnectWorker", pChannel, pChannel->m_pImpl->m_unRemoteWorkerIdx); - if (nullptr == pStepConnectWorker) - { - LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); - return(false); - } - if (CMD_STATUS_RUNNING != pStepConnectWorker->Emit(ERR_OK)) - { - m_pLabor->GetActorBuilder()->RemoveStep(pStepConnectWorker); - } - return(true); + m_pLabor->GetActorBuilder()->RemoveStep(pStepConnectWorker); } + return(true); } } else @@ -1116,9 +1108,10 @@ bool Dispatcher::AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiS return(false); } std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); - int iWorkerIndex = atoi(strWorkerIndex.c_str()); + iWorkerIndex = atoi(strWorkerIndex.c_str()); if (iWorkerIndex > 200) { + LOG4_ERROR("worker index must smaller than 200"); return(false); } } From cc45005844334b023eccc4176136e93857328fe6 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 1 Mar 2020 16:32:34 +0800 Subject: [PATCH 089/176] http chunked transfer bug fixed --- src/channel/SocketChannelImpl.cpp | 14 +++++++- src/codec/CodecHttp.cpp | 54 ++++++++++++++++--------------- src/codec/CodecHttp.hpp | 2 +- src/ios/Dispatcher.cpp | 4 +++ 4 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index f71104d3..caf4dd06 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -66,7 +66,7 @@ bool SocketChannelImpl::Init(E_CODEC_TYPE eCodecType, bool bIsClient) m_pCodec->SetKey(m_strKey); break; case CODEC_HTTP: - m_pCodec = new CodecHttp(m_pLogger, eCodecType, m_dKeepAlive); + m_pCodec = new CodecHttp(m_pLogger, eCodecType); m_pCodec->SetKey(m_strKey); break; default: @@ -491,6 +491,10 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; } } + if (((CodecHttp*)m_pCodec)->CloseRightAway()) + { + eCodecStatus = CODEC_STATUS_EOF; + } return(eCodecStatus); } else if (iReadLen == 0) @@ -554,6 +558,10 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, Htt m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; } } + if (((CodecHttp*)m_pCodec)->CloseRightAway()) + { + eCodecStatus = CODEC_STATUS_EOF; + } return(eCodecStatus); } else @@ -640,6 +648,10 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody, Ht if (CODEC_HTTP == m_pCodec->GetCodecType()) { eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); + if (((CodecHttp*)m_pCodec)->CloseRightAway()) + { + eCodecStatus = CODEC_STATUS_EOF; + } return(eCodecStatus); } else diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 044240a4..7894502c 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -374,10 +374,7 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { iHadEncodedSize += iWriteSize; } - } - else - { - iWriteSize = pBuff->Printf("%x\r\n", oHttpMsg.body().size()); + iWriteSize = pBuff->Printf("\r\n0\r\n\r\n"); if (iWriteSize < 0) { pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); @@ -388,12 +385,22 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { iHadEncodedSize += iWriteSize; } - if (oHttpMsg.body().size() == 0) - { - pBuff->Printf("\r\n"); - } - else + } + else + { + if (oHttpMsg.body().size() > 0) { + iWriteSize = pBuff->Printf("%x\r\n", oHttpMsg.body().size()); + if (iWriteSize < 0) + { + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); + m_mapAddingHttpHeader.clear(); + return(CODEC_STATUS_ERR); + } + else + { + iHadEncodedSize += iWriteSize; + } iWriteSize = pBuff->Write(oHttpMsg.body().c_str(), oHttpMsg.body().size()); if (iWriteSize < 0) { @@ -406,6 +413,17 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) iHadEncodedSize += iWriteSize; } } + iWriteSize = pBuff->Printf("\r\n0\r\n\r\n"); + if (iWriteSize < 0) + { + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); + m_mapAddingHttpHeader.clear(); + return(CODEC_STATUS_ERR); + } + else + { + iHadEncodedSize += iWriteSize; + } } } else // Content-Length: %u @@ -638,12 +656,6 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) LOG4_TRACE(" "); if (pBuff->ReadableBytes() == 0) { - /* - if (CloseRightAway()) - { - return(CODEC_STATUS_EOF); - } - */ return(CODEC_STATUS_PAUSE); } ++m_uiDecodedNum; @@ -682,17 +694,7 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) { m_iHttpMajor = oHttpMsg.http_major(); m_iHttpMinor = oHttpMsg.http_minor(); - m_dKeepAlive = oHttpMsg.keep_alive(); - } - else - { - if (m_bChannelIsClient) - { - if (oHttpMsg.keep_alive() == 0.0) - { - m_dKeepAlive = 0.0; - } - } + m_dKeepAlive = (oHttpMsg.keep_alive() > 0) ? oHttpMsg.keep_alive() : m_dKeepAlive; } auto iter = oHttpMsg.headers().find("Content-Encoding"); if (iter != oHttpMsg.headers().end()) diff --git a/src/codec/CodecHttp.hpp b/src/codec/CodecHttp.hpp index 9e2fe6ec..9a18346f 100644 --- a/src/codec/CodecHttp.hpp +++ b/src/codec/CodecHttp.hpp @@ -20,7 +20,7 @@ namespace neb class CodecHttp: public Codec { public: - CodecHttp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive = 10.0); + CodecHttp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive = -1.0); virtual ~CodecHttp(); virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 52415f23..eb68ffed 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -215,6 +215,10 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) { m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); } + else if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + } else { break; From 8d348f3d27e50489bb634978edaa6e08e6898d3f Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 8 Mar 2020 12:56:47 +0800 Subject: [PATCH 090/176] add codec auto switch --- src/channel/SocketChannelImpl.cpp | 211 +++++++++++------------------- src/channel/SocketChannelImpl.hpp | 18 +-- src/codec/Codec.cpp | 12 ++ src/codec/Codec.hpp | 5 + src/codec/CodecHttp.cpp | 180 +++++-------------------- src/codec/CodecPrivate.cpp | 12 +- src/codec/CodecProto.cpp | 2 +- src/ios/Dispatcher.cpp | 95 +++++++++++++- src/ios/Dispatcher.hpp | 1 + src/ios/Nodes.cpp | 2 +- src/labor/Manager.cpp | 4 +- 11 files changed, 232 insertions(+), 310 deletions(-) diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index caf4dd06..6cdf2212 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -20,8 +20,10 @@ namespace neb { SocketChannelImpl::SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) - : m_ucChannelStatus(CHANNEL_STATUS_INIT), m_unRemoteWorkerIdx(0), m_iFd(iFd), m_ulSeq(ulSeq), m_ulForeignSeq(0), m_uiStepSeq(0), - m_ulUnitTimeMsgNum(0), m_ulMsgNum(0), m_dActiveTime(0.0), m_dKeepAlive(dKeepAlive), + : m_ucChannelStatus(CHANNEL_STATUS_INIT), + m_unRemoteWorkerIdx(0), m_iFd(iFd), m_uiSeq(ulSeq), m_uiForeignSeq(0), m_uiStepSeq(0), + m_uiUnitTimeMsgNum(0), m_uiMsgNum(0), + m_dActiveTime(0.0), m_dKeepAlive(dKeepAlive), m_pIoWatcher(NULL), m_pTimerWatcher(NULL), m_pRecvBuff(nullptr), m_pSendBuff(nullptr), m_pWaitForSendBuff(nullptr), m_pCodec(nullptr), m_iErrno(0), m_pLabor(nullptr), m_pSocketChannel(pSocketChannel), m_pLogger(pLogger) @@ -31,7 +33,7 @@ SocketChannelImpl::SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ SocketChannelImpl::~SocketChannelImpl() { - LOG4_DEBUG("SocketChannelImpl::~SocketChannelImpl() fd %d, seq %u", m_iFd, m_ulSeq); + LOG4_DEBUG("SocketChannelImpl::~SocketChannelImpl() fd %d, seq %u", m_iFd, m_uiSeq); m_vecStepWaitForConnected.clear(); if (CHANNEL_STATUS_CLOSED != m_ucChannelStatus) { @@ -111,10 +113,10 @@ bool SocketChannelImpl::NeedAliveCheck() const E_CODEC_STATUS SocketChannelImpl::Send() { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_ulSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_ulSeq, m_ucChannelStatus); + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); return(CODEC_STATUS_EOF); } else if (CHANNEL_STATUS_ESTABLISHED != m_ucChannelStatus) @@ -178,10 +180,10 @@ E_CODEC_STATUS SocketChannelImpl::Send() E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], cmd[%u], seq[%u]", m_iFd, m_ulSeq, iCmd, uiSeq); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], cmd[%u], seq[%u]", m_iFd, m_uiSeq, iCmd, uiSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_ulSeq, m_ucChannelStatus); + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); return(CODEC_STATUS_EOF); } E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; @@ -292,10 +294,10 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_ulSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_ulSeq, m_ucChannelStatus); + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); return(CODEC_STATUS_EOF); } E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; @@ -370,10 +372,10 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_ulSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_ulSeq, m_ucChannelStatus); + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); return(CODEC_STATUS_EOF); } int iReadLen = 0; @@ -395,11 +397,11 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) case CHANNEL_STATUS_ESTABLISHED: if ((gc_uiCmdReq & oMsgHead.cmd()) && (m_strClientData.size() > 0)) { - m_ulForeignSeq = oMsgHead.seq(); - ++m_ulUnitTimeMsgNum; - ++m_ulMsgNum; + m_uiForeignSeq = oMsgHead.seq(); + ++m_uiUnitTimeMsgNum; + ++m_uiMsgNum; oMsgBody.set_add_on(m_strClientData); - if (m_ulMsgNum) + if (m_uiMsgNum == 1) { m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; } @@ -437,7 +439,14 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) return(CODEC_STATUS_ERR); } } - LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_ulSeq, oMsgHead.cmd(), oMsgHead.seq()); + else + { + if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) + { + return(CODEC_STATUS_INVALID); + } + } + LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_uiSeq, oMsgHead.cmd(), oMsgHead.seq()); return(eCodecStatus); } else if (iReadLen == 0) @@ -463,10 +472,10 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_ulSeq); + LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_ulSeq, m_ucChannelStatus); + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); return(CODEC_STATUS_EOF); } int iReadLen = 0; @@ -484,13 +493,20 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) E_CODEC_STATUS eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); if (CODEC_STATUS_OK == eCodecStatus) { - ++m_ulUnitTimeMsgNum; - ++m_ulMsgNum; - if (m_ulMsgNum > 0) + ++m_uiUnitTimeMsgNum; + ++m_uiMsgNum; + if (m_uiMsgNum == 1) { m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; } } + else + { + if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) + { + return(CODEC_STATUS_INVALID); + } + } if (((CodecHttp*)m_pCodec)->CloseRightAway()) { eCodecStatus = CODEC_STATUS_EOF; @@ -526,89 +542,12 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) } } -E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, HttpMsg& oHttpMsg) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_ulSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) - { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_ulSeq, m_ucChannelStatus); - return(CODEC_STATUS_EOF); - } - int iReadLen = 0; - iReadLen = Read(m_pRecvBuff, m_iErrno); - LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", - m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - if (iReadLen > 0) - { - if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ - && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) - { - m_pRecvBuff->Compact(m_pRecvBuff->ReadableBytes() * 2); - } - m_dActiveTime = m_pLabor->GetNowTime(); - if (CODEC_HTTP == m_pCodec->GetCodecType()) - { - E_CODEC_STATUS eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); - if (CODEC_STATUS_OK == eCodecStatus) - { - ++m_ulUnitTimeMsgNum; - ++m_ulMsgNum; - if (m_ulMsgNum > 0) - { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - } - } - if (((CodecHttp*)m_pCodec)->CloseRightAway()) - { - eCodecStatus = CODEC_STATUS_EOF; - } - return(eCodecStatus); - } - else - { - E_CODEC_STATUS eCodecStatus = m_pCodec->Decode(m_pRecvBuff, oMsgHead, oMsgBody); - if ((CODEC_STATUS_OK == eCodecStatus) && (gc_uiCmdReq & oMsgHead.cmd()) && (m_strClientData.size() > 0)) - { - m_ulForeignSeq = oMsgHead.seq(); - ++m_ulUnitTimeMsgNum; - ++m_ulMsgNum; - oMsgBody.set_add_on(m_strClientData); - if (m_ulMsgNum >0) - { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - } - LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_ulSeq, oMsgHead.cmd(), oMsgHead.seq()); - } - return(eCodecStatus); - } - } - else if (iReadLen == 0) - { - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_DEBUG("fd %d closed by peer, error %d %s!", - m_iFd, m_iErrno, m_strErrMsg.c_str()); - return(CODEC_STATUS_EOF); - } - else - { - if (EAGAIN == m_iErrno && EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - return(CODEC_STATUS_PAUSE); - } - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); - return(CODEC_STATUS_INT); - } -} - E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_ulSeq); + LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_ulSeq, m_ucChannelStatus); + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); return(CODEC_STATUS_EOF); } LOG4_TRACE("fetch from fd %d and m_pRecvBuff->ReadableBytes() = %d", @@ -616,20 +555,20 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) E_CODEC_STATUS eCodecStatus = m_pCodec->Decode(m_pRecvBuff, oMsgHead, oMsgBody); if (CODEC_STATUS_OK == eCodecStatus) { - m_ulForeignSeq = oMsgHead.seq(); - ++m_ulUnitTimeMsgNum; - ++m_ulMsgNum; - LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_ulSeq, oMsgHead.cmd(), oMsgHead.seq()); + m_uiForeignSeq = oMsgHead.seq(); + ++m_uiUnitTimeMsgNum; + ++m_uiMsgNum; + LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_uiSeq, oMsgHead.cmd(), oMsgHead.seq()); } return(eCodecStatus); } E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_ulSeq); + LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_ulSeq, m_ucChannelStatus); + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); return(CODEC_STATUS_EOF); } // 当http1.0响应包未带Content-Length头时,m_pRecvBuff可读字节数为0,以关闭连接表示数据发送完毕。 @@ -637,37 +576,6 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) return(eCodecStatus); } -E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody, HttpMsg& oHttpMsg) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_ulSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) - { - return(CODEC_STATUS_EOF); - } - E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; - if (CODEC_HTTP == m_pCodec->GetCodecType()) - { - eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); - if (((CodecHttp*)m_pCodec)->CloseRightAway()) - { - eCodecStatus = CODEC_STATUS_EOF; - } - return(eCodecStatus); - } - else - { - eCodecStatus = m_pCodec->Decode(m_pRecvBuff, oMsgHead, oMsgBody); - if (CODEC_STATUS_OK == eCodecStatus) - { - m_ulForeignSeq = oMsgHead.seq(); - ++m_ulUnitTimeMsgNum; - ++m_ulMsgNum; - LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_ulSeq, oMsgHead.cmd(), oMsgHead.seq()); - } - return(eCodecStatus); - } -} - void SocketChannelImpl::SetSecretKey(const std::string& strKey) { m_strKey = strKey; @@ -677,7 +585,7 @@ void SocketChannelImpl::SetSecretKey(const std::string& strKey) bool SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive) { LOG4_TRACE("channel_fd[%d], channel_seq[%d], codec_type[%d], new_codec_type[%d]", - m_iFd, m_ulSeq, m_pCodec->GetCodecType(), eCodecType); + m_iFd, m_uiSeq, m_pCodec->GetCodecType(), eCodecType); if (eCodecType == m_pCodec->GetCodecType()) { return(true); @@ -719,6 +627,33 @@ bool SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAliv return(true); } +bool SocketChannelImpl::AutoSwitchCodec() +{ + auto& vecAutoSwitchCodecType = Codec::GetAutoSwitchCodecType(); + for (auto codec_iter = vecAutoSwitchCodecType.begin(); + codec_iter != vecAutoSwitchCodecType.end(); ++codec_iter) + { + if (*codec_iter == GetCodecType()) + { + m_setSkipCodecType.insert(*codec_iter); + continue; + } + else + { + auto skip_iter = m_setSkipCodecType.find(*codec_iter); + if (skip_iter == m_setSkipCodecType.end()) + { + return(SwitchCodec(*codec_iter, -1.0)); + } + else + { + continue; + } + } + } + return(false); +} + ev_io* SocketChannelImpl::MutableIoWatcher() { if (NULL == m_pIoWatcher) diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index ed02f573..848def9c 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -54,10 +54,8 @@ class SocketChannelImpl: public Channel virtual E_CODEC_STATUS Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq); virtual E_CODEC_STATUS Recv(MsgHead& oMsgHead, MsgBody& oMsgBody); virtual E_CODEC_STATUS Recv(HttpMsg& oHttpMsg); - virtual E_CODEC_STATUS Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, HttpMsg& oHttpMsg); E_CODEC_STATUS Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody); E_CODEC_STATUS Fetch(HttpMsg& oHttpMsg); - E_CODEC_STATUS Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody, HttpMsg& oHttpMsg); template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); @@ -69,7 +67,7 @@ class SocketChannelImpl: public Channel uint32 GetSequence() const { - return(m_ulSeq); + return(m_uiSeq); } uint32 GetStepSeq() const @@ -113,12 +111,12 @@ class SocketChannelImpl: public Channel uint32 GetMsgNum() const { - return(m_ulMsgNum); + return(m_uiMsgNum); } uint32 GetUnitTimeMsgNum() const { - return(m_ulUnitTimeMsgNum); + return(m_uiUnitTimeMsgNum); } const std::vector& GetStepWaitForConnected() const @@ -183,6 +181,7 @@ class SocketChannelImpl: public Channel void SetSecretKey(const std::string& strKey); bool SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive); + bool AutoSwitchCodec(); ev_io* MutableIoWatcher(); @@ -199,11 +198,11 @@ class SocketChannelImpl: public Channel char m_szErrBuff[256]; uint16 m_unRemoteWorkerIdx; ///< 对端Worker进程ID,若不涉及则无需关心 int32 m_iFd; ///< 文件描述符 - uint32 m_ulSeq; ///< 文件描述符创建时对应的序列号 - uint32 m_ulForeignSeq; ///< 外来的seq,每个连接的包都是有序的,用作接入Server数据包检查,防止篡包 + uint32 m_uiSeq; ///< 文件描述符创建时对应的序列号 + uint32 m_uiForeignSeq; ///< 外来的seq,每个连接的包都是有序的,用作接入Server数据包检查,防止篡包 uint32 m_uiStepSeq; ///< 正在等待回调的Step seq(比如发出一个HttpPost或HttpGet请求,m_uiStepSeq即为正在等待响应的HttpStep的seq) - uint32 m_ulUnitTimeMsgNum; ///< 统计单位时间内接收消息数量 - uint32 m_ulMsgNum; ///< 接收消息数量 + uint32 m_uiUnitTimeMsgNum; ///< 统计单位时间内接收消息数量 + uint32 m_uiMsgNum; ///< 接收消息数量 ev_tstamp m_dActiveTime; ///< 最后一次访问时间 ev_tstamp m_dKeepAlive; ///< 连接保持时间 ev_io* m_pIoWatcher; ///< 不在结构体析构时回收 @@ -219,6 +218,7 @@ class SocketChannelImpl: public Channel std::string m_strIdentify; ///< 连接标识(可以为空,不为空时用于标识业务层与连接的关系) std::string m_strRemoteAddr; ///< 对端IP地址(不是客户端地址,但可能跟客户端地址相同) std::vector m_vecStepWaitForConnected; ///< 等待连接成功的Step + std::set m_setSkipCodecType; ///< Codec转换需跳过的CodecType Labor* m_pLabor; SocketChannel* m_pSocketChannel; std::shared_ptr m_pLogger; diff --git a/src/codec/Codec.cpp b/src/codec/Codec.cpp index dd9f35ec..9d938982 100644 --- a/src/codec/Codec.cpp +++ b/src/codec/Codec.cpp @@ -19,6 +19,8 @@ namespace neb { +std::vector Codec::m_vecAutoSwitchCodecType; + Codec::Codec(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) : m_pLogger(pLogger), m_eCodecType(eCodecType) { @@ -29,6 +31,16 @@ Codec::~Codec() LOG4_TRACE(""); } +const std::vector& Codec::GetAutoSwitchCodecType() +{ + return(m_vecAutoSwitchCodecType); +} + +void Codec::AddAutoSwitchCodecType(E_CODEC_TYPE eCodecType) +{ + m_vecAutoSwitchCodecType.push_back(eCodecType); +} + bool Codec::Zip(const std::string& strSrc, std::string& strDest) { /* diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 6871736e..9c704d4a 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -13,6 +13,7 @@ //#include //#include +#include #include #include "util/CBuffer.hpp" #include "pb/msg.pb.h" @@ -94,6 +95,9 @@ class Codec m_strKey = strKey; } + static const std::vector& GetAutoSwitchCodecType(); + static void AddAutoSwitchCodecType(E_CODEC_TYPE eCodecType); + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); protected: @@ -117,6 +121,7 @@ class Codec private: E_CODEC_TYPE m_eCodecType; std::string m_strKey; // 密钥 + static std::vector m_vecAutoSwitchCodecType; friend class SocketChannel; }; diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 7894502c..13416787 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -430,164 +430,52 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { if (strGzipData.size() > 0) { - if (strGzipData.size() > 8192) // 长度太长,使用chunked编码传输 + iWriteSize = pBuff->Printf("Content-Length: %u\r\n\r\n", strGzipData.size()); + if (iWriteSize < 0) { - bIsChunked = true; - iWriteSize = pBuff->Printf("Transfer-Encoding: chunked\r\n\r\n"); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } - size_t iChunkLength = 8192; - for (size_t iPos = 0; iPos < strGzipData.size(); iPos += iChunkLength) - { - iChunkLength = (iChunkLength < strGzipData.size() - iPos) ? iChunkLength : (strGzipData.size() - iPos); - iWriteSize = pBuff->Printf("%x\r\n", iChunkLength); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } - iWriteSize = pBuff->Printf("%s\r\n", strGzipData.substr(iPos, iChunkLength).c_str(), iChunkLength); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } - } - iWriteSize = pBuff->Printf("0\r\n\r\n"); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); + m_mapAddingHttpHeader.clear(); + return(CODEC_STATUS_ERR); } else { - iWriteSize = pBuff->Printf("Content-Length: %u\r\n\r\n", strGzipData.size()); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } - iWriteSize = pBuff->Write(strGzipData.c_str(), strGzipData.size()); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } + iHadEncodedSize += iWriteSize; + } + iWriteSize = pBuff->Write(strGzipData.c_str(), strGzipData.size()); + if (iWriteSize < 0) + { + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); + m_mapAddingHttpHeader.clear(); + return(CODEC_STATUS_ERR); + } + else + { + iHadEncodedSize += iWriteSize; } } else { - if (oHttpMsg.body().size() > 8192) + iWriteSize = pBuff->Printf("Content-Length: %u\r\n\r\n", oHttpMsg.body().size()); + if (iWriteSize < 0) { - bIsChunked = true; - iWriteSize = pBuff->Printf("Transfer-Encoding: chunked\r\n\r\n"); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } - size_t iChunkLength = 8192; - for (size_t iPos = 0; iPos < oHttpMsg.body().size(); iPos += iChunkLength) - { - iChunkLength = (iChunkLength < oHttpMsg.body().size() - iPos) ? iChunkLength : (oHttpMsg.body().size() - iPos); - iWriteSize = pBuff->Printf("%x\r\n", iChunkLength); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } - iWriteSize = pBuff->Printf("%s\r\n", oHttpMsg.body().substr(iPos, iChunkLength).c_str(), iChunkLength); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } - } - iWriteSize = pBuff->Printf("0\r\n\r\n"); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); + m_mapAddingHttpHeader.clear(); + return(CODEC_STATUS_ERR); } else { - iWriteSize = pBuff->Printf("Content-Length: %u\r\n\r\n", oHttpMsg.body().size()); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } - iWriteSize = pBuff->Write(oHttpMsg.body().c_str(), oHttpMsg.body().size()); - if (iWriteSize < 0) - { - pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - else - { - iHadEncodedSize += iWriteSize; - } + iHadEncodedSize += iWriteSize; + } + iWriteSize = pBuff->Write(oHttpMsg.body().c_str(), oHttpMsg.body().size()); + if (iWriteSize < 0) + { + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); + m_mapAddingHttpHeader.clear(); + return(CODEC_STATUS_ERR); + } + else + { + iHadEncodedSize += iWriteSize; } } } diff --git a/src/codec/CodecPrivate.cpp b/src/codec/CodecPrivate.cpp index 747a58c0..581571ef 100644 --- a/src/codec/CodecPrivate.cpp +++ b/src/codec/CodecPrivate.cpp @@ -229,7 +229,7 @@ E_CODEC_STATUS CodecPrivate::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& strRawData.assign((const char*)pBuff->GetRawReadBuffer(), stMsgHead.body_len); if (!Rc5Decrypt(strRawData, strDecryptData)) { - LOG4_ERROR("Rc5Decrypt error!"); + LOG4_WARNING("Rc5Decrypt error!"); return(CODEC_STATUS_ERR); } } @@ -239,7 +239,7 @@ E_CODEC_STATUS CodecPrivate::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& { if (!Unzip(strDecryptData, strUncompressData)) { - LOG4_ERROR("uncompress error!"); + LOG4_WARNING("uncompress error!"); return(CODEC_STATUS_ERR); } } @@ -249,7 +249,7 @@ E_CODEC_STATUS CodecPrivate::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& strRawData.assign((const char*)pBuff->GetRawReadBuffer(), stMsgHead.body_len); if (!Unzip(strRawData, strUncompressData)) { - LOG4_ERROR("uncompress error!"); + LOG4_WARNING("uncompress error!"); return(CODEC_STATUS_ERR); } } @@ -260,7 +260,7 @@ E_CODEC_STATUS CodecPrivate::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& { if (!Gunzip(strDecryptData, strUncompressData)) { - LOG4_ERROR("uncompress error!"); + LOG4_WARNING("uncompress error!"); return(CODEC_STATUS_ERR); } } @@ -270,7 +270,7 @@ E_CODEC_STATUS CodecPrivate::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& strRawData.assign((const char*)pBuff->GetRawReadBuffer(), stMsgHead.body_len); if (!Gunzip(strRawData, strUncompressData)) { - LOG4_ERROR("uncompress error!"); + LOG4_WARNING("uncompress error!"); return(CODEC_STATUS_ERR); } } @@ -298,7 +298,7 @@ E_CODEC_STATUS CodecPrivate::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& } else { - LOG4_ERROR("cmd[%u], seq[%u] oMsgBody.ParseFromArray() error!", oMsgHead.cmd(), oMsgHead.seq()); + LOG4_WARNING("cmd[%u], seq[%u] oMsgBody.ParseFromArray() error!", oMsgHead.cmd(), oMsgHead.seq()); return(CODEC_STATUS_ERR); } } diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp index 47d8e105..25db9749 100644 --- a/src/codec/CodecProto.cpp +++ b/src/codec/CodecProto.cpp @@ -86,7 +86,7 @@ E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oM } else { - LOG4_ERROR("cmd[%u], seq[%u] oMsgBody.ParseFromArray() error!", oMsgHead.cmd(), oMsgHead.seq()); + LOG4_WARNING("cmd[%u], seq[%u] oMsgBody.ParseFromArray() error!", oMsgHead.cmd(), oMsgHead.seq()); return(CODEC_STATUS_ERR); } } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index eb68ffed..a9c7ffad 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -9,11 +9,6 @@ ******************************************************************************/ #include "Dispatcher.hpp" -#include -#include -#include -#include -#include #include #include "util/process_helper.h" #include "Definition.hpp" @@ -215,7 +210,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) { m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); } - else if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) + else if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close { m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); } @@ -263,6 +258,89 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) } else // 编解码出错或连接关闭或连接中断 { + if (CODEC_STATUS_INVALID == eCodecStatus) + { + if (pChannel->m_pImpl->AutoSwitchCodec()) + { + return(DataFetchAndHandle(pChannel)); + } + } + LOG4_TRACE("codec error or connection closed!"); + auto& vecUncompletedStep = pChannel->m_pImpl->GetStepWaitForConnected(); + for (auto it = vecUncompletedStep.begin(); + it != vecUncompletedStep.end(); ++it) + { + m_pLabor->GetActorBuilder()->OnError(pChannel, *it, + pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); + } + DiscardSocketChannel(pChannel); + return(false); + } +} + +bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) +{ + LOG4_TRACE(" "); + E_CODEC_STATUS eCodecStatus; + if (CODEC_HTTP == pChannel->m_pImpl->GetCodecType()) + { + HttpMsg oHttpMsg; + eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); + while (CODEC_STATUS_OK == eCodecStatus) + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); + } + if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + } + } + else + { + MsgHead oMsgHead; + MsgBody oMsgBody; + eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); + while (CODEC_STATUS_OK == eCodecStatus) + { + if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) + { + if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 + { + LOG4_DEBUG("invalid request, please login first!"); + DiscardSocketChannel(pChannel); + return(false); + } + } + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); + oMsgHead.Clear(); // 注意protobuf的Clear()使用不当容易造成内存泄露 + oMsgBody.Clear(); + eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); + } + } + + if (CODEC_STATUS_PAUSE == eCodecStatus) + { + return(true); + } + else if (CODEC_STATUS_WANT_WRITE == eCodecStatus) + { + return(SendTo(pChannel)); + } + else if (CODEC_STATUS_WANT_READ == eCodecStatus) + { + RemoveIoWriteEvent(pChannel); + return(true); + } + else // 编解码出错或连接关闭或连接中断 + { + if (CODEC_STATUS_INVALID == eCodecStatus) + { + if (pChannel->m_pImpl->AutoSwitchCodec()) + { + return(DataFetchAndHandle(pChannel)); + } + } LOG4_TRACE("codec error or connection closed!"); auto& vecUncompletedStep = pChannel->m_pImpl->GetStepWaitForConnected(); for (auto it = vecUncompletedStep.begin(); @@ -1706,6 +1784,9 @@ bool Dispatcher::Init() #else m_pSessionNode = std::unique_ptr(new Nodes()); #endif + Codec::AddAutoSwitchCodecType(CODEC_HTTP); + Codec::AddAutoSwitchCodecType(CODEC_PROTO); + Codec::AddAutoSwitchCodecType(CODEC_PRIVATE); return(true); } @@ -2051,7 +2132,7 @@ bool Dispatcher::AcceptServerConn(int iFd) std::shared_ptr pChannel = CreateSocketChannel(iAcceptFd, CODEC_NEBULA); if (NULL != pChannel) { - AddIoTimeout(pChannel, 1.0); // 为了防止大量连接攻击,初始化连接只有一秒即超时,在正常发送第一个数据包之后才采用正常配置的网络IO超时检查 + AddIoTimeout(pChannel, 1.0); // 初始化连接只有一秒即超时,在正常发送第一个数据包之后才采用正常配置的网络IO超时检查 AddIoReadEvent(pChannel); } } diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index b3d0d16a..1cb52252 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -101,6 +101,7 @@ class Dispatcher bool OnIoRead(std::shared_ptr pChannel); bool DataRecvAndHandle(std::shared_ptr pChannel); + bool DataFetchAndHandle(std::shared_ptr pChannel); bool FdTransfer(int iFd); bool OnIoWrite(std::shared_ptr pChannel); bool OnIoError(std::shared_ptr pChannel); diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index 36b2b403..c3b9586d 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -156,7 +156,7 @@ void Nodes::AddNode(const std::string& strNodeType, const std::string& strNodeId pNode->mapNode2Hash.insert(std::make_pair(strNodeIdentify, vecHash)); for (int i = 0; i < m_iVirtualNodeNum / iPointPerHash; ++i) // distribution: ketama { - snprintf(szVirtualNodeIdentify, 32, "%s##%d", strNodeIdentify.c_str(), i); + snprintf(szVirtualNodeIdentify, 32, "%d@%s##%d", m_iVirtualNodeNum - i, strNodeIdentify.c_str(), i); oMd5.CalculateDigest(szDigest, (const CryptoPP::byte*)szVirtualNodeIdentify, strlen(szVirtualNodeIdentify)); for (int j = 0; j < iPointPerHash; ++j) { diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 8d889a3e..17ab4fe9 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -169,8 +169,8 @@ bool Manager::InitActorBuilder() return(false); } if (!m_pActorBuilder->Init( - m_oCurrentConf["load_conf"]["manager"]["boot_load"], - m_oCurrentConf["load_conf"]["manager"]["dynamic_loading"])) + m_oCurrentConf["load_config"]["manager"]["boot_load"], + m_oCurrentConf["load_config"]["manager"]["dynamic_loading"])) { LOG4_ERROR("ActorBuilder->Init() failed!"); return(false); From 43773bf415d0577c708a638e19619b6b443b7eb5 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 21 Mar 2020 22:22:17 +0800 Subject: [PATCH 091/176] V1.0 release --- README.md | 3 +++ README_cn.md | 3 +++ src/actor/Actor.cpp | 2 +- src/actor/chain/Chain.cpp | 4 +++ src/ios/Nodes.cpp | 55 ++++++++++++++++++++++----------------- src/labor/Labor.hpp | 1 + src/labor/Manager.cpp | 7 +++++ src/labor/Manager.hpp | 2 ++ src/labor/Worker.hpp | 2 +- 9 files changed, 53 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 0c25cbe7..2c9090ca 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,9 @@ A simple testing can be start with a NebulaInterface only, and also can be start - optimize Actor dynamic creator - optimize HTTP connection and HTTP data sending and receiving - RedisChannel bug fixed + - add bind ip + - add load stress + - add codec auto switch #### v0.10 - the plugin version control dynamically unloads and loads the instant validation feature. - optimize reflection dynamic creation of Actor. diff --git a/README_cn.md b/README_cn.md index dd279bd8..4531d3f0 100644 --- a/README_cn.md +++ b/README_cn.md @@ -121,6 +121,9 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 - 优化Actor通过反射动态创建实例 - 优化http短连接和http数据收发 - RedisChannel bug修复 + - 增加绑定IP配置 + - 增加压力测试相关接口 + - 增加编解码器自动转换 #### v0.10 - 增加插件so版本控制动态卸载和加载即时生效功能 - 优化反射动态创建Actor diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 623f5c9c..f3b9e35b 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -86,7 +86,7 @@ long Actor::GetNowTimeMs() const const CJsonObject& Actor::GetCustomConf() const { - return(((Worker*)m_pLabor)->GetCustomConf()); + return(m_pLabor->GetCustomConf()); } std::shared_ptr Actor::GetSession(uint32 uiSessionId) { diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 9e83a581..74b45674 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -156,6 +156,10 @@ E_CMD_STATUS Chain::Next() E_CMD_STATUS Chain::Timeout() { + if (m_uiWaitingStep == 0 && m_queChainBlock.empty()) + { + return(CMD_STATUS_COMPLETED); + } LOG4_ERROR("chain_id %d timeout, chain flag \"%s\"", GetSequence(), m_strChainFlag.c_str()); return(CMD_STATUS_FAULT); } diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index c3b9586d..3dbbbd41 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -8,10 +8,11 @@ * @note * Modify history: ******************************************************************************/ +#include "Nodes.hpp" +#include #define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 #include "cryptopp/md5.h" -//#include "cryptopp/hex.h" -#include "Nodes.hpp" +#include "cryptopp/hex.h" namespace neb { @@ -148,26 +149,29 @@ void Nodes::AddNode(const std::string& strNodeType, const std::string& strNodeId { std::shared_ptr pNode = std::make_shared(); m_mapNode.insert(std::make_pair(strNodeType, pNode)); - CryptoPP::Weak::MD5 oMd5; - char szVirtualNodeIdentify[32] = {0}; - CryptoPP::byte szDigest[CryptoPP::Weak::MD5::DIGESTSIZE] = {0}; + std::string strHash; + char szVirtualNodeIdentify[40] = {0}; int32 iPointPerHash = 4; std::vector vecHash; - pNode->mapNode2Hash.insert(std::make_pair(strNodeIdentify, vecHash)); for (int i = 0; i < m_iVirtualNodeNum / iPointPerHash; ++i) // distribution: ketama { - snprintf(szVirtualNodeIdentify, 32, "%d@%s##%d", m_iVirtualNodeNum - i, strNodeIdentify.c_str(), i); - oMd5.CalculateDigest(szDigest, (const CryptoPP::byte*)szVirtualNodeIdentify, strlen(szVirtualNodeIdentify)); + snprintf(szVirtualNodeIdentify, 40, "%d@%s#%d", m_iVirtualNodeNum - i, strNodeIdentify.c_str(), i); + CryptoPP::Weak1::MD5 oMd5; + CryptoPP::HexEncoder oHexEncoder; + oMd5.Update((const CryptoPP::byte*)szVirtualNodeIdentify, strlen(szVirtualNodeIdentify)); + strHash.resize(oMd5.DigestSize()); + oMd5.Final((CryptoPP::byte*)&strHash[0]); for (int j = 0; j < iPointPerHash; ++j) { - uint32 k = ((uint32)(szDigest[3 + j * iPointPerHash] & 0xFF) << 24) - | ((uint32)(szDigest[2 + j * iPointPerHash] & 0xFF) << 16) - | ((uint32)(szDigest[1 + j * iPointPerHash] & 0xFF) << 8) - | (szDigest[j * iPointPerHash] & 0xFF); - pNode->mapNode2Hash.begin()->second.push_back(k); + uint32 k = ((uint32)(strHash[3 + j * iPointPerHash] & 0xFF) << 24) + | ((uint32)(strHash[2 + j * iPointPerHash] & 0xFF) << 16) + | ((uint32)(strHash[1 + j * iPointPerHash] & 0xFF) << 8) + | (strHash[j * iPointPerHash] & 0xFF); + vecHash.push_back(k); pNode->mapHash2Node.insert(std::make_pair(k, strNodeIdentify)); } } + pNode->mapNode2Hash.insert(std::make_pair(strNodeIdentify, std::move(vecHash))); pNode->itPollingNode = pNode->mapNode2Hash.begin(); pNode->itHashRing = pNode->mapHash2Node.begin(); } @@ -176,26 +180,29 @@ void Nodes::AddNode(const std::string& strNodeType, const std::string& strNodeId auto node_iter = node_type_iter->second->mapNode2Hash.find(strNodeIdentify); if (node_iter == node_type_iter->second->mapNode2Hash.end()) { - CryptoPP::Weak::MD5 oMd5; - char szVirtualNodeIdentify[32] = {0}; - CryptoPP::byte szDigest[CryptoPP::Weak::MD5::DIGESTSIZE] = {0}; + std::string strHash; + char szVirtualNodeIdentify[40] = {0}; int32 iPointPerHash = 4; std::vector vecHash; - node_type_iter->second->mapNode2Hash.insert(std::make_pair(strNodeIdentify, vecHash)); for (int i = 0; i < m_iVirtualNodeNum / iPointPerHash; ++i) // distribution: ketama { - snprintf(szVirtualNodeIdentify, 32, "%s##%d", strNodeIdentify.c_str(), i); - oMd5.CalculateDigest(szDigest, (const CryptoPP::byte*)szVirtualNodeIdentify, strlen(szVirtualNodeIdentify)); + snprintf(szVirtualNodeIdentify, 40, "%d@%s#%d", m_iVirtualNodeNum - i, strNodeIdentify.c_str(), i); + CryptoPP::Weak1::MD5 oMd5; + CryptoPP::HexEncoder oHexEncoder; + oMd5.Update((const CryptoPP::byte*)szVirtualNodeIdentify, strlen(szVirtualNodeIdentify)); + strHash.resize(oMd5.DigestSize()); + oMd5.Final((CryptoPP::byte*)&strHash[0]); for (int j = 0; j < iPointPerHash; ++j) { - uint32 k = ((uint32)(szDigest[3 + j * iPointPerHash] & 0xFF) << 24) - | ((uint32)(szDigest[2 + j * iPointPerHash] & 0xFF) << 16) - | ((uint32)(szDigest[1 + j * iPointPerHash] & 0xFF) << 8) - | (szDigest[j * iPointPerHash] & 0xFF); - node_type_iter->second->mapNode2Hash.begin()->second.push_back(k); + uint32 k = ((uint32)(strHash[3 + j * iPointPerHash] & 0xFF) << 24) + | ((uint32)(strHash[2 + j * iPointPerHash] & 0xFF) << 16) + | ((uint32)(strHash[1 + j * iPointPerHash] & 0xFF) << 8) + | (strHash[j * iPointPerHash] & 0xFF); + vecHash.push_back(k); node_type_iter->second->mapHash2Node.insert(std::make_pair(k, strNodeIdentify)); } } + node_type_iter->second->mapNode2Hash.insert(std::make_pair(strNodeIdentify, std::move(vecHash))); node_type_iter->second->itPollingNode = node_type_iter->second->mapNode2Hash.begin(); node_type_iter->second->itHashRing = node_type_iter->second->mapHash2Node.begin(); } diff --git a/src/labor/Labor.hpp b/src/labor/Labor.hpp index 63ef34a1..11ca512a 100644 --- a/src/labor/Labor.hpp +++ b/src/labor/Labor.hpp @@ -62,6 +62,7 @@ class Labor virtual void SetNodeId(uint32 uiNodeId) = 0; virtual bool AddNetLogMsg(const MsgBody& oMsgBody) = 0; virtual void OnTerminated(struct ev_signal* watcher) = 0; + virtual const CJsonObject& GetCustomConf() const = 0; virtual bool WithSsl() { return(false); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 17ab4fe9..e6aaa6f2 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -228,6 +228,7 @@ bool Manager::GetConf() std::cerr << "failed to parse json file " << m_stNodeInfo.strConfFile << std::endl; return(false); } + m_oCustomConf = m_oCurrentConf["custom"]; ssContent.str(""); fin.close(); } @@ -666,6 +667,7 @@ const CJsonObject& Manager::GetNodeConf() const void Manager::SetNodeConf(const CJsonObject& oNodeConf) { m_oCurrentConf = oNodeConf; + m_oCustomConf = m_oCurrentConf["custom"]; } const NodeInfo& Manager::GetNodeInfo() const @@ -678,6 +680,11 @@ void Manager::SetNodeId(uint32 uiNodeId) m_stNodeInfo.uiNodeId = uiNodeId; } +const CJsonObject& Manager::GetCustomConf() const +{ + return(m_oCustomConf); +} + const Manager::tagManagerInfo& Manager::GetManagerInfo() const { return(m_stManagerInfo); diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index 680b6b65..27b884af 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -90,6 +90,7 @@ class Manager: public Labor virtual void SetNodeConf(const CJsonObject& oNodeConf); virtual const NodeInfo& GetNodeInfo() const; virtual void SetNodeId(uint32 uiNodeId); + virtual const CJsonObject& GetCustomConf() const; const tagManagerInfo& GetManagerInfo() const; virtual bool AddNetLogMsg(const MsgBody& oMsgBody); @@ -117,6 +118,7 @@ class Manager: public Labor CJsonObject m_oLastConf; ///< 上次加载的配置 CJsonObject m_oCurrentConf; ///< 当前加载的配置 + CJsonObject m_oCustomConf; ///< 自定义配置 NodeInfo m_stNodeInfo; tagManagerInfo m_stManagerInfo; ev_timer* m_pPeriodicTaskWatcher = NULL; ///< 进程周期任务定时器 diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 2f558f5e..84d68aaf 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -84,9 +84,9 @@ class Worker: public Labor virtual const NodeInfo& GetNodeInfo() const; virtual void SetNodeId(uint32 uiNodeId); virtual bool AddNetLogMsg(const MsgBody& oMsgBody); + virtual const CJsonObject& GetCustomConf() const; bool WithSsl(); const WorkerInfo& GetWorkerInfo() const; - const CJsonObject& GetCustomConf() const; std::shared_ptr GetManagerControlChannel(); bool SetCustomConf(const CJsonObject& oJsonConf); From ea01910ac58c005edf811f690db130f5a0371f17 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 16 May 2020 18:49:54 +0800 Subject: [PATCH 092/176] add thread mode --- src/actor/Actor.cpp | 23 ++- src/actor/ActorBuilder.cpp | 90 ++++++++---- src/actor/ActorBuilder.hpp | 1 + src/actor/context/Context.hpp | 5 + src/actor/context/HttpContext.cpp | 6 + src/actor/context/HttpContext.hpp | 2 + src/actor/context/PbContext.cpp | 17 +++ src/actor/context/PbContext.hpp | 8 ++ .../sys_session/manager/SessionManager.cpp | 135 ++++++++++++++---- .../sys_session/manager/SessionManager.hpp | 7 +- src/labor/Labor.hpp | 4 + src/labor/Manager.cpp | 115 ++++++++++++++- src/labor/Manager.hpp | 11 +- src/labor/NodeInfo.hpp | 1 + src/labor/Worker.cpp | 41 ++++-- src/labor/Worker.hpp | 13 +- 16 files changed, 395 insertions(+), 84 deletions(-) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index f3b9e35b..8a1ec610 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -88,14 +88,33 @@ const CJsonObject& Actor::GetCustomConf() const { return(m_pLabor->GetCustomConf()); } + std::shared_ptr Actor::GetSession(uint32 uiSessionId) { - return(m_pLabor->GetActorBuilder()->GetSession(uiSessionId)); + auto pSession = m_pLabor->GetActorBuilder()->GetSession(uiSessionId); + if (pSession == nullptr) + { + auto pActorBuilder = m_pLabor->GetLoaderActorBuilder(); + if (pActorBuilder != nullptr) + { + return(pActorBuilder->GetSession(uiSessionId)); + } + } + return(pSession); } std::shared_ptr Actor::GetSession(const std::string& strSessionId) { - return(m_pLabor->GetActorBuilder()->GetSession(strSessionId)); + auto pSession = m_pLabor->GetActorBuilder()->GetSession(strSessionId); + if (pSession == nullptr) + { + auto pActorBuilder = m_pLabor->GetLoaderActorBuilder(); + if (pActorBuilder != nullptr) + { + return(pActorBuilder->GetSession(strSessionId)); + } + } + return(pSession); } bool Actor::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg, void* data) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 5f658084..3c661b50 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -62,6 +62,12 @@ bool ActorBuilder::Init(CJsonObject& oBootLoadConf, CJsonObject& oDynamicLoadCon return(true); } +bool ActorBuilder::Init(CJsonObject& oDynamicLoadConf) +{ + DynamicLoad(oDynamicLoadConf); + return(true); +} + void ActorBuilder::StepTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) { if (watcher->data != NULL) @@ -1219,29 +1225,12 @@ void ActorBuilder::DynamicLoad(CJsonObject& oDynamicLoadingConf) { std::string strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); - if (0 != access(strSoFile.c_str(), F_OK)) + if (m_pLabor->GetNodeInfo().bThreadMode && Labor::LABOR_MANAGER != m_pLabor->GetLaborType()) { - strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); - if (0 != access(strSoFile.c_str(), F_OK)) - { - LOG4_WARNING("%s not exist!", strSoFile.c_str()); - continue; - } - } - pSo = LoadSo(strSoFile, strVersion); - if (pSo != nullptr) - { - LOG4_INFO("succeed in loading %s", strSoFile.c_str()); - m_mapLoadedSo.insert(std::make_pair(strSoPath, pSo)); LoadDynamicSymbol(oDynamicLoadingConf[i]); } - } - else - { - if (strVersion != so_iter->second->strVersion) // 版本升级,先卸载再加载 + else { - std::string strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") - + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); if (0 != access(strSoFile.c_str(), F_OK)) { strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); @@ -1251,19 +1240,51 @@ void ActorBuilder::DynamicLoad(CJsonObject& oDynamicLoadingConf) continue; } } - UnloadDynamicSymbol(oDynamicLoadingConf[i]); - dlclose(so_iter->second->pSoHandle); - delete so_iter->second; pSo = LoadSo(strSoFile, strVersion); if (pSo != nullptr) { LOG4_INFO("succeed in loading %s", strSoFile.c_str()); - so_iter->second = pSo; + m_mapLoadedSo.insert(std::make_pair(strSoPath, pSo)); + LoadDynamicSymbol(oDynamicLoadingConf[i]); + } + } + } + else + { + if (strVersion != so_iter->second->strVersion) // 版本升级,先卸载再加载 + { + std::string strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); + if (m_pLabor->GetNodeInfo().bThreadMode && Labor::LABOR_MANAGER != m_pLabor->GetLaborType()) + { + UnloadDynamicSymbol(oDynamicLoadingConf[i]); LoadDynamicSymbol(oDynamicLoadingConf[i]); } else { - m_mapLoadedSo.erase(so_iter); + if (0 != access(strSoFile.c_str(), F_OK)) + { + strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); + if (0 != access(strSoFile.c_str(), F_OK)) + { + LOG4_WARNING("%s not exist!", strSoFile.c_str()); + continue; + } + } + UnloadDynamicSymbol(oDynamicLoadingConf[i]); + dlclose(so_iter->second->pSoHandle); + delete so_iter->second; + pSo = LoadSo(strSoFile, strVersion); + if (pSo != nullptr) + { + LOG4_INFO("succeed in loading %s", strSoFile.c_str()); + so_iter->second = pSo; + LoadDynamicSymbol(oDynamicLoadingConf[i]); + } + else + { + m_mapLoadedSo.erase(so_iter); + } } } } @@ -1273,15 +1294,22 @@ void ActorBuilder::DynamicLoad(CJsonObject& oDynamicLoadingConf) { if (oDynamicLoadingConf[i].Get("so_path", strSoPath)) { - so_iter = m_mapLoadedSo.find(strSoPath); - if (so_iter != m_mapLoadedSo.end()) + if (m_pLabor->GetNodeInfo().bThreadMode && Labor::LABOR_MANAGER != m_pLabor->GetLaborType()) { UnloadDynamicSymbol(oDynamicLoadingConf[i]); - dlclose(so_iter->second->pSoHandle); - delete so_iter->second; - m_mapLoadedSo.erase(so_iter); - LOG4_INFO("unload %s.%s", strSoPath.c_str(), - oDynamicLoadingConf[i]("version").c_str()); + } + else + { + so_iter = m_mapLoadedSo.find(strSoPath); + if (so_iter != m_mapLoadedSo.end()) + { + UnloadDynamicSymbol(oDynamicLoadingConf[i]); + dlclose(so_iter->second->pSoHandle); + delete so_iter->second; + m_mapLoadedSo.erase(so_iter); + LOG4_INFO("unload %s.%s", strSoPath.c_str(), + oDynamicLoadingConf[i]("version").c_str()); + } } } } diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 25728ad4..6cd9a874 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -84,6 +84,7 @@ class ActorBuilder ActorBuilder(Labor* pLabor, std::shared_ptr pLogger); virtual ~ActorBuilder(); bool Init(CJsonObject& oBootLoadConf, CJsonObject& oDynamicLoadConf); + bool Init(CJsonObject& oDynamicLoadConf); static void StepTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); static void SessionTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); diff --git a/src/actor/context/Context.hpp b/src/actor/context/Context.hpp index 08a5be3c..94680c1f 100644 --- a/src/actor/context/Context.hpp +++ b/src/actor/context/Context.hpp @@ -64,6 +64,11 @@ class Context: public Actor { } + void ResetChannel(std::shared_ptr pChannel) + { + m_pChannel = pChannel; + } + std::shared_ptr GetChannel() { return(m_pChannel); diff --git a/src/actor/context/HttpContext.cpp b/src/actor/context/HttpContext.cpp index 232cb357..49813732 100644 --- a/src/actor/context/HttpContext.cpp +++ b/src/actor/context/HttpContext.cpp @@ -51,4 +51,10 @@ bool HttpContext::Response(const std::string& strData) return(false); } +void HttpContext::ResetContext(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +{ + ResetChannel(pChannel); + m_oReqHttpMsg = oHttpMsg; +} + } /* namespace neb */ diff --git a/src/actor/context/HttpContext.hpp b/src/actor/context/HttpContext.hpp index 84ccc0ee..234fc549 100644 --- a/src/actor/context/HttpContext.hpp +++ b/src/actor/context/HttpContext.hpp @@ -48,6 +48,8 @@ class HttpContext: public Context return(m_oReqHttpMsg); } + void ResetContext(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + private: HttpMsg m_oReqHttpMsg; friend class ActorBuilder; diff --git a/src/actor/context/PbContext.cpp b/src/actor/context/PbContext.cpp index c00c1a24..f3d52dea 100644 --- a/src/actor/context/PbContext.cpp +++ b/src/actor/context/PbContext.cpp @@ -61,4 +61,21 @@ bool PbContext::Response(int iErrno, const std::string& strData) return(false); } +void PbContext::ResetContext(std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq) +{ + ResetChannel(pChannel); + m_iReqCmd = iCmd; + m_uiReqSeq = uiSeq; +} + +void PbContext::ResetContext(std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq, const MsgBody& oReqMsgBody) +{ + ResetChannel(pChannel); + m_iReqCmd = iCmd; + m_uiReqSeq = uiSeq; + m_oReqMsgBody = oReqMsgBody; +} + } /* namespace neb */ diff --git a/src/actor/context/PbContext.hpp b/src/actor/context/PbContext.hpp index c1ef3218..5111b3dc 100644 --- a/src/actor/context/PbContext.hpp +++ b/src/actor/context/PbContext.hpp @@ -60,6 +60,14 @@ class PbContext: public Context return(m_oReqMsgBody); } + void ResetContext( + std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq); + void ResetContext( + std::shared_ptr pChannel, + int32 iCmd, uint32 uiSeq, + const MsgBody& oReqMsgBody); + private: int32 m_iReqCmd; uint32 m_uiReqSeq; diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 2f14f618..c888d154 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -15,6 +15,8 @@ #include "util/json/CJsonObject.hpp" #include "labor/NodeInfo.hpp" #include "labor/Manager.hpp" +#include "labor/Worker.hpp" +#include "labor/Loader.hpp" #include "ios/Dispatcher.hpp" #include "actor/cmd/CW.hpp" @@ -24,7 +26,7 @@ namespace neb SessionManager::SessionManager(bool bDirectToLoader) : Session("neb::SessionManager", gc_dNoTimeout), m_bDirectToLoader(bDirectToLoader) { - m_iterWorker = m_mapWorker.begin(); + m_iterWorkerInfo = m_mapWorkerInfo.begin(); } SessionManager::~SessionManager() @@ -35,7 +37,13 @@ SessionManager::~SessionManager() it->second = nullptr; } m_mapWorker.clear(); - m_iterWorker = m_mapWorker.begin(); + for (auto it = m_mapWorkerInfo.begin(); it != m_mapWorkerInfo.end(); ++it) + { + delete it->second; + it->second = nullptr; + } + m_mapWorkerInfo.clear(); + m_iterWorkerInfo = m_mapWorkerInfo.begin(); m_mapWorkerStartNum.clear(); m_mapWorkerFdPid.clear(); m_mapOnlineNodes.clear(); @@ -71,7 +79,7 @@ void SessionManager::DelOnlineNode(const std::string& strNodeIdentify) bool SessionManager::SendToChild(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - for (auto worker_iter = m_mapWorker.begin(); worker_iter != m_mapWorker.end(); ++worker_iter) + for (auto worker_iter = m_mapWorkerInfo.begin(); worker_iter != m_mapWorkerInfo.end(); ++worker_iter) { GetLabor(this)->GetDispatcher()->SendTo( worker_iter->second->iControlFd, iCmd, uiSeq, oMsgBody); @@ -81,7 +89,7 @@ bool SessionManager::SendToChild(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBo bool SessionManager::SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - for (auto worker_iter = m_mapWorker.begin(); worker_iter != m_mapWorker.end(); ++worker_iter) + for (auto worker_iter = m_mapWorkerInfo.begin(); worker_iter != m_mapWorkerInfo.end(); ++worker_iter) { if (m_iLoaderDataFd == worker_iter->second->iDataFd) { @@ -95,7 +103,7 @@ bool SessionManager::SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgB bool SessionManager::SendToLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - for (auto worker_iter = m_mapWorker.begin(); worker_iter != m_mapWorker.end(); ++worker_iter) + for (auto worker_iter = m_mapWorkerInfo.begin(); worker_iter != m_mapWorkerInfo.end(); ++worker_iter) { if (m_iLoaderDataFd == worker_iter->second->iDataFd) { @@ -122,10 +130,20 @@ void SessionManager::AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, i pWorkerAttr->iControlFd = iControlFd; pWorkerAttr->iDataFd = iDataFd; pWorkerAttr->dBeatTime = GetNowTime(); - m_mapWorker.insert(std::make_pair(iPid, pWorkerAttr)); - m_iterWorker = m_mapWorker.begin(); - m_mapWorkerFdPid.insert(std::pair(iControlFd, iPid)); - m_mapWorkerFdPid.insert(std::pair(iDataFd, iPid)); + if (GetLabor(this)->GetNodeInfo().bThreadMode) + { + m_mapWorkerInfo.insert(std::make_pair(iWorkerIndex, pWorkerAttr)); + m_iterWorkerInfo = m_mapWorkerInfo.begin(); + m_mapWorkerFdPid.insert(std::pair(iControlFd, iWorkerIndex)); + m_mapWorkerFdPid.insert(std::pair(iDataFd, iWorkerIndex)); + } + else + { + m_mapWorkerInfo.insert(std::make_pair(iPid, pWorkerAttr)); + m_iterWorkerInfo = m_mapWorkerInfo.begin(); + m_mapWorkerFdPid.insert(std::pair(iControlFd, iPid)); + m_mapWorkerFdPid.insert(std::pair(iDataFd, iPid)); + } auto start_num_iter = m_mapWorkerStartNum.find(iWorkerIndex); if (start_num_iter == m_mapWorkerStartNum.end()) { @@ -143,10 +161,58 @@ void SessionManager::AddLoaderInfo(int iWorkerIndex, int iPid, int iControlFd, i AddWorkerInfo(iWorkerIndex, iPid, iControlFd, iDataFd); } +Worker* SessionManager::MutableWorker(int iWorkerIndex, const std::string& strWorkerPath, int iControlFd, int iDataFd) +{ + auto iter = m_mapWorker.find(iWorkerIndex); + if (iter == m_mapWorker.end()) + { + Worker* pWorker = nullptr; + try + { + pWorker = new Worker(strWorkerPath, iControlFd, iDataFd, iWorkerIndex); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("new Worker error: %s", e.what()); + return(nullptr); + } + m_mapWorker.insert(std::make_pair(iWorkerIndex, pWorker)); + return(pWorker); + } + else + { + return(iter->second); + } +} + +Worker* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWorkerPath, int iControlFd, int iDataFd) +{ + auto iter = m_mapWorker.find(iWorkerIndex); + if (iter == m_mapWorker.end()) + { + Worker* pLoader = nullptr; + try + { + pLoader = new Loader(strWorkerPath, iControlFd, iDataFd, iWorkerIndex); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("new Worker error: %s", e.what()); + return(nullptr); + } + m_mapWorker.insert(std::make_pair(iWorkerIndex, pLoader)); + return(pLoader); + } + else + { + return(iter->second); + } +} + const WorkerInfo* SessionManager::GetWorkerInfo(int32 iWorkerIndex) const { - for (auto worker_iter = m_mapWorker.begin(); - worker_iter != m_mapWorker.end(); ++worker_iter) + for (auto worker_iter = m_mapWorkerInfo.begin(); + worker_iter != m_mapWorkerInfo.end(); ++worker_iter) { if (iWorkerIndex == worker_iter->second->iWorkerIndex) { @@ -162,8 +228,8 @@ bool SessionManager::SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad) if (fd_pid_iter != m_mapWorkerFdPid.end()) { auto iPid = fd_pid_iter->second; - auto it = m_mapWorker.find(iPid); - if (it != m_mapWorker.end()) + auto it = m_mapWorkerInfo.find(iPid); + if (it != m_mapWorkerInfo.end()) { oJsonLoad.Get("load", it->second->iLoad); oJsonLoad.Get("connect", it->second->iConnect); @@ -197,16 +263,24 @@ int SessionManager::GetNextWorkerDataFd() } else { - if (m_mapWorker.empty()) + if (m_mapWorkerInfo.empty()) { return(-1); } - ++m_iterWorker; - if (m_iterWorker == m_mapWorker.end()) + ++m_iterWorkerInfo; + if (m_iterWorkerInfo == m_mapWorkerInfo.end()) { - m_iterWorker = m_mapWorker.begin(); + m_iterWorkerInfo = m_mapWorkerInfo.begin(); + } + else if (m_iterWorkerInfo->second->iDataFd == m_iLoaderDataFd) + { + ++m_iterWorkerInfo; + if (m_iterWorkerInfo == m_mapWorkerInfo.end()) + { + m_iterWorkerInfo = m_mapWorkerInfo.begin(); + } } - return(m_iterWorker->second->iDataFd); + return(m_iterWorkerInfo->second->iDataFd); } } @@ -230,7 +304,7 @@ std::pair SessionManager::GetMinLoadWorkerDataFd() } else { - for (auto iter = m_mapWorker.begin(); iter != m_mapWorker.end(); ++iter) + for (auto iter = m_mapWorkerInfo.begin(); iter != m_mapWorkerInfo.end(); ++iter) { if (iMinLoad == -1 && iter->second->iDataFd != m_iLoaderDataFd) { @@ -252,8 +326,8 @@ std::pair SessionManager::GetMinLoadWorkerDataFd() bool SessionManager::CheckWorker() { LOG4_TRACE(" "); - for (auto worker_iter = m_mapWorker.begin(); - worker_iter != m_mapWorker.end(); ++worker_iter) + for (auto worker_iter = m_mapWorkerInfo.begin(); + worker_iter != m_mapWorkerInfo.end(); ++worker_iter) { if (worker_iter->second->bStartBeatCheck) { @@ -272,8 +346,8 @@ bool SessionManager::CheckWorker() bool SessionManager::WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& eLaborType) { - auto worker_iter = m_mapWorker.find(iPid); - if (worker_iter != m_mapWorker.end()) + auto worker_iter = m_mapWorkerInfo.find(iPid); + if (worker_iter != m_mapWorkerInfo.end()) { LOG4_TRACE("restart worker %d, close control fd %d and data fd %d first.", worker_iter->second->iWorkerIndex, worker_iter->second->iControlFd, worker_iter->second->iDataFd); @@ -301,8 +375,8 @@ bool SessionManager::WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& auto pDataChannel = GetLabor(this)->GetDispatcher()->GetChannel(worker_iter->second->iDataFd); GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pDataChannel); delete worker_iter->second; - m_mapWorker.erase(worker_iter); - m_iterWorker = m_mapWorker.begin(); + m_mapWorkerInfo.erase(worker_iter); + m_iterWorkerInfo = m_mapWorkerInfo.begin(); auto restart_num_iter = m_mapWorkerStartNum.find(iWorkerIndex); if (restart_num_iter != m_mapWorkerStartNum.end()) @@ -386,17 +460,17 @@ void SessionManager::MakeReportData(CJsonObject& oReportData) } if (m_iLoaderDataFd == -1) { - oReportData.Add("worker_num", (int)m_mapWorker.size()); + oReportData.Add("worker_num", (int)m_mapWorkerInfo.size()); } else { - oReportData.Add("worker_num", (int)m_mapWorker.size() - 1); + oReportData.Add("worker_num", (int)m_mapWorkerInfo.size() - 1); } oReportData.Add("active_time", (uint64)GetNowTime()); oReportData.Add("node", CJsonObject("{}")); oReportData.Add("worker", CJsonObject("[]")); - auto worker_iter = m_mapWorker.begin(); - for (; worker_iter != m_mapWorker.end(); ++worker_iter) + auto worker_iter = m_mapWorkerInfo.begin(); + for (; worker_iter != m_mapWorkerInfo.end(); ++worker_iter) { if (m_iLoaderDataFd == worker_iter->second->iDataFd) { @@ -448,6 +522,7 @@ bool SessionManager::NewSocketWhenWorkerCreated(int iWorkerDataFd) } x_sock_set_block(iFds[0], 0); x_sock_set_block(iFds[1], 0); + LOG4_TRACE("Transfer fd to Loader and Worker."); GetLabor(this)->GetDispatcher()->SendFd(m_iLoaderDataFd, iFds[0], PF_UNIX, CODEC_NEBULA_IN_NODE); GetLabor(this)->GetDispatcher()->SendFd(iWorkerDataFd, iFds[1], PF_UNIX, CODEC_NEBULA_IN_NODE); return(true); @@ -460,7 +535,7 @@ bool SessionManager::NewSocketWhenLoaderCreated() LOG4_TRACE("no Loader process."); return(true); } - for (auto iter = m_mapWorker.begin(); iter != m_mapWorker.end(); ++iter) + for (auto iter = m_mapWorkerInfo.begin(); iter != m_mapWorkerInfo.end(); ++iter) { if (m_iLoaderDataFd == iter->second->iDataFd) { diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index 7d16d88f..36ec8b30 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -34,6 +34,8 @@ class SessionManager : public Session, bool SendToLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); void AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); void AddLoaderInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); + Worker* MutableWorker(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd); + Worker* MutableLoader(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd); const WorkerInfo* GetWorkerInfo(int32 iWorkerIndex) const; bool SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad); int GetNextWorkerDataFd(); @@ -49,8 +51,9 @@ class SessionManager : public Session, private: bool m_bDirectToLoader = false; int m_iLoaderDataFd = -1; - std::unordered_map m_mapWorker; ///< 业务逻辑工作进程及进程属性,key为pid - std::unordered_map::iterator m_iterWorker; + std::unordered_map m_mapWorker; ///< only thread worker + std::unordered_map m_mapWorkerInfo; ///< 业务逻辑工作进程及进程属性,key为pid + std::unordered_map::iterator m_iterWorkerInfo; std::unordered_map m_mapWorkerStartNum; ///< 进程被启动次数,key为WorkerIdx std::unordered_map m_mapWorkerFdPid; ///< 工作进程通信FD对应的进程号 std::unordered_map m_mapOnlineNodes; ///< 订阅的节点在线信息 diff --git a/src/labor/Labor.hpp b/src/labor/Labor.hpp index 11ca512a..0b4677e0 100644 --- a/src/labor/Labor.hpp +++ b/src/labor/Labor.hpp @@ -53,6 +53,10 @@ class Labor public: virtual Dispatcher* GetDispatcher() = 0; virtual ActorBuilder* GetActorBuilder() = 0; + virtual ActorBuilder* GetLoaderActorBuilder() // thread shared + { + return(nullptr); + } virtual uint32 GetSequence() const = 0; virtual time_t GetNowTime() const = 0; virtual long GetNowTimeMs() const = 0; diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index e6aaa6f2..6605c657 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -56,8 +56,16 @@ Manager::Manager(const std::string& strConfFile) } m_stManagerInfo.iWorkerBeat = (gc_iBeatInterval * 2) + 1; CreateEvents(); - CreateLoader(); - CreateWorker(); + if (m_stNodeInfo.bThreadMode) + { + CreateLoaderThread(); + CreateWorkerThread(); + } + else + { + CreateLoader(); + CreateWorker(); + } } Manager::~Manager() @@ -175,6 +183,11 @@ bool Manager::InitActorBuilder() LOG4_ERROR("ActorBuilder->Init() failed!"); return(false); } + if (m_stNodeInfo.bThreadMode) + { + m_pActorBuilder->Init(m_oCurrentConf["load_config"]["loader"]["dynamic_loading"]); + m_pActorBuilder->Init(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"]); + } return(true); } @@ -267,6 +280,7 @@ bool Manager::GetConf() bool Manager::Init() { + m_oCurrentConf.Get("thread_mode", m_stNodeInfo.bThreadMode); if (!InitLogger(m_oCurrentConf) || !InitDispatcher() || !InitActorBuilder()) { return(false); @@ -454,6 +468,52 @@ void Manager::CreateLoader() } } +void Manager::CreateLoaderThread() +{ + bool bWithLoader = false; + m_oCurrentConf.Get("with_loader", bWithLoader); + if (!bWithLoader) + { + return; + } + LOG4_TRACE(" "); + int iControlFds[2]; + int iDataFds[2]; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, iControlFds) < 0) + { + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); + } + if (socketpair(PF_UNIX, SOCK_STREAM, 0, iDataFds) < 0) + { + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); + } + + x_sock_set_block(iControlFds[0], 0); + x_sock_set_block(iDataFds[0], 0); + x_sock_set_block(iControlFds[1], 0); + x_sock_set_block(iDataFds[1], 0); + Worker* pWorker = m_pSessionManager->MutableLoader(0, m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1]); + if (pWorker == nullptr) + { + return; + } + if (pWorker->Init(m_oCurrentConf)) + { + return; + } + m_pLoaderActorBuilder = pWorker->GetActorBuilder(); + std::thread t(&Worker::Run, pWorker); + t.detach(); + m_pSessionManager->AddLoaderInfo(0, getpid(), iControlFds[0], iDataFds[0]); + std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); + std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); + m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->AddIoReadEvent(pChannelData); + m_pDispatcher->AddIoReadEvent(pChannelControl); + m_pSessionManager->SendOnlineNodesToWorker(); +} + void Manager::CreateWorker() { LOG4_TRACE(" "); @@ -514,6 +574,49 @@ void Manager::CreateWorker() } } +void Manager::CreateWorkerThread() +{ + LOG4_TRACE(" "); + for (unsigned int i = 1; i <= m_stNodeInfo.uiWorkerNum; ++i) + { + int iControlFds[2]; + int iDataFds[2]; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, iControlFds) < 0) + { + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); + } + if (socketpair(PF_UNIX, SOCK_STREAM, 0, iDataFds) < 0) + { + LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); + } + + x_sock_set_block(iControlFds[0], 0); + x_sock_set_block(iDataFds[0], 0); + x_sock_set_block(iControlFds[1], 0); + x_sock_set_block(iDataFds[1], 0); + Worker* pWorker = m_pSessionManager->MutableWorker(i, m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1]); + if (pWorker == nullptr) + { + continue; + } + if (!pWorker->Init(m_oCurrentConf)) + { + continue; + } + pWorker->SetLoaderActorBuilder(m_pLoaderActorBuilder); + std::thread t(&Worker::Run, pWorker); + t.detach(); + m_pSessionManager->AddWorkerInfo(i, getpid(), iControlFds[0], iDataFds[0]); + std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); + std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); + m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->AddIoReadEvent(pChannelData); + m_pDispatcher->AddIoReadEvent(pChannelControl); + m_pSessionManager->NewSocketWhenWorkerCreated(iDataFds[0]); + } +} + bool Manager::RestartWorker(int iDeathPid) { LOG4_DEBUG("%d", iDeathPid); @@ -626,6 +729,10 @@ void Manager::RefreshServer() { MsgBody oMsgBody; oMsgBody.set_data(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"].ToString()); + if (m_stNodeInfo.bThreadMode) + { + m_pActorBuilder->DynamicLoad(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"]); + } m_pSessionManager->SendToWorker(CMD_REQ_RELOAD_SO, GetSequence(), oMsgBody); } if (m_oLastConf["load_config"]["loader"]["dynamic_loading"].ToString() @@ -633,6 +740,10 @@ void Manager::RefreshServer() { MsgBody oMsgBody; oMsgBody.set_data(m_oCurrentConf["load_config"]["loader"]["dynamic_loading"].ToString()); + if (m_stNodeInfo.bThreadMode) + { + m_pActorBuilder->DynamicLoad(m_oCurrentConf["load_config"]["loader"]["dynamic_loading"]); + } m_pSessionManager->SendToLoader(CMD_REQ_RELOAD_SO, GetSequence(), oMsgBody); } } diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index 27b884af..e89f1af8 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "util/json/CJsonObject.hpp" #include "logger/NetLogger.hpp" @@ -79,6 +80,11 @@ class Manager: public Labor return(m_pActorBuilder); } + virtual ActorBuilder* GetLoaderActorBuilder() + { + return(m_pLoaderActorBuilder); + } + std::shared_ptr GetSessionManager() { return m_pSessionManager; @@ -106,7 +112,9 @@ class Manager: public Labor bool CreateEvents(); void CreateLoader(); - void CreateWorker(); + void CreateLoaderThread(); + void CreateWorker(); //muti process + void CreateWorkerThread(); //muti thread bool RestartWorker(int iDeathPid); bool AddPeriodicTaskEvent(); @@ -115,6 +123,7 @@ class Manager: public Labor mutable uint32 m_uiSequence = 0; Dispatcher* m_pDispatcher = nullptr; ActorBuilder* m_pActorBuilder = nullptr; + ActorBuilder* m_pLoaderActorBuilder = nullptr; CJsonObject m_oLastConf; ///< 上次加载的配置 CJsonObject m_oCurrentConf; ///< 当前加载的配置 diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index 2149dc66..f4806b27 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -30,6 +30,7 @@ struct NodeInfo int32 iPortForServer = 0; ///< Server间通信监听端口,对应 iS2SListenFd int32 iPortForClient = 0; ///< 对Client通信监听端口,对应 iC2SListenFd int32 iGatewayPort = 0; ///< 对Client服务的真实端口 + bool bThreadMode = 0; ///< 是否线程模型 bool bIsAccess = false; ///< 是否接入Server ev_tstamp dIoTimeout = 10.0; ///< IO(连接)超时配置 ev_tstamp dDataReportInterval = 60.0; ///< 统计数据上报时间间隔 diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 9ffc18e0..33f3c49f 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -24,7 +24,8 @@ extern "C" { namespace neb { -Worker::Worker(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex, Labor::LABOR_TYPE eLaborType) +Worker::Worker(const std::string& strWorkPath, int iControlFd, int iDataFd, + int iWorkerIndex, Labor::LABOR_TYPE eLaborType) : Labor(eLaborType) { m_stWorkerInfo.iControlFd = iControlFd; @@ -65,12 +66,15 @@ void Worker::OnTerminated(struct ev_signal* watcher) bool Worker::CheckParent() { - pid_t iParentPid = getppid(); - if (iParentPid == 1) // manager进程已不存在 + if (!m_stNodeInfo.bThreadMode) { - LOG4_INFO("no manager process exist, worker %d exit.", m_stWorkerInfo.iWorkerIndex); - Destroy(); - exit(0); + pid_t iParentPid = getppid(); + if (iParentPid == 1) // manager进程已不存在 + { + LOG4_INFO("no manager process exist, worker %d exit.", m_stWorkerInfo.iWorkerIndex); + Destroy(); + exit(0); + } } MsgBody oMsgBody; CJsonObject oJsonLoad; @@ -97,7 +101,11 @@ bool Worker::Init(CJsonObject& oJsonConf) { char szProcessName[64] = {0}; snprintf(szProcessName, sizeof(szProcessName), "%s_W%d", oJsonConf("server_name").c_str(), m_stWorkerInfo.iWorkerIndex); - ngx_setproctitle(szProcessName); + oJsonConf.Get("thread_mode", m_stNodeInfo.bThreadMode); + if (!m_stNodeInfo.bThreadMode) + { + ngx_setproctitle(szProcessName); + } oJsonConf.Get("io_timeout", m_stNodeInfo.dIoTimeout); if (!oJsonConf.Get("step_timeout", m_stNodeInfo.dStepTimeout)) { @@ -119,7 +127,7 @@ bool Worker::Init(CJsonObject& oJsonConf) oJsonConf["permission"]["uin_permit"].Get("stat_interval", m_stNodeInfo.dMsgStatInterval); oJsonConf["permission"]["uin_permit"].Get("permit_num", m_stNodeInfo.iMsgPermitNum); } - if (!InitLogger(oJsonConf)) + if (!InitLogger(oJsonConf, szProcessName)) { return(false); } @@ -194,7 +202,7 @@ bool Worker::Init(CJsonObject& oJsonConf) return(true); } -bool Worker::InitLogger(const CJsonObject& oJsonConf) +bool Worker::InitLogger(const CJsonObject& oJsonConf, const std::string& strLogNameBase) { if (nullptr != m_pLogger) // 已经被初始化过,只修改日志级别 { @@ -214,7 +222,7 @@ bool Worker::InitLogger(const CJsonObject& oJsonConf) int32 iLogLevel = 0; int32 iNetLogLevel = 0; std::string strLogname = m_stNodeInfo.strWorkPath + std::string("/") + oJsonConf("log_path") - + std::string("/") + getproctitle() + std::string(".log"); + + std::string("/") + strLogNameBase + std::string(".log"); std::string strParttern = "[%D,%d{%q}][%p] [%l] %m%n"; oJsonConf.Get("max_log_file_size", iMaxLogFileSize); oJsonConf.Get("max_log_file_num", iMaxLogFileNum); @@ -278,11 +286,14 @@ bool Worker::NewActorBuilder() bool Worker::CreateEvents() { - signal(SIGPIPE, SIG_IGN); - // 注册信号事件 - ev_signal* signal_watcher = (ev_signal*)malloc(sizeof(ev_signal)); - signal_watcher->data = (void*)this; - m_pDispatcher->AddEvent(signal_watcher, Dispatcher::SignalCallback, SIGINT); + if (!m_stNodeInfo.bThreadMode) + { + signal(SIGPIPE, SIG_IGN); + // 注册信号事件 + ev_signal* signal_watcher = (ev_signal*)malloc(sizeof(ev_signal)); + signal_watcher->data = (void*)this; + m_pDispatcher->AddEvent(signal_watcher, Dispatcher::SignalCallback, SIGINT); + } AddPeriodicTaskEvent(); // 注册网络IO事件 diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 84d68aaf..2754229c 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -67,6 +67,16 @@ class Worker: public Labor return(m_pActorBuilder); } + virtual ActorBuilder* GetLoaderActorBuilder() + { + return(m_pLoaderActorBuilder); + } + + void SetLoaderActorBuilder(ActorBuilder* pActorBuilder) + { + m_pLoaderActorBuilder = pActorBuilder; + } + virtual uint32 GetSequence() const { ++m_ulSequence; @@ -94,7 +104,7 @@ class Worker: public Labor void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); protected: - bool InitLogger(const CJsonObject& oJsonConf); + bool InitLogger(const CJsonObject& oJsonConf, const std::string& strLogNameBase = ""); virtual bool InitDispatcher(); virtual bool InitActorBuilder(); bool NewDispatcher(); @@ -108,6 +118,7 @@ class Worker: public Labor mutable uint32 m_ulSequence = 0; Dispatcher* m_pDispatcher = nullptr; ActorBuilder* m_pActorBuilder = nullptr; + ActorBuilder* m_pLoaderActorBuilder = nullptr; CJsonObject m_oNodeConf; CJsonObject m_oCustomConf; ///< 自定义配置 From fb54f277b89442c518e7c8569bdc44ff0c9f0652 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 16 May 2020 20:20:10 +0800 Subject: [PATCH 093/176] add thread mode --- src/actor/session/sys_session/manager/SessionManager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index c888d154..3c15976c 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -161,7 +161,7 @@ void SessionManager::AddLoaderInfo(int iWorkerIndex, int iPid, int iControlFd, i AddWorkerInfo(iWorkerIndex, iPid, iControlFd, iDataFd); } -Worker* SessionManager::MutableWorker(int iWorkerIndex, const std::string& strWorkerPath, int iControlFd, int iDataFd) +Worker* SessionManager::MutableWorker(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd) { auto iter = m_mapWorker.find(iWorkerIndex); if (iter == m_mapWorker.end()) @@ -169,7 +169,7 @@ Worker* SessionManager::MutableWorker(int iWorkerIndex, const std::string& strWo Worker* pWorker = nullptr; try { - pWorker = new Worker(strWorkerPath, iControlFd, iDataFd, iWorkerIndex); + pWorker = new Worker(strWorkPath, iControlFd, iDataFd, iWorkerIndex); } catch(std::bad_alloc& e) { @@ -185,7 +185,7 @@ Worker* SessionManager::MutableWorker(int iWorkerIndex, const std::string& strWo } } -Worker* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWorkerPath, int iControlFd, int iDataFd) +Worker* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd) { auto iter = m_mapWorker.find(iWorkerIndex); if (iter == m_mapWorker.end()) @@ -193,7 +193,7 @@ Worker* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWo Worker* pLoader = nullptr; try { - pLoader = new Loader(strWorkerPath, iControlFd, iDataFd, iWorkerIndex); + pLoader = new Loader(strWorkPath, iControlFd, iDataFd, iWorkerIndex); } catch(std::bad_alloc& e) { From be46866ed1a2154fea4c365657ef8cfb88c3682c Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 24 May 2020 17:44:31 +0800 Subject: [PATCH 094/176] thread mode test passing. --- conf/nebula.json | 2 ++ src/labor/Manager.cpp | 2 +- src/labor/Worker.cpp | 18 ++++++++++++++++++ src/labor/Worker.hpp | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/conf/nebula.json b/conf/nebula.json index 528cd009..c1ad0b0b 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -19,6 +19,8 @@ "server_name": "AsyncServer", "//worker_num": "进程数量", "worker_num": 10, + "//thread_mode":"是否采用线程模型", + "thread_mode":false, "//with_loader":"是否启动loader进程", "with_loader":false, "//new_client_to_loader":"集群外部(从access_port端口进来)的新连接直接转发到loader,不转发给worker", diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 6605c657..c77518db 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -497,7 +497,7 @@ void Manager::CreateLoaderThread() { return; } - if (pWorker->Init(m_oCurrentConf)) + if (!pWorker->Init(m_oCurrentConf)) { return; } diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 33f3c49f..6512fa97 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -51,6 +51,24 @@ void Worker::Run() exit(-2); } +#ifndef __CYGWIN__ + bool bCpuAffinity = false; + m_oNodeConf.Get("cpu_affinity", bCpuAffinity); + if (bCpuAffinity && m_stNodeInfo.bThreadMode) + { + int iCpuNum = sysconf(_SC_NPROCESSORS_CONF); + cpu_set_t stCpuMask; + CPU_ZERO(&stCpuMask); + CPU_SET(m_stWorkerInfo.iWorkerIndex % iCpuNum, &stCpuMask); + if (sched_setaffinity( + std::hash{}(std::this_thread::get_id()), + sizeof(cpu_set_t), &stCpuMask) == -1) + { + LOG4_WARNING("sched_setaffinity failed."); + } + } +#endif + m_pDispatcher->EventRun(); } diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 2754229c..13abf071 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -25,6 +25,7 @@ extern "C" { #endif #include +#include #include "util/CBuffer.hpp" #include "labor/Labor.hpp" From 4c21e8ff06d58fd314d5652dd97dbb2dc9f213da Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 26 Jun 2020 17:13:48 +0800 Subject: [PATCH 095/176] v1.1 release --- .travis.yml | 10 ++++++++-- README.md | 2 ++ README_cn.md | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8f79e4d2..f216b312 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,10 @@ script: - make - make install - cd .. - - git clone https://github.com/kindy/libev.git libev + - wget https://github.com/kindy/libev/archive/master.zip + - mv master.zip libev.zip + - unzip libev.zip + - mv libev-master libev - cd libev/src - chmod u+x autogen.sh - ./autogen.sh @@ -47,7 +50,10 @@ script: - make - make install - cd ../../ - - git clone https://github.com/redis/hiredis.git hiredis + - wget https://github.com/redis/hiredis/archive/v0.13.0.zip + - mv v0.13.0.zip hiredis_v0.13.0.zip + - unzip hiredis_v0.13.0.zip + - mv hiredis-0.13.0 hiredis - cd hiredis - make - mkdir ../../NebulaDepend/include/hiredis diff --git a/README.md b/README.md index 2c9090ca..4b2a08c3 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,8 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v1.1 + - add worker thread mode #### v1.0 - separate the network dispatch and actor management from Labor (Manager and Worker) to Dispatcher and ActorBuilder - add supports of Actor classes to Manager process, and move the manager system management features to Cmd and Step diff --git a/README_cn.md b/README_cn.md index 4531d3f0..031adb2e 100644 --- a/README_cn.md +++ b/README_cn.md @@ -114,6 +114,8 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 +#### v1.1 + - 增加Worker的线程支持 #### v1.0 - 从Manager和Worker类中分离出网络分发功能类Dispatcher和Actor创建及管理类ActorBuilder - Manager进程支持Actor类的使用,将Manager系统管理功能分离到Cmd类和Step类中 From 3454cad9587ce25bed95e9b0127531bad953fba6 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 29 Aug 2020 17:22:05 +0800 Subject: [PATCH 096/176] delay start when loading a large amount of data --- proto/http.proto | 26 +++- src/actor/ActorBuilder.cpp | 1 + src/actor/cmd/CW.hpp | 2 + .../cmd/sys_cmd/manager/CmdOnStartService.cpp | 53 ++++++++ .../cmd/sys_cmd/manager/CmdOnStartService.hpp | 41 ++++++ .../sys_session/manager/SessionManager.cpp | 2 +- src/codec/Codec.cpp | 2 +- src/codec/Codec.hpp | 25 +++- src/labor/Manager.cpp | 120 ++++++++---------- src/labor/Manager.hpp | 3 + src/labor/NodeInfo.hpp | 1 + src/labor/Worker.cpp | 13 +- src/labor/Worker.hpp | 1 + 13 files changed, 210 insertions(+), 80 deletions(-) create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnStartService.cpp create mode 100644 src/actor/cmd/sys_cmd/manager/CmdOnStartService.hpp diff --git a/proto/http.proto b/proto/http.proto index 3297eaa8..6c409322 100644 --- a/proto/http.proto +++ b/proto/http.proto @@ -2,6 +2,12 @@ syntax = "proto3"; message HttpMsg { + message Upgrade + { + bool is_upgrade = 1; + string protocol = 2; + } + int32 type = 1; ///< http_parser_type 请求或响应 int32 http_major = 2; ///< http大版本号 int32 http_minor = 3; ///< http小版本号 @@ -18,9 +24,19 @@ message HttpMsg string path = 14; ///< Http Decode时从url中解析出来,不需要人为填充(encode时不需要填) bool is_decoding = 15; ///< 是否正在解码(true 正在解码, false 未解码或已完成解码) - message Upgrade - { - bool is_upgrade = 1; - string protocol = 2; - } + bool chunk_notice = 19; ///< 是否启用分块传输通知(当包体比较大时,部分传输完毕也会通知业务层而无需等待整个http包传输并解码完毕。) + + // http2 only + uint32 stream_id = 20; ///< 流ID,请求发送时为0则自动分配新的流ID,响应或push时为0则视为错误 + string hpack_data = 21; ///< 压缩后的头部数据(解码过程中使用) + repeated string adding_without_index_headers = 22; ///< 添加不需要新增到动态表的http头 + repeated string deleting_without_index_headers = 23; ///< 删除不需要新增到动态表的http头 + repeated string adding_never_index_headers = 24; ///< 添加永不新增到动态表的http头 + repeated string deleting_never_index_headers = 25; ///< 删除永不新增到动态表的http头 + uint32 dynamic_table_update_size = 26; ///< 更改动态表size + bool with_huffman = 27; ///< 是否使用huffman编码 + string headers_frame_padding = 28; ///< 头部帧填充字节 + string data_frame_padding = 29; ///< 数据帧填充字节 + string push_promise_frame_padding = 30; ///< PUSH_PROMISE帧填充字节 + map settings = 31; ///< SETTINGS类型和参数值 } diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 3c661b50..0e4dae2d 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -822,6 +822,7 @@ void ActorBuilder::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdOnGetNodeCustomConf", (int)CMD_REQ_GET_NODE_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdOnSetCustomConf", (int)CMD_REQ_SET_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdOnGetCustomConf", (int)CMD_REQ_GET_CUSTOM_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdOnStartService", (int)CMD_REQ_START_SERVICE); MakeSharedCmd(nullptr, "neb::CmdDataReport", (int)CMD_REQ_DATA_REPORT); } else diff --git a/src/actor/cmd/CW.hpp b/src/actor/cmd/CW.hpp index 35683229..b4ed7d12 100644 --- a/src/actor/cmd/CW.hpp +++ b/src/actor/cmd/CW.hpp @@ -38,6 +38,8 @@ enum E_CMD CMD_RSP_RELOAD_SO = 12, ///< 重新加载so响应(无须响应) CMD_REQ_UPDATE_WORKER_LOAD = 13, ///< 更新Worker进程负载信息请求 CMD_RSP_UPDATE_WORKER_LOAD = 14, ///< 更新Worker进程负载信息应答(一般无须应答) + CMD_REQ_START_SERVICE = 15, ///< 服务就绪请求 + CMD_RSP_START_SERVICE = 16, ///< 服务就绪响应(无须响应) CMD_REQ_NODE_STATUS_REPORT = 101, ///< 节点Server状态上报请求(各节点向控制中心上报自身状态信息) CMD_RSP_NODE_STATUS_REPORT = 102, ///< 节点Server状态上报应答 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnStartService.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnStartService.cpp new file mode 100644 index 00000000..16ca5dca --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnStartService.cpp @@ -0,0 +1,53 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnStartService.hpp + * @brief + * @author Bwar + * @date: 2020-07-05 + * @note + * Modify history: + ******************************************************************************/ + +#include "CmdOnStartService.hpp" +#include "labor/NodeInfo.hpp" +#include "labor/Manager.hpp" + +namespace neb +{ + +CmdOnStartService::CmdOnStartService(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdOnStartService::~CmdOnStartService() +{ +} + +bool CmdOnStartService::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + Labor* pLabor = GetLabor(this); + if (pLabor->GetNodeInfo().uiWorkerNum + pLabor->GetNodeInfo().uiLoaderNum + == m_setReadyWorker.size()) + { + return(true); // worker restarted + } + + uint32 uiWorkerId = strtoul(oInMsgBody.data().c_str(), NULL, 10); + m_setReadyWorker.insert(uiWorkerId); + if (pLabor->GetNodeInfo().uiWorkerNum + pLabor->GetNodeInfo().uiLoaderNum + == m_setReadyWorker.size()) + { + ((Manager*)pLabor)->StartService(); + } + else + { + LOG4_TRACE("waitting for worker or loader ready..."); + } + return(true); +} + +} /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnStartService.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnStartService.hpp new file mode 100644 index 00000000..3c3ae887 --- /dev/null +++ b/src/actor/cmd/sys_cmd/manager/CmdOnStartService.hpp @@ -0,0 +1,41 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdOnStartService.hpp + * @brief 启动服务 + * @author Bwar + * @date: 2020-07-05 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSTARTSERVICE_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSTARTSERVICE_HPP_ + +#include +#include "actor/ActorSys.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class SessionManager; + +class CmdOnStartService: public Cmd, + public DynamicCreator, public ActorSys +{ +public: + CmdOnStartService(int32 iCmd); + virtual ~CmdOnStartService(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, + const MsgBody& oMsgBody); + +private: + std::unordered_set m_setReadyWorker; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MANAGER_CMDONSTARTSERVICE_HPP_ */ + diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 3c15976c..1594d169 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -329,7 +329,7 @@ bool SessionManager::CheckWorker() for (auto worker_iter = m_mapWorkerInfo.begin(); worker_iter != m_mapWorkerInfo.end(); ++worker_iter) { - if (worker_iter->second->bStartBeatCheck) + if (worker_iter->second->bStartBeatCheck && !GetLabor(this)->GetNodeInfo().bThreadMode) { LOG4_TRACE("now %lf, worker_beat_time %lf, worker_beat %d", GetNowTime(), worker_iter->second->dBeatTime, ((Manager*)GetLabor(this))->GetManagerInfo().iWorkerBeat); diff --git a/src/codec/Codec.cpp b/src/codec/Codec.cpp index 9d938982..5e706b90 100644 --- a/src/codec/Codec.cpp +++ b/src/codec/Codec.cpp @@ -22,7 +22,7 @@ namespace neb std::vector Codec::m_vecAutoSwitchCodecType; Codec::Codec(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) - : m_pLogger(pLogger), m_eCodecType(eCodecType) + : m_pLogger(pLogger), m_iErrno(0), m_eCodecType(eCodecType) { } diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 9c704d4a..85e94b35 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -13,6 +13,7 @@ //#include //#include +#include #include #include #include "util/CBuffer.hpp" @@ -53,12 +54,14 @@ enum E_CODEC_STATUS { CODEC_STATUS_OK = 0, ///< 编解码成功 CODEC_STATUS_PAUSE = 1, ///< 编解码暂停(数据不完整,等待数据完整之后再解码) - CODEC_STATUS_WANT_READ = 2, ///< 等待对端握手信息(此时,应该不再监听FD的可写事件,直到对端握手信息到达。此状态用于SSL连接握手) - CODEC_STATUS_WANT_WRITE = 3, ///< 等待己端握手信息(此时,应发起握手信息,并添加FD的可写事件监听。此状态用于SSL连接握手)或握手成功等待数据发送 - CODEC_STATUS_INVALID = 4, ///< 使用了错误的编解码方式(此时为调用错误,应修改调用方式) - CODEC_STATUS_ERR = 5, ///< 编解码失败 - CODEC_STATUS_EOF = 6, ///< 连接正常关闭 - CODEC_STATUS_INT = 7, ///< 连接非正常关闭 + CODEC_STATUS_PART_OK = 2, ///< 部分编解码成功(消息比较大,分成多个数据包,比如http chunk或http2 stream的一个frame) + CODEC_STATUS_PART_ERR = 3, ///< 部分编解码失败(比如http2 stream的frame错误),无须关闭连接 + CODEC_STATUS_WANT_READ = 4, ///< 等待对端握手信息(此时,应该不再监听FD的可写事件,直到对端握手信息到达。此状态用于SSL连接握手) + CODEC_STATUS_WANT_WRITE = 5, ///< 等待己端握手信息(此时,应发起握手信息,并添加FD的可写事件监听。此状态用于SSL连接握手)或握手成功等待数据发送 + CODEC_STATUS_INVALID = 6, ///< 使用了错误的编解码方式(此时为调用错误,应修改调用方式) + CODEC_STATUS_ERR = 7, ///< 编解码失败 + CODEC_STATUS_EOF = 8, ///< 连接正常关闭 + CODEC_STATUS_INT = 9, ///< 连接非正常关闭 }; class Codec @@ -95,6 +98,11 @@ class Codec m_strKey = strKey; } + int32 GetErrno() const + { + return(m_iErrno); + } + static const std::vector& GetAutoSwitchCodecType(); static void AddAutoSwitchCodecType(E_CODEC_TYPE eCodecType); @@ -114,11 +122,16 @@ class Codec bool Rc5Decrypt(const std::string& strSrc, std::string& strDest); bool AesEncrypt(const std::string& strSrc, std::string& strDest); bool AesDecrypt(const std::string& strSrc, std::string& strDest); + inline void SetErrno(int32 iErrno) + { + m_iErrno = iErrno; + } protected: std::shared_ptr m_pLogger; private: + int32 m_iErrno; E_CODEC_TYPE m_eCodecType; std::string m_strKey; // 密钥 static std::vector m_vecAutoSwitchCodecType; diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index c77518db..7a31275d 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -191,6 +191,60 @@ bool Manager::InitActorBuilder() return(true); } +void Manager::StartService() +{ + std::string strBindIp; + if (m_oCurrentConf.Get("bind_ip", strBindIp) && strBindIp.length() > 0) + { + m_pDispatcher->CreateListenFd(strBindIp, + m_stNodeInfo.iPortForServer, m_stManagerInfo.iS2SListenFd, + m_stManagerInfo.iS2SFamily); + + if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) + { + // 接入节点才需要监听客户端连接 + m_pDispatcher->CreateListenFd(strBindIp, + m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd, + m_stManagerInfo.iC2SFamily); + } + } + else + { + m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForServer, + m_stNodeInfo.iPortForServer, m_stManagerInfo.iS2SListenFd, + m_stManagerInfo.iS2SFamily); + + if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) + { + // 接入节点才需要监听客户端连接 + m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForClient, + m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd, + m_stManagerInfo.iC2SFamily); + } + } + + std::shared_ptr pChannelListen = nullptr; + if (m_stManagerInfo.iC2SListenFd > 2) + { + LOG4_TRACE("C2SListenFd[%d]", m_stManagerInfo.iC2SListenFd); + pChannelListen = m_pDispatcher->CreateSocketChannel(m_stManagerInfo.iC2SListenFd, m_stNodeInfo.eCodec); + m_pDispatcher->SetChannelStatus(pChannelListen, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->AddIoReadEvent(pChannelListen); + } + LOG4_TRACE("S2SListenFd[%d]", m_stManagerInfo.iS2SListenFd); + pChannelListen = m_pDispatcher->CreateSocketChannel(m_stManagerInfo.iS2SListenFd, CODEC_NEBULA); + m_pDispatcher->SetChannelStatus(pChannelListen, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->AddIoReadEvent(pChannelListen); + + // 创建到beacon的连接信息 + for (int i = 0; i < m_oCurrentConf["beacon"].GetArraySize(); ++i) + { + std::string strIdentify = m_oCurrentConf["beacon"][i]("host") + std::string(":") + + m_oCurrentConf["beacon"][i]("port") + std::string(".1"); // BeaconServer只有一个Worker + m_pDispatcher->AddNodeIdentify(std::string("BEACON"), strIdentify); + } +} + bool Manager::AddNetLogMsg(const MsgBody& oMsgBody) { if (std::string("BEACON") != m_stNodeInfo.strNodeType @@ -285,57 +339,6 @@ bool Manager::Init() { return(false); } - - std::string strBindIp; - if (m_oCurrentConf.Get("bind_ip", strBindIp) && strBindIp.length() > 0) - { - if (!m_pDispatcher->CreateListenFd(strBindIp, - m_stNodeInfo.iPortForServer, m_stManagerInfo.iS2SListenFd, - m_stManagerInfo.iS2SFamily)) - { - return(false); - } - - if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) - { - // 接入节点才需要监听客户端连接 - if (!m_pDispatcher->CreateListenFd(strBindIp, - m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd, - m_stManagerInfo.iC2SFamily)) - { - return(false); - } - } - } - else - { - if (!m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForServer, - m_stNodeInfo.iPortForServer, m_stManagerInfo.iS2SListenFd, - m_stManagerInfo.iS2SFamily)) - { - return(false); - } - - if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) - { - // 接入节点才需要监听客户端连接 - if (!m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForClient, - m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd, - m_stManagerInfo.iC2SFamily)) - { - return(false); - } - } - } - - // 创建到beacon的连接信息 - for (int i = 0; i < m_oCurrentConf["beacon"].GetArraySize(); ++i) - { - std::string strIdentify = m_oCurrentConf["beacon"][i]("host") + std::string(":") - + m_oCurrentConf["beacon"][i]("port") + std::string(".1"); // BeaconServer只有一个Worker - m_pDispatcher->AddNodeIdentify(std::string("BEACON"), strIdentify); - } - return(true); } @@ -369,19 +372,6 @@ void Manager::Destroy() bool Manager::CreateEvents() { LOG4_TRACE(" "); - std::shared_ptr pChannelListen = NULL; - if (m_stManagerInfo.iC2SListenFd > 2) - { - LOG4_DEBUG("C2SListenFd[%d]", m_stManagerInfo.iC2SListenFd); - pChannelListen = m_pDispatcher->CreateSocketChannel(m_stManagerInfo.iC2SListenFd, m_stNodeInfo.eCodec); - m_pDispatcher->SetChannelStatus(pChannelListen, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->AddIoReadEvent(pChannelListen); - } - LOG4_DEBUG("S2SListenFd[%d]", m_stManagerInfo.iS2SListenFd); - pChannelListen = m_pDispatcher->CreateSocketChannel(m_stManagerInfo.iS2SListenFd, CODEC_NEBULA); - m_pDispatcher->SetChannelStatus(pChannelListen, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->AddIoReadEvent(pChannelListen); - ev_signal* child_signal_watcher = new ev_signal(); child_signal_watcher->data = (void*)this; m_pDispatcher->AddEvent(child_signal_watcher, Dispatcher::SignalCallback, SIGCHLD); @@ -453,6 +443,7 @@ void Manager::CreateLoader() close(iDataFds[1]); x_sock_set_block(iControlFds[0], 0); x_sock_set_block(iDataFds[0], 0); + m_stNodeInfo.uiLoaderNum = 1; m_pSessionManager->AddLoaderInfo(0, iPid, iControlFds[0], iDataFds[0]); std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); @@ -504,6 +495,7 @@ void Manager::CreateLoaderThread() m_pLoaderActorBuilder = pWorker->GetActorBuilder(); std::thread t(&Worker::Run, pWorker); t.detach(); + m_stNodeInfo.uiLoaderNum = 1; m_pSessionManager->AddLoaderInfo(0, getpid(), iControlFds[0], iDataFds[0]); std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index e89f1af8..523afb07 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -40,6 +40,7 @@ class Dispatcher; class ActorBuilder; class Step; class SessionManager; +class CmdOnStartService; class Manager: public Labor { @@ -108,6 +109,7 @@ class Manager: public Labor bool InitLogger(const CJsonObject& oJsonConf); bool InitDispatcher(); bool InitActorBuilder(); + void StartService(); void Destroy(); bool CreateEvents(); @@ -119,6 +121,7 @@ class Manager: public Labor bool AddPeriodicTaskEvent(); private: + friend class CmdOnStartService; char* m_pErrBuff = NULL; mutable uint32 m_uiSequence = 0; Dispatcher* m_pDispatcher = nullptr; diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index f4806b27..951f6c52 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -25,6 +25,7 @@ struct NodeInfo E_CODEC_TYPE eCodec = CODEC_UNKNOW; ///< 接入端编解码器 uint32 uiNodeId = 0; ///< 节点ID(由beacon分配) uint32 uiWorkerNum = 0; ///< Worker子进程数量 + uint32 uiLoaderNum = 0; ///< Loader子进程数量,有效值为0或1 int32 iAddrPermitNum = 0; ///< IP地址统计时间内允许连接次数 int32 iMsgPermitNum = 0; ///< 客户端统计时间内允许发送消息数量 int32 iPortForServer = 0; ///< Server间通信监听端口,对应 iS2SListenFd diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 6512fa97..bc5a348d 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -60,15 +60,15 @@ void Worker::Run() cpu_set_t stCpuMask; CPU_ZERO(&stCpuMask); CPU_SET(m_stWorkerInfo.iWorkerIndex % iCpuNum, &stCpuMask); - if (sched_setaffinity( - std::hash{}(std::this_thread::get_id()), + if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &stCpuMask) == -1) { - LOG4_WARNING("sched_setaffinity failed."); + LOG4_WARNING("pthread_setaffinity_np thread %d failed, errno %d", pthread_self(), errno); } } #endif + StartService(); m_pDispatcher->EventRun(); } @@ -324,6 +324,13 @@ bool Worker::CreateEvents() return(true); } +void Worker::StartService() +{ + MsgBody oMsgBody; + oMsgBody.set_data(std::to_string(m_stWorkerInfo.iWorkerIndex)); + m_pDispatcher->SendTo(m_pManagerControlChannel, CMD_REQ_START_SERVICE, GetSequence(), oMsgBody); +} + void Worker::Destroy() { LOG4_TRACE(" "); diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 13abf071..ab59c993 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -111,6 +111,7 @@ class Worker: public Labor bool NewDispatcher(); bool NewActorBuilder(); bool CreateEvents(); + void StartService(); void Destroy(); bool AddPeriodicTaskEvent(); From 30090ec4e13ef797b29fc1ad148e0163dd2c4a46 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 29 Aug 2020 17:30:09 +0800 Subject: [PATCH 097/176] v1.2 release --- README.md | 7 ++++--- README_cn.md | 7 +++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4b2a08c3..df3dd60f 100644 --- a/README.md +++ b/README.md @@ -120,12 +120,13 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Todo list - - Complete writing user guide before September 2019. - - NebulaMydis Data Agency Service. - - Developing an IM with the Nebula. + - Complete writing user guide + - http2 support ## Change log +#### v1.2 + - delay start when loading a large amount of data #### v1.1 - add worker thread mode #### v1.0 diff --git a/README_cn.md b/README_cn.md index 031adb2e..91cc1712 100644 --- a/README_cn.md +++ b/README_cn.md @@ -109,11 +109,14 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 开发任务 - 完成开发指南 - - NebulaMydis数据代理服务 - - 应用Nebula开发IM项目 + - 支持http2 + - 原生支持redis cluster、codis、dubbo、grpc等协议 ## 版本历史 +#### v1.2 + - 增加延迟启动功能:因loader加载大量本地数据文件而延迟绑定端口提供服务并向Beacon注册。 + - 线程模式下Manager不再监控和重启不健康的worker和loader服务。 #### v1.1 - 增加Worker的线程支持 #### v1.0 From a1ccbb079e46d739c275e57e54e9160464bd547b Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 29 Aug 2020 23:28:30 +0800 Subject: [PATCH 098/176] redefine typedef --- src/Definition.hpp | 17 +++++----- src/mydis/DbOperator.cpp | 14 ++++---- src/mydis/RedisOperator.cpp | 4 +-- src/util/json/CJsonObject.cpp | 61 ++++++++--------------------------- src/util/json/CJsonObject.hpp | 6 ++++ src/util/json/cJSON.c | 4 +-- src/util/json/cJSON.h | 10 +++--- 7 files changed, 45 insertions(+), 71 deletions(-) diff --git a/src/Definition.hpp b/src/Definition.hpp index b2340178..8e1523e1 100644 --- a/src/Definition.hpp +++ b/src/Definition.hpp @@ -11,6 +11,7 @@ #define SRC_DEFINITION_HPP_ #include +#include #ifndef NODE_BEAT #define NODE_BEAT 7.0 @@ -104,14 +105,14 @@ #define LOG4_TRACE(args...) Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ##args) //#define LOG4_TRACE(...) Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) -typedef char int8; -typedef unsigned char uint8; -typedef short int16; -typedef unsigned short uint16; -typedef int int32; -typedef unsigned int uint32; -typedef long long int int64; -typedef unsigned long long int uint64; +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; typedef double ev_tstamp; // ev.h #ifdef __CYGWIN__ diff --git a/src/mydis/DbOperator.cpp b/src/mydis/DbOperator.cpp index 5d06a078..a15f8447 100644 --- a/src/mydis/DbOperator.cpp +++ b/src/mydis/DbOperator.cpp @@ -112,7 +112,7 @@ bool DbOperator::AddDbField(const std::string& strFieldName, int64 llFieldValue, const std::string& strColAs, bool bGroupBy, bool bOrderBy, const std::string& strOrder) { char szFieldValue[64] = {0}; - snprintf(szFieldValue, sizeof(szFieldValue), "%lld", llFieldValue); + snprintf(szFieldValue, sizeof(szFieldValue), "%ld", llFieldValue); return(AddDbField(strFieldName, (std::string)szFieldValue, BIGINT, strColAs, bGroupBy, bOrderBy, strOrder)); } @@ -120,7 +120,7 @@ bool DbOperator::AddDbField(const std::string& strFieldName, uint64 ullFieldValu const std::string& strColAs, bool bGroupBy, bool bOrderBy, const std::string& strOrder) { char szFieldValue[64] = {0}; - snprintf(szFieldValue, sizeof(szFieldValue), "%llu", ullFieldValue); + snprintf(szFieldValue, sizeof(szFieldValue), "%lu", ullFieldValue); return(AddDbField(strFieldName, (std::string)szFieldValue, BIGINT, strColAs, bGroupBy, bOrderBy, strOrder)); } @@ -195,7 +195,7 @@ bool DbOperator::AddCondition(Mydis::DbOperate::Condition::E_RELATION eRelation, const std::string& strRightFieldName) { char szFieldValue[40] = {0}; - snprintf(szFieldValue, sizeof(szFieldValue), "%lld", llFieldValue); + snprintf(szFieldValue, sizeof(szFieldValue), "%ld", llFieldValue); return(AddCondition(eRelation, strFieldName, szFieldValue, BIGINT, strRightFieldName)); } @@ -204,7 +204,7 @@ bool DbOperator::AddCondition(Mydis::DbOperate::Condition::E_RELATION eRelation, const std::string& strRightFieldName) { char szFieldValue[64] = {0}; - snprintf(szFieldValue, sizeof(szFieldValue), "%llu", ullFieldValue); + snprintf(szFieldValue, sizeof(szFieldValue), "%lu", ullFieldValue); return(AddCondition(eRelation, strFieldName, szFieldValue, BIGINT, strRightFieldName)); } @@ -280,7 +280,7 @@ bool DbOperator::AddCondition(Mydis::DbOperate::Condition::E_RELATION eRelation, for (std::vector::const_iterator c_iter = vecFieldValues.begin(); c_iter != vecFieldValues.end(); ++c_iter) { - snprintf(szFieldValue, sizeof(szFieldValue), "%llu", *c_iter); + snprintf(szFieldValue, sizeof(szFieldValue), "%lu", *c_iter); pCondition->add_col_values(szFieldValue); } return(true); @@ -384,7 +384,7 @@ bool DbOperator::AddCondition(int iGroupIdx, const std::string& strRightFieldName) { char szFieldValue[40] = {0}; - snprintf(szFieldValue, sizeof(szFieldValue), "%lld", llFieldValue); + snprintf(szFieldValue, sizeof(szFieldValue), "%ld", llFieldValue); return(AddCondition(iGroupIdx, eGroupRelation, eRelation, strFieldName, szFieldValue, BIGINT, strRightFieldName)); } @@ -395,7 +395,7 @@ bool DbOperator::AddCondition(int iGroupIdx, const std::string& strRightFieldName) { char szFieldValue[40] = {0}; - snprintf(szFieldValue, sizeof(szFieldValue), "%llu", ullFieldValue); + snprintf(szFieldValue, sizeof(szFieldValue), "%lu", ullFieldValue); return(AddCondition(iGroupIdx, eGroupRelation, eRelation, strFieldName, szFieldValue, BIGINT, strRightFieldName)); } diff --git a/src/mydis/RedisOperator.cpp b/src/mydis/RedisOperator.cpp index 8d57c5d9..dd8783f2 100644 --- a/src/mydis/RedisOperator.cpp +++ b/src/mydis/RedisOperator.cpp @@ -110,14 +110,14 @@ bool RedisOperator::AddRedisField(const std::string& strFieldName, uint32 uiFiel bool RedisOperator::AddRedisField(const std::string& strFieldName, int64 llFieldValue) { char szFieldValue[40] = {0}; - snprintf(szFieldValue, sizeof(szFieldValue), "%lld", llFieldValue); + snprintf(szFieldValue, sizeof(szFieldValue), "%ld", llFieldValue); return(AddRedisField(strFieldName, szFieldValue)); } bool RedisOperator::AddRedisField(const std::string& strFieldName, uint64 ullFieldValue) { char szFieldValue[40] = {0}; - snprintf(szFieldValue, sizeof(szFieldValue), "%llu", ullFieldValue); + snprintf(szFieldValue, sizeof(szFieldValue), "%lu", ullFieldValue); return(AddRedisField(strFieldName, szFieldValue)); } diff --git a/src/util/json/CJsonObject.cpp b/src/util/json/CJsonObject.cpp index 1f6a2775..7728b743 100644 --- a/src/util/json/CJsonObject.cpp +++ b/src/util/json/CJsonObject.cpp @@ -9,6 +9,11 @@ ******************************************************************************/ #include "CJsonObject.hpp" +#include + +#ifdef _WIN32 +#define snprintf _snprintf_s +#endif namespace neb { @@ -303,30 +308,9 @@ std::string CJsonObject::operator()(const std::string& strKey) const } else if (pJsonStruct->type == cJSON_Int) { - char szNumber[128] = {0}; - if (pJsonStruct->sign == -1) - { - if (pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) - { - snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); - } - else - { - snprintf(szNumber, sizeof(szNumber), "%lld", (int64)pJsonStruct->valueint); - } - } - else - { - if ((uint64)pJsonStruct->valueint <= (uint64)UINT_MAX) - { - snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); - } - else - { - snprintf(szNumber, sizeof(szNumber), "%llu", pJsonStruct->valueint); - } - } - return(std::string(szNumber)); + std::ostringstream ossNumber; + ossNumber << pJsonStruct->valueint; + return(ossNumber.str()); } else if (pJsonStruct->type == cJSON_Double) { @@ -339,6 +323,7 @@ std::string CJsonObject::operator()(const std::string& strKey) const { snprintf(szNumber, sizeof(szNumber), "%f", pJsonStruct->valuedouble); } + return(std::string(szNumber)); } else if (pJsonStruct->type == cJSON_False) { @@ -378,30 +363,9 @@ std::string CJsonObject::operator()(unsigned int uiWhich) const } else if (pJsonStruct->type == cJSON_Int) { - char szNumber[128] = {0}; - if (pJsonStruct->sign == -1) - { - if (pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) - { - snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); - } - else - { - snprintf(szNumber, sizeof(szNumber), "%lld", (int64)pJsonStruct->valueint); - } - } - else - { - if ((uint64)pJsonStruct->valueint <= (uint64)UINT_MAX) - { - snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); - } - else - { - snprintf(szNumber, sizeof(szNumber), "%llu", pJsonStruct->valueint); - } - } - return(std::string(szNumber)); + std::ostringstream ossNumber; + ossNumber << pJsonStruct->valueint; + return(ossNumber.str()); } else if (pJsonStruct->type == cJSON_Double) { @@ -414,6 +378,7 @@ std::string CJsonObject::operator()(unsigned int uiWhich) const { snprintf(szNumber, sizeof(szNumber), "%f", pJsonStruct->valuedouble); } + return(std::string(szNumber)); } else if (pJsonStruct->type == cJSON_False) { diff --git a/src/util/json/CJsonObject.hpp b/src/util/json/CJsonObject.hpp index 164fbaae..812d440c 100644 --- a/src/util/json/CJsonObject.hpp +++ b/src/util/json/CJsonObject.hpp @@ -92,6 +92,12 @@ class CJsonObject bool Replace(const std::string& strKey, float fValue); bool Replace(const std::string& strKey, double dValue); bool ReplaceWithNull(const std::string& strKey); // replace value with null + template bool ReplaceAdd(const std::string& strKey,T& value) + { + if(Replace(strKey,value) == false) + return Add(strKey,value); + return true; + } public: // method of json array int GetArraySize(); diff --git a/src/util/json/cJSON.c b/src/util/json/cJSON.c index be7f0fa2..a1f4ba8d 100644 --- a/src/util/json/cJSON.c +++ b/src/util/json/cJSON.c @@ -193,7 +193,7 @@ static char *print_int(cJSON *item) } else { - sprintf(str, "%lld", (int64)item->valueint); + sprintf(str, "%ld", (int64)item->valueint); } } else @@ -204,7 +204,7 @@ static char *print_int(cJSON *item) } else { - sprintf(str, "%llu", item->valueint); + sprintf(str, "%lu", item->valueint); } } } diff --git a/src/util/json/cJSON.h b/src/util/json/cJSON.h index f6ea981b..506d7f3f 100644 --- a/src/util/json/cJSON.h +++ b/src/util/json/cJSON.h @@ -23,11 +23,13 @@ #ifndef cJSON__h #define cJSON__h +#include + +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; -typedef int int32; -typedef unsigned int uint32; -typedef long long int64; -typedef unsigned long long uint64; #ifdef __cplusplus extern "C" From ee7e744dcfc6ee54afe6026bcdaa6cb45e981af1 Mon Sep 17 00:00:00 2001 From: nebim Date: Tue, 13 Oct 2020 21:01:58 +0800 Subject: [PATCH 099/176] add http2 codec --- src/codec/http2/CodecHttp2.cpp | 963 ++++++++++++++++++++++++++++++++ src/codec/http2/CodecHttp2.hpp | 132 +++++ src/codec/http2/H2Comm.hpp | 114 ++++ src/codec/http2/Http2Frame.cpp | 823 +++++++++++++++++++++++++++ src/codec/http2/Http2Frame.hpp | 133 +++++ src/codec/http2/Http2Header.cpp | 148 +++++ src/codec/http2/Http2Header.hpp | 251 +++++++++ src/codec/http2/Http2Stream.cpp | 315 +++++++++++ src/codec/http2/Http2Stream.hpp | 85 +++ src/codec/http2/Huffman.cpp | 163 ++++++ src/codec/http2/Huffman.hpp | 161 ++++++ src/codec/http2/Tree.hpp | 28 + 12 files changed, 3316 insertions(+) create mode 100644 src/codec/http2/CodecHttp2.cpp create mode 100644 src/codec/http2/CodecHttp2.hpp create mode 100644 src/codec/http2/H2Comm.hpp create mode 100644 src/codec/http2/Http2Frame.cpp create mode 100644 src/codec/http2/Http2Frame.hpp create mode 100644 src/codec/http2/Http2Header.cpp create mode 100644 src/codec/http2/Http2Header.hpp create mode 100644 src/codec/http2/Http2Stream.cpp create mode 100644 src/codec/http2/Http2Stream.hpp create mode 100644 src/codec/http2/Huffman.cpp create mode 100644 src/codec/http2/Huffman.hpp create mode 100644 src/codec/http2/Tree.hpp diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp new file mode 100644 index 00000000..e08b9792 --- /dev/null +++ b/src/codec/http2/CodecHttp2.cpp @@ -0,0 +1,963 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecHttp2.cpp + * @brief + * @author nebim + * @date: 2020-05-01 + * @note + * Modify history: + ******************************************************************************/ +#include "CodecHttp2.hpp" +#include "Http2Stream.hpp" +#include "Http2Frame.hpp" +#include "Http2Header.hpp" + +namespace neb +{ + +CodecHttp2::CodecHttp2(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) + : Codec(pLogger, eCodecType) +{ +#if __cplusplus >= 201401L + m_pFrame = std::make_unique(pLogger, eCodecType); +#else + m_pFrame = std::unique_ptr(new Http2Frame(pLogger, eCodecType)); +#endif +} + +CodecHttp2::~CodecHttp2() +{ + ReleaseStreamWeight(m_pStreamWeight); + m_pStreamWeight = nullptr; + for (auto iter = m_mapStream.begin(); iter != m_mapStream.end(); ++iter) + { + delete iter->second; + iter->second = nullptr; + } + m_mapStream.clear(); +} + +E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) +{ + m_bChunkNotice = oHttpMsg.chunk_notice(); + for (int i = 0; i < oHttpMsg.adding_without_index_headers_size(); ++i) + { + m_setEncodingWithoutIndexHeaders.insert(oHttpMsg.adding_without_index_headers(i)); + } + for (int i = 0; i < oHttpMsg.adding_never_index_headers_size(); ++i) + { + m_setEncodingNeverIndexHeaders.insert(oHttpMsg.adding_never_index_headers(i)); + } + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); + if (pBuff->ReadableBytes() <= H2_FRAME_HEAD_SIZE) + { + return(CODEC_STATUS_PAUSE); + } + + int iReadIdx = pBuff->GetReadIndex(); + pBuff->Read(&m_stFrameHead.uiLength, 3); + pBuff->Read(&m_stFrameHead.ucType, 1); + pBuff->Read(&m_stFrameHead.ucFlag, 1); + pBuff->Read(&m_stFrameHead.uiStreamIdentifier, 4); + m_stFrameHead.cR = (m_stFrameHead.uiStreamIdentifier & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; + m_stFrameHead.uiLength = ntohl(m_stFrameHead.uiLength); + m_stFrameHead.uiStreamIdentifier = ntohl(m_stFrameHead.uiStreamIdentifier & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + if (m_stFrameHead.uiLength > m_uiSettingsMaxFrameSize) + { + SetErrno(H2_ERR_FRAME_SIZE_ERROR); + return(CODEC_STATUS_PART_ERR); + } + if (pBuff->ReadableBytes() < m_stFrameHead.uiLength) + { + pBuff->SetReadIndex(iReadIdx); + return(CODEC_STATUS_PAUSE); + } + + if (m_uiGoawayLastStreamId > 0 && m_stFrameHead.uiStreamIdentifier > m_uiGoawayLastStreamId) + { + SetErrno(H2_ERR_CANCEL); + return(CODEC_STATUS_PART_ERR); + } + + if (m_pCodingStream != nullptr) + { + if (m_stFrameHead.uiStreamIdentifier == m_pCodingStream->GetStreamId()) + { + return(m_pCodingStream->Decode(this, m_stFrameHead, pBuff, oHttpMsg)); + } + if (!m_pCodingStream->IsEndHeaders()) + { + // If the END_HEADERS bit is not set, this frame MUST be followed by another CONTINUATION frame. + // A receiver MUST treat the receipt of any other type of frame or a frame on a different + // stream as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. + SetErrno(H2_ERR_PROTOCOL_ERROR); + m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "The endpoint " + "detected an unspecific protocol error. This error is for " + "use when a more specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + } + + if (m_stFrameHead.uiStreamIdentifier > 0) + { + auto stream_iter = m_mapStream.find(m_stFrameHead.uiStreamIdentifier); + if (stream_iter == m_mapStream.end()) + { + /** The identifier of a newly established stream MUST be numerically + * greater than all streams that the initiating endpoint has opened + * or reserved. This governs streams that are opened using a HEADERS + * frame and streams that are reserved using PUSH_PROMISE. An endpoint + * that receives an unexpected stream identifier MUST respond with + * a connection error (Section 5.4.1) of type PROTOCOL_ERROR. + */ + if (m_stFrameHead.uiStreamIdentifier <= m_uiStreamIdGenerate) + { + SetErrno(H2_ERR_PROTOCOL_ERROR); + m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "The endpoint " + "detected an unspecific protocol error. This error is for " + "use when a more specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + m_uiStreamIdGenerate = m_stFrameHead.uiStreamIdentifier; + try + { + m_pCodingStream = new Http2Stream(m_pLogger, GetCodecType()); + m_mapStream.insert(std::make_pair(m_stFrameHead.uiStreamIdentifier, m_pCodingStream)); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + return(CODEC_STATUS_ERR); + } + } + else + { + m_pCodingStream = stream_iter->second; + } + return(m_pCodingStream->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff)); + } + else + { + return(m_pFrame->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff)); + } +} + +void CodecHttp2::SetPriority(uint32 uiStreamId, const tagPriority& stPriority) +{ + if (m_pStreamWeight == nullptr) + { + try + { + m_pStreamWeight = new TreeNode(); + m_pStreamWeight->pData = new tagStreamWeight(); + m_pStreamWeight->pData->uiStreamId = stPriority.uiDependency; + m_pStreamWeight->pFirstChild = new TreeNode(); + m_pStreamWeight->pFirstChild->pData = new tagStreamWeight(); + m_pStreamWeight->pFirstChild->pData->uiStreamId = uiStreamId; + m_pStreamWeight->pFirstChild->pData->ucWeight = stPriority.ucWeight; + m_pStreamWeight->pFirstChild->pParent = m_pStreamWeight; + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + return; + } + } + + auto pCurrentStreamWeight = FindStreamWeight(uiStreamId, m_pStreamWeight); + if (pCurrentStreamWeight == nullptr) + { + try + { + pCurrentStreamWeight = new TreeNode(); + pCurrentStreamWeight->pData = new tagStreamWeight(); + pCurrentStreamWeight->pData->uiStreamId = uiStreamId; + pCurrentStreamWeight->pData->ucWeight = stPriority.ucWeight; + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + return; + } + } + if (pCurrentStreamWeight->pParent != nullptr) + { + if (pCurrentStreamWeight->pParent->pFirstChild == pCurrentStreamWeight) + { + pCurrentStreamWeight->pParent->pFirstChild = pCurrentStreamWeight->pRightBrother; + } + else + { + auto pLast = pCurrentStreamWeight->pParent->pFirstChild; + while (pLast->pRightBrother != pCurrentStreamWeight) + { + pLast = pLast->pRightBrother; + } + pLast->pRightBrother = pCurrentStreamWeight->pRightBrother; + } + } + auto pDependencyStreamWeight = FindStreamWeight(stPriority.uiDependency, m_pStreamWeight); + if (pDependencyStreamWeight == nullptr) + { + pCurrentStreamWeight->pRightBrother = m_pStreamWeight->pRightBrother; + m_pStreamWeight->pRightBrother = pCurrentStreamWeight; + } + else + { + if (stPriority.E) + { + pCurrentStreamWeight->pFirstChild = pDependencyStreamWeight->pFirstChild; + } + else + { + pCurrentStreamWeight->pRightBrother = pDependencyStreamWeight->pFirstChild; + } + pDependencyStreamWeight->pFirstChild = pCurrentStreamWeight; + pCurrentStreamWeight->pParent = pDependencyStreamWeight; + } +} + +void CodecHttp2::RstStream(uint32 uiStreamId) +{ + auto iter = m_mapStream.find(uiStreamId); + if (iter != m_mapStream.end()) + { + auto pStreamWeight = FindStreamWeight(uiStreamId, m_pStreamWeight); + if (pStreamWeight != nullptr) + { + auto pParent = pStreamWeight->pParent; + if (pParent != nullptr) + { + if (pParent->pFirstChild == pStreamWeight) + { + if (pStreamWeight->pFirstChild != nullptr) + { + pParent->pFirstChild = pStreamWeight->pFirstChild; + } + else + { + pParent->pFirstChild = pStreamWeight->pRightBrother; + } + } + else + { + auto pBrother = pParent->pFirstChild; + while (pBrother->pRightBrother != pStreamWeight) + { + pBrother = pBrother->pRightBrother; + } + pBrother->pRightBrother = pStreamWeight->pRightBrother; + } + delete pStreamWeight; + } + } + else + { + // TODO? + } + + if (iter->second == m_pCodingStream) + { + m_pCodingStream == nullptr; + } + delete iter->second; + iter->second = nullptr; + m_mapStream.erase(iter); + } +} + +E_H2_ERR_CODE CodecHttp2::Setting(const std::vector& vecSetting) +{ + for (size_t i = 0; i < vecSetting.size(); ++i) + { + switch (vecSetting[i].unIdentifier) + { + case H2_SETTINGS_HEADER_TABLE_SIZE: + if (vecSetting[i].uiValue <= SETTINGS_MAX_FRAME_SIZE) + { + m_uiSettingsHeaderTableSize = vecSetting[i].uiValue; + } + break; + case H2_SETTINGS_ENABLE_PUSH: + if (vecSetting[i].uiValue == 0 || vecSetting[i].uiValue == 1) + { + m_uiSettingsEnablePush = vecSetting[i].uiValue; + } + else + { + return(H2_ERR_PROTOCOL_ERROR); + } + break; + case H2_SETTINGS_MAX_CONCURRENT_STREAMS: + m_uiSettingsMaxConcurrentStreams = vecSetting[i].uiValue; + break; + case H2_SETTINGS_INITIAL_WINDOW_SIZE: + if (vecSetting[i].uiValue <= SETTINGS_MAX_INITIAL_WINDOW_SIZE) + { + m_uiSettingsMaxWindowSize = vecSetting[i].uiValue; + } + else + { + return(H2_ERR_FLOW_CONTROL_ERROR); + } + break; + case H2_SETTINGS_MAX_FRAME_SIZE: + if (vecSetting[i].uiValue <= SETTINGS_MAX_FRAME_SIZE) + { + m_uiSettingsMaxFrameSize = vecSetting[i].uiValue; + } + else + { + return(H2_ERR_PROTOCOL_ERROR); + } + break; + case H2_SETTINGS_MAX_HEADER_LIST_SIZE: + m_uiSettingsMaxHeaderListSize = vecSetting[i].uiValue; + break; + default: + ; // undefine setting, ignore + } + } + return(H2_ERR_NO_ERROR); +} + +void CodecHttp2::WindowUpdate(uint32 uiStreamId, uint32 uiIncrement) +{ + // TODO window update +} + +E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBuff, HttpMsg& oHttpMsg) +{ + char B; + size_t uiReadIndex = 0; + + E_CODEC_STATUS eStatus; + bool bWithHuffman = false; + int iDynamicTableIndex = -1; + std::string strHeaderName; + std::string strHeaderValue; + std::vector>& vecNewHeaders; + auto pHeader = oHttpMsg.mutable_headers(); + while (pBuff->GetReadIndex() < uiHeaderBlockEndPos) + { + uiReadIndex = pBuff->GetReadIndex(); + pBuff->ReadByte(B); + pBuff->SetReadIndex(uiReadIndex); + if (H2_HPACK_CONDITION_INDEXED_HEADER & B) + { + eStatus = UnpackHeaderIndexed(pBuff, pHeader); + if (eStatus != CODEC_STATUS_PART_OK) + { + return(eStatus); + } + } + else if (H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING & B) + { + eStatus = UnpackHeaderLiteralIndexing(pBuff, B, + H2_HPACK_PREFIX_6_BITS, iDynamicTableIndex, + strHeaderName, strHeaderValue, bWithHuffman); + if (eStatus != CODEC_STATUS_PART_OK) + { + return(eStatus); + } + pHeader->insert({strHeaderName, strHeaderValue}); + //UpdateDecodingDynamicTable(iDynamicTableIndex, strHeaderName, strHeaderValue); + UpdateDecodingDynamicTable(-1, strHeaderName, strHeaderValue); + } + else if (H2_HPACK_CONDITION_LITERAL_HEADER_NEVER_INDEXED & B) + { + eStatus = UnpackHeaderLiteralIndexing(pBuff, B, + H2_HPACK_PREFIX_4_BITS, iDynamicTableIndex, + strHeaderName, strHeaderValue, bWithHuffman); + if (eStatus != CODEC_STATUS_PART_OK) + { + return(eStatus); + } + pHeader->insert({strHeaderName, strHeaderValue}); + oHttpMsg.add_adding_never_index_headers(strHeaderName); + } + else if (H2_HPACK_CONDITION_DYNAMIC_TABLE_SIZE_UPDATE & B) + { + uint32 uiTableSize = (uint32)Http2Header::DecodeInt(H2_HPACK_PREFIX_5_BITS, pBuff); + if (uiTableSize > m_uiSettingsHeaderTableSize) + { + SetErrno(H2_ERR_COMPRESSION_ERROR); + LOG4_ERROR("The new maximum size MUST be lower than or equal to " + "the limit(SETTINGS_HEADER_TABLE_SIZE) determined by the" + " protocol using HPACK!"); + return(CODEC_STATUS_ERR); + } + pHeader->insert({strHeaderName, strHeaderValue}); + UpdateDecodingDynamicTable(uiTableSize); + } + else // H2_HPACK_CONDITION_LITERAL_HEADER_WITHOUT_INDEXING + { + eStatus = UnpackHeaderLiteralIndexing(pBuff, B, + H2_HPACK_PREFIX_4_BITS, iDynamicTableIndex, + strHeaderName, strHeaderValue, bWithHuffman); + if (eStatus != CODEC_STATUS_PART_OK) + { + return(eStatus); + } + pHeader->insert({strHeaderName, strHeaderValue}); + oHttpMsg.add_adding_without_index_headers(strHeaderName); + } + } + oHttpMsg.set_with_huffman(bWithHuffman); + return(CODEC_STATUS_PART_OK); +} + +void CodecHttp2::PackHeader(const HttpMsg& oHttpMsg, CBuffer* pBuff) +{ + size_t uiTableIndex = 0; + + if (oHttpMsg.dynamic_table_update_size() > 0) + { + if (oHttpMsg.dynamic_table_update_size() > SETTINGS_MAX_FRAME_SIZE) + { + LOG4_WARNING("invalid dynamic table update size %u, the size must smaller than %u.", + oHttpMsg.dynamic_table_update_size(), SETTINGS_MAX_FRAME_SIZE); + } + else + { + PackHeaderDynamicTableSize(oHttpMsg.dynamic_table_update_size(), pBuff); + } + } + + for (auto c_iter = oHttpMsg.headers().begin(); + c_iter != oHttpMsg.headers().end(); ++c_iter) + { + uiTableIndex = Http2Header::GetStaticTableIndex(c_iter->first, c_iter->second); + if (uiTableIndex == 0) + { + uiTableIndex = GetEncodingTableIndex(c_iter->first, c_iter->second); + } + if (uiTableIndex > 0) + { + PackHeaderIndexed(uiTableIndex, pBuff); + } + else + { + auto never_index_iter = m_setEncodingNeverIndexHeaders.find(c_iter->first); + if (never_index_iter != m_setEncodingNeverIndexHeaders.end()) + { + PackHeaderNeverIndexing(c_iter->first, c_iter->second, oHttpMsg.with_huffman(), pBuff); + continue; + } + auto without_index_iter = m_setEncodingWithoutIndexHeaders.find(c_iter->first); + if (without_index_iter != m_setEncodingWithoutIndexHeaders.end()) + { + PackHeaderWithoutIndexing(c_iter->first, c_iter->second, oHttpMsg.with_huffman(), pBuff); + continue; + } + PackHeaderWithIndexing(c_iter->first, c_iter->second, oHttpMsg.with_huffman(), pBuff); + } + } +} + +// TODO 在另一个 stream 流上发送 PUSH_PROMISE 帧保留了用于以后使用的空闲流。保留 stream 流的流状态转换为 "reserved (local)" 保留(本地)状态。 +E_CODEC_STATUS CodecHttp2::PromiseStream(uint32 uiStreamId, CBuffer* pReactBuff) +{ + if (m_uiGoawayLastStreamId > 0 && uiStreamId > m_uiGoawayLastStreamId) + { + SetErrno(H2_ERR_INTERNAL_ERROR); + return(CODEC_STATUS_PART_ERR); + } + /** The identifier of a newly established stream MUST be numerically + * greater than all streams that the initiating endpoint has opened + * or reserved. This governs streams that are opened using a HEADERS + * frame and streams that are reserved using PUSH_PROMISE. An endpoint + * that receives an unexpected stream identifier MUST respond with + * a connection error (Section 5.4.1) of type PROTOCOL_ERROR. + */ + if (uiStreamId <= m_uiStreamIdGenerate) + { + SetErrno(H2_ERR_REFUSED_STREAM); + m_pFrame->EncodeRstStream(this, uiStreamId, H2_ERR_REFUSED_STREAM, pReactBuff); + return(CODEC_STATUS_PART_ERR); + } + m_uiStreamIdGenerate = uiStreamId; + Http2Stream* pPromiseStream = nullptr; + try + { + pPromiseStream = new Http2Stream(m_pLogger, GetCodecType()); + pPromiseStream->SetState(H2_STREAM_RESERVED_REMOTE); + m_mapStream.insert(std::make_pair(uiStreamId, pPromiseStream)); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + return(CODEC_STATUS_PART_ERR); + } + return(CODEC_STATUS_PART_OK); +} + +uint32 CodecHttp2::StreamIdGenerate() +{ + if (m_bChannelIsClient) + { + if (m_uiStreamIdGenerate & 0x01) // odd number + { + m_uiStreamIdGenerate = (m_uiStreamIdGenerate + 2) & STREAM_IDENTIFY_MASK; + } + else + { + m_uiStreamIdGenerate = (m_uiStreamIdGenerate + 1) & STREAM_IDENTIFY_MASK; + } + } + else + { + if (m_uiStreamIdGenerate & 0x01) + { + m_uiStreamIdGenerate = (m_uiStreamIdGenerate + 1) & STREAM_IDENTIFY_MASK; + } + else + { + m_uiStreamIdGenerate = (m_uiStreamIdGenerate + 2) & STREAM_IDENTIFY_MASK; + } + } + return(m_uiStreamIdGenerate); +} + +TreeNode* CodecHttp2::FindStreamWeight(uint32 uiStreamId, TreeNode* pTarget) +{ + if (pTarget == nullptr) + { + return(nullptr); + } + if (pTarget->pData->uiStreamId == uiStreamId) + { + return(pTarget); + } + else + { + auto pFound = FindStreamWeight(uiStreamId, pTarget->pFirstChild); + if (pFound == nullptr) + { + pFound = FindStreamWeight(uiStreamId, pTarget->pRightBrother); + } + return(pFound); + } +} + +void CodecHttp2::ReleaseStreamWeight(TreeNode* pNode) +{ + if (pNode == nullptr) + { + return; + } + ReleaseStreamWeight(pNode->pFirstChild); + pNode->pFirstChild = nullptr; + ReleaseStreamWeight(pNode->pRightBrother); + pNode->pRightBrother = nullptr; + pNode->pParent = nullptr; + delete pNode->pData; + pNode->pData = nullptr; + delete pNode; +} + +E_CODEC_STATUS CodecHttp2::UnpackHeaderIndexed(CBuffer* pBuff, + ::google::protobuf::Map< ::std::string, ::std::string >* pHeader) +{ + int iTableIndex = Http2Header::DecodeInt(H2_HPACK_PREFIX_7_BITS, pBuff); + if (iTableIndex == 0) + { + SetErrno(H2_ERR_COMPRESSION_ERROR); + LOG4_ERROR("hpack index value of 0 is not used!"); + return(CODEC_STATUS_ERR); + } + else if (iTableIndex <= Http2Header::sc_uiMaxStaticTableIndex) + { + pHeader->insert({ + Http2Header::sc_vecStaticTable[iTableIndex].first, + Http2Header::sc_vecStaticTable[iTableIndex].second}); + return(CODEC_STATUS_PART_OK); + } + else if (iTableIndex <= Http2Header::sc_uiMaxStaticTableIndex + m_vecDecodingDynamicTable.size()) + { + int iDynamicTableIndex = iTableIndex - Http2Header::sc_uiMaxStaticTableIndex - 1; + pHeader->insert({ + m_vecDecodingDynamicTable[iDynamicTableIndex].Name(), + m_vecDecodingDynamicTable[iDynamicTableIndex].Value()}); + return(CODEC_STATUS_PART_OK); + } + else + { + SetErrno(H2_ERR_COMPRESSION_ERROR); + LOG4_ERROR("hpack index value of %d was greater than the sum of " + "the lengths of both static table and dynamic tables!", iTableIndex); + return(CODEC_STATUS_ERR); + } +} + +E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucFirstByte, int32 iPrefixMask, + int& iDynamicTableIndex, std::string& strHeaderName, std::string& strHeaderValue, bool& bWithHuffman) +{ + // Literal Header Field with Incremental Indexing — Indexed Name + if (iPrefixMask & ucFirstByte) + { + int iTableIndex = Http2Header::DecodeInt(iPrefixMask, pBuff); + if (iTableIndex == 0) + { + SetErrno(H2_ERR_COMPRESSION_ERROR); + LOG4_ERROR("hpack index value of 0 is not used!"); + return(CODEC_STATUS_ERR); + } + else if (iTableIndex <= Http2Header::sc_uiMaxStaticTableIndex) + { + if (!Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) + { + SetErrno(H2_ERR_COMPRESSION_ERROR); + LOG4_ERROR("DecodeStringLiteral failed!"); + return(CODEC_STATUS_ERR); + } + strHeaderName = Http2Header::sc_vecStaticTable[iTableIndex].first; + return(CODEC_STATUS_PART_OK); + } + else if (iTableIndex < Http2Header::sc_uiMaxStaticTableIndex + m_vecDecodingDynamicTable.size()) + { + iDynamicTableIndex = iTableIndex - Http2Header::sc_uiMaxStaticTableIndex - 1; + if (!Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) + { + SetErrno(H2_ERR_COMPRESSION_ERROR); + LOG4_ERROR("DecodeStringLiteral failed!"); + return(CODEC_STATUS_ERR); + } + strHeaderName = m_vecDecodingDynamicTable[iDynamicTableIndex].Name(); + return(CODEC_STATUS_PART_OK); + } + else + { + SetErrno(H2_ERR_COMPRESSION_ERROR); + LOG4_ERROR("hpack index value of %d was greater than the sum of " + "the lengths of both static table and dynamic tables!", iTableIndex); + return(CODEC_STATUS_ERR); + } + } + else // Literal Header Field with Incremental Indexing — New Name + { + pBuff->SkipBytes(1); + if (Http2Header::DecodeStringLiteral(pBuff, strHeaderName, bWithHuffman) + && Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) + { + return(CODEC_STATUS_PART_OK); + } + SetErrno(H2_ERR_COMPRESSION_ERROR); + LOG4_ERROR("DecodeStringLiteral header name or header value failed!"); + return(CODEC_STATUS_ERR); + } +} + +void CodecHttp2::PackHeaderIndexed(size_t uiTableIndex, CBuffer* pBuff) +{ + Http2Header::EncodeInt(uiTableIndex, (size_t)H2_HPACK_PREFIX_7_BITS, + (char)H2_HPACK_CONDITION_INDEXED_HEADER, pBuff); +} + +void CodecHttp2::PackHeaderWithIndexing(const std::string& strHeaderName, + const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff) +{ + size_t uiTableIndex = 0; + uiTableIndex = Http2Header::GetStaticTableIndex(strHeaderName); + if (uiTableIndex == 0) + { + uiTableIndex = GetEncodingTableIndex(strHeaderName); + } + + if (uiTableIndex > 0) + { + Http2Header::EncodeInt(uiTableIndex, (size_t)H2_HPACK_PREFIX_6_BITS, + (char)H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING, pBuff); + if (bWithHuffman) + { + Http2Header::EncodeStringLiteralWithHuffman(strHeaderValue, pBuff); + } + else + { + Http2Header::EncodeStringLiteral(strHeaderValue, pBuff); + } + } + else + { + Http2Header::EncodeInt(0, (size_t)H2_HPACK_PREFIX_6_BITS, + (char)H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING, pBuff); + if (bWithHuffman) + { + Http2Header::EncodeStringLiteralWithHuffman(strHeaderName, pBuff); + Http2Header::EncodeStringLiteralWithHuffman(strHeaderValue, pBuff); + } + else + { + Http2Header::EncodeStringLiteral(strHeaderName, pBuff); + Http2Header::EncodeStringLiteral(strHeaderValue, pBuff); + } + } +} + +void CodecHttp2::PackHeaderWithoutIndexing(const std::string& strHeaderName, + const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff) +{ + size_t uiTableIndex = 0; + uiTableIndex = Http2Header::GetStaticTableIndex(strHeaderName); + if (uiTableIndex == 0) + { + uiTableIndex = GetEncodingTableIndex(strHeaderName); + } + + if (uiTableIndex > 0) + { + Http2Header::EncodeInt(uiTableIndex, (size_t)H2_HPACK_PREFIX_4_BITS, + (char)H2_HPACK_CONDITION_LITERAL_HEADER_WITHOUT_INDEXING, pBuff); + if (bWithHuffman) + { + Http2Header::EncodeStringLiteralWithHuffman(strHeaderValue, pBuff); + } + else + { + Http2Header::EncodeStringLiteral(strHeaderValue, pBuff); + } + } + else + { + Http2Header::EncodeInt(0, (size_t)H2_HPACK_PREFIX_4_BITS, + (char)H2_HPACK_CONDITION_LITERAL_HEADER_WITHOUT_INDEXING, pBuff); + if (bWithHuffman) + { + Http2Header::EncodeStringLiteralWithHuffman(strHeaderName, pBuff); + Http2Header::EncodeStringLiteralWithHuffman(strHeaderValue, pBuff); + } + else + { + Http2Header::EncodeStringLiteral(strHeaderName, pBuff); + Http2Header::EncodeStringLiteral(strHeaderValue, pBuff); + } + } +} + +void CodecHttp2::PackHeaderNeverIndexing(const std::string& strHeaderName, + const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff) +{ + size_t uiTableIndex = 0; + uiTableIndex = Http2Header::GetStaticTableIndex(strHeaderName); + if (uiTableIndex == 0) + { + uiTableIndex = GetEncodingTableIndex(strHeaderName); + } + + if (uiTableIndex > 0) + { + Http2Header::EncodeInt(uiTableIndex, (size_t)H2_HPACK_PREFIX_4_BITS, + (char)H2_HPACK_CONDITION_LITERAL_HEADER_NEVER_INDEXED, pBuff); + if (bWithHuffman) + { + Http2Header::EncodeStringLiteralWithHuffman(strHeaderValue, pBuff); + } + else + { + Http2Header::EncodeStringLiteral(strHeaderValue, pBuff); + } + } + else + { + Http2Header::EncodeInt(0, (size_t)H2_HPACK_PREFIX_4_BITS, + (char)H2_HPACK_CONDITION_LITERAL_HEADER_NEVER_INDEXED, pBuff); + if (bWithHuffman) + { + Http2Header::EncodeStringLiteralWithHuffman(strHeaderName, pBuff); + Http2Header::EncodeStringLiteralWithHuffman(strHeaderValue, pBuff); + } + else + { + Http2Header::EncodeStringLiteral(strHeaderName, pBuff); + Http2Header::EncodeStringLiteral(strHeaderValue, pBuff); + } + } +} + +void CodecHttp2::PackHeaderDynamicTableSize(uint32 uiDynamicTableSize, CBuffer* pBuff) +{ + Http2Header::EncodeInt(uiDynamicTableSize, (size_t)H2_HPACK_PREFIX_5_BITS, + (char)H2_HPACK_CONDITION_DYNAMIC_TABLE_SIZE_UPDATE, pBuff); +} + +size_t CodecHttp2::GetEncodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue) +{ + size_t uiTableIndex = 0; + if (strHeaderValue.size() == 0) + { + for (size_t i = 0; i < m_vecEncodingDynamicTable.size(); ++i) + { + if (m_vecEncodingDynamicTable[i].Name() == strHeaderName) + { + uiTableIndex = Http2Header::sc_uiMaxStaticTableIndex + i + 1; + break; + } + } + } + else + { + for (size_t i = 0; i < m_vecEncodingDynamicTable.size(); ++i) + { + if (m_vecEncodingDynamicTable[i].Name() == strHeaderName + && m_vecEncodingDynamicTable[i].Value() == strHeaderValue) + { + uiTableIndex = Http2Header::sc_uiMaxStaticTableIndex + i + 1; + break; + } + } + } + return(uiTableIndex); +} + +//size_t CodecHttp2::GetDecodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue) +//{ +// size_t uiTableIndex = 0; +// if (strHeaderValue.size() == 0) +// { +// for (size_t i = 0; i < m_vecDecodingDynamicTable.size(); ++i) +// { +// if (m_vecDecodingDynamicTable[i].Name() == strHeaderName) +// { +// uiTableIndex = Http2Header::sc_uiMaxStaticTableIndex + i + 1; +// break; +// } +// } +// } +// else +// { +// for (size_t i = 0; i < m_vecDecodingDynamicTable.size(); ++i) +// { +// if (m_vecDecodingDynamicTable[i].Name() == strHeaderName +// && m_vecDecodingDynamicTable[i].Value() == strHeaderValue) +// { +// uiTableIndex = Http2Header::sc_uiMaxStaticTableIndex + i + 1; +// break; +// } +// } +// } +// return(uiTableIndex); +//} + +void CodecHttp2::UpdateEncodingDynamicTable(int iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) +{ + Http2Header oHeader(strHeaderName, strHeaderValue); + if (iDynamicTableIndex < 0 || iDynamicTableIndex >= m_vecEncodingDynamicTable.size()) // new header + { + while (m_uiEncodingDynamicTableSize + oHeader.HpackSize() > m_uiSettingsMaxFrameSize + && m_uiEncodingDynamicTableSize > 0) + { + auto& rLastElement = m_vecEncodingDynamicTable.back(); + m_uiEncodingDynamicTableSize -= rLastElement.HpackSize(); + m_vecEncodingDynamicTable.pop_back(); + } + if (oHeader.HpackSize() <= m_uiSettingsMaxFrameSize) + { + m_vecEncodingDynamicTable.insert(m_vecEncodingDynamicTable.begin(), + std::move(oHeader)); + m_uiEncodingDynamicTableSize += oHeader.HpackSize(); + } + } + else // replace header ? + { + while ((m_uiEncodingDynamicTableSize + oHeader.HpackSize() + - m_vecEncodingDynamicTable[iDynamicTableIndex].HpackSize()) + > m_uiSettingsMaxFrameSize + && m_uiEncodingDynamicTableSize > 0) + { + auto& rLastElement = m_vecEncodingDynamicTable.back(); + m_uiEncodingDynamicTableSize -= rLastElement.HpackSize(); + m_vecEncodingDynamicTable.pop_back(); + } + if (oHeader.HpackSize() <= m_uiSettingsMaxFrameSize) + { + if (iDynamicTableIndex < m_vecEncodingDynamicTable.size()) // replace header + { + m_vecEncodingDynamicTable[iDynamicTableIndex] = std::move(oHeader); + } + else // new header + { + m_vecEncodingDynamicTable.insert(m_vecEncodingDynamicTable.begin(), + std::move(oHeader)); + } + m_uiEncodingDynamicTableSize += oHeader.HpackSize(); + } + } +} + +void CodecHttp2::UpdateEncodingDynamicTable(uint32 uiTableSize) +{ + m_uiSettingsMaxFrameSize = uiTableSize; + while (m_uiEncodingDynamicTableSize > m_uiSettingsMaxFrameSize + && m_uiEncodingDynamicTableSize > 0) + { + auto& rLastElement = m_vecEncodingDynamicTable.back(); + m_uiEncodingDynamicTableSize -= rLastElement.HpackSize(); + m_vecEncodingDynamicTable.pop_back(); + } +} + +void CodecHttp2::UpdateDecodingDynamicTable(int iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) +{ + Http2Header oHeader(strHeaderName, strHeaderValue); + if (iDynamicTableIndex < 0 || iDynamicTableIndex >= m_vecDecodingDynamicTable.size()) // new header + { + while (m_uiDecodingDynamicTableSize + oHeader.HpackSize() > m_uiSettingsMaxFrameSize + && m_uiDecodingDynamicTableSize > 0) + { + auto& rLastElement = m_vecDecodingDynamicTable.back(); + m_uiDecodingDynamicTableSize -= oHeader.HpackSize(); + m_vecDecodingDynamicTable.pop_back(); + } + if (oHeader.HpackSize() <= m_uiSettingsMaxFrameSize) + { + m_vecDecodingDynamicTable.insert(m_vecDecodingDynamicTable.begin(), + std::move(oHeader)); + m_uiDecodingDynamicTableSize += oHeader.HpackSize(); + } + } + else // replace header ? + { + while ((m_uiDecodingDynamicTableSize + oHeader.HpackSize() + - m_vecDecodingDynamicTable[iDynamicTableIndex].HpackSize()) + > m_uiSettingsMaxFrameSize + && m_uiDecodingDynamicTableSize > 0) + { + auto& rLastElement = m_vecDecodingDynamicTable.back(); + m_uiDecodingDynamicTableSize -= rLastElement.HpackSize(); + m_vecDecodingDynamicTable.pop_back(); + } + if (oHeader.HpackSize() <= m_uiSettingsMaxFrameSize) + { + if (iDynamicTableIndex < m_vecDecodingDynamicTable.size()) // replace header + { + m_vecDecodingDynamicTable[iDynamicTableIndex] = std::move(oHeader); + } + else // new header + { + m_vecDecodingDynamicTable.insert(m_vecDecodingDynamicTable.begin(), + std::move(oHeader)); + } + m_uiDecodingDynamicTableSize += oHeader.HpackSize(); + } + } +} + +void CodecHttp2::UpdateDecodingDynamicTable(uint32 uiTableSize) +{ + m_uiSettingsMaxFrameSize = uiTableSize; + while (m_uiDecodingDynamicTableSize > m_uiSettingsMaxFrameSize + && m_uiDecodingDynamicTableSize > 0) + { + auto& rLastElement = m_vecDecodingDynamicTable.back(); + m_uiDecodingDynamicTableSize -= rLastElement.HpackSize(); + m_vecDecodingDynamicTable.pop_back(); + } +} + +} /* namespace neb */ + diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp new file mode 100644 index 00000000..aa8ee99d --- /dev/null +++ b/src/codec/http2/CodecHttp2.hpp @@ -0,0 +1,132 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecHttp2.hpp + * @brief + * @author nebim + * @date: 2020-05-01 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_HTTP2_CODECHTTP2_HPP_ +#define SRC_CODEC_HTTP2_CODECHTTP2_HPP_ + +#include +#include "codec/Codec.hpp" +#include "pb/http.pb.h" +#include "H2Comm.hpp" +#include "Tree.hpp" +#include "Http2Header.hpp" + +namespace neb +{ + +const uint32 STREAM_IDENTIFY_MASK = 0x7FFFFFFF; + +struct tagStreamWeight +{ + uint32 uiStreamId = 0; + uint8 ucWeight = 0; + uint8 ucCurrentWeight = 0; +}; + +class Http2Frame; +class Http2Stream; + +class CodecHttp2: public Codec +{ +public: + CodecHttp2(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + virtual ~CodecHttp2(); + + virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) + { + return(CODEC_STATUS_INVALID); + } + virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) + { + return(CODEC_STATUS_INVALID); + } + + virtual E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff); + virtual E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff); + +public: + bool IsChunkNotice() const + { + return(m_bChunkNotice); + } + void SetPriority(uint32 uiStreamId, const tagPriority& stPriority); + void RstStream(uint32 uiStreamId); + E_H2_ERR_CODE Setting(const std::vector& vecSetting); + void WindowUpdate(uint32 uiStreamId, uint32 uiIncrement); + uint32 GetMaxFrameSize() + { + return(m_uiSettingsMaxFrameSize); + } + E_CODEC_STATUS UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBuff, HttpMsg& oHttpMsg); + void PackHeader(const HttpMsg& oHttpMsg, CBuffer* pBuff); + E_CODEC_STATUS PromiseStream(uint32 uiStreamId, CBuffer* pReactBuff); + void SetGoaway(uint32 uiLastStreamId) + { + m_uiGoawayLastStreamId = uiLastStreamId; + } + uint32 GetLastStreamId() + { + return(m_uiStreamIdGenerate); + } + +protected: + uint32 StreamIdGenerate(); + TreeNode* FindStreamWeight(uint32 uiStreamId, TreeNode* pTarget); + TreeNode* FindHighestWeightStream(); + void ReleaseStreamWeight(TreeNode* pNode); + + E_CODEC_STATUS UnpackHeaderIndexed(CBuffer* pBuff, + ::google::protobuf::Map< ::std::string, ::std::string >* pHeader); + E_CODEC_STATUS UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucFirstByte, int32 iPrefixMask, + int& iDynamicTableIndex, std::string& strHeaderName, std::string& strHeaderValue, + bool& bWithHuffman); + void PackHeaderIndexed(size_t uiTableIndex, CBuffer* pBuff); + void PackHeaderWithIndexing(const std::string& strHeaderName, + const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff); + void PackHeaderWithoutIndexing(const std::string& strHeaderName, + const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff); + void PackHeaderNeverIndexing(const std::string& strHeaderName, + const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff); + void PackHeaderDynamicTableSize(uint32 uiDynamicTableSize, CBuffer* pBuff); + size_t GetEncodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue = ""); +// size_t GetDecodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue = ""); + void UpdateEncodingDynamicTable(int iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue); + void UpdateEncodingDynamicTable(uint32 uiTableSize); + void UpdateDecodingDynamicTable(int iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue); + void UpdateDecodingDynamicTable(uint32 uiTableSize); + +private: + bool m_bChannelIsClient = false; // 当前编解码器所在channel是作为http客户端还是作为http服务端 + bool m_bChunkNotice = false; // 是否启用分块传输通知(当包体比较大时,部分传输完毕也会通知业务层而无须等待整个http包传输完毕。) + uint32 m_uiStreamIdGenerate = 0; + uint32 m_uiGoawayLastStreamId = 0; + uint32 m_uiSettingsEnablePush = 1; + uint32 m_uiSettingsHeaderTableSize = 4096; + uint32 m_uiSettingsMaxConcurrentStreams = 100; + uint32 m_uiSettingsMaxWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + uint32 m_uiSettingsMaxFrameSize = DEFAULT_SETTINGS_MAX_FRAME_SIZE; + uint32 m_uiSettingsMaxHeaderListSize = 50; // TODO SettingsMaxHeaderListSize + tagH2FrameHead m_stFrameHead; + std::unique_ptr m_pFrame = nullptr; + Http2Stream* m_pCodingStream = nullptr; + std::unordered_map m_mapStream; + TreeNode* m_pStreamWeight = nullptr; + + uint32 m_uiEncodingDynamicTableSize = 0; + uint32 m_uiDecodingDynamicTableSize = 0; + std::vector m_vecEncodingDynamicTable; + std::vector m_vecDecodingDynamicTable; + std::unordered_set m_setEncodingWithoutIndexHeaders; + std::unordered_set m_setEncodingNeverIndexHeaders; +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_CODECHTTP2_HPP_ */ + diff --git a/src/codec/http2/H2Comm.hpp b/src/codec/http2/H2Comm.hpp new file mode 100644 index 00000000..bd9206dc --- /dev/null +++ b/src/codec/http2/H2Comm.hpp @@ -0,0 +1,114 @@ +/******************************************************************************* + * Project: Nebula + * @file H2Comm.hpp + * @brief + * @author nebim + * @date: 2020-05-02 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_HTTP2_H2COMM_HPP_ +#define SRC_CODEC_HTTP2_H2COMM_HPP_ + +namespace neb +{ + +const uint32 H2_FRAME_HEAD_SIZE = 72; +const uint32 SETTINGS_MAX_FRAME_SIZE = (2^24) - 1; +const uint32 DEFAULT_SETTINGS_MAX_FRAME_SIZE = (2^14); +const uint32 SETTINGS_MAX_INITIAL_WINDOW_SIZE = (2^31) - 1; +const uint32 DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE = (2^16) - 1; + +/* + * @see https://httpwg.org/specs/rfc7540.html#FRAME_SIZE_ERROR + */ +enum E_H2_ERR_CODE +{ + H2_ERR_NO_ERROR = 0x0, + H2_ERR_PROTOCOL_ERROR = 0x1, + H2_ERR_INTERNAL_ERROR = 0x2, + H2_ERR_FLOW_CONTROL_ERROR = 0x3, + H2_ERR_SETTINGS_TIMEOUT = 0x4, + H2_ERR_STREAM_CLOSED = 0x5, + H2_ERR_FRAME_SIZE_ERROR = 0x6, + H2_ERR_REFUSED_STREAM = 0x7, + H2_ERR_CANCEL = 0x8, + H2_ERR_COMPRESSION_ERROR = 0x9, + H2_ERR_CONNECT_ERROR = 0xa, + H2_ERR_ENHANCE_YOUR_CALM = 0xb, + H2_ERR_INADEQUATE_SECURITY = 0xc, + H2_ERR_HTTP_1_1_REQUIRED = 0xd, +}; + +enum E_H2_DATA_MASK +{ + H2_DATA_MASK_1_BYTE_HIGHEST_BIT = 0x80, + H2_DATA_MASK_1_BYTE_LOW_7_BIT = 0x7F, + H2_DATA_MASK_4_BYTE_HIGHEST_BIT = 0x80000000, + H2_DATA_MASK_4_BYTE_LOW_31_BIT = 0x7FFFFFFF, +}; + +enum E_H2_SETTING_REGISTRY +{ + H2_SETTINGS_HEADER_TABLE_SIZE = 0x1, + H2_SETTINGS_ENABLE_PUSH = 0x2, + H2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x3, + H2_SETTINGS_INITIAL_WINDOW_SIZE = 0x4, + H2_SETTINGS_MAX_FRAME_SIZE = 0x5, + H2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x6, +}; + +struct tagH2FrameHead +{ + char cR = 0; + uint8 ucType = 0; ///< 帧的8位类型。帧类型决定帧的格式和语义。实现必须忽略并丢弃任何类型未知的帧。 + uint8 ucFlag = 0; ///< 为帧类型专用的布尔标志保留的8位字段。标志被分配特定于指定帧类型的语义。没有为特定帧类型定义语义的标志务必被忽略,并且在发送时务必保持未设置(0x0)。 ///< 保留的1位字段。该位的语义是未定义的,并且该位必须在发送时保持未设置(0x0),并且在接收时必须忽略。 + uint32 uiLength = 0; ///< 帧有效载荷的长度,表示为无符号的24位整数。除非接收方为SETTINGS_MAX_FRAME_SIZE设置了较大的值,否则不得发送大于2 ^ 14(16,384)的值。帧头的9个八位字节不包含在该值中。 + uint32 uiStreamIdentifier = 0; ///< 流标识符,表示为一个无符号的31位整数。值0x0保留给与整个连接相关联的帧,而不是单个流。 +}; + +struct tagPriority +{ + char E = 0; + uint8 ucWeight = 0; + uint32 uiDependency = 0; + + tagPriority(const tagPriority& stFrom) + { + E = stFrom.E; + ucWeight = stFrom.ucWeight; + uiDependency = stFrom.uiDependency; + } + + tagPriority& operator=(const tagPriority& stFrom) + { + E = stFrom.E; + ucWeight = stFrom.ucWeight; + uiDependency = stFrom.uiDependency; + return(*this); + } +}; + +struct tagSetting +{ + uint16 unIdentifier = 0; + uint32 uiValue = 0; + + tagSetting(const tagSetting& stSetting) + { + unIdentifier = stSetting.unIdentifier; + uiValue = stSetting.uiValue; + } + + tagSetting& operator=(const tagSetting& stSetting) + { + unIdentifier = stSetting.unIdentifier; + uiValue = stSetting.uiValue; + return(*this); + } +}; + +} + +#endif /* SRC_CODEC_HTTP2_H2COMM_HPP_ */ + diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp new file mode 100644 index 00000000..24153549 --- /dev/null +++ b/src/codec/http2/Http2Frame.cpp @@ -0,0 +1,823 @@ +/******************************************************************************* + * Project: Nebula + * @file Http2Frame.cpp + * @brief + * @author nebim + * @date: 2020-05-02 + * @note + * Modify history: + ******************************************************************************/ +#include "Http2Frame.hpp" +#include "CodecHttp2.hpp" + +namespace neb +{ + +Http2Frame::Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) + : Codec(pLogger, eCodecType) +{ +} + +Http2Frame::~Http2Frame() +{ +} + +E_CODEC_STATUS Http2Frame::Encode(CodecHttp2* pCodecH2, + const HttpMsg& oHttpMsg, CBuffer* pBuff) +{ + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS Http2Frame::Decode(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + switch (stFrameHead.ucType) + { + case H2_FRAME_DATA: + return(DecodeData(pCodecH2, stFrameHead, pBuff, oHttpMsg, pReactBuff)); + case H2_FRAME_HEADERS: + return(DecodeHeaders(pCodecH2, stFrameHead, pBuff, oHttpMsg, pReactBuff)); + case H2_FRAME_PRIORITY: + return(DecodePriority(pCodecH2, stFrameHead, pBuff, pReactBuff)); + case H2_FRAME_RST_STREAM: + return(DecodeRstStream(pCodecH2, stFrameHead, pBuff, oHttpMsg, pReactBuff)); + case H2_FRAME_SETTINGS: + return(DecodeSetting(pCodecH2, stFrameHead, pBuff, pReactBuff)); + case H2_FRAME_PUSH_PROMISE: + return(DecodePushPromise(pCodecH2, stFrameHead, pBuff, oHttpMsg, pReactBuff)); + case H2_FRAME_PING: + return(DecodePing(pCodecH2, stFrameHead, pBuff, pReactBuff)); + case H2_FRAME_GOAWAY: + return(DecodeGoaway(pCodecH2, stFrameHead, pBuff, oHttpMsg, pReactBuff)); + case H2_FRAME_WINDOW_UPDATE: + return(DecodeWindowUpdate(pCodecH2, stFrameHead, pBuff, oHttpMsg, pReactBuff)); + case H2_FRAME_CONTINUATION: + return(DecodeContinuation(pCodecH2, stFrameHead, pBuff, oHttpMsg, pReactBuff)); + default: + LOG4_ERROR("unknow frame type %d!", stFrameHead.ucType); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + return(CODEC_STATUS_PART_ERR); + } +} + +E_CODEC_STATUS Http2Frame::DecodeData(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + if (stFrameHead.uiStreamIdentifier == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + if (H2_FRAME_FLAG_PADDED & stFrameHead.ucFlag) + { + uint8 ucPadLength = 0; + pBuff->Read(&ucPadLength, 1); + uint32 uiDataLength = stFrameHead.uiLength - 1 - ucPadLength; + if (ucPadLength >= stFrameHead.uiLength - 1) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint " + "detected an unspecific protocol error. This error is for " + "use when a more specific error code is not available.", + pReactBuff); + return(CODEC_STATUS_ERR); + } + oHttpMsg.mutable_body()->append(pBuff->GetRawReadBuffer(), uiDataLength); + oHttpMsg.set_data_frame_padding(pBuff->GetRawReadBuffer() + uiDataLength, ucPadLength); + pBuff->AdvanceReadIndex(stFrameHead.uiLength - 1); + } + else + { + oHttpMsg.mutable_body()->append(pBuff->GetRawReadBuffer(), stFrameHead.uiLength); + pBuff->AdvanceReadIndex(stFrameHead.uiLength); + } + return(CODEC_STATUS_PART_OK); +} + +E_CODEC_STATUS Http2Frame::DecodeHeaders(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + if (stFrameHead.uiStreamIdentifier == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + uint8 ucPadLength = 0; + uint32 uiHeaderLength = stFrameHead.uiLength; + uint32 uiHeaderBlockEndPos = pBuff->GetReadIndex() + uiHeaderLength; + if (H2_FRAME_FLAG_PADDED & stFrameHead.ucFlag) + { + pBuff->Read(&ucPadLength, 1); + if (ucPadLength >= stFrameHead.uiLength - 1) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint " + "detected an unspecific protocol error. This error is for " + "use when a more specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + uiHeaderLength = stFrameHead.uiLength - 1 - ucPadLength; + uiHeaderBlockEndPos = pBuff->GetReadIndex() + uiHeaderLength; + oHttpMsg.set_headers_frame_padding(pBuff->GetRawReadBuffer() + uiHeaderLength, ucPadLength); + } + if (H2_FRAME_FLAG_PRIORITY & stFrameHead.ucFlag) + { + tagPriority stPriority; + pBuff->Read(&stPriority.uiDependency, 4); + stPriority.E = (stPriority.uiDependency & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; + stPriority.uiDependency = ntohl(stPriority.uiDependency & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + pBuff->Read(&stPriority.ucWeight, 1); + pCodecH2->SetPriority(stFrameHead.uiStreamIdentifier, stPriority); + } + E_CODEC_STATUS eCodecStatus; + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + eCodecStatus = pCodecH2->UnpackHeader(uiHeaderBlockEndPos, pBuff, oHttpMsg); + } + else + { + oHttpMsg.mutable_hpack_data()->append(pBuff->GetRawReadBuffer(), uiHeaderLength); + pBuff->AdvanceReadIndex(stFrameHead.uiLength); + } + return(eCodecStatus); +} + +E_CODEC_STATUS Http2Frame::DecodePriority(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, CBuffer* pReactBuff) +{ + if (stFrameHead.uiStreamIdentifier == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + if (stFrameHead.uiLength != 5) + { + pCodecH2->SetErrno(H2_ERR_FRAME_SIZE_ERROR); + return(CODEC_STATUS_ERR); + } + tagPriority stPriority; + pBuff->Read(&stPriority.uiDependency, 4); + stPriority.E = (stPriority.uiDependency & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; + stPriority.uiDependency = ntohl(stPriority.uiDependency & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + pBuff->Read(&stPriority.ucWeight, 1); + if (stPriority.uiDependency == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + pCodecH2->SetPriority(stFrameHead.uiStreamIdentifier, stPriority); + return(CODEC_STATUS_PART_OK); +} + +E_CODEC_STATUS Http2Frame::DecodeRstStream(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + if (stFrameHead.uiStreamIdentifier == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + if (stFrameHead.uiLength != 4) + { + pCodecH2->SetErrno(H2_ERR_FRAME_SIZE_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_FRAME_SIZE_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + int32 iErrCode = 0; + pBuff->Read(&iErrCode, 4); // the error code should be H2_ERR_CANCEL + iErrCode = ntohl(iErrCode); + pCodecH2->SetErrno(iErrCode); + pCodecH2->RstStream(stFrameHead.uiStreamIdentifier); + return(CODEC_STATUS_PART_ERR); +} + +E_CODEC_STATUS Http2Frame::DecodeSetting(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, CBuffer* pReactBuff) +{ + if (stFrameHead.uiStreamIdentifier != 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + + if (H2_FRAME_FLAG_ACK & stFrameHead.ucFlag) + { + if (stFrameHead.uiLength != 0) + { + pCodecH2->SetErrno(H2_ERR_FRAME_SIZE_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_FRAME_SIZE_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + return(CODEC_STATUS_OK); + } + else + { + if (stFrameHead.uiLength % 6 != 0) + { + pCodecH2->SetErrno(H2_ERR_FRAME_SIZE_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_FRAME_SIZE_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + + uint32 uiSettingNum = stFrameHead.uiLength / 6; + tagSetting stSetting; + std::vector vecSetting; + for (uint32 i = 0; i < uiSettingNum; ++i) + { + pBuff->Read(&stSetting.unIdentifier, 2); + pBuff->Read(&stSetting.uiValue, 4); + vecSetting.push_back(stSetting); + } + E_H2_ERR_CODE eSettingResult = pCodecH2->Setting(vecSetting); + if (eSettingResult == H2_ERR_NO_ERROR) + { + EncodeSetting(pCodecH2, pReactBuff); + return(CODEC_STATUS_OK); + } + pCodecH2->SetErrno(eSettingResult); + EncodeGoaway(pCodecH2, eSettingResult, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } +} + +E_CODEC_STATUS Http2Frame::DecodePushPromise(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + if (stFrameHead.uiStreamIdentifier == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + uint8 ucPadLength = 0; + uint32 uiHeaderLength = stFrameHead.uiLength; + uint32 uiHeaderBlockEndPos = pBuff->GetReadIndex() + uiHeaderLength; + if (H2_FRAME_FLAG_PADDED & stFrameHead.ucFlag) + { + pBuff->Read(&ucPadLength, 1); + if (ucPadLength >= stFrameHead.uiLength - 1) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint " + "detected an unspecific protocol error. This error is for " + "use when a more specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + uiHeaderLength = stFrameHead.uiLength - 1 - ucPadLength; + uiHeaderBlockEndPos = pBuff->GetReadIndex() + uiHeaderLength; + oHttpMsg.set_push_promise_frame_padding(pBuff->GetRawReadBuffer() + uiHeaderLength, ucPadLength); + } + unsigned char R; + uint32 uiStreamId = 0; + pBuff->Read(&uiStreamId, 4); + R = (uiStreamId & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; + uiStreamId = ntohl(uiStreamId & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + E_CODEC_STATUS eCodecStatus = pCodecH2->PromiseStream(uiStreamId, pReactBuff); + if (eCodecStatus == CODEC_STATUS_PART_OK) + { + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + eCodecStatus = pCodecH2->UnpackHeader(uiHeaderBlockEndPos, pBuff, oHttpMsg); + } + else + { + oHttpMsg.mutable_hpack_data()->append(pBuff->GetRawReadBuffer(), uiHeaderLength); + pBuff->AdvanceReadIndex(stFrameHead.uiLength); + } + } + return(eCodecStatus); +} + +E_CODEC_STATUS Http2Frame::DecodePing(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + CBuffer* pReactBuff) +{ + if (stFrameHead.uiStreamIdentifier != 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "TYPE_PING streamId != 0", pReactBuff); + return(CODEC_STATUS_ERR); + } + if (stFrameHead.uiLength != 8) + { + pCodecH2->SetErrno(H2_ERR_FRAME_SIZE_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_FRAME_SIZE_ERROR, "TYPE_PING length != 8", pReactBuff); + return(CODEC_STATUS_ERR); + } + int iPayload1 = 0; + int iPayload2 = 0; + pBuff->Read(&iPayload1, 4); + pBuff->Read(&iPayload2, 4); + if (!(stFrameHead.ucFlag & H2_FRAME_FLAG_ACK)) + { + EncodePing(pCodecH2, true, iPayload1, iPayload2, pReactBuff); + } + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS Http2Frame::DecodeGoaway(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + if (stFrameHead.uiStreamIdentifier != 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + unsigned char R; + uint32 uiLastStreamId = 0; + int32 iErrCode = 0; + std::string strDebugData; + pBuff->Read(&uiLastStreamId, 4); + pBuff->Read(&iErrCode, 4); + R = (uiLastStreamId & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; + uiLastStreamId = ntohl(uiLastStreamId & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + iErrCode = ntohl(iErrCode); + strDebugData.assign(pBuff->GetRawReadBuffer(), stFrameHead.uiLength - 8); + pBuff->AdvanceReadIndex(stFrameHead.uiLength - 8); + pCodecH2->SetGoaway(uiLastStreamId); + LOG4_WARNING("errcode %d: %s", iErrCode, strDebugData.c_str()); + pCodecH2->SetErrno(iErrCode); + return(CODEC_STATUS_PART_ERR); +} + +E_CODEC_STATUS Http2Frame::DecodeWindowUpdate(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + if (stFrameHead.uiLength != 4) + { + pCodecH2->SetErrno(H2_ERR_FRAME_SIZE_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_FRAME_SIZE_ERROR, "TYPE_WINDOW_UPDATE length != 4", pReactBuff); + return(CODEC_STATUS_ERR); + } + uint32 uiIncrement = 0; + pBuff->Read(&uiIncrement, 4); + uiIncrement &= H2_DATA_MASK_4_BYTE_LOW_31_BIT; + if (uiIncrement == 0) + { + if (stFrameHead.uiStreamIdentifier == 0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "TYPE_WINDOW_UPDATE length == 0 on connection", pReactBuff); + return(CODEC_STATUS_ERR); + } + else + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeRstStream(pCodecH2, stFrameHead.uiStreamIdentifier, H2_ERR_PROTOCOL_ERROR, pReactBuff); + return(CODEC_STATUS_PART_ERR); + } + } + else + { + pCodecH2->WindowUpdate(stFrameHead.uiStreamIdentifier, uiIncrement); + } + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS Http2Frame::DecodeContinuation(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + if (stFrameHead.uiStreamIdentifier == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an" + " unspecific protocol error. This error is for use when a more" + " specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + CBuffer oBuffer; + oHttpMsg.mutable_hpack_data()->append(pBuff->GetRawReadBuffer(), stFrameHead.uiLength); + pBuff->AdvanceReadIndex(stFrameHead.uiLength); + oBuffer.Write(oHttpMsg.hpack_data().c_str(), oHttpMsg.hpack_data().size()); + oHttpMsg.mutable_hpack_data()->clear(); + E_CODEC_STATUS eCodecStatus = pCodecH2->UnpackHeader(oBuffer.GetReadIndex() + oBuffer.ReadableBytes(), &oBuffer, oHttpMsg); + return(eCodecStatus); + } + else + { + oHttpMsg.mutable_hpack_data()->append(pBuff->GetRawReadBuffer(), stFrameHead.uiLength); + pBuff->AdvanceReadIndex(stFrameHead.uiLength); + return(CODEC_STATUS_PAUSE); + } +} + +void Http2Frame::EncodeFrameHeader(const tagH2FrameHead& stFrameHead, CBuffer* pBuff) +{ + uint32 uiIdentifier = stFrameHead.cR; + uiIdentifier <<= 31; + uiIdentifier |= (htonl(stFrameHead.uiStreamIdentifier)); + pBuff->Write(&htonl(stFrameHead.uiLength), 3); + pBuff->Write(&stFrameHead.ucType, 1); + pBuff->Write(&stFrameHead.ucFlag, 1); + pBuff->Write(&uiIdentifier, 4); +} + +void Http2Frame::EncodePriority(const tagPriority& stPriority, CBuffer* pBuff) +{ + if (stPriority.uiDependency > 0) + { + uint32 uiPriority = stPriority.E; + uiPriority <<= 31; + uiPriority |= (htonl(stPriority.uiDependency)); + pBuff->Write(&uiPriority, 4); + pBuff->Write(&stPriority.ucWeight, 1); + } +} + +E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, + uint32 uiStreamId, const HttpMsg& oHttpMsg, + const std::string& strPadding, CBuffer* pBuff) +{ + if (uiStreamId == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + return(CODEC_STATUS_PART_ERR); + } + tagH2FrameHead stFrameHead; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_DATA; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = uiStreamId; + if (strPadding.size() > 0) + { + stFrameHead.uiLength = 1 + oHttpMsg.body().size() + strPadding.size(); + stFrameHead.ucFlag |= H2_FRAME_FLAG_PADDED; + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(&(htonl(strPadding.size())), 1); + pBuff->Write(oHttpMsg.body().c_str(), oHttpMsg.body().size()); + pBuff->Write(strPadding.c_str(), strPadding.size()); + } + else + { + stFrameHead.uiLength = oHttpMsg.body().size(); + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(oHttpMsg.body().c_str(), oHttpMsg.body().size()); + } + return(CODEC_STATUS_PART_OK); +} + +E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, + uint32 uiStreamId, const HttpMsg& oHttpMsg, + const tagPriority& stPriority, const std::string& strPadding, CBuffer* pBuff) +{ + if (uiStreamId == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + return(CODEC_STATUS_PART_ERR); + } + tagH2FrameHead stFrameHead; + CBuffer oBuffer; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_HEADERS; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = uiStreamId; + pCodecH2->PackHeader(oHttpMsg, &oBuffer); + if (strPadding.size() > 0) + { + uint32 uiAddtionLength = 0; + stFrameHead.ucFlag |= H2_FRAME_FLAG_PADDED; + if (stPriority.uiDependency > 0) + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_PRIORITY; + // 6bytes = 48bits = 8 + 1 + 31 + 8 + stFrameHead.uiLength = 6 + oBuffer.ReadableBytes() + strPadding.size(); + uiAddtionLength = 6 + strPadding.size(); + } + else + { + stFrameHead.uiLength = 1 + oBuffer.ReadableBytes() + strPadding.size(); + uiAddtionLength = 1 + strPadding.size(); + } + + if (stFrameHead.uiLength > pCodecH2->GetMaxFrameSize()) + { + stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(&(htonl(strPadding.size())), 1); + EncodePriority(stPriority, pBuff); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); + pBuff->Write(strPadding.c_str(), strPadding.size()); + oBuffer.AdvanceReadIndex(stFrameHead.uiLength - uiAddtionLength); + return(EncodeContinuation(pCodecH2, uiStreamId, &oBuffer, pBuff)); + } + else + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(&(htonl(strPadding.size())), 1); + EncodePriority(stPriority, pBuff); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); + pBuff->Write(strPadding.c_str(), strPadding.size()); + return(CODEC_STATUS_OK); + } + } + else + { + uint32 uiAddtionLength = 0; + if (stPriority.uiDependency > 0) + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_PRIORITY; + // 5bytes = 40bits = 1 + 31 + 8 + stFrameHead.uiLength = 5 + oBuffer.ReadableBytes(); + uiAddtionLength = 5; + } + else + { + stFrameHead.uiLength = oBuffer.ReadableBytes(); + } + + if (stFrameHead.uiLength > pCodecH2->GetMaxFrameSize()) + { + stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + EncodeFrameHeader(stFrameHead, pBuff); + EncodePriority(stPriority, pBuff); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); + oBuffer.AdvanceReadIndex(stFrameHead.uiLength - uiAddtionLength); + return(EncodeContinuation(pCodecH2, uiStreamId, &oBuffer, pBuff)); + } + else + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; + EncodeFrameHeader(stFrameHead, pBuff); + EncodePriority(stPriority, pBuff); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); + return(CODEC_STATUS_OK); + } + } +} + +E_CODEC_STATUS Http2Frame::EncodePriority(CodecHttp2* pCodecH2, + uint32 uiStreamId, const tagPriority& stPriority, CBuffer* pBuff) +{ + if (stPriority.uiDependency == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + return(CODEC_STATUS_PART_ERR); + } + tagH2FrameHead stFrameHead; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_HEADERS; + stFrameHead.ucFlag = H2_FRAME_FLAG_PRIORITY; + stFrameHead.uiStreamIdentifier = uiStreamId; + stFrameHead.uiLength = 5; + uint32 uiPriority = stPriority.E; + uiPriority <<= 31; + uiPriority |= (htonl(stPriority.uiDependency)); + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(&uiPriority, 4); + pBuff->Write(&stPriority.ucWeight, 1); + return(CODEC_STATUS_PART_OK); +} + +E_CODEC_STATUS Http2Frame::EncodeRstStream(CodecHttp2* pCodecH2, + uint32 uiStreamId, int32 iErrCode, CBuffer* pBuff) +{ + tagH2FrameHead stFrameHead; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_RST_STREAM; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = uiStreamId; + stFrameHead.uiLength = 4; + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(&(htonl(iErrCode)), 4); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS Http2Frame::EncodeSetting(CodecHttp2* pCodecH2, + const std::vector& vecSetting, CBuffer* pBuff) +{ + tagH2FrameHead stFrameHead; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_RST_STREAM; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = 0; + stFrameHead.uiLength = 6 * vecSetting.size(); + if (stFrameHead.uiLength == 0) // H2_FRAME_FLAG_ACK + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_ACK; + } + EncodeFrameHeader(stFrameHead, pBuff); + for (uint32 i = 0; i < vecSetting.size(); ++i) + { + pBuff->Write(&vecSetting[i].unIdentifier, 2); + pBuff->Write(&vecSetting[i].uiValue, 4); + } + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS Http2Frame::EncodeSetting(CodecHttp2* pCodecH2, CBuffer* pBuff) +{ + tagH2FrameHead stFrameHead; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_RST_STREAM; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = 0; + stFrameHead.uiLength = 0; + stFrameHead.ucFlag |= H2_FRAME_FLAG_ACK; + EncodeFrameHeader(stFrameHead, pBuff); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, + uint32 uiStreamId, uint32 uiPromiseStreamId, + const std::string& strPadding, const HttpMsg& oHttpMsg, CBuffer* pBuff) +{ + if (uiStreamId == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + return(CODEC_STATUS_ERR); + } + + tagH2FrameHead stFrameHead; + CBuffer oBuffer; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_HEADERS; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = uiStreamId; + pCodecH2->PackHeader(oHttpMsg, &oBuffer); + uint32 uiAddtionLength = 0; + if (strPadding.size() > 0) + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_PADDED; + stFrameHead.uiLength = 5 + oBuffer.ReadableBytes() + strPadding.size(); + uiAddtionLength = 5 + strPadding.size(); + if (stFrameHead.uiLength > pCodecH2->GetMaxFrameSize()) + { + stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(&(htonl(strPadding.size())), 1); + // ignore R + pBuff->Write(&uiPromiseStreamId, 4); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); + pBuff->Write(strPadding.c_str(), strPadding.size()); + oBuffer.AdvanceReadIndex(stFrameHead.uiLength - uiAddtionLength); + return(EncodeContinuation(pCodecH2, uiStreamId, &oBuffer, pBuff)); + } + else + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(&(htonl(strPadding.size())), 1); + // ignore R + pBuff->Write(&uiPromiseStreamId, 4); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); + pBuff->Write(strPadding.c_str(), strPadding.size()); + return(CODEC_STATUS_OK); + } + } + else + { + stFrameHead.uiLength = 4 + oBuffer.ReadableBytes(); + uiAddtionLength = 4; + if (stFrameHead.uiLength > pCodecH2->GetMaxFrameSize()) + { + stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + EncodeFrameHeader(stFrameHead, pBuff); + // ignore R + pBuff->Write(&uiPromiseStreamId, 4); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); + oBuffer.AdvanceReadIndex(stFrameHead.uiLength - uiAddtionLength); + return(EncodeContinuation(pCodecH2, uiStreamId, &oBuffer, pBuff)); + } + else + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; + EncodeFrameHeader(stFrameHead, pBuff); + // ignore R + pBuff->Write(&uiPromiseStreamId, 4); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); + return(CODEC_STATUS_OK); + } + } +} + +E_CODEC_STATUS Http2Frame::EncodePing(CodecHttp2* pCodecH2, + bool bAck, int32 iPayload1, int32 iPayload2, CBuffer* pBuff) +{ + tagH2FrameHead stFrameHead; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_PING; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = 0; + stFrameHead.uiLength = 8; + if (bAck) // H2_FRAME_FLAG_ACK + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_ACK; + } + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(&iPayload1, 4); + pBuff->Write(&iPayload2, 4); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS Http2Frame::EncodeGoaway(CodecHttp2* pCodecH2, + int32 iErrCode, const std::string& strDebugData, CBuffer* pBuff) +{ + tagH2FrameHead stFrameHead; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_GOAWAY; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = 0x0; + stFrameHead.uiLength = 8 + strDebugData.size(); + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(&(htonl(pCodecH2->GetLastStreamId())), 4); + pBuff->Write(&(htonl(iErrCode)), 4); + pBuff->Write(strDebugData.c_str(), strDebugData.size()); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS Http2Frame::EncodeWindowUpdate(CodecHttp2* pCodecH2, + uint32 uiStreamId, uint32 uiIncrement, CBuffer* pBuff) +{ + if (uiIncrement == 0 || uiIncrement > H2_DATA_MASK_4_BYTE_LOW_31_BIT) + { + return(CODEC_STATUS_PART_ERR); + } + tagH2FrameHead stFrameHead; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_WINDOW_UPDATE; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = uiStreamId; + stFrameHead.uiLength = 4; + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(&uiIncrement, 4); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS Http2Frame::EncodeContinuation(CodecHttp2* pCodecH2, + uint32 uiStreamId, CBuffer* pHpackBuff, CBuffer* pBuff) +{ + if (uiStreamId == 0x0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + return(CODEC_STATUS_ERR); + } + tagH2FrameHead stFrameHead; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_CONTINUATION; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = uiStreamId; + while (pHpackBuff->ReadableBytes() > 0) + { + if (pHpackBuff->ReadableBytes() > pCodecH2->GetMaxFrameSize()) + { + stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + } + else + { + stFrameHead.uiLength = pHpackBuff->ReadableBytes(); + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; + } + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(pHpackBuff->GetRawBuffer(), stFrameHead.uiLength); + pHpackBuff->AdvanceReadIndex(stFrameHead.uiLength); + } + return(CODEC_STATUS_OK); +} + +} /* namespace neb */ + diff --git a/src/codec/http2/Http2Frame.hpp b/src/codec/http2/Http2Frame.hpp new file mode 100644 index 00000000..d20cf923 --- /dev/null +++ b/src/codec/http2/Http2Frame.hpp @@ -0,0 +1,133 @@ +/******************************************************************************* + * Project: Nebula + * @file Http2Frame.hpp + * @brief + * @author nebim + * @date: 2020-05-02 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_HTTP2_HTTP2FRAME_HPP_ +#define SRC_CODEC_HTTP2_HTTP2FRAME_HPP_ + +#include +#include "Definition.hpp" +#include "codec/Codec.hpp" +#include "pb/http.pb.h" +#include "H2Comm.hpp" + +namespace neb +{ + +enum E_H2_FRAME_TYPE +{ + H2_FRAME_DATA = 0x0, ///< 帧包含消息的所有或部分有效负载。 + H2_FRAME_HEADERS = 0x1, ///< 帧仅包含 HTTP 标头信息。 + H2_FRAME_PRIORITY = 0x2, ///< 指定分配给流的优先级。 + H2_FRAME_RST_STREAM = 0x3, ///< 错误通知:一个推送承诺遭到拒绝。终止流。 + H2_FRAME_SETTINGS = 0x4, ///< 指定连接配置。 + H2_FRAME_PUSH_PROMISE = 0x5, ///< 通知一个将资源推送到客户端的意图。 + H2_FRAME_PING = 0x6, ///< 检测信号和往返时间。 + H2_FRAME_GOAWAY = 0x7, ///< 停止为当前连接生成流的停止通知。 + H2_FRAME_WINDOW_UPDATE = 0x8, ///< 用于管理流的流控制。 + H2_FRAME_CONTINUATION = 0x9, ///< 用于延续某个标头碎片序列。 +}; + +enum E_H2_FRAME_FLAG +{ + H2_FRAME_FLAG_END_STREAM = 0x1, ///< END_STREAM (0x1) + H2_FRAME_FLAG_ACK = 0x1, ///< SETTING ACK (0x1) + H2_FRAME_FLAG_END_HEADERS = 0x4, ///< END_HEADERS (0x4) + H2_FRAME_FLAG_PADDED = 0x8, ///< PADDED (0x8) + H2_FRAME_FLAG_PRIORITY = 0x20, ///< PRIORITY(0x20) +}; + +class CodecHttp2; + +class Http2Frame: public Codec +{ +public: + Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + virtual ~Http2Frame(); + + virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) + { + return(CODEC_STATUS_INVALID); + } + virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) + { + return(CODEC_STATUS_INVALID); + } + + virtual E_CODEC_STATUS Encode(CodecHttp2* pCodecH2, + const HttpMsg& oHttpMsg, CBuffer* pBuff); + virtual E_CODEC_STATUS Decode(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff); + +protected: + E_CODEC_STATUS DecodeData(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff); + E_CODEC_STATUS DecodeHeaders(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff); + E_CODEC_STATUS DecodePriority(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + CBuffer* pReactBuff); + E_CODEC_STATUS DecodeRstStream(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff); + E_CODEC_STATUS DecodeSetting(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + CBuffer* pReactBuff); + E_CODEC_STATUS DecodePushPromise(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff); + E_CODEC_STATUS DecodePing(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + CBuffer* pReactBuff); + E_CODEC_STATUS DecodeGoaway(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff); + E_CODEC_STATUS DecodeWindowUpdate(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff); + E_CODEC_STATUS DecodeContinuation(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff); + + + void EncodeFrameHeader(const tagH2FrameHead& stFrameHead, CBuffer* pBuff); + void EncodePriority(const tagPriority& stPriority, CBuffer* pBuff); + E_CODEC_STATUS EncodeData(CodecHttp2* pCodecH2, + uint32 uiStreamId, const HttpMsg& oHttpMsg, + const std::string& strPadding, CBuffer* pBuff); + E_CODEC_STATUS EncodeHeaders(CodecHttp2* pCodecH2, + uint32 uiStreamId, const HttpMsg& oHttpMsg, + const tagPriority& stPriority, const std::string& strPadding, + CBuffer* pBuff); + E_CODEC_STATUS EncodePriority(CodecHttp2* pCodecH2, + uint32 uiStreamId, const tagPriority& stPriority, CBuffer* pBuff); + E_CODEC_STATUS EncodeRstStream(CodecHttp2* pCodecH2, + uint32 uiStreamId, int32 iErrCode, CBuffer* pBuff); + E_CODEC_STATUS EncodeSetting(CodecHttp2* pCodecH2, + const std::vector& vecSetting, CBuffer* pBuff); + E_CODEC_STATUS EncodeSetting(CodecHttp2* pCodecH2, CBuffer* pBuff); // ACK + E_CODEC_STATUS EncodePushPromise(CodecHttp2* pCodecH2, + uint32 uiStreamId, uint32 uiPromiseStreamId, + const std::string& strPadding, const HttpMsg& oHttpMsg, CBuffer* pBuff); + E_CODEC_STATUS EncodePing(CodecHttp2* pCodecH2, + bool bAck, int32 iPayload1, int32 iPayload2, CBuffer* pBuff); + E_CODEC_STATUS EncodeGoaway(CodecHttp2* pCodecH2, + int32 iErrCode, const std::string& strDebugData, CBuffer* pBuff); + E_CODEC_STATUS EncodeWindowUpdate(CodecHttp2* pCodecH2, + uint32 uiStreamId, uint32 uiIncrement, CBuffer* pBuff); + E_CODEC_STATUS EncodeContinuation(CodecHttp2* pCodecH2, + uint32 uiStreamId, CBuffer* pHpackBuff, CBuffer* pBuff); +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_HTTP2_HTTP2FRAME_HPP_ */ + diff --git a/src/codec/http2/Http2Header.cpp b/src/codec/http2/Http2Header.cpp new file mode 100644 index 00000000..a5827b56 --- /dev/null +++ b/src/codec/http2/Http2Header.cpp @@ -0,0 +1,148 @@ +/******************************************************************************* + * Project: Nebula + * @file Http2Header.cpp + * @brief + * @author nebim + * @date: 2020-05-03 + * @note + * Modify history: + ******************************************************************************/ +#include "Http2Header.hpp" +#include "Huffman.hpp" + +namespace neb +{ + +Http2Header::Http2Header(const std::string& strName, const std::string& strValue) + : m_strName(strName), m_strValue(strValue), m_uiHpackSize(0) +{ + m_uiHpackSize = m_strName.size() + m_strValue.size() + 32; +} + +Http2Header::Http2Header(Http2Header&& rHeader) + : m_strName(std::move(rHeader.m_strName)), + m_strValue(std::move(rHeader.m_strValue)), + m_uiHpackSize(rHeader.m_uiHpackSize) +{ +} + +Http2Header::~Http2Header() +{ +} + +Http2Header& Http2Header::operator=(Http2Header&& rHeader) +{ + m_strName = std::move(rHeader.m_strName); + m_strValue = std::move(rHeader.m_strValue); + m_uiHpackSize = rHeader.m_uiHpackSize; + return(*this); +} + +void Http2Header::EncodeInt(size_t uiValue, size_t uiPrefix, char cBits, CBuffer* pBuff) +{ + if (uiValue < uiPrefix) + { + pBuff->WriteByte(cBits | uiValue); + } + else + { + pBuff->WriteByte(cBits | uiPrefix); + int I = uiValue - uiPrefix; + int B = 0; + while (I >= 128) + { + B = (I & 0x7f) | 0x80; // I % 128 + 128 + I >>= 7; // I / 128 + pBuff->WriteByte(B); + } + pBuff->WriteByte(I); + } +} + +int Http2Header::DecodeInt(int iPrefixMask, CBuffer* pBuff) +{ + char B; + pBuff->ReadByte(B); + int I = B & iPrefixMask; + if (I < iPrefixMask) + { + return(I); // This was a single byte value. + } + else + { + // This is a multibyte value. Read 7 bits at a time. + I = iPrefixMask; + int M = 0; + do + { + pBuff->ReadByte(B); // next octet + I += (B & 0x7f) << M; // I = I + (B & 127) * 2^M + M += 7; // M = M + 7 + } + while ((B & 128) == 128); + + return(I); + } +} + +void Http2Header::EncodeStringLiteral(const std::string& strLiteral, CBuffer* pBuff) +{ + Http2Header::EncodeInt(strLiteral.size(), (size_t)H2_HPACK_PREFIX_7_BITS, + (char)0, pBuff); + pBuff->Write(strLiteral.c_str(), strLiteral.size()); +} + +void Http2Header::EncodeStringLiteralWithHuffman(const std::string& strLiteral, CBuffer* pBuff) +{ + Huffman::Instance()->Encode(strLiteral, pBuff); +} + +bool Http2Header::DecodeStringLiteral(CBuffer* pBuff, std::string& strLiteral, bool& bWithHuffman) +{ + char B; + size_t uiReadIndex = pBuff->GetReadIndex(); + pBuff->ReadByte(B); + pBuff->SetReadIndex(uiReadIndex); + int iStringLength = DecodeInt(H2_HPACK_PREFIX_7_BITS, pBuff); + if (pBuff->ReadableBytes() < (size_t)iStringLength) + { + return(false); + } + + if (B & 0x80) // HUFFMAN + { + bWithHuffman = true; + return(Huffman::Instance()->Decode(pBuff, iStringLength, strLiteral)); + } + else + { + strLiteral.assign(pBuff->GetRawReadBuffer(), iStringLength); + pBuff->AdvanceReadIndex(iStringLength); + return(true); + } +} + +size_t Http2Header::GetStaticTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue) +{ + if (strHeaderValue.size() == 0) + { + auto c_iter = sc_mapStaticTable.find(strHeaderName); + if (c_iter == sc_mapStaticTable.end()) + { + return(0); + } + return(c_iter->second); + } + else + { + auto c_iter = sc_mapStaticTable.find(strHeaderName + " " + strHeaderValue); + if (c_iter == sc_mapStaticTable.end()) + { + return(0); + } + return(c_iter->second); + } +} + +} /* namespace neb */ + diff --git a/src/codec/http2/Http2Header.hpp b/src/codec/http2/Http2Header.hpp new file mode 100644 index 00000000..4d9db793 --- /dev/null +++ b/src/codec/http2/Http2Header.hpp @@ -0,0 +1,251 @@ +/******************************************************************************* + * Project: Nebula + * @file Http2Header.hpp + * @brief + * @author nebim + * @date: 2020-05-03 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_HTTP2_HTTP2HEADER_HPP_ +#define SRC_CODEC_HTTP2_HTTP2HEADER_HPP_ + +#include +#include +#include +#include "util/CBuffer.hpp" + +namespace neb +{ + +enum E_H2_HPACK_CONDITION +{ + H2_HPACK_CONDITION_INDEXED_HEADER = 0x80, ///< rfc7541 6.1 + H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING = 0x40, ///< rfc7541 6.2.1 + H2_HPACK_CONDITION_LITERAL_HEADER_WITHOUT_INDEXING = 0x00, ///< rfc7541 6.2.2 + H2_HPACK_CONDITION_LITERAL_HEADER_NEVER_INDEXED = 0x10, ///< rfc7541 6.2.3 + H2_HPACK_CONDITION_DYNAMIC_TABLE_SIZE_UPDATE = 0x20, ///< rfc7541 6.3 +}; + +enum E_H2_HPACK_PREFIX +{ + H2_HPACK_PREFIX_0_BITS = 0x00, + H2_HPACK_PREFIX_1_BITS = 0x01, + H2_HPACK_PREFIX_2_BITS = 0x03, + H2_HPACK_PREFIX_3_BITS = 0x07, + H2_HPACK_PREFIX_4_BITS = 0x0F, + H2_HPACK_PREFIX_5_BITS = 0x1F, + H2_HPACK_PREFIX_6_BITS = 0x3F, + H2_HPACK_PREFIX_7_BITS = 0x7F, + H2_HPACK_PREFIX_8_BITS = 0xFF, +}; + +class Http2Header +{ +public: + Http2Header(const std::string& strName, const std::string& strValue); + Http2Header(const Http2Header& rHeader) = delete; + Http2Header(Http2Header&& rHeader); + virtual ~Http2Header(); + + Http2Header& operator=(const Http2Header& rHeader) = delete; + Http2Header& operator=(Http2Header&& rHeader); + + inline const std::string& Name() const + { + return(m_strName); + } + + inline const std::string& Value() const + { + return(m_strValue); + } + + inline size_t HpackSize() const + { + return(m_uiHpackSize); + } + +private: + size_t m_uiHpackSize; + std::string m_strName; + std::string m_strValue; + +public: + /** + * @brief Pseudocode to represent an integer I + * @param[in] uiValue + * @param[in] uiPrefix 2^N-1, The number of bits of the prefix (called N) is a parameter of the integer representation. + * @param[in] cBits bits of the prefix byte. + * @param[in,out] pBuff encode buffer. + */ + static void EncodeInt(size_t uiValue, size_t uiPrefix, char cBits, CBuffer* pBuff); + + /** + * @brief Pseudocode to decode an integer I + * @param[in] iPrefixMask N bits of 1 and 8-N bits of zero + * @param[in,out] pBuff decode buffer + */ + static int DecodeInt(int iPrefixMask, CBuffer* pBuff); + + /** + * @brief encode string literal + * @param[in] strLiteral header name or header value string literal + * @param[in,out] pBuff pack string + */ + static void EncodeStringLiteral(const std::string& strLiteral, CBuffer* pBuff); + + /** + * @brief encode string literal + * @param[in] strLiteral header name or header value string literal + * @param[in,out] pBuff pack string + */ + static void EncodeStringLiteralWithHuffman(const std::string& strLiteral, CBuffer* pBuff); + + /** + * @brief decode string literal + * @param[in,out] pBuff pack string + * @param[out] strLiteral header name or header value string literal + * @param[out] bWithHuffman user huffman or not + */ + static bool DecodeStringLiteral(CBuffer* pBuff, std::string& strLiteral, bool& bWithHuffman); + + static size_t GetStaticTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue = ""); + +public: + static const size_t sc_uiMaxStaticTableIndex = 61; + static const std::vector< + std::pair> sc_vecStaticTable = { + {"unknow", "undefine"}, + {":authority", ""}, ///< 1 + {":method", "GET"}, ///< 2 + {":method", "POST"}, ///< 3 + {":path", "/"}, ///< 4 + {":path", "/index.html"}, ///< 5 + {":scheme", "http"}, ///< 6 + {":scheme", "https"}, ///< 7 + {":status", "200"}, ///< 8 + {":status", "204"}, ///< 9 + {":status", "206"}, ///< 10 + {":status", "304"}, ///< 11 + {":status", "400"}, ///< 12 + {":status", "404"}, ///< 13 + {":status", "500"}, ///< 14 + {"accept-charset", ""}, ///< 15 + {"accept-encoding", "gzip, deflate"}, ///< 16 + {"accept-language", ""}, ///< 17 + {"accept-ranges", ""}, ///< 18 + {"accept", ""}, ///< 19 + {"access-control-allow-origin", ""}, ///< 20 + {"age", ""}, ///< 21 + {"allow", ""}, ///< 22 + {"authorization", ""}, ///< 23 + {"cache-control", ""}, ///< 24 + {"content-disposition", ""}, ///< 25 + {"content-encoding", ""}, ///< 26 + {"content-language", ""}, ///< 27 + {"content-length", ""}, ///< 28 + {"content-location", ""}, ///< 29 + {"content-range", ""}, ///< 30 + {"content-type", ""}, ///< 31 + {"cookie", ""}, ///< 32 + {"date", ""}, ///< 33 + {"etag", ""}, ///< 34 + {"expect", ""}, ///< 35 + {"expires", ""}, ///< 36 + {"from", ""}, ///< 37 + {"host", ""}, ///< 38 + {"if-match", ""}, ///< 39 + {"if-modified-since", ""}, ///< 40 + {"if-none-match", ""}, ///< 41 + {"if-range", ""}, ///< 42 + {"if-unmodified-since", ""}, ///< 43 + {"last-modified", ""}, ///< 44 + {"link", ""}, ///< 45 + {"location", ""}, ///< 46 + {"max-forwards", ""}, ///< 47 + {"proxy-authenticate", ""}, ///< 48 + {"proxy-authorization", ""}, ///< 49 + {"range", ""}, ///< 50 + {"referer", ""}, ///< 51 + {"refresh", ""}, ///< 52 + {"retry-after", ""}, ///< 53 + {"server", ""}, ///< 54 + {"set-cookie", ""}, ///< 55 + {"strict-transport-security", ""}, ///< 56 + {"transfer-encoding", ""}, ///< 57 + {"user-agent", ""}, ///< 58 + {"vary", ""}, ///< 59 + {"via", ""}, ///< 60 + {"www-authenticate", ""} ///< 61 + }; + + static const std::unordered_map sc_mapStaticTable = { + {":authority", 1}, + {":method GET", 2}, + {":method POST", 3}, + {":path /", 4}, + {":path /index.html", 5}, + {":scheme http", 6}, + {":scheme https", 7}, + {":status 200", 8}, + {":status 204", 9}, + {":status 206", 10}, + {":status 304", 11}, + {":status 400", 12}, + {":status 404", 13}, + {":status 500", 14}, + {"accept-charset", 15}, + {"accept-encoding gzip, deflate", 16}, + {"accept-language", 17}, + {"accept-ranges", 18}, + {"accept", 19}, + {"access-control-allow-origin", 20}, + {"age", 21}, + {"allow", 22}, + {"authorization", 23}, + {"cache-control", 24}, + {"content-disposition", 25}, + {"content-encoding", 26}, + {"content-language", 27}, + {"content-length", 28}, + {"content-location", 29}, + {"content-range", 30}, + {"content-type", 31}, + {"cookie", 32}, + {"date", 33}, + {"etag", 34}, + {"expect", 35}, + {"expires", 36}, + {"from", 37}, + {"host", 38}, + {"if-match", 39}, + {"if-modified-since", 40}, + {"if-none-match", 41}, + {"if-range", 42}, + {"if-unmodified-since", 43}, + {"last-modified", 44}, + {"link", 45}, + {"location", 46}, + {"max-forwards", 47}, + {"proxy-authenticate", 48}, + {"proxy-authorization", 49}, + {"range", 50}, + {"referer", 51}, + {"refresh", 52}, + {"retry-after", 53}, + {"server", 54}, + {"set-cookie", 55}, + {"strict-transport-security", 56}, + {"transfer-encoding", 57}, + {"user-agent", 58}, + {"vary", 59}, + {"via", 60}, + {"www-authenticate", 61} + }; +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_HTTP2_HTTP2HEADER_HPP_ */ + diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp new file mode 100644 index 00000000..e1702b3a --- /dev/null +++ b/src/codec/http2/Http2Stream.cpp @@ -0,0 +1,315 @@ +/******************************************************************************* + * Project: Nebula + * @file Http2Stream.cpp + * @brief + * @author nebim + * @date: 2020-05-03 + * @note + * Modify history: + ******************************************************************************/ +#include "Http2Stream.hpp" +#include "Http2Frame.hpp" +#include "CodecHttp2.hpp" + +namespace neb +{ + +Http2Stream::Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) + : Codec(pLogger, eCodecType), + m_eStreamState(H2_STREAM_IDLE), m_uiStreamId(0) +{ +#if __cplusplus >= 201401L + m_pFrame = std::make_unique(pLogger, eCodecType); +#else + m_pFrame = std::unique_ptr(new Http2Frame(pLogger, eCodecType)); +#endif +} + +Http2Stream::~Http2Stream() +{ +} + +E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, + const HttpMsg& oHttpMsg, CBuffer* pBuff) +{ + tagH2FrameHead stFrameHead; + m_pFrame->Encode(pCodecH2, stFrameHead, pBuff, oHttpMsg, pReactBuff); + switch (m_eStreamState) + { + case H2_STREAM_IDLE: + switch (stFrameHead.ucType) + { + case H2_FRAME_HEADERS: + m_eStreamState = H2_STREAM_OPEN; + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + m_bEndHeaders = true; + } + break; + case H2_FRAME_PRIORITY: + break; + default: + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + m_pFrame->EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, + "The endpoint detected an unspecific protocol error." + " This error is for use when a more specific error " + "code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) + { + m_eStreamState = H2_STREAM_HALF_CLOSE_REMOTE; + } + break; + case H2_STREAM_RESERVED_LOCAL: + switch (stFrameHead.ucType) + { + case H2_FRAME_RST_STREAM: + m_eStreamState = H2_STREAM_CLOSE; + break; + default: + break; + } + break; + case H2_STREAM_RESERVED_REMOTE: + switch (stFrameHead.ucType) + { + case H2_FRAME_HEADERS: + m_eStreamState = H2_STREAM_HALF_CLOSE_LOCAL; + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + m_bEndHeaders = true; + } + break; + case H2_FRAME_RST_STREAM: + m_eStreamState = H2_STREAM_CLOSE; + break; + default: + break; + } + break; + case H2_STREAM_OPEN: + switch (stFrameHead.ucType) + { + case H2_FRAME_RST_STREAM: + m_eStreamState = H2_STREAM_CLOSE; + break; + default: + break; + } + if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) + { + m_eStreamState = H2_STREAM_HALF_CLOSE_REMOTE; + } + break; + case H2_STREAM_HALF_CLOSE_LOCAL: + switch (stFrameHead.ucType) + { + case H2_FRAME_RST_STREAM: + m_eStreamState = H2_STREAM_CLOSE; + break; + default: + break; + } + if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) + { + m_eStreamState = H2_STREAM_CLOSE; + } + break; + case H2_STREAM_HALF_CLOSE_REMOTE: + /** + * If an endpoint receives additional frames, other than WINDOW_UPDATE, + * PRIORITY, or RST_STREAM, for a stream that is in this state, it + * MUST respond with a stream error (Section 5.4.2) of type. + */ + switch (stFrameHead.ucType) + { + case H2_FRAME_RST_STREAM: + m_eStreamState = H2_STREAM_CLOSE; + break; + case H2_FRAME_WINDOW_UPDATE: + case H2_FRAME_PRIORITY: + break; + default: + m_pFrame->EncodeRstStream(pCodecH2, + stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); + return(CODEC_STATUS_PART_ERR); + } + break; + case H2_STREAM_CLOSE: + break; + default: + break; + } + return(); +} + +E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + switch (m_eStreamState) + { + case H2_STREAM_IDLE: + switch (stFrameHead.ucType) + { + case H2_FRAME_HEADERS: + m_eStreamState = H2_STREAM_OPEN; + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + m_bEndHeaders = true; + } + break; + case H2_FRAME_PRIORITY: + break; + default: + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + m_pFrame->EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, + "The endpoint detected an unspecific protocol error." + " This error is for use when a more specific error " + "code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) + { + m_eStreamState = H2_STREAM_HALF_CLOSE_REMOTE; + } + break; + case H2_STREAM_RESERVED_LOCAL: + switch (stFrameHead.ucType) + { + case H2_FRAME_RST_STREAM: + m_eStreamState = H2_STREAM_CLOSE; + break; + default: + break; + } + break; + case H2_STREAM_RESERVED_REMOTE: + switch (stFrameHead.ucType) + { + case H2_FRAME_HEADERS: + m_eStreamState = H2_STREAM_HALF_CLOSE_LOCAL; + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + m_bEndHeaders = true; + } + break; + case H2_FRAME_RST_STREAM: + m_eStreamState = H2_STREAM_CLOSE; + break; + default: + break; + } + break; + case H2_STREAM_OPEN: + switch (stFrameHead.ucType) + { + case H2_FRAME_RST_STREAM: + m_eStreamState = H2_STREAM_CLOSE; + break; + case H2_FRAME_CONTINUATION: + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + m_bEndHeaders = true; + } + break; + default: + break; + } + if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) + { + m_eStreamState = H2_STREAM_HALF_CLOSE_REMOTE; + } + break; + case H2_STREAM_HALF_CLOSE_LOCAL: + switch (stFrameHead.ucType) + { + case H2_FRAME_HEADERS: + case H2_FRAME_CONTINUATION: + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + m_bEndHeaders = true; + } + break; + case H2_FRAME_RST_STREAM: + m_eStreamState = H2_STREAM_CLOSE; + break; + default: + break; + } + if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) + { + m_eStreamState = H2_STREAM_CLOSE; + } + break; + case H2_STREAM_HALF_CLOSE_REMOTE: + /** + * If an endpoint receives additional frames, other than WINDOW_UPDATE, + * PRIORITY, or RST_STREAM, for a stream that is in this state, it + * MUST respond with a stream error (Section 5.4.2) of type. + */ + switch (stFrameHead.ucType) + { + case H2_FRAME_RST_STREAM: + m_eStreamState = H2_STREAM_CLOSE; + break; + case H2_FRAME_WINDOW_UPDATE: + case H2_FRAME_PRIORITY: + break; + default: + m_pFrame->EncodeRstStream(pCodecH2, + stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); + return(CODEC_STATUS_PART_ERR); + } + break; + case H2_STREAM_CLOSE: + switch (stFrameHead.ucType) + { + case H2_FRAME_PRIORITY: + break; + default: + m_pFrame->EncodeRstStream(pCodecH2, + stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); + return(CODEC_STATUS_PART_ERR); + } + break; + default: + break; + } + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + if (m_bEndHeaders) + { + eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); + m_oHttpMsg.set_stream_id(m_uiStreamId); + if (pCodecH2->IsChunkNotice()) + { + oHttpMsg = std::move(m_oHttpMsg); + } + else + { + if (m_eStreamState == H2_STREAM_CLOSE) + { + oHttpMsg = std::move(m_oHttpMsg); + } + } + } + else + { + if (stFrameHead.ucType != H2_FRAME_CONTINUATION) + { + // If the END_HEADERS bit is not set, this frame MUST be followed by another CONTINUATION frame. + // A receiver MUST treat the receipt of any other type of frame or a frame on a different + // stream as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. + SetErrno(H2_ERR_PROTOCOL_ERROR); + m_pFrame->EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint " + "detected an unspecific protocol error. This error is for " + "use when a more specific error code is not available.", pReactBuff); + return(CODEC_STATUS_ERR); + } + eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); + } + return(eStatus); +} + +} /* namespace neb */ + diff --git a/src/codec/http2/Http2Stream.hpp b/src/codec/http2/Http2Stream.hpp new file mode 100644 index 00000000..0b821603 --- /dev/null +++ b/src/codec/http2/Http2Stream.hpp @@ -0,0 +1,85 @@ +/******************************************************************************* + * Project: Nebula + * @file Http2Stream.hpp + * @brief + * @author nebim + * @date: 2020-05-03 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_HTTP2_HTTP2STREAM_HPP_ +#define SRC_CODEC_HTTP2_HTTP2STREAM_HPP_ + +#include +#include "Definition.hpp" +#include "codec/Codec.hpp" +#include "pb/http.pb.h" +#include "H2Comm.hpp" + +namespace neb +{ + +const uint32 HTTP2_CLIENT_STREAM = 0x00000001; + +enum E_H2_STREAM_STATES +{ + H2_STREAM_IDLE = 0, + H2_STREAM_RESERVED_LOCAL = 1, + H2_STREAM_RESERVED_REMOTE = 2, + H2_STREAM_OPEN = 3, + H2_STREAM_HALF_CLOSE_LOCAL = 4, + H2_STREAM_HALF_CLOSE_REMOTE = 5, + H2_STREAM_CLOSE = 6, +}; + +class Http2Frame; +class CodecHttp2; + +class Http2Stream: public Codec +{ +public: + Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + virtual ~Http2Stream(); + + virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) + { + return(CODEC_STATUS_INVALID); + } + virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) + { + return(CODEC_STATUS_INVALID); + } + + virtual E_CODEC_STATUS Encode(CodecHttp2* pCodecH2, + const HttpMsg& oHttpMsg, CBuffer* pBuff); + virtual E_CODEC_STATUS Decode(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, + HttpMsg& oHttpMsg, CBuffer* pReactBuff); + + uint32 GetStreamId() const + { + return(m_uiStreamId); + } + + bool IsEndHeaders() const + { + return(m_bEndHeaders); + } + + void SetState(E_H2_STREAM_STATES eStreamState) + { + m_eStreamState = eStreamState; + } + +private: + E_H2_STREAM_STATES m_eStreamState; + uint32 m_uiStreamId; + bool m_bEndHeaders = false; + std::unique_ptr m_pFrame; + HttpMsg m_oHttpMsg; +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_HTTP2_HTTP2STREAM_HPP_ */ + diff --git a/src/codec/http2/Huffman.cpp b/src/codec/http2/Huffman.cpp new file mode 100644 index 00000000..7b5e4902 --- /dev/null +++ b/src/codec/http2/Huffman.cpp @@ -0,0 +1,163 @@ +/******************************************************************************* + * Project: Nebula + * @file Huffman.cpp + * @brief + * @author nebim + * @date: 2020-05-04 + * @note + * Modify history: + ******************************************************************************/ +#include + +namespace neb +{ + +Huffman::Huffman() + : m_pRoot(nullptr) +{ + m_pRoot = new Node(); + BuildTree(); +} + +Huffman::~Huffman() +{ + if (m_pRoot != nullptr) + { + delete m_pRoot; + m_pRoot = nullptr; + } +} + +Huffman* Huffman::Instance() +{ + if (m_pInstance == nullptr) + { + m_pInstance = new Huffman(); + } + return(m_pInstance); +} + +void Huffman::Encode(const std::string& strData, CBuffer* pBuff) +{ + uint32_t uiCurrent = 0; + uint32_t uiNotWrittenBits = 0; + int iByte = 0; + uint32_t uiCode = 0; + uint8_t ucBits = 0; + uint8_t ucMask = 0xFF; + + for (int i = 0; i < strData.size(); ++i) + { + iByte = strData[i]; + uiCode = CODES[iByte]; + ucBits = CODE_LENGTHS[iByte]; + + uiCurrent <<= ucBits; + uiCurrent |= uiCode; + uiNotWrittenBits += ucBits; + + while (uiNotWrittenBits >= 8) + { + uiNotWrittenBits -= 8; + pBuff->WriteByte((uiCurrent >> uiNotWrittenBits)); + } + } + + if (uiNotWrittenBits > 0) + { + uiCurrent <<= (8 - uiNotWrittenBits); + uiCurrent |= (ucMask >> uiNotWrittenBits); + pBuff->WriteByte((uiCurrent)); + } +} + +bool Huffman::Decode(CBuffer* pBuff, uint32_t iBuffLength, std::string& strData) +{ + Node *pNode = m_pRoot; + uint32_t uiCurrent = 0; + uint8_t ucBits = 0; + char c; + for (int i = 0; i < iBuffLength; ++i) + { + pBuff->ReadByte(c); + uiCurrent = (uiCurrent << 8) | c; + ucBits += 8; + while (ucBits >= 8) + { + uint32_t uiChild = (uiCurrent >> (ucBits - 8)) & 0xFF; + pNode = pNode->vecChildren[uiChild]; + if (pNode->vecChildren.size() == 0) + { + // terminal pNode + strData.append(1, pNode->ucSymbol); + ucBits -= pNode->ucTerminalBits; + pNode = m_pRoot; + } + else + { + // non-terminal pNode + ucBits -= 8; + } + } + } + + /* TODO confirm + A padding strictly longer than 7 bits MUST be treated as a decoding error. + A padding not corresponding to the most significant bits of the code for the EOS symbol MUST be treated as a decoding error. + A Huffman-encoded string literal containing the EOS symbol MUST be treated as a decoding error. + */ + while (ucBits > 0) + { + uint32_t uiChild = (uiCurrent << (8 - ucBits)) & 0xFF; + pNode = pNode->vecChildren[uiChild]; + if (pNode->vecChildren.size() > 0 || pNode->ucTerminalBits > ucBits) + { + return(false); + } + strData.append(1, pNode->ucSymbol); + ucBits -= pNode->ucTerminalBits; + pNode = m_pRoot; + } + return(true); +} + +void Huffman::BuildTree() +{ + uint8_t ucSym; + uint32_t uiCode; + uint8_t ucLen; + for (int i = 0; i < 256; i++) + { + Node* pTerminal = new Node(ucSym, ucLen); + + Node* pCurrent = m_pRoot; + while (ucLen > 8) + { + ucLen -= 8; + int i = ((uiCode >> ucLen) & 0xFF); + if (pCurrent->vecChildren.size() > 0) + { + if (pCurrent->vecChildren[i] == nullptr) + { + pCurrent->vecChildren[i] = new Node(); + } + pCurrent = pCurrent->vecChildren[i]; + } + else // Impossible to reach + { + ; //error + } + } + + uint8_t ucShift = 8 - ucLen; + uint32_t uiStart = (uiCode << ucShift) & 0xFF; + uint32_t uiEnd = 1 << ucShift; + for (uint32_t i = uiStart; i < uiStart + uiEnd; i++) + { + pCurrent->vecChildren[i] = pTerminal; + } + } +} + +} /* namespace neb */ + diff --git a/src/codec/http2/Huffman.hpp b/src/codec/http2/Huffman.hpp new file mode 100644 index 00000000..106ea379 --- /dev/null +++ b/src/codec/http2/Huffman.hpp @@ -0,0 +1,161 @@ +/******************************************************************************* + * Project: Nebula + * @file Huffman.hpp + * @brief + * @author nebim + * @date: 2020-05-04 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_HTTP2_HUFFMAN_HPP_ +#define SRC_CODEC_HTTP2_HUFFMAN_HPP_ + +#include +#include +#include "util/CBuffer.hpp" + +namespace neb +{ + +struct Node +{ + // Terminal nodes have a symbol. + uint8_t ucSymbol; + // Number of bits represented in the terminal node. + uint8_t ucTerminalBits; + // size() = 0 if terminal. + std::vector vecChildren; + + Node() + { + ucSymbol = 0; + ucTerminalBits = 0; + vecChildren.resize(256, nullptr); + } + + Node(uint8_t ucSym, uint8_t ucBits) + { + ucSymbol = ucSym; + uint8_t b = ucBits & 0x07; + ucTerminalBits = (b == 0) ? 8 : b; + } + + Node(const Node& stNode) = delete; + + Node(Node&& stNode) + { + ucSymbol = stNode.ucSymbol; + stNode.ucSymbol = 0; + ucTerminalBits = stNode.ucTerminalBits; + stNode.ucTerminalBits = 0; + vecChildren = std::move(stNode.vecChildren); + } + + virtual ~Node() + { + for (auto it = vecChildren.begin(); it != vecChildren.end(); ++it) + { + if (*it != nullptr) + { + delete (*it); + } + } + vecChildren.clear(); + } + + Node& operator=(const Node& stNode) = delete; + + Node& operator=(Node&& stNode) + { + ucSymbol = stNode.ucSymbol; + stNode.ucSymbol = 0; + ucTerminalBits = stNode.ucTerminalBits; + stNode.ucTerminalBits = 0; + vecChildren = std::move(stNode.vecChildren); + return(*this); + } +}; + +class Huffman +{ +public: + virtual ~Huffman(); + + static Huffman* Instance(); + + void Encode(const std::string& strData, CBuffer* pBuff); + bool Decode(CBuffer* pBuff, int iBuffLength, std::string& strData); + +protected: + void BuildTree(); + +private: + Huffman(); + Node* m_pRoot; + + static Huffman* m_pInstance = nullptr; + + // Appendix C: Huffman Codes + // http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-B + static const uint32_t CODES[] = + { + 0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, + 0xfffffe7, 0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, + 0x3ffffffd, 0xfffffeb, 0xfffffec, 0xfffffed, 0xfffffee, 0xfffffef, + 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3, 0xffffff4, + 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, + 0xffffffb, 0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa, + 0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18, 0x0, 0x1, 0x2, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, + 0xffb, 0x3fc, 0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, + 0x1ffc, 0x3ffc, 0x22, 0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26, + 0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7, 0x2b, 0x76, 0x2c, 0x8, + 0x9, 0x2d, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, + 0x1ffd, 0xffffffc, 0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, + 0x3fffd4, 0x3fffd5, 0x7fffd9, 0x3fffd6, 0x7fffda, 0x7fffdb, + 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf, 0xffffec, + 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, + 0x7fffe3, 0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, + 0x7fffe6, 0x7fffe7, 0xffffef, 0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, + 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde, 0x7fffea, 0x3fffdd, + 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec, + 0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, + 0x7fffee, 0x7fffef, 0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, + 0x3fffe5, 0x3fffe6, 0x7ffff1, 0x3ffffe0, 0x3ffffe1, 0xfffeb, + 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec, 0x3ffffe2, + 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, + 0x1ffffed, 0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, + 0x3ffffe7, 0x7ffffe2, 0xfffff2, 0x1fffe4, 0x1fffe5, 0x3ffffe8, + 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5, 0xfffec, + 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3, + 0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, + 0x3ffffea, 0x7ffff4, 0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, + 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea, 0x7ffffeb, 0xffffffe, + 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee + }; + + static const uint8_t CODE_LENGTHS[] = + { + 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, 28, 28, + 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, 6, 10, 10, + 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, 5, 5, 5, 6, 6, 6, 6, 6, + 6, 6, 7, 8, 15, 6, 12, 10, 13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6, 15, 5, + 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5, 6, 7, 6, 5, 5, 6, 7, 7, 7, + 7, 7, 15, 11, 14, 13, 28, 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, + 23, 23, 23, 23, 24, 23, 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, + 23, 22, 23, 23, 24, 22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, + 21, 22, 23, 23, 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, + 22, 22, 23, 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, + 24, 25, 19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, + 27, 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23, + 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26 + }; +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_HTTP2_HUFFMAN_HPP_ */ + diff --git a/src/codec/http2/Tree.hpp b/src/codec/http2/Tree.hpp new file mode 100644 index 00000000..79f810cd --- /dev/null +++ b/src/codec/http2/Tree.hpp @@ -0,0 +1,28 @@ +/******************************************************************************* + * Project: Nebula + * @file Tree.hpp + * @brief + * @author nebim + * @date: 2020-05-03 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_HTTP2_TREE_HPP_ +#define SRC_CODEC_HTTP2_TREE_HPP_ + +namespace neb +{ + +template +struct TreeNode +{ + T* pData = nullptr; + struct TreeNode* pFirstChild = nullptr; + struct TreeNode* pRightBrother = nullptr; + struct TreeNode* pParent = nullptr; +}; + +} + +#endif /* SRC_CODEC_HTTP2_TREE_HPP_ */ + From 0ce2e3427ae56a51e0549a30a671230cf4a790cf Mon Sep 17 00:00:00 2001 From: nebim Date: Fri, 16 Oct 2020 18:11:48 +0800 Subject: [PATCH 100/176] modify http2 --- src/codec/Codec.hpp | 9 ++- src/codec/CodecUtil.cpp | 80 ++++++++++++++++--- src/codec/CodecUtil.hpp | 45 ++++++++++- src/codec/http2/CodecHttp2.cpp | 55 +++++++------ src/codec/http2/H2Comm.hpp | 4 + src/codec/http2/Http2Frame.cpp | 38 +++++---- src/codec/http2/Http2Frame.hpp | 5 ++ src/codec/http2/Http2Header.cpp | 134 ++++++++++++++++++++++++++++++- src/codec/http2/Http2Header.hpp | 135 +------------------------------- src/codec/http2/Http2Stream.cpp | 8 +- src/codec/http2/Huffman.cpp | 73 +++++++++++++++-- src/codec/http2/Huffman.hpp | 63 +-------------- 12 files changed, 386 insertions(+), 263 deletions(-) diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 85e94b35..18a007d9 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -22,6 +22,7 @@ #include "Definition.hpp" #include "CodecDefine.hpp" #include "logger/NetLogger.hpp" +#include "CodecUtil.hpp" namespace neb { @@ -108,6 +109,10 @@ class Codec template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); + inline void SetErrno(int32 iErrno) + { + m_iErrno = iErrno; + } protected: const std::string& GetKey() const { @@ -122,10 +127,6 @@ class Codec bool Rc5Decrypt(const std::string& strSrc, std::string& strDest); bool AesEncrypt(const std::string& strSrc, std::string& strDest); bool AesDecrypt(const std::string& strSrc, std::string& strDest); - inline void SetErrno(int32 iErrno) - { - m_iErrno = iErrno; - } protected: std::shared_ptr m_pLogger; diff --git a/src/codec/CodecUtil.cpp b/src/codec/CodecUtil.cpp index 73e6267d..fafe9dd1 100644 --- a/src/codec/CodecUtil.cpp +++ b/src/codec/CodecUtil.cpp @@ -12,28 +12,88 @@ namespace neb { -unsigned long long ntohll(unsigned long long val) +CodecUtil::UnionHostOrder CodecUtil::s_uHostOrder = { { 'L', '?', '?', 'B' } }; + +CodecUtil::CodecUtil() +{ +} + +CodecUtil::~CodecUtil() +{ +} + +uint16 CodecUtil::N2H(uint16 unValue) { - if (__BYTE_ORDER == __LITTLE_ENDIAN) + if (IsLittleEndian()) { - return (((unsigned long long )htonl((int)((val << 32) >> 32))) << 32) | (unsigned int)htonl((int)(val >> 32)); + return((unValue << 8) | (unValue >> 8)); } - else if (__BYTE_ORDER == __BIG_ENDIAN) + return(unValue); +} + +uint16 CodecUtil::H2N(uint16 unValue) +{ + if (IsLittleEndian()) { - return val; + return((unValue << 8) | (unValue >> 8)); } + return(unValue); } -unsigned long long htonll(unsigned long long val) +uint32 CodecUtil::N2H(uint32 uiValue) { - if (__BYTE_ORDER == __LITTLE_ENDIAN) + if (IsLittleEndian()) { - return (((unsigned long long )htonl((int)((val << 32) >> 32))) << 32) | (unsigned int)htonl((int)(val >> 32)); + return(((uiValue ) << 24) + | ((uiValue & 0x0000FF00) << 8) + | ((uiValue & 0x00FF0000) >> 8) + | ((uiValue ) >> 24)); } - else if (__BYTE_ORDER == __BIG_ENDIAN) + return(uiValue); +} + +uint32 CodecUtil::H2N(uint32 uiValue) +{ + if (IsLittleEndian()) + { + return(((uiValue ) << 24) + | ((uiValue & 0x0000FF00) << 8) + | ((uiValue & 0x00FF0000) >> 8) + | ((uiValue ) >> 24)); + } + return uiValue; +} + +uint64 CodecUtil::N2H(uint64 ullValue) +{ + if (IsLittleEndian()) + { + return(((ullValue ) << 56) + | ((ullValue & 0x000000000000FF00) << 40) + | ((ullValue & 0x0000000000FF0000) << 24) + | ((ullValue & 0x00000000FF000000) << 8) + | ((ullValue & 0x000000FF00000000) >> 8) + | ((ullValue & 0x0000FF0000000000) >> 24) + | ((ullValue & 0x00FF000000000000) >> 40) + | ((ullValue ) >> 56)); + } + return(ullValue); +} + +uint64 CodecUtil::H2N(uint64 ullValue) +{ + if (IsLittleEndian()) { - return val; + return(((ullValue ) << 56) + | ((ullValue & 0x000000000000FF00) << 40) + | ((ullValue & 0x0000000000FF0000) << 24) + | ((ullValue & 0x00000000FF000000) << 8) + | ((ullValue & 0x000000FF00000000) >> 8) + | ((ullValue & 0x0000FF0000000000) >> 24) + | ((ullValue & 0x00FF000000000000) >> 40) + | ((ullValue ) >> 56)); } + return(ullValue); } } diff --git a/src/codec/CodecUtil.hpp b/src/codec/CodecUtil.hpp index 4299dd7a..f66cf59e 100644 --- a/src/codec/CodecUtil.hpp +++ b/src/codec/CodecUtil.hpp @@ -10,14 +10,51 @@ #ifndef SRC_CODEC_CODECUTIL_HPP_ #define SRC_CODEC_CODECUTIL_HPP_ -#include -#include +#include "Definition.hpp" namespace neb { -unsigned long long ntohll(unsigned long long val); -unsigned long long htonll(unsigned long long val); +class CodecUtil +{ +public: + /** + * @brief 用于进行判断主机字节序的联合体。 + * @note + * 小端:低地址存放低字节,高地址存放高字节; + * 大端:高地址存放低字节,低地址存放高字节; + * 网络字节序是大端。 + */ + union UnionHostOrder + { + uint8 ucOrder[4]; + uint32 uiOrder; + }; + + CodecUtil(); + virtual ~CodecUtil(); + + static uint16 N2H(uint16 unValue); + static uint16 H2N(uint16 unValue); + static uint32 N2H(uint32 uiValue); + static uint32 H2N(uint32 uiValue); + static uint64 N2H(uint64 ullValue); + static uint64 H2N(uint64 ullValue); + +protected: + static inline bool IsLittleEndian() + { + return('L' == (uint8)s_uHostOrder.uiOrder); + } + + static inline bool IsBigEndian() + { + return('B' == (uint8)s_uHostOrder.uiOrder); + } + +private: + static UnionHostOrder s_uHostOrder; +}; } diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index e08b9792..cdcd0024 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -88,7 +88,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR { if (m_stFrameHead.uiStreamIdentifier == m_pCodingStream->GetStreamId()) { - return(m_pCodingStream->Decode(this, m_stFrameHead, pBuff, oHttpMsg)); + return(m_pCodingStream->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff)); } if (!m_pCodingStream->IsEndHeaders()) { @@ -263,7 +263,7 @@ void CodecHttp2::RstStream(uint32 uiStreamId) if (iter->second == m_pCodingStream) { - m_pCodingStream == nullptr; + m_pCodingStream = nullptr; } delete iter->second; iter->second = nullptr; @@ -333,7 +333,7 @@ void CodecHttp2::WindowUpdate(uint32 uiStreamId, uint32 uiIncrement) E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBuff, HttpMsg& oHttpMsg) { - char B; + char B = 0; size_t uiReadIndex = 0; E_CODEC_STATUS eStatus; @@ -341,7 +341,6 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu int iDynamicTableIndex = -1; std::string strHeaderName; std::string strHeaderValue; - std::vector>& vecNewHeaders; auto pHeader = oHttpMsg.mutable_headers(); while (pBuff->GetReadIndex() < uiHeaderBlockEndPos) { @@ -564,33 +563,33 @@ void CodecHttp2::ReleaseStreamWeight(TreeNode* pNode) E_CODEC_STATUS CodecHttp2::UnpackHeaderIndexed(CBuffer* pBuff, ::google::protobuf::Map< ::std::string, ::std::string >* pHeader) { - int iTableIndex = Http2Header::DecodeInt(H2_HPACK_PREFIX_7_BITS, pBuff); - if (iTableIndex == 0) + uint32 uiTableIndex = (uint32)Http2Header::DecodeInt(H2_HPACK_PREFIX_7_BITS, pBuff); + if (uiTableIndex == 0) { SetErrno(H2_ERR_COMPRESSION_ERROR); LOG4_ERROR("hpack index value of 0 is not used!"); return(CODEC_STATUS_ERR); } - else if (iTableIndex <= Http2Header::sc_uiMaxStaticTableIndex) + else if (uiTableIndex <= Http2Header::sc_uiMaxStaticTableIndex) { pHeader->insert({ - Http2Header::sc_vecStaticTable[iTableIndex].first, - Http2Header::sc_vecStaticTable[iTableIndex].second}); + Http2Header::sc_vecStaticTable[uiTableIndex].first, + Http2Header::sc_vecStaticTable[uiTableIndex].second}); return(CODEC_STATUS_PART_OK); } - else if (iTableIndex <= Http2Header::sc_uiMaxStaticTableIndex + m_vecDecodingDynamicTable.size()) + else if (uiTableIndex <= Http2Header::sc_uiMaxStaticTableIndex + m_vecDecodingDynamicTable.size()) { - int iDynamicTableIndex = iTableIndex - Http2Header::sc_uiMaxStaticTableIndex - 1; + uint32 uiDynamicTableIndex = uiTableIndex - Http2Header::sc_uiMaxStaticTableIndex - 1; pHeader->insert({ - m_vecDecodingDynamicTable[iDynamicTableIndex].Name(), - m_vecDecodingDynamicTable[iDynamicTableIndex].Value()}); + m_vecDecodingDynamicTable[uiDynamicTableIndex].Name(), + m_vecDecodingDynamicTable[uiDynamicTableIndex].Value()}); return(CODEC_STATUS_PART_OK); } else { SetErrno(H2_ERR_COMPRESSION_ERROR); - LOG4_ERROR("hpack index value of %d was greater than the sum of " - "the lengths of both static table and dynamic tables!", iTableIndex); + LOG4_ERROR("hpack index value of %u was greater than the sum of " + "the lengths of both static table and dynamic tables!", uiTableIndex); return(CODEC_STATUS_ERR); } } @@ -601,14 +600,14 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF // Literal Header Field with Incremental Indexing — Indexed Name if (iPrefixMask & ucFirstByte) { - int iTableIndex = Http2Header::DecodeInt(iPrefixMask, pBuff); - if (iTableIndex == 0) + uint32 uiTableIndex = (uint32)Http2Header::DecodeInt(iPrefixMask, pBuff); + if (uiTableIndex == 0) { SetErrno(H2_ERR_COMPRESSION_ERROR); LOG4_ERROR("hpack index value of 0 is not used!"); return(CODEC_STATUS_ERR); } - else if (iTableIndex <= Http2Header::sc_uiMaxStaticTableIndex) + else if (uiTableIndex <= Http2Header::sc_uiMaxStaticTableIndex) { if (!Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) { @@ -616,26 +615,26 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF LOG4_ERROR("DecodeStringLiteral failed!"); return(CODEC_STATUS_ERR); } - strHeaderName = Http2Header::sc_vecStaticTable[iTableIndex].first; + strHeaderName = Http2Header::sc_vecStaticTable[uiTableIndex].first; return(CODEC_STATUS_PART_OK); } - else if (iTableIndex < Http2Header::sc_uiMaxStaticTableIndex + m_vecDecodingDynamicTable.size()) + else if (uiTableIndex < Http2Header::sc_uiMaxStaticTableIndex + m_vecDecodingDynamicTable.size()) { - iDynamicTableIndex = iTableIndex - Http2Header::sc_uiMaxStaticTableIndex - 1; + uint32 uiDynamicTableIndex = uiTableIndex - Http2Header::sc_uiMaxStaticTableIndex - 1; if (!Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) { SetErrno(H2_ERR_COMPRESSION_ERROR); LOG4_ERROR("DecodeStringLiteral failed!"); return(CODEC_STATUS_ERR); } - strHeaderName = m_vecDecodingDynamicTable[iDynamicTableIndex].Name(); + strHeaderName = m_vecDecodingDynamicTable[uiDynamicTableIndex].Name(); return(CODEC_STATUS_PART_OK); } else { SetErrno(H2_ERR_COMPRESSION_ERROR); LOG4_ERROR("hpack index value of %d was greater than the sum of " - "the lengths of both static table and dynamic tables!", iTableIndex); + "the lengths of both static table and dynamic tables!", uiTableIndex); return(CODEC_STATUS_ERR); } } @@ -846,7 +845,7 @@ size_t CodecHttp2::GetEncodingTableIndex(const std::string& strHeaderName, const void CodecHttp2::UpdateEncodingDynamicTable(int iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) { Http2Header oHeader(strHeaderName, strHeaderValue); - if (iDynamicTableIndex < 0 || iDynamicTableIndex >= m_vecEncodingDynamicTable.size()) // new header + if (iDynamicTableIndex < 0 || iDynamicTableIndex >= (int)m_vecEncodingDynamicTable.size()) // new header { while (m_uiEncodingDynamicTableSize + oHeader.HpackSize() > m_uiSettingsMaxFrameSize && m_uiEncodingDynamicTableSize > 0) @@ -875,7 +874,7 @@ void CodecHttp2::UpdateEncodingDynamicTable(int iDynamicTableIndex, const std::s } if (oHeader.HpackSize() <= m_uiSettingsMaxFrameSize) { - if (iDynamicTableIndex < m_vecEncodingDynamicTable.size()) // replace header + if (iDynamicTableIndex < (int)m_vecEncodingDynamicTable.size()) // replace header { m_vecEncodingDynamicTable[iDynamicTableIndex] = std::move(oHeader); } @@ -904,13 +903,13 @@ void CodecHttp2::UpdateEncodingDynamicTable(uint32 uiTableSize) void CodecHttp2::UpdateDecodingDynamicTable(int iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) { Http2Header oHeader(strHeaderName, strHeaderValue); - if (iDynamicTableIndex < 0 || iDynamicTableIndex >= m_vecDecodingDynamicTable.size()) // new header + if (iDynamicTableIndex < 0 || iDynamicTableIndex >= (int)m_vecDecodingDynamicTable.size()) // new header { while (m_uiDecodingDynamicTableSize + oHeader.HpackSize() > m_uiSettingsMaxFrameSize && m_uiDecodingDynamicTableSize > 0) { auto& rLastElement = m_vecDecodingDynamicTable.back(); - m_uiDecodingDynamicTableSize -= oHeader.HpackSize(); + m_uiDecodingDynamicTableSize -= rLastElement.HpackSize(); m_vecDecodingDynamicTable.pop_back(); } if (oHeader.HpackSize() <= m_uiSettingsMaxFrameSize) @@ -933,7 +932,7 @@ void CodecHttp2::UpdateDecodingDynamicTable(int iDynamicTableIndex, const std::s } if (oHeader.HpackSize() <= m_uiSettingsMaxFrameSize) { - if (iDynamicTableIndex < m_vecDecodingDynamicTable.size()) // replace header + if (iDynamicTableIndex < (int)m_vecDecodingDynamicTable.size()) // replace header { m_vecDecodingDynamicTable[iDynamicTableIndex] = std::move(oHeader); } diff --git a/src/codec/http2/H2Comm.hpp b/src/codec/http2/H2Comm.hpp index bd9206dc..6df68cf6 100644 --- a/src/codec/http2/H2Comm.hpp +++ b/src/codec/http2/H2Comm.hpp @@ -73,6 +73,8 @@ struct tagPriority uint8 ucWeight = 0; uint32 uiDependency = 0; + tagPriority(){} + tagPriority(const tagPriority& stFrom) { E = stFrom.E; @@ -94,6 +96,8 @@ struct tagSetting uint16 unIdentifier = 0; uint32 uiValue = 0; + tagSetting(){} + tagSetting(const tagSetting& stSetting) { unIdentifier = stSetting.unIdentifier; diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index 24153549..3508162b 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -141,7 +141,7 @@ E_CODEC_STATUS Http2Frame::DecodeHeaders(CodecHttp2* pCodecH2, pBuff->Read(&stPriority.ucWeight, 1); pCodecH2->SetPriority(stFrameHead.uiStreamIdentifier, stPriority); } - E_CODEC_STATUS eCodecStatus; + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) { eCodecStatus = pCodecH2->UnpackHeader(uiHeaderBlockEndPos, pBuff, oHttpMsg); @@ -303,10 +303,10 @@ E_CODEC_STATUS Http2Frame::DecodePushPromise(CodecHttp2* pCodecH2, uiHeaderBlockEndPos = pBuff->GetReadIndex() + uiHeaderLength; oHttpMsg.set_push_promise_frame_padding(pBuff->GetRawReadBuffer() + uiHeaderLength, ucPadLength); } - unsigned char R; + //unsigned char R; uint32 uiStreamId = 0; pBuff->Read(&uiStreamId, 4); - R = (uiStreamId & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; + //R = (uiStreamId & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; uiStreamId = ntohl(uiStreamId & H2_DATA_MASK_4_BYTE_LOW_31_BIT); E_CODEC_STATUS eCodecStatus = pCodecH2->PromiseStream(uiStreamId, pReactBuff); if (eCodecStatus == CODEC_STATUS_PART_OK) @@ -363,13 +363,13 @@ E_CODEC_STATUS Http2Frame::DecodeGoaway(CodecHttp2* pCodecH2, " specific error code is not available.", pReactBuff); return(CODEC_STATUS_ERR); } - unsigned char R; + //unsigned char R; uint32 uiLastStreamId = 0; int32 iErrCode = 0; std::string strDebugData; pBuff->Read(&uiLastStreamId, 4); pBuff->Read(&iErrCode, 4); - R = (uiLastStreamId & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; + //R = (uiLastStreamId & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; uiLastStreamId = ntohl(uiLastStreamId & H2_DATA_MASK_4_BYTE_LOW_31_BIT); iErrCode = ntohl(iErrCode); strDebugData.assign(pBuff->GetRawReadBuffer(), stFrameHead.uiLength - 8); @@ -450,7 +450,8 @@ void Http2Frame::EncodeFrameHeader(const tagH2FrameHead& stFrameHead, CBuffer* p uint32 uiIdentifier = stFrameHead.cR; uiIdentifier <<= 31; uiIdentifier |= (htonl(stFrameHead.uiStreamIdentifier)); - pBuff->Write(&htonl(stFrameHead.uiLength), 3); + uint32 uiNetLength = htonl(stFrameHead.uiLength); + pBuff->Write(&uiNetLength, 3); pBuff->Write(&stFrameHead.ucType, 1); pBuff->Write(&stFrameHead.ucFlag, 1); pBuff->Write(&uiIdentifier, 4); @@ -487,7 +488,8 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, stFrameHead.uiLength = 1 + oHttpMsg.body().size() + strPadding.size(); stFrameHead.ucFlag |= H2_FRAME_FLAG_PADDED; EncodeFrameHeader(stFrameHead, pBuff); - pBuff->Write(&(htonl(strPadding.size())), 1); + uint16 unNetLength = htons(strPadding.size()); + pBuff->Write(&unNetLength, 1); pBuff->Write(oHttpMsg.body().c_str(), oHttpMsg.body().size()); pBuff->Write(strPadding.c_str(), strPadding.size()); } @@ -537,7 +539,8 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, { stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); EncodeFrameHeader(stFrameHead, pBuff); - pBuff->Write(&(htonl(strPadding.size())), 1); + uint16 unNetLength = htons(strPadding.size()); + pBuff->Write(&unNetLength, 1); EncodePriority(stPriority, pBuff); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); pBuff->Write(strPadding.c_str(), strPadding.size()); @@ -548,7 +551,8 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, { stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; EncodeFrameHeader(stFrameHead, pBuff); - pBuff->Write(&(htonl(strPadding.size())), 1); + uint16 unNetLength = htons(strPadding.size()); + pBuff->Write(&unNetLength, 1); EncodePriority(stPriority, pBuff); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); pBuff->Write(strPadding.c_str(), strPadding.size()); @@ -623,7 +627,8 @@ E_CODEC_STATUS Http2Frame::EncodeRstStream(CodecHttp2* pCodecH2, stFrameHead.uiStreamIdentifier = uiStreamId; stFrameHead.uiLength = 4; EncodeFrameHeader(stFrameHead, pBuff); - pBuff->Write(&(htonl(iErrCode)), 4); + uint32 uiErrCode = htonl(iErrCode); + pBuff->Write(&uiErrCode, 4); return(CODEC_STATUS_OK); } @@ -689,7 +694,8 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, { stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); EncodeFrameHeader(stFrameHead, pBuff); - pBuff->Write(&(htonl(strPadding.size())), 1); + uint16 unNetLength = htons(strPadding.size()); + pBuff->Write(&unNetLength, 1); // ignore R pBuff->Write(&uiPromiseStreamId, 4); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); @@ -701,7 +707,8 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, { stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; EncodeFrameHeader(stFrameHead, pBuff); - pBuff->Write(&(htonl(strPadding.size())), 1); + uint16 unNetLength = htons(strPadding.size()); + pBuff->Write(&unNetLength, 1); // ignore R pBuff->Write(&uiPromiseStreamId, 4); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); @@ -764,8 +771,11 @@ E_CODEC_STATUS Http2Frame::EncodeGoaway(CodecHttp2* pCodecH2, stFrameHead.uiStreamIdentifier = 0x0; stFrameHead.uiLength = 8 + strDebugData.size(); EncodeFrameHeader(stFrameHead, pBuff); - pBuff->Write(&(htonl(pCodecH2->GetLastStreamId())), 4); - pBuff->Write(&(htonl(iErrCode)), 4); + + uint32 uiNetStreamId = htonl(pCodecH2->GetLastStreamId()); + uint32 uiNetErrCode = htonl(iErrCode); + pBuff->Write(&uiNetStreamId, 4); + pBuff->Write(&uiNetErrCode, 4); pBuff->Write(strDebugData.c_str(), strDebugData.size()); return(CODEC_STATUS_OK); } diff --git a/src/codec/http2/Http2Frame.hpp b/src/codec/http2/Http2Frame.hpp index d20cf923..d895ec2c 100644 --- a/src/codec/http2/Http2Frame.hpp +++ b/src/codec/http2/Http2Frame.hpp @@ -43,6 +43,7 @@ enum E_H2_FRAME_FLAG }; class CodecHttp2; +class Http2Stream; class Http2Frame: public Codec { @@ -125,6 +126,10 @@ class Http2Frame: public Codec uint32 uiStreamId, uint32 uiIncrement, CBuffer* pBuff); E_CODEC_STATUS EncodeContinuation(CodecHttp2* pCodecH2, uint32 uiStreamId, CBuffer* pHpackBuff, CBuffer* pBuff); + +private: + friend class CodecHttp2; + friend class Http2Stream; }; } /* namespace neb */ diff --git a/src/codec/http2/Http2Header.cpp b/src/codec/http2/Http2Header.cpp index a5827b56..0c264ebd 100644 --- a/src/codec/http2/Http2Header.cpp +++ b/src/codec/http2/Http2Header.cpp @@ -13,6 +13,136 @@ namespace neb { +const size_t Http2Header::sc_uiMaxStaticTableIndex = 61; +const std::vector> Http2Header::sc_vecStaticTable = { + {"unknow", "undefine"}, + {":authority", ""}, ///< 1 + {":method", "GET"}, ///< 2 + {":method", "POST"}, ///< 3 + {":path", "/"}, ///< 4 + {":path", "/index.html"}, ///< 5 + {":scheme", "http"}, ///< 6 + {":scheme", "https"}, ///< 7 + {":status", "200"}, ///< 8 + {":status", "204"}, ///< 9 + {":status", "206"}, ///< 10 + {":status", "304"}, ///< 11 + {":status", "400"}, ///< 12 + {":status", "404"}, ///< 13 + {":status", "500"}, ///< 14 + {"accept-charset", ""}, ///< 15 + {"accept-encoding", "gzip, deflate"}, ///< 16 + {"accept-language", ""}, ///< 17 + {"accept-ranges", ""}, ///< 18 + {"accept", ""}, ///< 19 + {"access-control-allow-origin", ""}, ///< 20 + {"age", ""}, ///< 21 + {"allow", ""}, ///< 22 + {"authorization", ""}, ///< 23 + {"cache-control", ""}, ///< 24 + {"content-disposition", ""}, ///< 25 + {"content-encoding", ""}, ///< 26 + {"content-language", ""}, ///< 27 + {"content-length", ""}, ///< 28 + {"content-location", ""}, ///< 29 + {"content-range", ""}, ///< 30 + {"content-type", ""}, ///< 31 + {"cookie", ""}, ///< 32 + {"date", ""}, ///< 33 + {"etag", ""}, ///< 34 + {"expect", ""}, ///< 35 + {"expires", ""}, ///< 36 + {"from", ""}, ///< 37 + {"host", ""}, ///< 38 + {"if-match", ""}, ///< 39 + {"if-modified-since", ""}, ///< 40 + {"if-none-match", ""}, ///< 41 + {"if-range", ""}, ///< 42 + {"if-unmodified-since", ""}, ///< 43 + {"last-modified", ""}, ///< 44 + {"link", ""}, ///< 45 + {"location", ""}, ///< 46 + {"max-forwards", ""}, ///< 47 + {"proxy-authenticate", ""}, ///< 48 + {"proxy-authorization", ""}, ///< 49 + {"range", ""}, ///< 50 + {"referer", ""}, ///< 51 + {"refresh", ""}, ///< 52 + {"retry-after", ""}, ///< 53 + {"server", ""}, ///< 54 + {"set-cookie", ""}, ///< 55 + {"strict-transport-security", ""}, ///< 56 + {"transfer-encoding", ""}, ///< 57 + {"user-agent", ""}, ///< 58 + {"vary", ""}, ///< 59 + {"via", ""}, ///< 60 + {"www-authenticate", ""} ///< 61 +}; + +const std::unordered_map Http2Header::sc_mapStaticTable = { + {":authority", 1}, + {":method GET", 2}, + {":method POST", 3}, + {":path /", 4}, + {":path /index.html", 5}, + {":scheme http", 6}, + {":scheme https", 7}, + {":status 200", 8}, + {":status 204", 9}, + {":status 206", 10}, + {":status 304", 11}, + {":status 400", 12}, + {":status 404", 13}, + {":status 500", 14}, + {"accept-charset", 15}, + {"accept-encoding gzip, deflate", 16}, + {"accept-language", 17}, + {"accept-ranges", 18}, + {"accept", 19}, + {"access-control-allow-origin", 20}, + {"age", 21}, + {"allow", 22}, + {"authorization", 23}, + {"cache-control", 24}, + {"content-disposition", 25}, + {"content-encoding", 26}, + {"content-language", 27}, + {"content-length", 28}, + {"content-location", 29}, + {"content-range", 30}, + {"content-type", 31}, + {"cookie", 32}, + {"date", 33}, + {"etag", 34}, + {"expect", 35}, + {"expires", 36}, + {"from", 37}, + {"host", 38}, + {"if-match", 39}, + {"if-modified-since", 40}, + {"if-none-match", 41}, + {"if-range", 42}, + {"if-unmodified-since", 43}, + {"last-modified", 44}, + {"link", 45}, + {"location", 46}, + {"max-forwards", 47}, + {"proxy-authenticate", 48}, + {"proxy-authorization", 49}, + {"range", 50}, + {"referer", 51}, + {"refresh", 52}, + {"retry-after", 53}, + {"server", 54}, + {"set-cookie", 55}, + {"strict-transport-security", 56}, + {"transfer-encoding", 57}, + {"user-agent", 58}, + {"vary", 59}, + {"via", 60}, + {"www-authenticate", 61} +}; + Http2Header::Http2Header(const std::string& strName, const std::string& strValue) : m_strName(strName), m_strValue(strValue), m_uiHpackSize(0) { @@ -61,7 +191,7 @@ void Http2Header::EncodeInt(size_t uiValue, size_t uiPrefix, char cBits, CBuffer int Http2Header::DecodeInt(int iPrefixMask, CBuffer* pBuff) { - char B; + char B = 0; pBuff->ReadByte(B); int I = B & iPrefixMask; if (I < iPrefixMask) @@ -99,7 +229,7 @@ void Http2Header::EncodeStringLiteralWithHuffman(const std::string& strLiteral, bool Http2Header::DecodeStringLiteral(CBuffer* pBuff, std::string& strLiteral, bool& bWithHuffman) { - char B; + char B = 0; size_t uiReadIndex = pBuff->GetReadIndex(); pBuff->ReadByte(B); pBuff->SetReadIndex(uiReadIndex); diff --git a/src/codec/http2/Http2Header.hpp b/src/codec/http2/Http2Header.hpp index 4d9db793..9f9fe5b2 100644 --- a/src/codec/http2/Http2Header.hpp +++ b/src/codec/http2/Http2Header.hpp @@ -67,9 +67,9 @@ class Http2Header } private: - size_t m_uiHpackSize; std::string m_strName; std::string m_strValue; + size_t m_uiHpackSize; public: /** @@ -113,136 +113,9 @@ class Http2Header static size_t GetStaticTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue = ""); public: - static const size_t sc_uiMaxStaticTableIndex = 61; - static const std::vector< - std::pair> sc_vecStaticTable = { - {"unknow", "undefine"}, - {":authority", ""}, ///< 1 - {":method", "GET"}, ///< 2 - {":method", "POST"}, ///< 3 - {":path", "/"}, ///< 4 - {":path", "/index.html"}, ///< 5 - {":scheme", "http"}, ///< 6 - {":scheme", "https"}, ///< 7 - {":status", "200"}, ///< 8 - {":status", "204"}, ///< 9 - {":status", "206"}, ///< 10 - {":status", "304"}, ///< 11 - {":status", "400"}, ///< 12 - {":status", "404"}, ///< 13 - {":status", "500"}, ///< 14 - {"accept-charset", ""}, ///< 15 - {"accept-encoding", "gzip, deflate"}, ///< 16 - {"accept-language", ""}, ///< 17 - {"accept-ranges", ""}, ///< 18 - {"accept", ""}, ///< 19 - {"access-control-allow-origin", ""}, ///< 20 - {"age", ""}, ///< 21 - {"allow", ""}, ///< 22 - {"authorization", ""}, ///< 23 - {"cache-control", ""}, ///< 24 - {"content-disposition", ""}, ///< 25 - {"content-encoding", ""}, ///< 26 - {"content-language", ""}, ///< 27 - {"content-length", ""}, ///< 28 - {"content-location", ""}, ///< 29 - {"content-range", ""}, ///< 30 - {"content-type", ""}, ///< 31 - {"cookie", ""}, ///< 32 - {"date", ""}, ///< 33 - {"etag", ""}, ///< 34 - {"expect", ""}, ///< 35 - {"expires", ""}, ///< 36 - {"from", ""}, ///< 37 - {"host", ""}, ///< 38 - {"if-match", ""}, ///< 39 - {"if-modified-since", ""}, ///< 40 - {"if-none-match", ""}, ///< 41 - {"if-range", ""}, ///< 42 - {"if-unmodified-since", ""}, ///< 43 - {"last-modified", ""}, ///< 44 - {"link", ""}, ///< 45 - {"location", ""}, ///< 46 - {"max-forwards", ""}, ///< 47 - {"proxy-authenticate", ""}, ///< 48 - {"proxy-authorization", ""}, ///< 49 - {"range", ""}, ///< 50 - {"referer", ""}, ///< 51 - {"refresh", ""}, ///< 52 - {"retry-after", ""}, ///< 53 - {"server", ""}, ///< 54 - {"set-cookie", ""}, ///< 55 - {"strict-transport-security", ""}, ///< 56 - {"transfer-encoding", ""}, ///< 57 - {"user-agent", ""}, ///< 58 - {"vary", ""}, ///< 59 - {"via", ""}, ///< 60 - {"www-authenticate", ""} ///< 61 - }; - - static const std::unordered_map sc_mapStaticTable = { - {":authority", 1}, - {":method GET", 2}, - {":method POST", 3}, - {":path /", 4}, - {":path /index.html", 5}, - {":scheme http", 6}, - {":scheme https", 7}, - {":status 200", 8}, - {":status 204", 9}, - {":status 206", 10}, - {":status 304", 11}, - {":status 400", 12}, - {":status 404", 13}, - {":status 500", 14}, - {"accept-charset", 15}, - {"accept-encoding gzip, deflate", 16}, - {"accept-language", 17}, - {"accept-ranges", 18}, - {"accept", 19}, - {"access-control-allow-origin", 20}, - {"age", 21}, - {"allow", 22}, - {"authorization", 23}, - {"cache-control", 24}, - {"content-disposition", 25}, - {"content-encoding", 26}, - {"content-language", 27}, - {"content-length", 28}, - {"content-location", 29}, - {"content-range", 30}, - {"content-type", 31}, - {"cookie", 32}, - {"date", 33}, - {"etag", 34}, - {"expect", 35}, - {"expires", 36}, - {"from", 37}, - {"host", 38}, - {"if-match", 39}, - {"if-modified-since", 40}, - {"if-none-match", 41}, - {"if-range", 42}, - {"if-unmodified-since", 43}, - {"last-modified", 44}, - {"link", 45}, - {"location", 46}, - {"max-forwards", 47}, - {"proxy-authenticate", 48}, - {"proxy-authorization", 49}, - {"range", 50}, - {"referer", 51}, - {"refresh", 52}, - {"retry-after", 53}, - {"server", 54}, - {"set-cookie", 55}, - {"strict-transport-security", 56}, - {"transfer-encoding", 57}, - {"user-agent", 58}, - {"vary", 59}, - {"via", 60}, - {"www-authenticate", 61} - }; + static const size_t sc_uiMaxStaticTableIndex; + static const std::vector> sc_vecStaticTable; + static const std::unordered_map sc_mapStaticTable; }; } /* namespace neb */ diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index e1702b3a..5807bf31 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -33,7 +33,7 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, const HttpMsg& oHttpMsg, CBuffer* pBuff) { tagH2FrameHead stFrameHead; - m_pFrame->Encode(pCodecH2, stFrameHead, pBuff, oHttpMsg, pReactBuff); + m_pFrame->Encode(pCodecH2, oHttpMsg, pBuff); switch (m_eStreamState) { case H2_STREAM_IDLE: @@ -53,7 +53,7 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, m_pFrame->EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an unspecific protocol error." " This error is for use when a more specific error " - "code is not available.", pReactBuff); + "code is not available.", pBuff); return(CODEC_STATUS_ERR); } if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) @@ -132,7 +132,7 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, break; default: m_pFrame->EncodeRstStream(pCodecH2, - stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); + stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pBuff); return(CODEC_STATUS_PART_ERR); } break; @@ -141,7 +141,7 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, default: break; } - return(); + return(CODEC_STATUS_OK); } E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, diff --git a/src/codec/http2/Huffman.cpp b/src/codec/http2/Huffman.cpp index 7b5e4902..253dac1b 100644 --- a/src/codec/http2/Huffman.cpp +++ b/src/codec/http2/Huffman.cpp @@ -12,6 +12,67 @@ namespace neb { +Huffman* Huffman::m_pInstance = nullptr; + +// Appendix C: Huffman Codes +// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-B +const uint32_t Huffman::CODES[] = +{ + 0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, + 0xfffffe7, 0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, + 0x3ffffffd, 0xfffffeb, 0xfffffec, 0xfffffed, 0xfffffee, 0xfffffef, + 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3, 0xffffff4, + 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, + 0xffffffb, 0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa, + 0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18, 0x0, 0x1, 0x2, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, + 0xffb, 0x3fc, 0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, + 0x1ffc, 0x3ffc, 0x22, 0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26, + 0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7, 0x2b, 0x76, 0x2c, 0x8, + 0x9, 0x2d, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, + 0x1ffd, 0xffffffc, 0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, + 0x3fffd4, 0x3fffd5, 0x7fffd9, 0x3fffd6, 0x7fffda, 0x7fffdb, + 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf, 0xffffec, + 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, + 0x7fffe3, 0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, + 0x7fffe6, 0x7fffe7, 0xffffef, 0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, + 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde, 0x7fffea, 0x3fffdd, + 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec, + 0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, + 0x7fffee, 0x7fffef, 0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, + 0x3fffe5, 0x3fffe6, 0x7ffff1, 0x3ffffe0, 0x3ffffe1, 0xfffeb, + 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec, 0x3ffffe2, + 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, + 0x1ffffed, 0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, + 0x3ffffe7, 0x7ffffe2, 0xfffff2, 0x1fffe4, 0x1fffe5, 0x3ffffe8, + 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5, 0xfffec, + 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3, + 0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, + 0x3ffffea, 0x7ffff4, 0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, + 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea, 0x7ffffeb, 0xffffffe, + 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee +}; + +const uint8_t Huffman::CODE_LENGTHS[] = +{ + 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, 28, 28, + 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, 6, 10, 10, + 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, 5, 5, 5, 6, 6, 6, 6, 6, + 6, 6, 7, 8, 15, 6, 12, 10, 13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6, 15, 5, + 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5, 6, 7, 6, 5, 5, 6, 7, 7, 7, + 7, 7, 15, 11, 14, 13, 28, 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, + 23, 23, 23, 23, 24, 23, 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, + 23, 22, 23, 23, 24, 22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, + 21, 22, 23, 23, 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, + 22, 22, 23, 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, + 24, 25, 19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, + 27, 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23, + 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26 +}; + Huffman::Huffman() : m_pRoot(nullptr) { @@ -46,7 +107,7 @@ void Huffman::Encode(const std::string& strData, CBuffer* pBuff) uint8_t ucBits = 0; uint8_t ucMask = 0xFF; - for (int i = 0; i < strData.size(); ++i) + for (size_t i = 0; i < strData.size(); ++i) { iByte = strData[i]; uiCode = CODES[iByte]; @@ -71,12 +132,12 @@ void Huffman::Encode(const std::string& strData, CBuffer* pBuff) } } -bool Huffman::Decode(CBuffer* pBuff, uint32_t iBuffLength, std::string& strData) +bool Huffman::Decode(CBuffer* pBuff, int iBuffLength, std::string& strData) { Node *pNode = m_pRoot; uint32_t uiCurrent = 0; uint8_t ucBits = 0; - char c; + char c = 0; for (int i = 0; i < iBuffLength; ++i) { pBuff->ReadByte(c); @@ -123,9 +184,9 @@ bool Huffman::Decode(CBuffer* pBuff, uint32_t iBuffLength, std::string& strData) void Huffman::BuildTree() { - uint8_t ucSym; - uint32_t uiCode; - uint8_t ucLen; + uint8_t ucSym = 0; + uint32_t uiCode = 0; + uint8_t ucLen = 0; for (int i = 0; i < 256; i++) { Node* pTerminal = new Node(ucSym, ucLen); diff --git a/src/codec/http2/Huffman.hpp b/src/codec/http2/Huffman.hpp index 106ea379..63a74f97 100644 --- a/src/codec/http2/Huffman.hpp +++ b/src/codec/http2/Huffman.hpp @@ -93,66 +93,9 @@ class Huffman Huffman(); Node* m_pRoot; - static Huffman* m_pInstance = nullptr; - - // Appendix C: Huffman Codes - // http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-B - static const uint32_t CODES[] = - { - 0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, - 0xfffffe7, 0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, - 0x3ffffffd, 0xfffffeb, 0xfffffec, 0xfffffed, 0xfffffee, 0xfffffef, - 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3, 0xffffff4, - 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, - 0xffffffb, 0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa, - 0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18, 0x0, 0x1, 0x2, - 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, - 0xffb, 0x3fc, 0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, - 0x6e, 0x6f, 0x70, 0x71, 0x72, 0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, - 0x1ffc, 0x3ffc, 0x22, 0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26, - 0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7, 0x2b, 0x76, 0x2c, 0x8, - 0x9, 0x2d, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, - 0x1ffd, 0xffffffc, 0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, - 0x3fffd4, 0x3fffd5, 0x7fffd9, 0x3fffd6, 0x7fffda, 0x7fffdb, - 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf, 0xffffec, - 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, - 0x7fffe3, 0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, - 0x7fffe6, 0x7fffe7, 0xffffef, 0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, - 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde, 0x7fffea, 0x3fffdd, - 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec, - 0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, - 0x7fffee, 0x7fffef, 0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, - 0x3fffe5, 0x3fffe6, 0x7ffff1, 0x3ffffe0, 0x3ffffe1, 0xfffeb, - 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec, 0x3ffffe2, - 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, - 0x1ffffed, 0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, - 0x3ffffe7, 0x7ffffe2, 0xfffff2, 0x1fffe4, 0x1fffe5, 0x3ffffe8, - 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5, 0xfffec, - 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3, - 0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, - 0x3ffffea, 0x7ffff4, 0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, - 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea, 0x7ffffeb, 0xffffffe, - 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee - }; - - static const uint8_t CODE_LENGTHS[] = - { - 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, 28, 28, - 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, 6, 10, 10, - 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, 5, 5, 5, 6, 6, 6, 6, 6, - 6, 6, 7, 8, 15, 6, 12, 10, 13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6, 15, 5, - 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5, 6, 7, 6, 5, 5, 6, 7, 7, 7, - 7, 7, 15, 11, 14, 13, 28, 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, - 23, 23, 23, 23, 24, 23, 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, - 23, 22, 23, 23, 24, 22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, - 21, 22, 23, 23, 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, - 22, 22, 23, 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, - 24, 25, 19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, - 27, 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23, - 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26 - }; + static Huffman* m_pInstance; + static const uint32_t CODES[]; + static const uint8_t CODE_LENGTHS[]; }; } /* namespace neb */ From c3accc1b4fcdc2cfa8482c9d49845fd1a4578660 Mon Sep 17 00:00:00 2001 From: nebim Date: Fri, 23 Oct 2020 12:32:14 +0800 Subject: [PATCH 101/176] add http2 codec and resp codec --- proto/redis.proto | 40 + src/codec/CodecResp.cpp | 124 ++++ src/codec/CodecResp.hpp | 57 ++ src/codec/CodecWsExtentJson.cpp | 2 +- src/codec/CodecWsExtentPb.cpp | 2 +- src/pb/http.pb.cc | 1228 ++++++++++++++++++++++++++++++- src/pb/http.pb.h | 634 ++++++++++++++++ src/pb/redis.pb.cc | 610 +++++++++++++++ src/pb/redis.pb.h | 345 +++++++++ src/util/StringConverter.cpp | 24 + src/util/StringConverter.hpp | 61 ++ 11 files changed, 3110 insertions(+), 17 deletions(-) create mode 100644 proto/redis.proto create mode 100644 src/codec/CodecResp.cpp create mode 100644 src/codec/CodecResp.hpp create mode 100644 src/pb/redis.pb.cc create mode 100644 src/pb/redis.pb.h create mode 100644 src/util/StringConverter.cpp create mode 100644 src/util/StringConverter.hpp diff --git a/proto/redis.proto b/proto/redis.proto new file mode 100644 index 00000000..c2d3d7e0 --- /dev/null +++ b/proto/redis.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; + +package neb; + +/* When an error occurs, the err flag in a context is set to hold the type of + * error that occured. REDIS_ERR_IO means there was an I/O error and you + * should use the "errno" variable to find out what is wrong. + * For other values, the "errstr" field will hold a description. */ +enum E_REDIS_ERR +{ + REDIS_OK = 0; + REDIS_ERR_IO = 1; /* Error in read or write */ + REDIS_ERR_OTHER = 2; /* Everything else... */ + REDIS_ERR_EOF = 3; /* End of file */ + REDIS_ERR_PROTOCOL = 4; /* Protocol error */ + REDIS_ERR_OOM = 5; /* Out of memory */ + REDIS_ERR = -1; +} + +enum E_REDIS_REPLY +{ + REDIS_REPLY_UNDEFINE = 0; + REDIS_REPLY_STRING = 1; + REDIS_REPLY_ARRAY = 2; + REDIS_REPLY_INTEGER = 3; + REDIS_REPLY_NIL = 4; + REDIS_REPLY_STATUS = 5; + REDIS_REPLY_ERROR = 6; +} + +message RedisReply +{ + int32 type = 1; // REDIS_REPLY_* + int64 integer = 2; // The integer when type is REDIS_REPLY_INTEGER + // int len; /* Length of string */ + bytes str = 3; // Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING + // size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ + repeated RedisReply element = 4; //elements vector for REDIS_REPLY_ARRAY +} + diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp new file mode 100644 index 00000000..0072afe3 --- /dev/null +++ b/src/codec/CodecResp.cpp @@ -0,0 +1,124 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecResp.cpp + * @brief + * @author nebim + * @date: 2020-10-02 + * @note + * Modify history: + ******************************************************************************/ +#include "CodecResp.hpp" +#include "util/StringConverter.hpp" +#include "actor/step/RedisStep.hpp" // TODO move to dispatcher + +namespace neb +{ + +CodecResp::CodecResp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) + : Codec(pLogger, eCodecType) +{ +} + +CodecResp::~CodecResp() +{ +} + +E_CODEC_STATUS CodecResp::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) +{ + return(CODEC_STATUS_INVALID); +} + +E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) +{ + return(CODEC_STATUS_INVALID); +} + +E_CODEC_STATUS CodecResp::Encode(std::shared_ptr pRedisStep, CBuffer* pBuff) +{ + RedisReply oReply; + oReply.set_type(REDIS_REPLY_ARRAY); + auto pElement = oReply.add_element(); + pElement->set_type(REDIS_REPLY_STRING); + pElement->set_str(pRedisStep->GetCmd()); + auto& rArgs = pRedisStep->GetCmdArguments(); + for (size_t i = 0; i < rArgs.size(); ++i) + { + pElement = oReply.add_element(); + pElement->set_type(REDIS_REPLY_STRING); + pElement->set_str(rArgs[i].first); + } + return(Encode(oReply, pBuff)); +} + +E_CODEC_STATUS CodecResp::Encode(const RedisReply& oReply, CBuffer* pBuff) +{ + int iHadWriteLen = 0; + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + switch (oReply.type()) + { + case REDIS_REPLY_STRING: + eStatus = EncodeBulkString(oReply, pBuff); + break; + case REDIS_REPLY_ARRAY: + eStatus = EncodeArray(oReply, pBuff); + break; + case REDIS_REPLY_STATUS: + eStatus = EncodeSimpleString(oReply, pBuff); + break; + case REDIS_REPLY_INTEGER: + eStatus = EncodeInteger(oReply, pBuff); + break; + case REDIS_REPLY_ERROR: + eStatus = EncodeError(oReply, pBuff); + break; + case REDIS_REPLY_NIL: + eStatus = EncodeNull(oReply, pBuff); + break; + default: + LOG4_ERROR("invalid redis reply type %d", oReply.type()); + return(CODEC_STATUS_ERR); + } + if (CODEC_STATUS_OK != eStatus) + { + pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteLen); + } + return(eStatus); +} + +E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply) +{ + char cFirstByte = 0; + size_t uiReadIndex = pBuff->GetReadIndex(); + pBuff->ReadByte(cFirstByte); + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + switch (cFirstByte) + { + case RESP_SIMPLE_STRING: + eStatus = DecodeSimpleString(pBuff, oReply); + break; + case RESP_INTEGER: + eStatus = DecodeInteger(pBuff, oReply); + break; + case RESP_BULK_STRING: + eStatus = DecodeBulkString(pBuff, oReply); + break; + case RESP_ARRAY: + eStatus = DecodeArray(pBuff, oReply); + break; + case RESP_ERROR: + eStatus = DecodeError(pBuff, oReply); + break; + default: + oReply.set_type(REDIS_REPLY_ERROR); + oReply.set_integer(REDIS_ERR_PROTOCOL); + return(CODEC_STATUS_ERR); + } + if (CODEC_STATUS_PAUSE == eStatus) + { + pBuff->SetReadIndex(uiReadIndex); + } + return(eStatus); +} + +} + diff --git a/src/codec/CodecResp.hpp b/src/codec/CodecResp.hpp new file mode 100644 index 00000000..7d8915a6 --- /dev/null +++ b/src/codec/CodecResp.hpp @@ -0,0 +1,57 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecResp.hpp + * @brief + * @author nebim + * @date: 2020-10-02 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_CODECRESP_HPP_ +#define SRC_CODEC_CODECRESP_HPP_ + +#include "Codec.hpp" +#include "pb/redis.pb.h" + +namespace neb +{ + +const char RESP_SIMPLE_STRING = '+'; +const char RESP_ERROR = '-'; +const char RESP_INTEGER = ':'; +const char RESP_BULK_STRING = '$'; +const char RESP_ARRAY = '*'; + +class RedisStep; + +class CodecResp: public neb::Codec +{ +public: + CodecResp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + virtual ~CodecResp(); + + virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); + virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); + + virtual E_CODEC_STATUS Encode(std::shared_ptr pRedisStep, CBuffer* pBuff); // TODO move to dispatcher + virtual E_CODEC_STATUS Encode(const RedisReply& oReply, CBuffer* pBuff); + virtual E_CODEC_STATUS Decode(CBuffer* pBuff, RedisReply& oReply); + +protected: + E_CODEC_STATUS EncodeSimpleString(const RedisReply& oReply, CBuffer* pBuff); + E_CODEC_STATUS EncodeError(const RedisReply& oReply, CBuffer* pBuff); + E_CODEC_STATUS EncodeInteger(const RedisReply& oReply, CBuffer* pBuff); + E_CODEC_STATUS EncodeBulkString(const RedisReply& oReply, CBuffer* pBuff); + E_CODEC_STATUS EncodeArray(const RedisReply& oReply, CBuffer* pBuff); + E_CODEC_STATUS EncodeNull(const RedisReply& oReply, CBuffer* pBuff); + E_CODEC_STATUS DecodeSimpleString(CBuffer* pBuff, RedisReply& oReply); + E_CODEC_STATUS DecodeError(CBuffer* pBuff, RedisReply& oReply); + E_CODEC_STATUS DecodeInteger(CBuffer* pBuff, RedisReply& oReply); + E_CODEC_STATUS DecodeBulkString(CBuffer* pBuff, RedisReply& oReply); + E_CODEC_STATUS DecodeArray(CBuffer* pBuff, RedisReply& oReply); +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_CODECRESP_HPP_ */ + diff --git a/src/codec/CodecWsExtentJson.cpp b/src/codec/CodecWsExtentJson.cpp index 6729908b..29db6be5 100644 --- a/src/codec/CodecWsExtentJson.cpp +++ b/src/codec/CodecWsExtentJson.cpp @@ -142,7 +142,7 @@ E_CODEC_STATUS CodecWsExtentJson::Encode(const MsgHead& oMsgHead, iWriteLen = pBuff->Write(&ucSecondByte, 1); iHadWriteLen += iWriteLen; uint64 ullPayload = iNeedWriteLen; - ullPayload = htonll(ullPayload); + ullPayload = CodecUtil::H2N(ullPayload); iWriteLen = pBuff->Write(&ullPayload, sizeof(ullPayload)); iHadWriteLen += iWriteLen; } diff --git a/src/codec/CodecWsExtentPb.cpp b/src/codec/CodecWsExtentPb.cpp index 31d129b5..6139d1b9 100644 --- a/src/codec/CodecWsExtentPb.cpp +++ b/src/codec/CodecWsExtentPb.cpp @@ -138,7 +138,7 @@ E_CODEC_STATUS CodecWsExtentPb::Encode(const MsgHead& oMsgHead, iWriteLen = pBuff->Write(&ucSecondByte, 1); iHadWriteLen += iWriteLen; uint64 ullPayload = iNeedWriteLen; - ullPayload = htonll(ullPayload); + ullPayload = CodecUtil::H2N(ullPayload); iWriteLen = pBuff->Write(&ullPayload, sizeof(ullPayload)); iHadWriteLen += iWriteLen; } diff --git a/src/pb/http.pb.cc b/src/pb/http.pb.cc index 6e7df241..ffc0360a 100644 --- a/src/pb/http.pb.cc +++ b/src/pb/http.pb.cc @@ -22,11 +22,12 @@ namespace { const ::google::protobuf::Descriptor* HttpMsg_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* HttpMsg_reflection_ = NULL; -const ::google::protobuf::Descriptor* HttpMsg_HeadersEntry_descriptor_ = NULL; -const ::google::protobuf::Descriptor* HttpMsg_ParamsEntry_descriptor_ = NULL; const ::google::protobuf::Descriptor* HttpMsg_Upgrade_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* HttpMsg_Upgrade_reflection_ = NULL; +const ::google::protobuf::Descriptor* HttpMsg_HeadersEntry_descriptor_ = NULL; +const ::google::protobuf::Descriptor* HttpMsg_ParamsEntry_descriptor_ = NULL; +const ::google::protobuf::Descriptor* HttpMsg_SettingsEntry_descriptor_ = NULL; } // namespace @@ -39,7 +40,7 @@ void protobuf_AssignDesc_http_2eproto() { "http.proto"); GOOGLE_CHECK(file != NULL); HttpMsg_descriptor_ = file->message_type(0); - static const int HttpMsg_offsets_[15] = { + static const int HttpMsg_offsets_[28] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, type_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, http_major_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, http_minor_), @@ -55,6 +56,19 @@ void protobuf_AssignDesc_http_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, keep_alive_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, path_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, is_decoding_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, chunk_notice_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, stream_id_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, hpack_data_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, adding_without_index_headers_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, deleting_without_index_headers_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, adding_never_index_headers_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, deleting_never_index_headers_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, dynamic_table_update_size_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, with_huffman_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, headers_frame_padding_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, data_frame_padding_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, push_promise_frame_padding_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, settings_), }; HttpMsg_reflection_ = ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( @@ -67,9 +81,7 @@ void protobuf_AssignDesc_http_2eproto() { sizeof(HttpMsg), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, _internal_metadata_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, _is_default_instance_)); - HttpMsg_HeadersEntry_descriptor_ = HttpMsg_descriptor_->nested_type(0); - HttpMsg_ParamsEntry_descriptor_ = HttpMsg_descriptor_->nested_type(1); - HttpMsg_Upgrade_descriptor_ = HttpMsg_descriptor_->nested_type(2); + HttpMsg_Upgrade_descriptor_ = HttpMsg_descriptor_->nested_type(0); static const int HttpMsg_Upgrade_offsets_[2] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Upgrade, is_upgrade_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Upgrade, protocol_), @@ -85,6 +97,9 @@ void protobuf_AssignDesc_http_2eproto() { sizeof(HttpMsg_Upgrade), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Upgrade, _internal_metadata_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Upgrade, _is_default_instance_)); + HttpMsg_HeadersEntry_descriptor_ = HttpMsg_descriptor_->nested_type(1); + HttpMsg_ParamsEntry_descriptor_ = HttpMsg_descriptor_->nested_type(2); + HttpMsg_SettingsEntry_descriptor_ = HttpMsg_descriptor_->nested_type(3); } namespace { @@ -100,6 +115,8 @@ void protobuf_RegisterTypes(const ::std::string&) { protobuf_AssignDescriptorsOnce(); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( HttpMsg_descriptor_, &HttpMsg::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + HttpMsg_Upgrade_descriptor_, &HttpMsg_Upgrade::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( HttpMsg_HeadersEntry_descriptor_, ::google::protobuf::internal::MapEntry< @@ -119,7 +136,14 @@ void protobuf_RegisterTypes(const ::std::string&) { 0>::CreateDefaultInstance( HttpMsg_ParamsEntry_descriptor_)); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( - HttpMsg_Upgrade_descriptor_, &HttpMsg_Upgrade::default_instance()); + HttpMsg_SettingsEntry_descriptor_, + ::google::protobuf::internal::MapEntry< + ::google::protobuf::uint32, + ::google::protobuf::uint32, + ::google::protobuf::internal::WireFormatLite::TYPE_UINT32, + ::google::protobuf::internal::WireFormatLite::TYPE_UINT32, + 0>::CreateDefaultInstance( + HttpMsg_SettingsEntry_descriptor_)); } } // namespace @@ -139,7 +163,7 @@ void protobuf_AddDesc_http_2eproto() { GOOGLE_PROTOBUF_VERIFY_VERSION; ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( - "\n\nhttp.proto\"\341\003\n\007HttpMsg\022\014\n\004type\030\001 \001(\005\022\022" + "\n\nhttp.proto\"\251\007\n\007HttpMsg\022\014\n\004type\030\001 \001(\005\022\022" "\n\nhttp_major\030\002 \001(\005\022\022\n\nhttp_minor\030\003 \001(\005\022\026" "\n\016content_length\030\004 \001(\005\022\016\n\006method\030\005 \001(\005\022\023" "\n\013status_code\030\006 \001(\005\022\020\n\010encoding\030\007 \001(\005\022\013\n" @@ -147,11 +171,22 @@ void protobuf_AddDesc_http_2eproto() { "adersEntry\022\014\n\004body\030\n \001(\014\022$\n\006params\030\013 \003(\013" "2\024.HttpMsg.ParamsEntry\022!\n\007upgrade\030\014 \001(\0132" "\020.HttpMsg.Upgrade\022\022\n\nkeep_alive\030\r \001(\002\022\014\n" - "\004path\030\016 \001(\t\022\023\n\013is_decoding\030\017 \001(\010\032.\n\014Head" - "ersEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001" - "\032-\n\013ParamsEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 " - "\001(\t:\0028\001\032/\n\007Upgrade\022\022\n\nis_upgrade\030\001 \001(\010\022\020" - "\n\010protocol\030\002 \001(\tb\006proto3", 504); + "\004path\030\016 \001(\t\022\023\n\013is_decoding\030\017 \001(\010\022\024\n\014chun" + "k_notice\030\023 \001(\010\022\021\n\tstream_id\030\024 \001(\r\022\022\n\nhpa" + "ck_data\030\025 \001(\t\022$\n\034adding_without_index_he" + "aders\030\026 \003(\t\022&\n\036deleting_without_index_he" + "aders\030\027 \003(\t\022\"\n\032adding_never_index_header" + "s\030\030 \003(\t\022$\n\034deleting_never_index_headers\030" + "\031 \003(\t\022!\n\031dynamic_table_update_size\030\032 \001(\r" + "\022\024\n\014with_huffman\030\033 \001(\010\022\035\n\025headers_frame_" + "padding\030\034 \001(\t\022\032\n\022data_frame_padding\030\035 \001(" + "\t\022\"\n\032push_promise_frame_padding\030\036 \001(\t\022(\n" + "\010settings\030\037 \003(\0132\026.HttpMsg.SettingsEntry\032" + "/\n\007Upgrade\022\022\n\nis_upgrade\030\001 \001(\010\022\020\n\010protoc" + "ol\030\002 \001(\t\032.\n\014HeadersEntry\022\013\n\003key\030\001 \001(\t\022\r\n" + "\005value\030\002 \001(\t:\0028\001\032-\n\013ParamsEntry\022\013\n\003key\030\001" + " \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\032/\n\rSettingsEntry" + "\022\013\n\003key\030\001 \001(\r\022\r\n\005value\030\002 \001(\r:\0028\001b\006proto3", 960); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "http.proto", &protobuf_RegisterTypes); HttpMsg::default_instance_ = new HttpMsg(); @@ -459,6 +494,19 @@ const int HttpMsg::kUpgradeFieldNumber; const int HttpMsg::kKeepAliveFieldNumber; const int HttpMsg::kPathFieldNumber; const int HttpMsg::kIsDecodingFieldNumber; +const int HttpMsg::kChunkNoticeFieldNumber; +const int HttpMsg::kStreamIdFieldNumber; +const int HttpMsg::kHpackDataFieldNumber; +const int HttpMsg::kAddingWithoutIndexHeadersFieldNumber; +const int HttpMsg::kDeletingWithoutIndexHeadersFieldNumber; +const int HttpMsg::kAddingNeverIndexHeadersFieldNumber; +const int HttpMsg::kDeletingNeverIndexHeadersFieldNumber; +const int HttpMsg::kDynamicTableUpdateSizeFieldNumber; +const int HttpMsg::kWithHuffmanFieldNumber; +const int HttpMsg::kHeadersFramePaddingFieldNumber; +const int HttpMsg::kDataFramePaddingFieldNumber; +const int HttpMsg::kPushPromiseFramePaddingFieldNumber; +const int HttpMsg::kSettingsFieldNumber; #endif // !defined(_MSC_VER) || _MSC_VER >= 1900 HttpMsg::HttpMsg() @@ -505,6 +553,18 @@ void HttpMsg::SharedCtor() { keep_alive_ = 0; path_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); is_decoding_ = false; + chunk_notice_ = false; + stream_id_ = 0u; + hpack_data_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + dynamic_table_update_size_ = 0u; + with_huffman_ = false; + headers_frame_padding_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + data_frame_padding_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + push_promise_frame_padding_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + settings_.SetAssignDescriptorCallback( + protobuf_AssignDescriptorsOnce); + settings_.SetEntryDescriptor( + &::HttpMsg_SettingsEntry_descriptor_); } HttpMsg::~HttpMsg() { @@ -516,6 +576,10 @@ void HttpMsg::SharedDtor() { url_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); body_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); path_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + hpack_data_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + headers_frame_padding_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + data_frame_padding_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + push_promise_frame_padding_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); if (this != default_instance_) { delete upgrade_; } @@ -567,18 +631,30 @@ void HttpMsg::Clear() { ZR_(type_, status_code_); encoding_ = 0; url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + ZR_(is_decoding_, chunk_notice_); body_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); if (GetArenaNoVirtual() == NULL && upgrade_ != NULL) delete upgrade_; upgrade_ = NULL; keep_alive_ = 0; path_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); - is_decoding_ = false; + stream_id_ = 0u; + hpack_data_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + dynamic_table_update_size_ = 0u; + with_huffman_ = false; + headers_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + data_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + push_promise_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); #undef ZR_HELPER_ #undef ZR_ headers_.Clear(); params_.Clear(); + adding_without_index_headers_.Clear(); + deleting_without_index_headers_.Clear(); + adding_never_index_headers_.Clear(); + deleting_never_index_headers_.Clear(); + settings_.Clear(); } bool HttpMsg::MergePartialFromCodedStream( @@ -587,7 +663,7 @@ bool HttpMsg::MergePartialFromCodedStream( ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:HttpMsg) for (;;) { - ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(16383); tag = p.first; if (!p.second) goto handle_unusual; switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { @@ -843,6 +919,233 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } + if (input->ExpectTag(152)) goto parse_chunk_notice; + break; + } + + // optional bool chunk_notice = 19; + case 19: { + if (tag == 152) { + parse_chunk_notice: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( + input, &chunk_notice_))); + + } else { + goto handle_unusual; + } + if (input->ExpectTag(160)) goto parse_stream_id; + break; + } + + // optional uint32 stream_id = 20; + case 20: { + if (tag == 160) { + parse_stream_id: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &stream_id_))); + + } else { + goto handle_unusual; + } + if (input->ExpectTag(170)) goto parse_hpack_data; + break; + } + + // optional string hpack_data = 21; + case 21: { + if (tag == 170) { + parse_hpack_data: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_hpack_data())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->hpack_data().data(), this->hpack_data().length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "HttpMsg.hpack_data")); + } else { + goto handle_unusual; + } + if (input->ExpectTag(178)) goto parse_adding_without_index_headers; + break; + } + + // repeated string adding_without_index_headers = 22; + case 22: { + if (tag == 178) { + parse_adding_without_index_headers: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->add_adding_without_index_headers())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->adding_without_index_headers(this->adding_without_index_headers_size() - 1).data(), + this->adding_without_index_headers(this->adding_without_index_headers_size() - 1).length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "HttpMsg.adding_without_index_headers")); + } else { + goto handle_unusual; + } + if (input->ExpectTag(178)) goto parse_adding_without_index_headers; + if (input->ExpectTag(186)) goto parse_deleting_without_index_headers; + break; + } + + // repeated string deleting_without_index_headers = 23; + case 23: { + if (tag == 186) { + parse_deleting_without_index_headers: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->add_deleting_without_index_headers())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->deleting_without_index_headers(this->deleting_without_index_headers_size() - 1).data(), + this->deleting_without_index_headers(this->deleting_without_index_headers_size() - 1).length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "HttpMsg.deleting_without_index_headers")); + } else { + goto handle_unusual; + } + if (input->ExpectTag(186)) goto parse_deleting_without_index_headers; + if (input->ExpectTag(194)) goto parse_adding_never_index_headers; + break; + } + + // repeated string adding_never_index_headers = 24; + case 24: { + if (tag == 194) { + parse_adding_never_index_headers: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->add_adding_never_index_headers())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->adding_never_index_headers(this->adding_never_index_headers_size() - 1).data(), + this->adding_never_index_headers(this->adding_never_index_headers_size() - 1).length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "HttpMsg.adding_never_index_headers")); + } else { + goto handle_unusual; + } + if (input->ExpectTag(194)) goto parse_adding_never_index_headers; + if (input->ExpectTag(202)) goto parse_deleting_never_index_headers; + break; + } + + // repeated string deleting_never_index_headers = 25; + case 25: { + if (tag == 202) { + parse_deleting_never_index_headers: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->add_deleting_never_index_headers())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->deleting_never_index_headers(this->deleting_never_index_headers_size() - 1).data(), + this->deleting_never_index_headers(this->deleting_never_index_headers_size() - 1).length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "HttpMsg.deleting_never_index_headers")); + } else { + goto handle_unusual; + } + if (input->ExpectTag(202)) goto parse_deleting_never_index_headers; + if (input->ExpectTag(208)) goto parse_dynamic_table_update_size; + break; + } + + // optional uint32 dynamic_table_update_size = 26; + case 26: { + if (tag == 208) { + parse_dynamic_table_update_size: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &dynamic_table_update_size_))); + + } else { + goto handle_unusual; + } + if (input->ExpectTag(216)) goto parse_with_huffman; + break; + } + + // optional bool with_huffman = 27; + case 27: { + if (tag == 216) { + parse_with_huffman: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( + input, &with_huffman_))); + + } else { + goto handle_unusual; + } + if (input->ExpectTag(226)) goto parse_headers_frame_padding; + break; + } + + // optional string headers_frame_padding = 28; + case 28: { + if (tag == 226) { + parse_headers_frame_padding: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_headers_frame_padding())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->headers_frame_padding().data(), this->headers_frame_padding().length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "HttpMsg.headers_frame_padding")); + } else { + goto handle_unusual; + } + if (input->ExpectTag(234)) goto parse_data_frame_padding; + break; + } + + // optional string data_frame_padding = 29; + case 29: { + if (tag == 234) { + parse_data_frame_padding: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_data_frame_padding())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->data_frame_padding().data(), this->data_frame_padding().length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "HttpMsg.data_frame_padding")); + } else { + goto handle_unusual; + } + if (input->ExpectTag(242)) goto parse_push_promise_frame_padding; + break; + } + + // optional string push_promise_frame_padding = 30; + case 30: { + if (tag == 242) { + parse_push_promise_frame_padding: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_push_promise_frame_padding())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->push_promise_frame_padding().data(), this->push_promise_frame_padding().length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "HttpMsg.push_promise_frame_padding")); + } else { + goto handle_unusual; + } + if (input->ExpectTag(250)) goto parse_settings; + break; + } + + // map settings = 31; + case 31: { + if (tag == 250) { + parse_settings: + DO_(input->IncrementRecursionDepth()); + parse_loop_settings: + HttpMsg_SettingsEntry::Parser< ::google::protobuf::internal::MapField< + ::google::protobuf::uint32, ::google::protobuf::uint32, + ::google::protobuf::internal::WireFormatLite::TYPE_UINT32, + ::google::protobuf::internal::WireFormatLite::TYPE_UINT32, + 0 >, + ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 > > parser(&settings_); + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, &parser)); + } else { + goto handle_unusual; + } + if (input->ExpectTag(250)) goto parse_loop_settings; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -1054,6 +1357,145 @@ void HttpMsg::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteBool(15, this->is_decoding(), output); } + // optional bool chunk_notice = 19; + if (this->chunk_notice() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteBool(19, this->chunk_notice(), output); + } + + // optional uint32 stream_id = 20; + if (this->stream_id() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(20, this->stream_id(), output); + } + + // optional string hpack_data = 21; + if (this->hpack_data().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->hpack_data().data(), this->hpack_data().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.hpack_data"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 21, this->hpack_data(), output); + } + + // repeated string adding_without_index_headers = 22; + for (int i = 0; i < this->adding_without_index_headers_size(); i++) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->adding_without_index_headers(i).data(), this->adding_without_index_headers(i).length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.adding_without_index_headers"); + ::google::protobuf::internal::WireFormatLite::WriteString( + 22, this->adding_without_index_headers(i), output); + } + + // repeated string deleting_without_index_headers = 23; + for (int i = 0; i < this->deleting_without_index_headers_size(); i++) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->deleting_without_index_headers(i).data(), this->deleting_without_index_headers(i).length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.deleting_without_index_headers"); + ::google::protobuf::internal::WireFormatLite::WriteString( + 23, this->deleting_without_index_headers(i), output); + } + + // repeated string adding_never_index_headers = 24; + for (int i = 0; i < this->adding_never_index_headers_size(); i++) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->adding_never_index_headers(i).data(), this->adding_never_index_headers(i).length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.adding_never_index_headers"); + ::google::protobuf::internal::WireFormatLite::WriteString( + 24, this->adding_never_index_headers(i), output); + } + + // repeated string deleting_never_index_headers = 25; + for (int i = 0; i < this->deleting_never_index_headers_size(); i++) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->deleting_never_index_headers(i).data(), this->deleting_never_index_headers(i).length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.deleting_never_index_headers"); + ::google::protobuf::internal::WireFormatLite::WriteString( + 25, this->deleting_never_index_headers(i), output); + } + + // optional uint32 dynamic_table_update_size = 26; + if (this->dynamic_table_update_size() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(26, this->dynamic_table_update_size(), output); + } + + // optional bool with_huffman = 27; + if (this->with_huffman() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteBool(27, this->with_huffman(), output); + } + + // optional string headers_frame_padding = 28; + if (this->headers_frame_padding().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->headers_frame_padding().data(), this->headers_frame_padding().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.headers_frame_padding"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 28, this->headers_frame_padding(), output); + } + + // optional string data_frame_padding = 29; + if (this->data_frame_padding().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->data_frame_padding().data(), this->data_frame_padding().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.data_frame_padding"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 29, this->data_frame_padding(), output); + } + + // optional string push_promise_frame_padding = 30; + if (this->push_promise_frame_padding().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->push_promise_frame_padding().data(), this->push_promise_frame_padding().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.push_promise_frame_padding"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 30, this->push_promise_frame_padding(), output); + } + + // map settings = 31; + if (!this->settings().empty()) { + typedef ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >::const_pointer + ConstPtr; + typedef ::google::protobuf::internal::SortItem< ::google::protobuf::uint32, ConstPtr > SortItem; + typedef ::google::protobuf::internal::CompareByFirstField Less; + + if (output->IsSerializationDeterminstic() && + this->settings().size() > 1) { + ::google::protobuf::scoped_array items( + new SortItem[this->settings().size()]); + typedef ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >::size_type size_type; + size_type n = 0; + for (::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >::const_iterator + it = this->settings().begin(); + it != this->settings().end(); ++it, ++n) { + items[n] = SortItem(&*it); + } + ::std::sort(&items[0], &items[n], Less()); + ::google::protobuf::scoped_ptr entry; + for (size_type i = 0; i < n; i++) { + entry.reset(settings_.NewEntryWrapper( + items[i].second->first, items[i].second->second)); + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 31, *entry, output); + } + } else { + ::google::protobuf::scoped_ptr entry; + for (::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >::const_iterator + it = this->settings().begin(); + it != this->settings().end(); ++it) { + entry.reset(settings_.NewEntryWrapper( + it->first, it->second)); + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 31, *entry, output); + } + } + } + // @@protoc_insertion_point(serialize_end:HttpMsg) } @@ -1255,6 +1697,153 @@ ::google::protobuf::uint8* HttpMsg::InternalSerializeWithCachedSizesToArray( target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(15, this->is_decoding(), target); } + // optional bool chunk_notice = 19; + if (this->chunk_notice() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(19, this->chunk_notice(), target); + } + + // optional uint32 stream_id = 20; + if (this->stream_id() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(20, this->stream_id(), target); + } + + // optional string hpack_data = 21; + if (this->hpack_data().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->hpack_data().data(), this->hpack_data().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.hpack_data"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 21, this->hpack_data(), target); + } + + // repeated string adding_without_index_headers = 22; + for (int i = 0; i < this->adding_without_index_headers_size(); i++) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->adding_without_index_headers(i).data(), this->adding_without_index_headers(i).length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.adding_without_index_headers"); + target = ::google::protobuf::internal::WireFormatLite:: + WriteStringToArray(22, this->adding_without_index_headers(i), target); + } + + // repeated string deleting_without_index_headers = 23; + for (int i = 0; i < this->deleting_without_index_headers_size(); i++) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->deleting_without_index_headers(i).data(), this->deleting_without_index_headers(i).length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.deleting_without_index_headers"); + target = ::google::protobuf::internal::WireFormatLite:: + WriteStringToArray(23, this->deleting_without_index_headers(i), target); + } + + // repeated string adding_never_index_headers = 24; + for (int i = 0; i < this->adding_never_index_headers_size(); i++) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->adding_never_index_headers(i).data(), this->adding_never_index_headers(i).length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.adding_never_index_headers"); + target = ::google::protobuf::internal::WireFormatLite:: + WriteStringToArray(24, this->adding_never_index_headers(i), target); + } + + // repeated string deleting_never_index_headers = 25; + for (int i = 0; i < this->deleting_never_index_headers_size(); i++) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->deleting_never_index_headers(i).data(), this->deleting_never_index_headers(i).length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.deleting_never_index_headers"); + target = ::google::protobuf::internal::WireFormatLite:: + WriteStringToArray(25, this->deleting_never_index_headers(i), target); + } + + // optional uint32 dynamic_table_update_size = 26; + if (this->dynamic_table_update_size() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(26, this->dynamic_table_update_size(), target); + } + + // optional bool with_huffman = 27; + if (this->with_huffman() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(27, this->with_huffman(), target); + } + + // optional string headers_frame_padding = 28; + if (this->headers_frame_padding().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->headers_frame_padding().data(), this->headers_frame_padding().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.headers_frame_padding"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 28, this->headers_frame_padding(), target); + } + + // optional string data_frame_padding = 29; + if (this->data_frame_padding().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->data_frame_padding().data(), this->data_frame_padding().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.data_frame_padding"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 29, this->data_frame_padding(), target); + } + + // optional string push_promise_frame_padding = 30; + if (this->push_promise_frame_padding().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->push_promise_frame_padding().data(), this->push_promise_frame_padding().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.push_promise_frame_padding"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 30, this->push_promise_frame_padding(), target); + } + + // map settings = 31; + if (!this->settings().empty()) { + typedef ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >::const_pointer + ConstPtr; + typedef ::google::protobuf::internal::SortItem< ::google::protobuf::uint32, ConstPtr > SortItem; + typedef ::google::protobuf::internal::CompareByFirstField Less; + + if (deterministic && + this->settings().size() > 1) { + ::google::protobuf::scoped_array items( + new SortItem[this->settings().size()]); + typedef ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >::size_type size_type; + size_type n = 0; + for (::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >::const_iterator + it = this->settings().begin(); + it != this->settings().end(); ++it, ++n) { + items[n] = SortItem(&*it); + } + ::std::sort(&items[0], &items[n], Less()); + ::google::protobuf::scoped_ptr entry; + for (size_type i = 0; i < n; i++) { + entry.reset(settings_.NewEntryWrapper( + items[i].second->first, items[i].second->second)); + target = ::google::protobuf::internal::WireFormatLite:: + InternalWriteMessageNoVirtualToArray( + 31, *entry, deterministic, target); +; + } + } else { + ::google::protobuf::scoped_ptr entry; + for (::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >::const_iterator + it = this->settings().begin(); + it != this->settings().end(); ++it) { + entry.reset(settings_.NewEntryWrapper( + it->first, it->second)); + target = ::google::protobuf::internal::WireFormatLite:: + InternalWriteMessageNoVirtualToArray( + 31, *entry, deterministic, target); +; + } + } + } + // @@protoc_insertion_point(serialize_to_array_end:HttpMsg) return target; } @@ -1350,6 +1939,58 @@ int HttpMsg::ByteSize() const { total_size += 1 + 1; } + // optional bool chunk_notice = 19; + if (this->chunk_notice() != 0) { + total_size += 2 + 1; + } + + // optional uint32 stream_id = 20; + if (this->stream_id() != 0) { + total_size += 2 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->stream_id()); + } + + // optional string hpack_data = 21; + if (this->hpack_data().size() > 0) { + total_size += 2 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->hpack_data()); + } + + // optional uint32 dynamic_table_update_size = 26; + if (this->dynamic_table_update_size() != 0) { + total_size += 2 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->dynamic_table_update_size()); + } + + // optional bool with_huffman = 27; + if (this->with_huffman() != 0) { + total_size += 2 + 1; + } + + // optional string headers_frame_padding = 28; + if (this->headers_frame_padding().size() > 0) { + total_size += 2 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->headers_frame_padding()); + } + + // optional string data_frame_padding = 29; + if (this->data_frame_padding().size() > 0) { + total_size += 2 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->data_frame_padding()); + } + + // optional string push_promise_frame_padding = 30; + if (this->push_promise_frame_padding().size() > 0) { + total_size += 2 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->push_promise_frame_padding()); + } + // map headers = 9; total_size += 1 * this->headers_size(); { @@ -1376,6 +2017,47 @@ int HttpMsg::ByteSize() const { } } + // repeated string adding_without_index_headers = 22; + total_size += 2 * this->adding_without_index_headers_size(); + for (int i = 0; i < this->adding_without_index_headers_size(); i++) { + total_size += ::google::protobuf::internal::WireFormatLite::StringSize( + this->adding_without_index_headers(i)); + } + + // repeated string deleting_without_index_headers = 23; + total_size += 2 * this->deleting_without_index_headers_size(); + for (int i = 0; i < this->deleting_without_index_headers_size(); i++) { + total_size += ::google::protobuf::internal::WireFormatLite::StringSize( + this->deleting_without_index_headers(i)); + } + + // repeated string adding_never_index_headers = 24; + total_size += 2 * this->adding_never_index_headers_size(); + for (int i = 0; i < this->adding_never_index_headers_size(); i++) { + total_size += ::google::protobuf::internal::WireFormatLite::StringSize( + this->adding_never_index_headers(i)); + } + + // repeated string deleting_never_index_headers = 25; + total_size += 2 * this->deleting_never_index_headers_size(); + for (int i = 0; i < this->deleting_never_index_headers_size(); i++) { + total_size += ::google::protobuf::internal::WireFormatLite::StringSize( + this->deleting_never_index_headers(i)); + } + + // map settings = 31; + total_size += 2 * this->settings_size(); + { + ::google::protobuf::scoped_ptr entry; + for (::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >::const_iterator + it = this->settings().begin(); + it != this->settings().end(); ++it) { + entry.reset(settings_.NewEntryWrapper(it->first, it->second)); + total_size += ::google::protobuf::internal::WireFormatLite:: + MessageSizeNoVirtual(*entry); + } + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = total_size; GOOGLE_SAFE_CONCURRENT_WRITES_END(); @@ -1406,6 +2088,11 @@ void HttpMsg::MergeFrom(const HttpMsg& from) { } headers_.MergeFrom(from.headers_); params_.MergeFrom(from.params_); + adding_without_index_headers_.MergeFrom(from.adding_without_index_headers_); + deleting_without_index_headers_.MergeFrom(from.deleting_without_index_headers_); + adding_never_index_headers_.MergeFrom(from.adding_never_index_headers_); + deleting_never_index_headers_.MergeFrom(from.deleting_never_index_headers_); + settings_.MergeFrom(from.settings_); if (from.type() != 0) { set_type(from.type()); } @@ -1448,6 +2135,34 @@ void HttpMsg::MergeFrom(const HttpMsg& from) { if (from.is_decoding() != 0) { set_is_decoding(from.is_decoding()); } + if (from.chunk_notice() != 0) { + set_chunk_notice(from.chunk_notice()); + } + if (from.stream_id() != 0) { + set_stream_id(from.stream_id()); + } + if (from.hpack_data().size() > 0) { + + hpack_data_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.hpack_data_); + } + if (from.dynamic_table_update_size() != 0) { + set_dynamic_table_update_size(from.dynamic_table_update_size()); + } + if (from.with_huffman() != 0) { + set_with_huffman(from.with_huffman()); + } + if (from.headers_frame_padding().size() > 0) { + + headers_frame_padding_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.headers_frame_padding_); + } + if (from.data_frame_padding().size() > 0) { + + data_frame_padding_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.data_frame_padding_); + } + if (from.push_promise_frame_padding().size() > 0) { + + push_promise_frame_padding_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.push_promise_frame_padding_); + } } void HttpMsg::CopyFrom(const ::google::protobuf::Message& from) { @@ -1489,6 +2204,19 @@ void HttpMsg::InternalSwap(HttpMsg* other) { std::swap(keep_alive_, other->keep_alive_); path_.Swap(&other->path_); std::swap(is_decoding_, other->is_decoding_); + std::swap(chunk_notice_, other->chunk_notice_); + std::swap(stream_id_, other->stream_id_); + hpack_data_.Swap(&other->hpack_data_); + adding_without_index_headers_.UnsafeArenaSwap(&other->adding_without_index_headers_); + deleting_without_index_headers_.UnsafeArenaSwap(&other->deleting_without_index_headers_); + adding_never_index_headers_.UnsafeArenaSwap(&other->adding_never_index_headers_); + deleting_never_index_headers_.UnsafeArenaSwap(&other->deleting_never_index_headers_); + std::swap(dynamic_table_update_size_, other->dynamic_table_update_size_); + std::swap(with_huffman_, other->with_huffman_); + headers_frame_padding_.Swap(&other->headers_frame_padding_); + data_frame_padding_.Swap(&other->data_frame_padding_); + push_promise_frame_padding_.Swap(&other->push_promise_frame_padding_); + settings_.Swap(&other->settings_); _internal_metadata_.Swap(&other->_internal_metadata_); std::swap(_cached_size_, other->_cached_size_); } @@ -1898,6 +2626,476 @@ void HttpMsg::clear_is_decoding() { // @@protoc_insertion_point(field_set:HttpMsg.is_decoding) } +// optional bool chunk_notice = 19; +void HttpMsg::clear_chunk_notice() { + chunk_notice_ = false; +} + bool HttpMsg::chunk_notice() const { + // @@protoc_insertion_point(field_get:HttpMsg.chunk_notice) + return chunk_notice_; +} + void HttpMsg::set_chunk_notice(bool value) { + + chunk_notice_ = value; + // @@protoc_insertion_point(field_set:HttpMsg.chunk_notice) +} + +// optional uint32 stream_id = 20; +void HttpMsg::clear_stream_id() { + stream_id_ = 0u; +} + ::google::protobuf::uint32 HttpMsg::stream_id() const { + // @@protoc_insertion_point(field_get:HttpMsg.stream_id) + return stream_id_; +} + void HttpMsg::set_stream_id(::google::protobuf::uint32 value) { + + stream_id_ = value; + // @@protoc_insertion_point(field_set:HttpMsg.stream_id) +} + +// optional string hpack_data = 21; +void HttpMsg::clear_hpack_data() { + hpack_data_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& HttpMsg::hpack_data() const { + // @@protoc_insertion_point(field_get:HttpMsg.hpack_data) + return hpack_data_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg::set_hpack_data(const ::std::string& value) { + + hpack_data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.hpack_data) +} + void HttpMsg::set_hpack_data(const char* value) { + + hpack_data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.hpack_data) +} + void HttpMsg::set_hpack_data(const char* value, size_t size) { + + hpack_data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.hpack_data) +} + ::std::string* HttpMsg::mutable_hpack_data() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.hpack_data) + return hpack_data_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* HttpMsg::release_hpack_data() { + // @@protoc_insertion_point(field_release:HttpMsg.hpack_data) + + return hpack_data_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg::set_allocated_hpack_data(::std::string* hpack_data) { + if (hpack_data != NULL) { + + } else { + + } + hpack_data_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), hpack_data); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.hpack_data) +} + +// repeated string adding_without_index_headers = 22; +int HttpMsg::adding_without_index_headers_size() const { + return adding_without_index_headers_.size(); +} +void HttpMsg::clear_adding_without_index_headers() { + adding_without_index_headers_.Clear(); +} + const ::std::string& HttpMsg::adding_without_index_headers(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.adding_without_index_headers) + return adding_without_index_headers_.Get(index); +} + ::std::string* HttpMsg::mutable_adding_without_index_headers(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.adding_without_index_headers) + return adding_without_index_headers_.Mutable(index); +} + void HttpMsg::set_adding_without_index_headers(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:HttpMsg.adding_without_index_headers) + adding_without_index_headers_.Mutable(index)->assign(value); +} + void HttpMsg::set_adding_without_index_headers(int index, const char* value) { + adding_without_index_headers_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:HttpMsg.adding_without_index_headers) +} + void HttpMsg::set_adding_without_index_headers(int index, const char* value, size_t size) { + adding_without_index_headers_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.adding_without_index_headers) +} + ::std::string* HttpMsg::add_adding_without_index_headers() { + // @@protoc_insertion_point(field_add_mutable:HttpMsg.adding_without_index_headers) + return adding_without_index_headers_.Add(); +} + void HttpMsg::add_adding_without_index_headers(const ::std::string& value) { + adding_without_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add:HttpMsg.adding_without_index_headers) +} + void HttpMsg::add_adding_without_index_headers(const char* value) { + adding_without_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:HttpMsg.adding_without_index_headers) +} + void HttpMsg::add_adding_without_index_headers(const char* value, size_t size) { + adding_without_index_headers_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:HttpMsg.adding_without_index_headers) +} + const ::google::protobuf::RepeatedPtrField< ::std::string>& +HttpMsg::adding_without_index_headers() const { + // @@protoc_insertion_point(field_list:HttpMsg.adding_without_index_headers) + return adding_without_index_headers_; +} + ::google::protobuf::RepeatedPtrField< ::std::string>* +HttpMsg::mutable_adding_without_index_headers() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.adding_without_index_headers) + return &adding_without_index_headers_; +} + +// repeated string deleting_without_index_headers = 23; +int HttpMsg::deleting_without_index_headers_size() const { + return deleting_without_index_headers_.size(); +} +void HttpMsg::clear_deleting_without_index_headers() { + deleting_without_index_headers_.Clear(); +} + const ::std::string& HttpMsg::deleting_without_index_headers(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.deleting_without_index_headers) + return deleting_without_index_headers_.Get(index); +} + ::std::string* HttpMsg::mutable_deleting_without_index_headers(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.deleting_without_index_headers) + return deleting_without_index_headers_.Mutable(index); +} + void HttpMsg::set_deleting_without_index_headers(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:HttpMsg.deleting_without_index_headers) + deleting_without_index_headers_.Mutable(index)->assign(value); +} + void HttpMsg::set_deleting_without_index_headers(int index, const char* value) { + deleting_without_index_headers_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:HttpMsg.deleting_without_index_headers) +} + void HttpMsg::set_deleting_without_index_headers(int index, const char* value, size_t size) { + deleting_without_index_headers_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.deleting_without_index_headers) +} + ::std::string* HttpMsg::add_deleting_without_index_headers() { + // @@protoc_insertion_point(field_add_mutable:HttpMsg.deleting_without_index_headers) + return deleting_without_index_headers_.Add(); +} + void HttpMsg::add_deleting_without_index_headers(const ::std::string& value) { + deleting_without_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add:HttpMsg.deleting_without_index_headers) +} + void HttpMsg::add_deleting_without_index_headers(const char* value) { + deleting_without_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:HttpMsg.deleting_without_index_headers) +} + void HttpMsg::add_deleting_without_index_headers(const char* value, size_t size) { + deleting_without_index_headers_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:HttpMsg.deleting_without_index_headers) +} + const ::google::protobuf::RepeatedPtrField< ::std::string>& +HttpMsg::deleting_without_index_headers() const { + // @@protoc_insertion_point(field_list:HttpMsg.deleting_without_index_headers) + return deleting_without_index_headers_; +} + ::google::protobuf::RepeatedPtrField< ::std::string>* +HttpMsg::mutable_deleting_without_index_headers() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.deleting_without_index_headers) + return &deleting_without_index_headers_; +} + +// repeated string adding_never_index_headers = 24; +int HttpMsg::adding_never_index_headers_size() const { + return adding_never_index_headers_.size(); +} +void HttpMsg::clear_adding_never_index_headers() { + adding_never_index_headers_.Clear(); +} + const ::std::string& HttpMsg::adding_never_index_headers(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.adding_never_index_headers) + return adding_never_index_headers_.Get(index); +} + ::std::string* HttpMsg::mutable_adding_never_index_headers(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.adding_never_index_headers) + return adding_never_index_headers_.Mutable(index); +} + void HttpMsg::set_adding_never_index_headers(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:HttpMsg.adding_never_index_headers) + adding_never_index_headers_.Mutable(index)->assign(value); +} + void HttpMsg::set_adding_never_index_headers(int index, const char* value) { + adding_never_index_headers_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:HttpMsg.adding_never_index_headers) +} + void HttpMsg::set_adding_never_index_headers(int index, const char* value, size_t size) { + adding_never_index_headers_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.adding_never_index_headers) +} + ::std::string* HttpMsg::add_adding_never_index_headers() { + // @@protoc_insertion_point(field_add_mutable:HttpMsg.adding_never_index_headers) + return adding_never_index_headers_.Add(); +} + void HttpMsg::add_adding_never_index_headers(const ::std::string& value) { + adding_never_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add:HttpMsg.adding_never_index_headers) +} + void HttpMsg::add_adding_never_index_headers(const char* value) { + adding_never_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:HttpMsg.adding_never_index_headers) +} + void HttpMsg::add_adding_never_index_headers(const char* value, size_t size) { + adding_never_index_headers_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:HttpMsg.adding_never_index_headers) +} + const ::google::protobuf::RepeatedPtrField< ::std::string>& +HttpMsg::adding_never_index_headers() const { + // @@protoc_insertion_point(field_list:HttpMsg.adding_never_index_headers) + return adding_never_index_headers_; +} + ::google::protobuf::RepeatedPtrField< ::std::string>* +HttpMsg::mutable_adding_never_index_headers() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.adding_never_index_headers) + return &adding_never_index_headers_; +} + +// repeated string deleting_never_index_headers = 25; +int HttpMsg::deleting_never_index_headers_size() const { + return deleting_never_index_headers_.size(); +} +void HttpMsg::clear_deleting_never_index_headers() { + deleting_never_index_headers_.Clear(); +} + const ::std::string& HttpMsg::deleting_never_index_headers(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.deleting_never_index_headers) + return deleting_never_index_headers_.Get(index); +} + ::std::string* HttpMsg::mutable_deleting_never_index_headers(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.deleting_never_index_headers) + return deleting_never_index_headers_.Mutable(index); +} + void HttpMsg::set_deleting_never_index_headers(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:HttpMsg.deleting_never_index_headers) + deleting_never_index_headers_.Mutable(index)->assign(value); +} + void HttpMsg::set_deleting_never_index_headers(int index, const char* value) { + deleting_never_index_headers_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:HttpMsg.deleting_never_index_headers) +} + void HttpMsg::set_deleting_never_index_headers(int index, const char* value, size_t size) { + deleting_never_index_headers_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.deleting_never_index_headers) +} + ::std::string* HttpMsg::add_deleting_never_index_headers() { + // @@protoc_insertion_point(field_add_mutable:HttpMsg.deleting_never_index_headers) + return deleting_never_index_headers_.Add(); +} + void HttpMsg::add_deleting_never_index_headers(const ::std::string& value) { + deleting_never_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add:HttpMsg.deleting_never_index_headers) +} + void HttpMsg::add_deleting_never_index_headers(const char* value) { + deleting_never_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:HttpMsg.deleting_never_index_headers) +} + void HttpMsg::add_deleting_never_index_headers(const char* value, size_t size) { + deleting_never_index_headers_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:HttpMsg.deleting_never_index_headers) +} + const ::google::protobuf::RepeatedPtrField< ::std::string>& +HttpMsg::deleting_never_index_headers() const { + // @@protoc_insertion_point(field_list:HttpMsg.deleting_never_index_headers) + return deleting_never_index_headers_; +} + ::google::protobuf::RepeatedPtrField< ::std::string>* +HttpMsg::mutable_deleting_never_index_headers() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.deleting_never_index_headers) + return &deleting_never_index_headers_; +} + +// optional uint32 dynamic_table_update_size = 26; +void HttpMsg::clear_dynamic_table_update_size() { + dynamic_table_update_size_ = 0u; +} + ::google::protobuf::uint32 HttpMsg::dynamic_table_update_size() const { + // @@protoc_insertion_point(field_get:HttpMsg.dynamic_table_update_size) + return dynamic_table_update_size_; +} + void HttpMsg::set_dynamic_table_update_size(::google::protobuf::uint32 value) { + + dynamic_table_update_size_ = value; + // @@protoc_insertion_point(field_set:HttpMsg.dynamic_table_update_size) +} + +// optional bool with_huffman = 27; +void HttpMsg::clear_with_huffman() { + with_huffman_ = false; +} + bool HttpMsg::with_huffman() const { + // @@protoc_insertion_point(field_get:HttpMsg.with_huffman) + return with_huffman_; +} + void HttpMsg::set_with_huffman(bool value) { + + with_huffman_ = value; + // @@protoc_insertion_point(field_set:HttpMsg.with_huffman) +} + +// optional string headers_frame_padding = 28; +void HttpMsg::clear_headers_frame_padding() { + headers_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& HttpMsg::headers_frame_padding() const { + // @@protoc_insertion_point(field_get:HttpMsg.headers_frame_padding) + return headers_frame_padding_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg::set_headers_frame_padding(const ::std::string& value) { + + headers_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.headers_frame_padding) +} + void HttpMsg::set_headers_frame_padding(const char* value) { + + headers_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.headers_frame_padding) +} + void HttpMsg::set_headers_frame_padding(const char* value, size_t size) { + + headers_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.headers_frame_padding) +} + ::std::string* HttpMsg::mutable_headers_frame_padding() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.headers_frame_padding) + return headers_frame_padding_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* HttpMsg::release_headers_frame_padding() { + // @@protoc_insertion_point(field_release:HttpMsg.headers_frame_padding) + + return headers_frame_padding_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg::set_allocated_headers_frame_padding(::std::string* headers_frame_padding) { + if (headers_frame_padding != NULL) { + + } else { + + } + headers_frame_padding_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), headers_frame_padding); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.headers_frame_padding) +} + +// optional string data_frame_padding = 29; +void HttpMsg::clear_data_frame_padding() { + data_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& HttpMsg::data_frame_padding() const { + // @@protoc_insertion_point(field_get:HttpMsg.data_frame_padding) + return data_frame_padding_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg::set_data_frame_padding(const ::std::string& value) { + + data_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.data_frame_padding) +} + void HttpMsg::set_data_frame_padding(const char* value) { + + data_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.data_frame_padding) +} + void HttpMsg::set_data_frame_padding(const char* value, size_t size) { + + data_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.data_frame_padding) +} + ::std::string* HttpMsg::mutable_data_frame_padding() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.data_frame_padding) + return data_frame_padding_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* HttpMsg::release_data_frame_padding() { + // @@protoc_insertion_point(field_release:HttpMsg.data_frame_padding) + + return data_frame_padding_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg::set_allocated_data_frame_padding(::std::string* data_frame_padding) { + if (data_frame_padding != NULL) { + + } else { + + } + data_frame_padding_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), data_frame_padding); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.data_frame_padding) +} + +// optional string push_promise_frame_padding = 30; +void HttpMsg::clear_push_promise_frame_padding() { + push_promise_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& HttpMsg::push_promise_frame_padding() const { + // @@protoc_insertion_point(field_get:HttpMsg.push_promise_frame_padding) + return push_promise_frame_padding_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg::set_push_promise_frame_padding(const ::std::string& value) { + + push_promise_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.push_promise_frame_padding) +} + void HttpMsg::set_push_promise_frame_padding(const char* value) { + + push_promise_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.push_promise_frame_padding) +} + void HttpMsg::set_push_promise_frame_padding(const char* value, size_t size) { + + push_promise_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.push_promise_frame_padding) +} + ::std::string* HttpMsg::mutable_push_promise_frame_padding() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.push_promise_frame_padding) + return push_promise_frame_padding_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* HttpMsg::release_push_promise_frame_padding() { + // @@protoc_insertion_point(field_release:HttpMsg.push_promise_frame_padding) + + return push_promise_frame_padding_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg::set_allocated_push_promise_frame_padding(::std::string* push_promise_frame_padding) { + if (push_promise_frame_padding != NULL) { + + } else { + + } + push_promise_frame_padding_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), push_promise_frame_padding); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.push_promise_frame_padding) +} + +// map settings = 31; +int HttpMsg::settings_size() const { + return settings_.size(); +} +void HttpMsg::clear_settings() { + settings_.Clear(); +} + const ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >& +HttpMsg::settings() const { + // @@protoc_insertion_point(field_map:HttpMsg.settings) + return settings_.GetMap(); +} + ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >* +HttpMsg::mutable_settings() { + // @@protoc_insertion_point(field_mutable_map:HttpMsg.settings) + return settings_.MutableMap(); +} + #endif // PROTOBUF_INLINE_NOT_IN_HEADERS // @@protoc_insertion_point(namespace_scope) diff --git a/src/pb/http.pb.h b/src/pb/http.pb.h index 92b6f28e..02c64375 100644 --- a/src/pb/http.pb.h +++ b/src/pb/http.pb.h @@ -311,6 +311,147 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c bool is_decoding() const; void set_is_decoding(bool value); + // optional bool chunk_notice = 19; + void clear_chunk_notice(); + static const int kChunkNoticeFieldNumber = 19; + bool chunk_notice() const; + void set_chunk_notice(bool value); + + // optional uint32 stream_id = 20; + void clear_stream_id(); + static const int kStreamIdFieldNumber = 20; + ::google::protobuf::uint32 stream_id() const; + void set_stream_id(::google::protobuf::uint32 value); + + // optional string hpack_data = 21; + void clear_hpack_data(); + static const int kHpackDataFieldNumber = 21; + const ::std::string& hpack_data() const; + void set_hpack_data(const ::std::string& value); + void set_hpack_data(const char* value); + void set_hpack_data(const char* value, size_t size); + ::std::string* mutable_hpack_data(); + ::std::string* release_hpack_data(); + void set_allocated_hpack_data(::std::string* hpack_data); + + // repeated string adding_without_index_headers = 22; + int adding_without_index_headers_size() const; + void clear_adding_without_index_headers(); + static const int kAddingWithoutIndexHeadersFieldNumber = 22; + const ::std::string& adding_without_index_headers(int index) const; + ::std::string* mutable_adding_without_index_headers(int index); + void set_adding_without_index_headers(int index, const ::std::string& value); + void set_adding_without_index_headers(int index, const char* value); + void set_adding_without_index_headers(int index, const char* value, size_t size); + ::std::string* add_adding_without_index_headers(); + void add_adding_without_index_headers(const ::std::string& value); + void add_adding_without_index_headers(const char* value); + void add_adding_without_index_headers(const char* value, size_t size); + const ::google::protobuf::RepeatedPtrField< ::std::string>& adding_without_index_headers() const; + ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_adding_without_index_headers(); + + // repeated string deleting_without_index_headers = 23; + int deleting_without_index_headers_size() const; + void clear_deleting_without_index_headers(); + static const int kDeletingWithoutIndexHeadersFieldNumber = 23; + const ::std::string& deleting_without_index_headers(int index) const; + ::std::string* mutable_deleting_without_index_headers(int index); + void set_deleting_without_index_headers(int index, const ::std::string& value); + void set_deleting_without_index_headers(int index, const char* value); + void set_deleting_without_index_headers(int index, const char* value, size_t size); + ::std::string* add_deleting_without_index_headers(); + void add_deleting_without_index_headers(const ::std::string& value); + void add_deleting_without_index_headers(const char* value); + void add_deleting_without_index_headers(const char* value, size_t size); + const ::google::protobuf::RepeatedPtrField< ::std::string>& deleting_without_index_headers() const; + ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_deleting_without_index_headers(); + + // repeated string adding_never_index_headers = 24; + int adding_never_index_headers_size() const; + void clear_adding_never_index_headers(); + static const int kAddingNeverIndexHeadersFieldNumber = 24; + const ::std::string& adding_never_index_headers(int index) const; + ::std::string* mutable_adding_never_index_headers(int index); + void set_adding_never_index_headers(int index, const ::std::string& value); + void set_adding_never_index_headers(int index, const char* value); + void set_adding_never_index_headers(int index, const char* value, size_t size); + ::std::string* add_adding_never_index_headers(); + void add_adding_never_index_headers(const ::std::string& value); + void add_adding_never_index_headers(const char* value); + void add_adding_never_index_headers(const char* value, size_t size); + const ::google::protobuf::RepeatedPtrField< ::std::string>& adding_never_index_headers() const; + ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_adding_never_index_headers(); + + // repeated string deleting_never_index_headers = 25; + int deleting_never_index_headers_size() const; + void clear_deleting_never_index_headers(); + static const int kDeletingNeverIndexHeadersFieldNumber = 25; + const ::std::string& deleting_never_index_headers(int index) const; + ::std::string* mutable_deleting_never_index_headers(int index); + void set_deleting_never_index_headers(int index, const ::std::string& value); + void set_deleting_never_index_headers(int index, const char* value); + void set_deleting_never_index_headers(int index, const char* value, size_t size); + ::std::string* add_deleting_never_index_headers(); + void add_deleting_never_index_headers(const ::std::string& value); + void add_deleting_never_index_headers(const char* value); + void add_deleting_never_index_headers(const char* value, size_t size); + const ::google::protobuf::RepeatedPtrField< ::std::string>& deleting_never_index_headers() const; + ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_deleting_never_index_headers(); + + // optional uint32 dynamic_table_update_size = 26; + void clear_dynamic_table_update_size(); + static const int kDynamicTableUpdateSizeFieldNumber = 26; + ::google::protobuf::uint32 dynamic_table_update_size() const; + void set_dynamic_table_update_size(::google::protobuf::uint32 value); + + // optional bool with_huffman = 27; + void clear_with_huffman(); + static const int kWithHuffmanFieldNumber = 27; + bool with_huffman() const; + void set_with_huffman(bool value); + + // optional string headers_frame_padding = 28; + void clear_headers_frame_padding(); + static const int kHeadersFramePaddingFieldNumber = 28; + const ::std::string& headers_frame_padding() const; + void set_headers_frame_padding(const ::std::string& value); + void set_headers_frame_padding(const char* value); + void set_headers_frame_padding(const char* value, size_t size); + ::std::string* mutable_headers_frame_padding(); + ::std::string* release_headers_frame_padding(); + void set_allocated_headers_frame_padding(::std::string* headers_frame_padding); + + // optional string data_frame_padding = 29; + void clear_data_frame_padding(); + static const int kDataFramePaddingFieldNumber = 29; + const ::std::string& data_frame_padding() const; + void set_data_frame_padding(const ::std::string& value); + void set_data_frame_padding(const char* value); + void set_data_frame_padding(const char* value, size_t size); + ::std::string* mutable_data_frame_padding(); + ::std::string* release_data_frame_padding(); + void set_allocated_data_frame_padding(::std::string* data_frame_padding); + + // optional string push_promise_frame_padding = 30; + void clear_push_promise_frame_padding(); + static const int kPushPromiseFramePaddingFieldNumber = 30; + const ::std::string& push_promise_frame_padding() const; + void set_push_promise_frame_padding(const ::std::string& value); + void set_push_promise_frame_padding(const char* value); + void set_push_promise_frame_padding(const char* value, size_t size); + ::std::string* mutable_push_promise_frame_padding(); + ::std::string* release_push_promise_frame_padding(); + void set_allocated_push_promise_frame_padding(::std::string* push_promise_frame_padding); + + // map settings = 31; + int settings_size() const; + void clear_settings(); + static const int kSettingsFieldNumber = 31; + const ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >& + settings() const; + ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >* + mutable_settings(); + // @@protoc_insertion_point(class_scope:HttpMsg) private: @@ -350,7 +491,30 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c 0 > params_; ::HttpMsg_Upgrade* upgrade_; ::google::protobuf::internal::ArenaStringPtr path_; + ::google::protobuf::internal::ArenaStringPtr hpack_data_; + ::google::protobuf::uint32 stream_id_; bool is_decoding_; + bool chunk_notice_; + bool with_huffman_; + ::google::protobuf::RepeatedPtrField< ::std::string> adding_without_index_headers_; + ::google::protobuf::RepeatedPtrField< ::std::string> deleting_without_index_headers_; + ::google::protobuf::RepeatedPtrField< ::std::string> adding_never_index_headers_; + ::google::protobuf::RepeatedPtrField< ::std::string> deleting_never_index_headers_; + ::google::protobuf::internal::ArenaStringPtr headers_frame_padding_; + ::google::protobuf::internal::ArenaStringPtr data_frame_padding_; + ::google::protobuf::internal::ArenaStringPtr push_promise_frame_padding_; + typedef ::google::protobuf::internal::MapEntryLite< + ::google::protobuf::uint32, ::google::protobuf::uint32, + ::google::protobuf::internal::WireFormatLite::TYPE_UINT32, + ::google::protobuf::internal::WireFormatLite::TYPE_UINT32, + 0 > + HttpMsg_SettingsEntry; + ::google::protobuf::internal::MapField< + ::google::protobuf::uint32, ::google::protobuf::uint32, + ::google::protobuf::internal::WireFormatLite::TYPE_UINT32, + ::google::protobuf::internal::WireFormatLite::TYPE_UINT32, + 0 > settings_; + ::google::protobuf::uint32 dynamic_table_update_size_; mutable int _cached_size_; friend void protobuf_AddDesc_http_2eproto(); friend void protobuf_AssignDesc_http_2eproto(); @@ -761,6 +925,476 @@ inline void HttpMsg::set_is_decoding(bool value) { // @@protoc_insertion_point(field_set:HttpMsg.is_decoding) } +// optional bool chunk_notice = 19; +inline void HttpMsg::clear_chunk_notice() { + chunk_notice_ = false; +} +inline bool HttpMsg::chunk_notice() const { + // @@protoc_insertion_point(field_get:HttpMsg.chunk_notice) + return chunk_notice_; +} +inline void HttpMsg::set_chunk_notice(bool value) { + + chunk_notice_ = value; + // @@protoc_insertion_point(field_set:HttpMsg.chunk_notice) +} + +// optional uint32 stream_id = 20; +inline void HttpMsg::clear_stream_id() { + stream_id_ = 0u; +} +inline ::google::protobuf::uint32 HttpMsg::stream_id() const { + // @@protoc_insertion_point(field_get:HttpMsg.stream_id) + return stream_id_; +} +inline void HttpMsg::set_stream_id(::google::protobuf::uint32 value) { + + stream_id_ = value; + // @@protoc_insertion_point(field_set:HttpMsg.stream_id) +} + +// optional string hpack_data = 21; +inline void HttpMsg::clear_hpack_data() { + hpack_data_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& HttpMsg::hpack_data() const { + // @@protoc_insertion_point(field_get:HttpMsg.hpack_data) + return hpack_data_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg::set_hpack_data(const ::std::string& value) { + + hpack_data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.hpack_data) +} +inline void HttpMsg::set_hpack_data(const char* value) { + + hpack_data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.hpack_data) +} +inline void HttpMsg::set_hpack_data(const char* value, size_t size) { + + hpack_data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.hpack_data) +} +inline ::std::string* HttpMsg::mutable_hpack_data() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.hpack_data) + return hpack_data_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* HttpMsg::release_hpack_data() { + // @@protoc_insertion_point(field_release:HttpMsg.hpack_data) + + return hpack_data_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg::set_allocated_hpack_data(::std::string* hpack_data) { + if (hpack_data != NULL) { + + } else { + + } + hpack_data_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), hpack_data); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.hpack_data) +} + +// repeated string adding_without_index_headers = 22; +inline int HttpMsg::adding_without_index_headers_size() const { + return adding_without_index_headers_.size(); +} +inline void HttpMsg::clear_adding_without_index_headers() { + adding_without_index_headers_.Clear(); +} +inline const ::std::string& HttpMsg::adding_without_index_headers(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.adding_without_index_headers) + return adding_without_index_headers_.Get(index); +} +inline ::std::string* HttpMsg::mutable_adding_without_index_headers(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.adding_without_index_headers) + return adding_without_index_headers_.Mutable(index); +} +inline void HttpMsg::set_adding_without_index_headers(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:HttpMsg.adding_without_index_headers) + adding_without_index_headers_.Mutable(index)->assign(value); +} +inline void HttpMsg::set_adding_without_index_headers(int index, const char* value) { + adding_without_index_headers_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:HttpMsg.adding_without_index_headers) +} +inline void HttpMsg::set_adding_without_index_headers(int index, const char* value, size_t size) { + adding_without_index_headers_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.adding_without_index_headers) +} +inline ::std::string* HttpMsg::add_adding_without_index_headers() { + // @@protoc_insertion_point(field_add_mutable:HttpMsg.adding_without_index_headers) + return adding_without_index_headers_.Add(); +} +inline void HttpMsg::add_adding_without_index_headers(const ::std::string& value) { + adding_without_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add:HttpMsg.adding_without_index_headers) +} +inline void HttpMsg::add_adding_without_index_headers(const char* value) { + adding_without_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:HttpMsg.adding_without_index_headers) +} +inline void HttpMsg::add_adding_without_index_headers(const char* value, size_t size) { + adding_without_index_headers_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:HttpMsg.adding_without_index_headers) +} +inline const ::google::protobuf::RepeatedPtrField< ::std::string>& +HttpMsg::adding_without_index_headers() const { + // @@protoc_insertion_point(field_list:HttpMsg.adding_without_index_headers) + return adding_without_index_headers_; +} +inline ::google::protobuf::RepeatedPtrField< ::std::string>* +HttpMsg::mutable_adding_without_index_headers() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.adding_without_index_headers) + return &adding_without_index_headers_; +} + +// repeated string deleting_without_index_headers = 23; +inline int HttpMsg::deleting_without_index_headers_size() const { + return deleting_without_index_headers_.size(); +} +inline void HttpMsg::clear_deleting_without_index_headers() { + deleting_without_index_headers_.Clear(); +} +inline const ::std::string& HttpMsg::deleting_without_index_headers(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.deleting_without_index_headers) + return deleting_without_index_headers_.Get(index); +} +inline ::std::string* HttpMsg::mutable_deleting_without_index_headers(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.deleting_without_index_headers) + return deleting_without_index_headers_.Mutable(index); +} +inline void HttpMsg::set_deleting_without_index_headers(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:HttpMsg.deleting_without_index_headers) + deleting_without_index_headers_.Mutable(index)->assign(value); +} +inline void HttpMsg::set_deleting_without_index_headers(int index, const char* value) { + deleting_without_index_headers_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:HttpMsg.deleting_without_index_headers) +} +inline void HttpMsg::set_deleting_without_index_headers(int index, const char* value, size_t size) { + deleting_without_index_headers_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.deleting_without_index_headers) +} +inline ::std::string* HttpMsg::add_deleting_without_index_headers() { + // @@protoc_insertion_point(field_add_mutable:HttpMsg.deleting_without_index_headers) + return deleting_without_index_headers_.Add(); +} +inline void HttpMsg::add_deleting_without_index_headers(const ::std::string& value) { + deleting_without_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add:HttpMsg.deleting_without_index_headers) +} +inline void HttpMsg::add_deleting_without_index_headers(const char* value) { + deleting_without_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:HttpMsg.deleting_without_index_headers) +} +inline void HttpMsg::add_deleting_without_index_headers(const char* value, size_t size) { + deleting_without_index_headers_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:HttpMsg.deleting_without_index_headers) +} +inline const ::google::protobuf::RepeatedPtrField< ::std::string>& +HttpMsg::deleting_without_index_headers() const { + // @@protoc_insertion_point(field_list:HttpMsg.deleting_without_index_headers) + return deleting_without_index_headers_; +} +inline ::google::protobuf::RepeatedPtrField< ::std::string>* +HttpMsg::mutable_deleting_without_index_headers() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.deleting_without_index_headers) + return &deleting_without_index_headers_; +} + +// repeated string adding_never_index_headers = 24; +inline int HttpMsg::adding_never_index_headers_size() const { + return adding_never_index_headers_.size(); +} +inline void HttpMsg::clear_adding_never_index_headers() { + adding_never_index_headers_.Clear(); +} +inline const ::std::string& HttpMsg::adding_never_index_headers(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.adding_never_index_headers) + return adding_never_index_headers_.Get(index); +} +inline ::std::string* HttpMsg::mutable_adding_never_index_headers(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.adding_never_index_headers) + return adding_never_index_headers_.Mutable(index); +} +inline void HttpMsg::set_adding_never_index_headers(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:HttpMsg.adding_never_index_headers) + adding_never_index_headers_.Mutable(index)->assign(value); +} +inline void HttpMsg::set_adding_never_index_headers(int index, const char* value) { + adding_never_index_headers_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:HttpMsg.adding_never_index_headers) +} +inline void HttpMsg::set_adding_never_index_headers(int index, const char* value, size_t size) { + adding_never_index_headers_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.adding_never_index_headers) +} +inline ::std::string* HttpMsg::add_adding_never_index_headers() { + // @@protoc_insertion_point(field_add_mutable:HttpMsg.adding_never_index_headers) + return adding_never_index_headers_.Add(); +} +inline void HttpMsg::add_adding_never_index_headers(const ::std::string& value) { + adding_never_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add:HttpMsg.adding_never_index_headers) +} +inline void HttpMsg::add_adding_never_index_headers(const char* value) { + adding_never_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:HttpMsg.adding_never_index_headers) +} +inline void HttpMsg::add_adding_never_index_headers(const char* value, size_t size) { + adding_never_index_headers_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:HttpMsg.adding_never_index_headers) +} +inline const ::google::protobuf::RepeatedPtrField< ::std::string>& +HttpMsg::adding_never_index_headers() const { + // @@protoc_insertion_point(field_list:HttpMsg.adding_never_index_headers) + return adding_never_index_headers_; +} +inline ::google::protobuf::RepeatedPtrField< ::std::string>* +HttpMsg::mutable_adding_never_index_headers() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.adding_never_index_headers) + return &adding_never_index_headers_; +} + +// repeated string deleting_never_index_headers = 25; +inline int HttpMsg::deleting_never_index_headers_size() const { + return deleting_never_index_headers_.size(); +} +inline void HttpMsg::clear_deleting_never_index_headers() { + deleting_never_index_headers_.Clear(); +} +inline const ::std::string& HttpMsg::deleting_never_index_headers(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.deleting_never_index_headers) + return deleting_never_index_headers_.Get(index); +} +inline ::std::string* HttpMsg::mutable_deleting_never_index_headers(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.deleting_never_index_headers) + return deleting_never_index_headers_.Mutable(index); +} +inline void HttpMsg::set_deleting_never_index_headers(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:HttpMsg.deleting_never_index_headers) + deleting_never_index_headers_.Mutable(index)->assign(value); +} +inline void HttpMsg::set_deleting_never_index_headers(int index, const char* value) { + deleting_never_index_headers_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:HttpMsg.deleting_never_index_headers) +} +inline void HttpMsg::set_deleting_never_index_headers(int index, const char* value, size_t size) { + deleting_never_index_headers_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.deleting_never_index_headers) +} +inline ::std::string* HttpMsg::add_deleting_never_index_headers() { + // @@protoc_insertion_point(field_add_mutable:HttpMsg.deleting_never_index_headers) + return deleting_never_index_headers_.Add(); +} +inline void HttpMsg::add_deleting_never_index_headers(const ::std::string& value) { + deleting_never_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add:HttpMsg.deleting_never_index_headers) +} +inline void HttpMsg::add_deleting_never_index_headers(const char* value) { + deleting_never_index_headers_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:HttpMsg.deleting_never_index_headers) +} +inline void HttpMsg::add_deleting_never_index_headers(const char* value, size_t size) { + deleting_never_index_headers_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:HttpMsg.deleting_never_index_headers) +} +inline const ::google::protobuf::RepeatedPtrField< ::std::string>& +HttpMsg::deleting_never_index_headers() const { + // @@protoc_insertion_point(field_list:HttpMsg.deleting_never_index_headers) + return deleting_never_index_headers_; +} +inline ::google::protobuf::RepeatedPtrField< ::std::string>* +HttpMsg::mutable_deleting_never_index_headers() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.deleting_never_index_headers) + return &deleting_never_index_headers_; +} + +// optional uint32 dynamic_table_update_size = 26; +inline void HttpMsg::clear_dynamic_table_update_size() { + dynamic_table_update_size_ = 0u; +} +inline ::google::protobuf::uint32 HttpMsg::dynamic_table_update_size() const { + // @@protoc_insertion_point(field_get:HttpMsg.dynamic_table_update_size) + return dynamic_table_update_size_; +} +inline void HttpMsg::set_dynamic_table_update_size(::google::protobuf::uint32 value) { + + dynamic_table_update_size_ = value; + // @@protoc_insertion_point(field_set:HttpMsg.dynamic_table_update_size) +} + +// optional bool with_huffman = 27; +inline void HttpMsg::clear_with_huffman() { + with_huffman_ = false; +} +inline bool HttpMsg::with_huffman() const { + // @@protoc_insertion_point(field_get:HttpMsg.with_huffman) + return with_huffman_; +} +inline void HttpMsg::set_with_huffman(bool value) { + + with_huffman_ = value; + // @@protoc_insertion_point(field_set:HttpMsg.with_huffman) +} + +// optional string headers_frame_padding = 28; +inline void HttpMsg::clear_headers_frame_padding() { + headers_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& HttpMsg::headers_frame_padding() const { + // @@protoc_insertion_point(field_get:HttpMsg.headers_frame_padding) + return headers_frame_padding_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg::set_headers_frame_padding(const ::std::string& value) { + + headers_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.headers_frame_padding) +} +inline void HttpMsg::set_headers_frame_padding(const char* value) { + + headers_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.headers_frame_padding) +} +inline void HttpMsg::set_headers_frame_padding(const char* value, size_t size) { + + headers_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.headers_frame_padding) +} +inline ::std::string* HttpMsg::mutable_headers_frame_padding() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.headers_frame_padding) + return headers_frame_padding_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* HttpMsg::release_headers_frame_padding() { + // @@protoc_insertion_point(field_release:HttpMsg.headers_frame_padding) + + return headers_frame_padding_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg::set_allocated_headers_frame_padding(::std::string* headers_frame_padding) { + if (headers_frame_padding != NULL) { + + } else { + + } + headers_frame_padding_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), headers_frame_padding); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.headers_frame_padding) +} + +// optional string data_frame_padding = 29; +inline void HttpMsg::clear_data_frame_padding() { + data_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& HttpMsg::data_frame_padding() const { + // @@protoc_insertion_point(field_get:HttpMsg.data_frame_padding) + return data_frame_padding_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg::set_data_frame_padding(const ::std::string& value) { + + data_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.data_frame_padding) +} +inline void HttpMsg::set_data_frame_padding(const char* value) { + + data_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.data_frame_padding) +} +inline void HttpMsg::set_data_frame_padding(const char* value, size_t size) { + + data_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.data_frame_padding) +} +inline ::std::string* HttpMsg::mutable_data_frame_padding() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.data_frame_padding) + return data_frame_padding_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* HttpMsg::release_data_frame_padding() { + // @@protoc_insertion_point(field_release:HttpMsg.data_frame_padding) + + return data_frame_padding_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg::set_allocated_data_frame_padding(::std::string* data_frame_padding) { + if (data_frame_padding != NULL) { + + } else { + + } + data_frame_padding_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), data_frame_padding); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.data_frame_padding) +} + +// optional string push_promise_frame_padding = 30; +inline void HttpMsg::clear_push_promise_frame_padding() { + push_promise_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& HttpMsg::push_promise_frame_padding() const { + // @@protoc_insertion_point(field_get:HttpMsg.push_promise_frame_padding) + return push_promise_frame_padding_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg::set_push_promise_frame_padding(const ::std::string& value) { + + push_promise_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.push_promise_frame_padding) +} +inline void HttpMsg::set_push_promise_frame_padding(const char* value) { + + push_promise_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.push_promise_frame_padding) +} +inline void HttpMsg::set_push_promise_frame_padding(const char* value, size_t size) { + + push_promise_frame_padding_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.push_promise_frame_padding) +} +inline ::std::string* HttpMsg::mutable_push_promise_frame_padding() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.push_promise_frame_padding) + return push_promise_frame_padding_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* HttpMsg::release_push_promise_frame_padding() { + // @@protoc_insertion_point(field_release:HttpMsg.push_promise_frame_padding) + + return push_promise_frame_padding_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg::set_allocated_push_promise_frame_padding(::std::string* push_promise_frame_padding) { + if (push_promise_frame_padding != NULL) { + + } else { + + } + push_promise_frame_padding_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), push_promise_frame_padding); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.push_promise_frame_padding) +} + +// map settings = 31; +inline int HttpMsg::settings_size() const { + return settings_.size(); +} +inline void HttpMsg::clear_settings() { + settings_.Clear(); +} +inline const ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >& +HttpMsg::settings() const { + // @@protoc_insertion_point(field_map:HttpMsg.settings) + return settings_.GetMap(); +} +inline ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >* +HttpMsg::mutable_settings() { + // @@protoc_insertion_point(field_mutable_map:HttpMsg.settings) + return settings_.MutableMap(); +} + #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS // ------------------------------------------------------------------- diff --git a/src/pb/redis.pb.cc b/src/pb/redis.pb.cc new file mode 100644 index 00000000..b652f2cf --- /dev/null +++ b/src/pb/redis.pb.cc @@ -0,0 +1,610 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: redis.proto + +#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION +#include "redis.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace neb { + +namespace { + +const ::google::protobuf::Descriptor* RedisReply_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + RedisReply_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* E_REDIS_ERR_descriptor_ = NULL; +const ::google::protobuf::EnumDescriptor* E_REDIS_REPLY_descriptor_ = NULL; + +} // namespace + + +void protobuf_AssignDesc_redis_2eproto() GOOGLE_ATTRIBUTE_COLD; +void protobuf_AssignDesc_redis_2eproto() { + protobuf_AddDesc_redis_2eproto(); + const ::google::protobuf::FileDescriptor* file = + ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( + "redis.proto"); + GOOGLE_CHECK(file != NULL); + RedisReply_descriptor_ = file->message_type(0); + static const int RedisReply_offsets_[4] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RedisReply, type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RedisReply, integer_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RedisReply, str_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RedisReply, element_), + }; + RedisReply_reflection_ = + ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( + RedisReply_descriptor_, + RedisReply::default_instance_, + RedisReply_offsets_, + -1, + -1, + -1, + sizeof(RedisReply), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RedisReply, _internal_metadata_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RedisReply, _is_default_instance_)); + E_REDIS_ERR_descriptor_ = file->enum_type(0); + E_REDIS_REPLY_descriptor_ = file->enum_type(1); +} + +namespace { + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); +inline void protobuf_AssignDescriptorsOnce() { + ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, + &protobuf_AssignDesc_redis_2eproto); +} + +void protobuf_RegisterTypes(const ::std::string&) GOOGLE_ATTRIBUTE_COLD; +void protobuf_RegisterTypes(const ::std::string&) { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + RedisReply_descriptor_, &RedisReply::default_instance()); +} + +} // namespace + +void protobuf_ShutdownFile_redis_2eproto() { + delete RedisReply::default_instance_; + delete RedisReply_reflection_; +} + +void protobuf_AddDesc_redis_2eproto() GOOGLE_ATTRIBUTE_COLD; +void protobuf_AddDesc_redis_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + + ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( + "\n\013redis.proto\022\003neb\"Z\n\nRedisReply\022\014\n\004type" + "\030\001 \001(\005\022\017\n\007integer\030\002 \001(\003\022\013\n\003str\030\003 \001(\014\022 \n\007" + "element\030\004 \003(\0132\017.neb.RedisReply*\230\001\n\013E_RED" + "IS_ERR\022\014\n\010REDIS_OK\020\000\022\020\n\014REDIS_ERR_IO\020\001\022\023" + "\n\017REDIS_ERR_OTHER\020\002\022\021\n\rREDIS_ERR_EOF\020\003\022\026" + "\n\022REDIS_ERR_PROTOCOL\020\004\022\021\n\rREDIS_ERR_OOM\020" + "\005\022\026\n\tREDIS_ERR\020\377\377\377\377\377\377\377\377\377\001*\265\001\n\rE_REDIS_RE" + "PLY\022\030\n\024REDIS_REPLY_UNDEFINE\020\000\022\026\n\022REDIS_R" + "EPLY_STRING\020\001\022\025\n\021REDIS_REPLY_ARRAY\020\002\022\027\n\023" + "REDIS_REPLY_INTEGER\020\003\022\023\n\017REDIS_REPLY_NIL" + "\020\004\022\026\n\022REDIS_REPLY_STATUS\020\005\022\025\n\021REDIS_REPL" + "Y_ERROR\020\006b\006proto3", 457); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( + "redis.proto", &protobuf_RegisterTypes); + RedisReply::default_instance_ = new RedisReply(); + RedisReply::default_instance_->InitAsDefaultInstance(); + ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_redis_2eproto); +} + +// Force AddDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_redis_2eproto { + StaticDescriptorInitializer_redis_2eproto() { + protobuf_AddDesc_redis_2eproto(); + } +} static_descriptor_initializer_redis_2eproto_; +const ::google::protobuf::EnumDescriptor* E_REDIS_ERR_descriptor() { + protobuf_AssignDescriptorsOnce(); + return E_REDIS_ERR_descriptor_; +} +bool E_REDIS_ERR_IsValid(int value) { + switch(value) { + case -1: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + return true; + default: + return false; + } +} + +const ::google::protobuf::EnumDescriptor* E_REDIS_REPLY_descriptor() { + protobuf_AssignDescriptorsOnce(); + return E_REDIS_REPLY_descriptor_; +} +bool E_REDIS_REPLY_IsValid(int value) { + switch(value) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + return true; + default: + return false; + } +} + + +// =================================================================== + +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +const int RedisReply::kTypeFieldNumber; +const int RedisReply::kIntegerFieldNumber; +const int RedisReply::kStrFieldNumber; +const int RedisReply::kElementFieldNumber; +#endif // !defined(_MSC_VER) || _MSC_VER >= 1900 + +RedisReply::RedisReply() + : ::google::protobuf::Message(), _internal_metadata_(NULL) { + SharedCtor(); + // @@protoc_insertion_point(constructor:neb.RedisReply) +} + +void RedisReply::InitAsDefaultInstance() { + _is_default_instance_ = true; +} + +RedisReply::RedisReply(const RedisReply& from) + : ::google::protobuf::Message(), + _internal_metadata_(NULL) { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:neb.RedisReply) +} + +void RedisReply::SharedCtor() { + _is_default_instance_ = false; + ::google::protobuf::internal::GetEmptyString(); + _cached_size_ = 0; + type_ = 0; + integer_ = GOOGLE_LONGLONG(0); + str_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + +RedisReply::~RedisReply() { + // @@protoc_insertion_point(destructor:neb.RedisReply) + SharedDtor(); +} + +void RedisReply::SharedDtor() { + str_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + if (this != default_instance_) { + } +} + +void RedisReply::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* RedisReply::descriptor() { + protobuf_AssignDescriptorsOnce(); + return RedisReply_descriptor_; +} + +const RedisReply& RedisReply::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_redis_2eproto(); + return *default_instance_; +} + +RedisReply* RedisReply::default_instance_ = NULL; + +RedisReply* RedisReply::New(::google::protobuf::Arena* arena) const { + RedisReply* n = new RedisReply; + if (arena != NULL) { + arena->Own(n); + } + return n; +} + +void RedisReply::Clear() { +// @@protoc_insertion_point(message_clear_start:neb.RedisReply) + type_ = 0; + integer_ = GOOGLE_LONGLONG(0); + str_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + element_.Clear(); +} + +bool RedisReply::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:neb.RedisReply) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional int32 type = 1; + case 1: { + if (tag == 8) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &type_))); + + } else { + goto handle_unusual; + } + if (input->ExpectTag(16)) goto parse_integer; + break; + } + + // optional int64 integer = 2; + case 2: { + if (tag == 16) { + parse_integer: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>( + input, &integer_))); + + } else { + goto handle_unusual; + } + if (input->ExpectTag(26)) goto parse_str; + break; + } + + // optional bytes str = 3; + case 3: { + if (tag == 26) { + parse_str: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_str())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(34)) goto parse_element; + break; + } + + // repeated .neb.RedisReply element = 4; + case 4: { + if (tag == 34) { + parse_element: + DO_(input->IncrementRecursionDepth()); + parse_loop_element: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( + input, add_element())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(34)) goto parse_loop_element; + input->UnsafeDecrementRecursionDepth(); + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag)); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:neb.RedisReply) + return true; +failure: + // @@protoc_insertion_point(parse_failure:neb.RedisReply) + return false; +#undef DO_ +} + +void RedisReply::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:neb.RedisReply) + // optional int32 type = 1; + if (this->type() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->type(), output); + } + + // optional int64 integer = 2; + if (this->integer() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteInt64(2, this->integer(), output); + } + + // optional bytes str = 3; + if (this->str().size() > 0) { + ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased( + 3, this->str(), output); + } + + // repeated .neb.RedisReply element = 4; + for (unsigned int i = 0, n = this->element_size(); i < n; i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 4, this->element(i), output); + } + + // @@protoc_insertion_point(serialize_end:neb.RedisReply) +} + +::google::protobuf::uint8* RedisReply::InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:neb.RedisReply) + // optional int32 type = 1; + if (this->type() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(1, this->type(), target); + } + + // optional int64 integer = 2; + if (this->integer() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt64ToArray(2, this->integer(), target); + } + + // optional bytes str = 3; + if (this->str().size() > 0) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 3, this->str(), target); + } + + // repeated .neb.RedisReply element = 4; + for (unsigned int i = 0, n = this->element_size(); i < n; i++) { + target = ::google::protobuf::internal::WireFormatLite:: + InternalWriteMessageNoVirtualToArray( + 4, this->element(i), false, target); + } + + // @@protoc_insertion_point(serialize_to_array_end:neb.RedisReply) + return target; +} + +int RedisReply::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:neb.RedisReply) + int total_size = 0; + + // optional int32 type = 1; + if (this->type() != 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->type()); + } + + // optional int64 integer = 2; + if (this->integer() != 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int64Size( + this->integer()); + } + + // optional bytes str = 3; + if (this->str().size() > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->str()); + } + + // repeated .neb.RedisReply element = 4; + total_size += 1 * this->element_size(); + for (int i = 0; i < this->element_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->element(i)); + } + + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void RedisReply::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:neb.RedisReply) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + const RedisReply* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); + if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:neb.RedisReply) + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:neb.RedisReply) + MergeFrom(*source); + } +} + +void RedisReply::MergeFrom(const RedisReply& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:neb.RedisReply) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + element_.MergeFrom(from.element_); + if (from.type() != 0) { + set_type(from.type()); + } + if (from.integer() != 0) { + set_integer(from.integer()); + } + if (from.str().size() > 0) { + + str_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.str_); + } +} + +void RedisReply::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:neb.RedisReply) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void RedisReply::CopyFrom(const RedisReply& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:neb.RedisReply) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool RedisReply::IsInitialized() const { + + return true; +} + +void RedisReply::Swap(RedisReply* other) { + if (other == this) return; + InternalSwap(other); +} +void RedisReply::InternalSwap(RedisReply* other) { + std::swap(type_, other->type_); + std::swap(integer_, other->integer_); + str_.Swap(&other->str_); + element_.UnsafeArenaSwap(&other->element_); + _internal_metadata_.Swap(&other->_internal_metadata_); + std::swap(_cached_size_, other->_cached_size_); +} + +::google::protobuf::Metadata RedisReply::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = RedisReply_descriptor_; + metadata.reflection = RedisReply_reflection_; + return metadata; +} + +#if PROTOBUF_INLINE_NOT_IN_HEADERS +// RedisReply + +// optional int32 type = 1; +void RedisReply::clear_type() { + type_ = 0; +} + ::google::protobuf::int32 RedisReply::type() const { + // @@protoc_insertion_point(field_get:neb.RedisReply.type) + return type_; +} + void RedisReply::set_type(::google::protobuf::int32 value) { + + type_ = value; + // @@protoc_insertion_point(field_set:neb.RedisReply.type) +} + +// optional int64 integer = 2; +void RedisReply::clear_integer() { + integer_ = GOOGLE_LONGLONG(0); +} + ::google::protobuf::int64 RedisReply::integer() const { + // @@protoc_insertion_point(field_get:neb.RedisReply.integer) + return integer_; +} + void RedisReply::set_integer(::google::protobuf::int64 value) { + + integer_ = value; + // @@protoc_insertion_point(field_set:neb.RedisReply.integer) +} + +// optional bytes str = 3; +void RedisReply::clear_str() { + str_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& RedisReply::str() const { + // @@protoc_insertion_point(field_get:neb.RedisReply.str) + return str_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void RedisReply::set_str(const ::std::string& value) { + + str_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:neb.RedisReply.str) +} + void RedisReply::set_str(const char* value) { + + str_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:neb.RedisReply.str) +} + void RedisReply::set_str(const void* value, size_t size) { + + str_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:neb.RedisReply.str) +} + ::std::string* RedisReply::mutable_str() { + + // @@protoc_insertion_point(field_mutable:neb.RedisReply.str) + return str_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* RedisReply::release_str() { + // @@protoc_insertion_point(field_release:neb.RedisReply.str) + + return str_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void RedisReply::set_allocated_str(::std::string* str) { + if (str != NULL) { + + } else { + + } + str_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), str); + // @@protoc_insertion_point(field_set_allocated:neb.RedisReply.str) +} + +// repeated .neb.RedisReply element = 4; +int RedisReply::element_size() const { + return element_.size(); +} +void RedisReply::clear_element() { + element_.Clear(); +} +const ::neb::RedisReply& RedisReply::element(int index) const { + // @@protoc_insertion_point(field_get:neb.RedisReply.element) + return element_.Get(index); +} +::neb::RedisReply* RedisReply::mutable_element(int index) { + // @@protoc_insertion_point(field_mutable:neb.RedisReply.element) + return element_.Mutable(index); +} +::neb::RedisReply* RedisReply::add_element() { + // @@protoc_insertion_point(field_add:neb.RedisReply.element) + return element_.Add(); +} +::google::protobuf::RepeatedPtrField< ::neb::RedisReply >* +RedisReply::mutable_element() { + // @@protoc_insertion_point(field_mutable_list:neb.RedisReply.element) + return &element_; +} +const ::google::protobuf::RepeatedPtrField< ::neb::RedisReply >& +RedisReply::element() const { + // @@protoc_insertion_point(field_list:neb.RedisReply.element) + return element_; +} + +#endif // PROTOBUF_INLINE_NOT_IN_HEADERS + +// @@protoc_insertion_point(namespace_scope) + +} // namespace neb + +// @@protoc_insertion_point(global_scope) diff --git a/src/pb/redis.pb.h b/src/pb/redis.pb.h new file mode 100644 index 00000000..fc8d7878 --- /dev/null +++ b/src/pb/redis.pb.h @@ -0,0 +1,345 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: redis.proto + +#ifndef PROTOBUF_redis_2eproto__INCLUDED +#define PROTOBUF_redis_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 3000000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3000000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace neb { + +// Internal implementation detail -- do not call these. +void protobuf_AddDesc_redis_2eproto(); +void protobuf_AssignDesc_redis_2eproto(); +void protobuf_ShutdownFile_redis_2eproto(); + +class RedisReply; + +enum E_REDIS_ERR { + REDIS_OK = 0, + REDIS_ERR_IO = 1, + REDIS_ERR_OTHER = 2, + REDIS_ERR_EOF = 3, + REDIS_ERR_PROTOCOL = 4, + REDIS_ERR_OOM = 5, + REDIS_ERR = -1, + E_REDIS_ERR_INT_MIN_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32min, + E_REDIS_ERR_INT_MAX_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32max +}; +bool E_REDIS_ERR_IsValid(int value); +const E_REDIS_ERR E_REDIS_ERR_MIN = REDIS_ERR; +const E_REDIS_ERR E_REDIS_ERR_MAX = REDIS_ERR_OOM; +const int E_REDIS_ERR_ARRAYSIZE = E_REDIS_ERR_MAX + 1; + +const ::google::protobuf::EnumDescriptor* E_REDIS_ERR_descriptor(); +inline const ::std::string& E_REDIS_ERR_Name(E_REDIS_ERR value) { + return ::google::protobuf::internal::NameOfEnum( + E_REDIS_ERR_descriptor(), value); +} +inline bool E_REDIS_ERR_Parse( + const ::std::string& name, E_REDIS_ERR* value) { + return ::google::protobuf::internal::ParseNamedEnum( + E_REDIS_ERR_descriptor(), name, value); +} +enum E_REDIS_REPLY { + REDIS_REPLY_UNDEFINE = 0, + REDIS_REPLY_STRING = 1, + REDIS_REPLY_ARRAY = 2, + REDIS_REPLY_INTEGER = 3, + REDIS_REPLY_NIL = 4, + REDIS_REPLY_STATUS = 5, + REDIS_REPLY_ERROR = 6, + E_REDIS_REPLY_INT_MIN_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32min, + E_REDIS_REPLY_INT_MAX_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32max +}; +bool E_REDIS_REPLY_IsValid(int value); +const E_REDIS_REPLY E_REDIS_REPLY_MIN = REDIS_REPLY_UNDEFINE; +const E_REDIS_REPLY E_REDIS_REPLY_MAX = REDIS_REPLY_ERROR; +const int E_REDIS_REPLY_ARRAYSIZE = E_REDIS_REPLY_MAX + 1; + +const ::google::protobuf::EnumDescriptor* E_REDIS_REPLY_descriptor(); +inline const ::std::string& E_REDIS_REPLY_Name(E_REDIS_REPLY value) { + return ::google::protobuf::internal::NameOfEnum( + E_REDIS_REPLY_descriptor(), value); +} +inline bool E_REDIS_REPLY_Parse( + const ::std::string& name, E_REDIS_REPLY* value) { + return ::google::protobuf::internal::ParseNamedEnum( + E_REDIS_REPLY_descriptor(), name, value); +} +// =================================================================== + +class RedisReply : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:neb.RedisReply) */ { + public: + RedisReply(); + virtual ~RedisReply(); + + RedisReply(const RedisReply& from); + + inline RedisReply& operator=(const RedisReply& from) { + CopyFrom(from); + return *this; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const RedisReply& default_instance(); + + void Swap(RedisReply* other); + + // implements Message ---------------------------------------------- + + inline RedisReply* New() const { return New(NULL); } + + RedisReply* New(::google::protobuf::Arena* arena) const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const RedisReply& from); + void MergeFrom(const RedisReply& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const { + return InternalSerializeWithCachedSizesToArray(false, output); + } + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(RedisReply* other); + private: + inline ::google::protobuf::Arena* GetArenaNoVirtual() const { + return _internal_metadata_.arena(); + } + inline void* MaybeArenaPtr() const { + return _internal_metadata_.raw_arena_ptr(); + } + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional int32 type = 1; + void clear_type(); + static const int kTypeFieldNumber = 1; + ::google::protobuf::int32 type() const; + void set_type(::google::protobuf::int32 value); + + // optional int64 integer = 2; + void clear_integer(); + static const int kIntegerFieldNumber = 2; + ::google::protobuf::int64 integer() const; + void set_integer(::google::protobuf::int64 value); + + // optional bytes str = 3; + void clear_str(); + static const int kStrFieldNumber = 3; + const ::std::string& str() const; + void set_str(const ::std::string& value); + void set_str(const char* value); + void set_str(const void* value, size_t size); + ::std::string* mutable_str(); + ::std::string* release_str(); + void set_allocated_str(::std::string* str); + + // repeated .neb.RedisReply element = 4; + int element_size() const; + void clear_element(); + static const int kElementFieldNumber = 4; + const ::neb::RedisReply& element(int index) const; + ::neb::RedisReply* mutable_element(int index); + ::neb::RedisReply* add_element(); + ::google::protobuf::RepeatedPtrField< ::neb::RedisReply >* + mutable_element(); + const ::google::protobuf::RepeatedPtrField< ::neb::RedisReply >& + element() const; + + // @@protoc_insertion_point(class_scope:neb.RedisReply) + private: + + ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + bool _is_default_instance_; + ::google::protobuf::int64 integer_; + ::google::protobuf::internal::ArenaStringPtr str_; + ::google::protobuf::RepeatedPtrField< ::neb::RedisReply > element_; + ::google::protobuf::int32 type_; + mutable int _cached_size_; + friend void protobuf_AddDesc_redis_2eproto(); + friend void protobuf_AssignDesc_redis_2eproto(); + friend void protobuf_ShutdownFile_redis_2eproto(); + + void InitAsDefaultInstance(); + static RedisReply* default_instance_; +}; +// =================================================================== + + +// =================================================================== + +#if !PROTOBUF_INLINE_NOT_IN_HEADERS +// RedisReply + +// optional int32 type = 1; +inline void RedisReply::clear_type() { + type_ = 0; +} +inline ::google::protobuf::int32 RedisReply::type() const { + // @@protoc_insertion_point(field_get:neb.RedisReply.type) + return type_; +} +inline void RedisReply::set_type(::google::protobuf::int32 value) { + + type_ = value; + // @@protoc_insertion_point(field_set:neb.RedisReply.type) +} + +// optional int64 integer = 2; +inline void RedisReply::clear_integer() { + integer_ = GOOGLE_LONGLONG(0); +} +inline ::google::protobuf::int64 RedisReply::integer() const { + // @@protoc_insertion_point(field_get:neb.RedisReply.integer) + return integer_; +} +inline void RedisReply::set_integer(::google::protobuf::int64 value) { + + integer_ = value; + // @@protoc_insertion_point(field_set:neb.RedisReply.integer) +} + +// optional bytes str = 3; +inline void RedisReply::clear_str() { + str_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& RedisReply::str() const { + // @@protoc_insertion_point(field_get:neb.RedisReply.str) + return str_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void RedisReply::set_str(const ::std::string& value) { + + str_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:neb.RedisReply.str) +} +inline void RedisReply::set_str(const char* value) { + + str_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:neb.RedisReply.str) +} +inline void RedisReply::set_str(const void* value, size_t size) { + + str_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:neb.RedisReply.str) +} +inline ::std::string* RedisReply::mutable_str() { + + // @@protoc_insertion_point(field_mutable:neb.RedisReply.str) + return str_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* RedisReply::release_str() { + // @@protoc_insertion_point(field_release:neb.RedisReply.str) + + return str_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void RedisReply::set_allocated_str(::std::string* str) { + if (str != NULL) { + + } else { + + } + str_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), str); + // @@protoc_insertion_point(field_set_allocated:neb.RedisReply.str) +} + +// repeated .neb.RedisReply element = 4; +inline int RedisReply::element_size() const { + return element_.size(); +} +inline void RedisReply::clear_element() { + element_.Clear(); +} +inline const ::neb::RedisReply& RedisReply::element(int index) const { + // @@protoc_insertion_point(field_get:neb.RedisReply.element) + return element_.Get(index); +} +inline ::neb::RedisReply* RedisReply::mutable_element(int index) { + // @@protoc_insertion_point(field_mutable:neb.RedisReply.element) + return element_.Mutable(index); +} +inline ::neb::RedisReply* RedisReply::add_element() { + // @@protoc_insertion_point(field_add:neb.RedisReply.element) + return element_.Add(); +} +inline ::google::protobuf::RepeatedPtrField< ::neb::RedisReply >* +RedisReply::mutable_element() { + // @@protoc_insertion_point(field_mutable_list:neb.RedisReply.element) + return &element_; +} +inline const ::google::protobuf::RepeatedPtrField< ::neb::RedisReply >& +RedisReply::element() const { + // @@protoc_insertion_point(field_list:neb.RedisReply.element) + return element_; +} + +#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS + +// @@protoc_insertion_point(namespace_scope) + +} // namespace neb + +#ifndef SWIG +namespace google { +namespace protobuf { + +template <> struct is_proto_enum< ::neb::E_REDIS_ERR> : ::google::protobuf::internal::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::neb::E_REDIS_ERR>() { + return ::neb::E_REDIS_ERR_descriptor(); +} +template <> struct is_proto_enum< ::neb::E_REDIS_REPLY> : ::google::protobuf::internal::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::neb::E_REDIS_REPLY>() { + return ::neb::E_REDIS_REPLY_descriptor(); +} + +} // namespace protobuf +} // namespace google +#endif // SWIG + +// @@protoc_insertion_point(global_scope) + +#endif // PROTOBUF_redis_2eproto__INCLUDED diff --git a/src/util/StringConverter.cpp b/src/util/StringConverter.cpp new file mode 100644 index 00000000..9dacc611 --- /dev/null +++ b/src/util/StringConverter.cpp @@ -0,0 +1,24 @@ +/******************************************************************************* + * Project: Nebula + * @file StringConverter.cpp + * @brief + * @author nebim + * @date: 2020-05-02 + * @note + * Modify history: + ******************************************************************************/ +#include "StringConverter.hpp" + +namespace neb +{ + +StringConverter::StringConverter() +{ +} + +StringConverter::~StringConverter() +{ +} + +} + diff --git a/src/util/StringConverter.hpp b/src/util/StringConverter.hpp new file mode 100644 index 00000000..07ee212e --- /dev/null +++ b/src/util/StringConverter.hpp @@ -0,0 +1,61 @@ +/******************************************************************************* + * Project: Nebula + * @file StringConverter.hpp + * @brief + * @author nebim + * @date: 2020-05-02 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_UTIL_STRINGCONVERTER_HPP_ +#define SRC_UTIL_STRINGCONVERTER_HPP_ + +#define _TO_NUM(v) ((v) ^ '0') +#define _IS_NUM(v) (v >= '0' && v <= '9') + +namespace neb +{ + +class StringConverter +{ +public: + StringConverter(); + virtual ~StringConverter(); + + template + static inline TInt RapidAtoi(const char* str) + { + TInt res = 0; + for (int k = 0; ; k +=4) + { + if (not _IS_NUM(str[k])) + { + return(res); + } + else if (not _IS_NUM(str[k + 1])) + { + return(res * 10 + _TO_NUM(str[k])); + } + else if (not _IS_NUM(str[k + 2])) + { + return(res * 100 + _TO_NUM(str[k]) * 10 + _TO_NUM(str[k + 1])); + } + else if (not _IS_NUM(str[k + 3])) + { + return(res * 1000 + _TO_NUM(str[k]) * 100 + + _TO_NUM(str[k + 1]) * 10 + _TO_NUM(str[k + 2])); + } + else + { + res = res * 10000 + _TO_NUM(str[k]) * 1000 + _TO_NUM(str[k + 1]) + * 100 + _TO_NUM(str[k + 2]) * 10 + _TO_NUM(str[k + 3]); + } + } + return(res); + } +}; + +} /* namespace neb */ + +#endif /* SRC_UTIL_STRINGCONVERTER_HPP_ */ + From 9685f839fbce8e9fd120358da77f06930d16bbd2 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 31 Oct 2020 15:07:04 +0800 Subject: [PATCH 102/176] =?UTF-8?q?redis=E8=BF=9E=E6=8E=A5=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E9=9D=9Epipeline=E6=A8=A1=E5=BC=8F=E6=94=AF=E6=8C=81?= =?UTF-8?q?=EF=BC=9B=E5=A2=9E=E5=8A=A0=E7=BA=BF=E7=A8=8B=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E4=B8=8BWorker=E7=BA=BF=E7=A8=8B=E6=AF=94Loader=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E5=85=88=E5=90=AF=E5=8A=A8=EF=BC=8C=E5=B9=B6=E6=8A=8A?= =?UTF-8?q?worker=E7=BA=BF=E7=A8=8BId=E5=B8=A6=E5=88=B0Loader=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BB=A5=E6=94=AF=E6=8C=81=E5=AE=9E=E6=97=B6=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E5=BC=95=E6=93=8E=E7=9A=84=E9=A2=84=E4=BC=B0=E9=9C=80?= =?UTF-8?q?=E8=A6=81=EF=BC=9B=E5=A2=9E=E5=8A=A0http=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E4=B8=BAwebsocket=E7=9A=84=E8=87=AA=E5=8A=A8=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actor/Actor.cpp | 8 +- src/actor/Actor.hpp | 6 +- src/actor/ActorBuilder.cpp | 23 ++++- src/actor/cmd/Module.hpp | 1 + .../sys_session/SessionWorkerThreadInfo.cpp | 25 +++++ .../sys_session/SessionWorkerThreadInfo.hpp | 46 +++++++++ .../sys_session/manager/SessionManager.cpp | 25 ++++- .../sys_session/manager/SessionManager.hpp | 4 + src/channel/RedisChannel.cpp | 2 +- src/channel/RedisChannel.hpp | 12 +++ src/channel/SocketChannelSslImpl.cpp | 2 + src/channel/SocketChannelSslImpl.hpp | 2 +- src/ios/Dispatcher.cpp | 96 +++++++++++++++---- src/ios/Dispatcher.hpp | 8 +- src/labor/Loader.cpp | 9 ++ src/labor/Loader.hpp | 4 + src/labor/Manager.cpp | 10 +- src/labor/Worker.cpp | 2 +- 18 files changed, 248 insertions(+), 37 deletions(-) create mode 100644 src/actor/session/sys_session/SessionWorkerThreadInfo.cpp create mode 100644 src/actor/session/sys_session/SessionWorkerThreadInfo.hpp diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 8a1ec610..390046d6 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -172,14 +172,14 @@ bool Actor::SendTo(std::shared_ptr pChannel) return(m_pLabor->GetDispatcher()->SendTo(pChannel, this)); } -bool Actor::SendTo(const std::string& strIdentify) +bool Actor::SendTo(const std::string& strIdentify, bool bPipeline) { - return(m_pLabor->GetDispatcher()->SendTo(strIdentify, this)); + return(m_pLabor->GetDispatcher()->SendTo(strIdentify, this, bPipeline)); } -bool Actor::SendTo(const std::string& strHost, int iPort) +bool Actor::SendTo(const std::string& strHost, int iPort, bool bPipeline) { - return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, this)); + return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, this, bPipeline)); } bool Actor::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index f7e8f873..1e9c5173 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -194,8 +194,9 @@ class Actor: public std::enable_shared_from_this * @note 只有RedisStep及其派生类才能调用此方法,调用此方法时将调用者自身(RedisStep)添加到 * RedisChannel的pipeline中。 * @param strIdentify RedisChannel通道标识 + * @param bPipeline 是否支持pipeline */ - bool SendTo(const std::string& strIdentify); + bool SendTo(const std::string& strIdentify, bool bPipeline = true); /** * @brief 发送到redis channel @@ -203,8 +204,9 @@ class Actor: public std::enable_shared_from_this * RedisChannel的pipeline中。 * @param strHost RedisChannel地址 * @param iPort RedisChannel端口 + * @param bPipeline 是否支持pipeline */ - bool SendTo(const std::string& strHost, int iPort); + bool SendTo(const std::string& strHost, int iPort, bool bPipeline = true); /** * @brief 从worker发送到loader或从loader发送到worker diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 0e4dae2d..ec36aad1 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -351,6 +351,18 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http auto module_iter = m_mapModule.find(oHttpMsg.path()); if (module_iter == m_mapModule.end()) { + if (oHttpMsg.has_upgrade() && oHttpMsg.upgrade().is_upgrade()) + { + module_iter = m_mapModule.find("http_upgrade"); + if (module_iter != m_mapModule.end()) + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + module_iter->second->SetTraceId(oss.str()); + module_iter->second->AnyMessage(pChannel, oHttpMsg); + return(true); + } + } module_iter = m_mapModule.find("/switch"); if (module_iter == m_mapModule.end()) { @@ -610,6 +622,10 @@ bool ActorBuilder::OnRedisCmdResult(std::shared_ptr pChannel, redi } } } + if (CMD_STATUS_RUNNING != eResult) + { + RemoveStep(std::dynamic_pointer_cast(*step_iter)); + } //freeReplyObject(reply); } else @@ -618,7 +634,10 @@ bool ActorBuilder::OnRedisCmdResult(std::shared_ptr pChannel, redi } } - RemoveStep(std::dynamic_pointer_cast(*step_iter)); + if (pChannel->listPipelineStep.size() == 0 && !pChannel->IsPipeline()) + { + m_pLabor->GetDispatcher()->AddNamedRedisChannel(pChannel->GetIdentify(), pChannel); + } return(true); } @@ -839,6 +858,8 @@ void ActorBuilder::LoadSysCmd() MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); strModulePath = "/status"; MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); + strModulePath = "http_upgrade"; + MakeSharedModule(nullptr, "neb::ModuleHttpUpgrade", strModulePath); } m_pSessionLogger = std::dynamic_pointer_cast(MakeSharedSession(nullptr, "neb::SessionLogger")); } diff --git a/src/actor/cmd/Module.hpp b/src/actor/cmd/Module.hpp index 4a682dc7..f1e1ef9c 100644 --- a/src/actor/cmd/Module.hpp +++ b/src/actor/cmd/Module.hpp @@ -13,6 +13,7 @@ #include "codec/CodecHttp.hpp" #include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" +#include "pb/http.pb.h" namespace neb { diff --git a/src/actor/session/sys_session/SessionWorkerThreadInfo.cpp b/src/actor/session/sys_session/SessionWorkerThreadInfo.cpp new file mode 100644 index 00000000..5b55b4ca --- /dev/null +++ b/src/actor/session/sys_session/SessionWorkerThreadInfo.cpp @@ -0,0 +1,25 @@ +/******************************************************************************* + * Project: Nebula + * @file SessionWorkerThreadInfo.cpp + * @brief 数据上报 + * @author Bwar + * @date: 2020-10-03 + * @note + * Modify history: + ******************************************************************************/ +#include "SessionWorkerThreadInfo.hpp" + +namespace neb +{ + +SessionWorkerThreadInfo::SessionWorkerThreadInfo(const std::string& strSessionId, const std::vector& vecWorkerThreadId) + : neb::Session(strSessionId), m_vecWorkerThreadId(vecWorkerThreadId) +{ +} + +SessionWorkerThreadInfo::~SessionWorkerThreadInfo() +{ +} + +} + diff --git a/src/actor/session/sys_session/SessionWorkerThreadInfo.hpp b/src/actor/session/sys_session/SessionWorkerThreadInfo.hpp new file mode 100644 index 00000000..893f9a0f --- /dev/null +++ b/src/actor/session/sys_session/SessionWorkerThreadInfo.hpp @@ -0,0 +1,46 @@ +/******************************************************************************* + * Project: Nebula + * @file SessionWorkerThreadInfo.hpp + * @brief 数据上报 + * @author Bwar + * @date: 2019-12-22 + * @note + * Modify history: + ******************************************************************************/ +#ifndef ACTOR_SESSION_SYS_SESSION_SESSIONWORKERTHREADINFO_HPP_ +#define ACTOR_SESSION_SYS_SESSION_SESSIONWORKERTHREADINFO_HPP_ + +#include +#include +#include "actor/session/Session.hpp" +#include "actor/ActorSys.hpp" +#include "pb/report.pb.h" + +namespace neb +{ + +class SessionWorkerThreadInfo: public Session, + DynamicCreator&>, + public ActorSys +{ +public: + SessionWorkerThreadInfo(const std::string& strSessionId, const std::vector& vecWorkerThreadId); + virtual ~SessionWorkerThreadInfo(); + + virtual E_CMD_STATUS Timeout() + { + return(CMD_STATUS_RUNNING); + } + + const std::vector& GetWorkerThreadId() const + { + return(m_vecWorkerThreadId); + } + +private: + std::vector m_vecWorkerThreadId; +}; + +} + +#endif /* ACTOR_SESSION_SYS_SESSION_SESSIONWORKERTHREADINFO_HPP_ */ diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 1594d169..47b2ae90 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -11,6 +11,7 @@ #include "SessionManager.hpp" #include #include +#include #include "util/process_helper.h" #include "util/json/CJsonObject.hpp" #include "labor/NodeInfo.hpp" @@ -193,7 +194,7 @@ Worker* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWo Worker* pLoader = nullptr; try { - pLoader = new Loader(strWorkPath, iControlFd, iDataFd, iWorkerIndex); + pLoader = new Loader(strWorkPath, iControlFd, iDataFd, iWorkerIndex, m_vecWorkerThreadId); } catch(std::bad_alloc& e) { @@ -255,6 +256,28 @@ bool SessionManager::SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad) } } +void SessionManager::SetLoaderActorBuilder(ActorBuilder* pActorBuilder) +{ + for (auto it = m_mapWorker.begin(); it != m_mapWorker.end(); ++it) + { + it->second->SetLoaderActorBuilder(pActorBuilder); + } +} + +const std::vector& SessionManager::GetWorkerThreadId() const +{ + return(m_vecWorkerThreadId); +} + +void SessionManager::AddWorkerThreadId(uint64 ullThreadId) +{ + if (std::find(m_vecWorkerThreadId.begin(), m_vecWorkerThreadId.end(), ullThreadId) + != m_vecWorkerThreadId.end()) + { + m_vecWorkerThreadId.push_back(ullThreadId); + } +} + int SessionManager::GetNextWorkerDataFd() { if (m_bDirectToLoader && m_iLoaderDataFd != -1) diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index 36ec8b30..a05ab976 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -38,6 +38,9 @@ class SessionManager : public Session, Worker* MutableLoader(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd); const WorkerInfo* GetWorkerInfo(int32 iWorkerIndex) const; bool SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad); + void SetLoaderActorBuilder(ActorBuilder* pActorBuilder); + const std::vector& GetWorkerThreadId() const; + void AddWorkerThreadId(uint64 ullThreadId); int GetNextWorkerDataFd(); std::pair GetMinLoadWorkerDataFd(); bool CheckWorker(); @@ -56,6 +59,7 @@ class SessionManager : public Session, std::unordered_map::iterator m_iterWorkerInfo; std::unordered_map m_mapWorkerStartNum; ///< 进程被启动次数,key为WorkerIdx std::unordered_map m_mapWorkerFdPid; ///< 工作进程通信FD对应的进程号 + std::vector m_vecWorkerThreadId; ///< Worker线程ID(线程模式下) std::unordered_map m_mapOnlineNodes; ///< 订阅的节点在线信息 }; diff --git a/src/channel/RedisChannel.cpp b/src/channel/RedisChannel.cpp index 90d6ea32..0cb1ca71 100644 --- a/src/channel/RedisChannel.cpp +++ b/src/channel/RedisChannel.cpp @@ -13,7 +13,7 @@ namespace neb { RedisChannel::RedisChannel(redisAsyncContext *c) - : bIsReady(false), m_pRedisCtx(c) + : m_bPipeline(false), bIsReady(false), m_pRedisCtx(c) { } diff --git a/src/channel/RedisChannel.hpp b/src/channel/RedisChannel.hpp index 5cbfc965..2651ad4e 100644 --- a/src/channel/RedisChannel.hpp +++ b/src/channel/RedisChannel.hpp @@ -45,7 +45,19 @@ class RedisChannel: public Channel return(m_pRedisCtx); } + bool IsPipeline() const + { + return(m_bPipeline); + } + +protected: + void SetPipeline(bool bPipeline) + { + m_bPipeline = bPipeline; + } + private: + bool m_bPipeline; ///< 是否支持pipeline bool bIsReady; ///< redis连接是否准备就绪 redisAsyncContext* m_pRedisCtx; ///< redis连接上下文地址 std::string m_strIdentify; ///< 连接标识(可以为空,不为空时用于标识业务层与连接的关系) diff --git a/src/channel/SocketChannelSslImpl.cpp b/src/channel/SocketChannelSslImpl.cpp index a36d4a03..d5bf3f31 100644 --- a/src/channel/SocketChannelSslImpl.cpp +++ b/src/channel/SocketChannelSslImpl.cpp @@ -756,6 +756,7 @@ E_CODEC_STATUS SocketChannelSslImpl::Recv(HttpMsg& oHttpMsg) } } +/* E_CODEC_STATUS SocketChannelSslImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, HttpMsg& oHttpMsg) { LOG4_TRACE(""); @@ -816,6 +817,7 @@ E_CODEC_STATUS SocketChannelSslImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, return(CODEC_STATUS_ERR); } } +*/ bool SocketChannelSslImpl::Close() { diff --git a/src/channel/SocketChannelSslImpl.hpp b/src/channel/SocketChannelSslImpl.hpp index f9890b0f..2fd67ecf 100644 --- a/src/channel/SocketChannelSslImpl.hpp +++ b/src/channel/SocketChannelSslImpl.hpp @@ -69,7 +69,7 @@ class SocketChannelSslImpl : public SocketChannelImpl virtual E_CODEC_STATUS Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq) override; virtual E_CODEC_STATUS Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) override; virtual E_CODEC_STATUS Recv(HttpMsg& oHttpMsg) override; - virtual E_CODEC_STATUS Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, HttpMsg& oHttpMsg) override; + //virtual E_CODEC_STATUS Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, HttpMsg& oHttpMsg) override; virtual bool Close() override; protected: diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index a9c7ffad..ee198fa5 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -1101,7 +1101,7 @@ bool Dispatcher::SendTo(std::shared_ptr pRedisChannel, Actor* pSen } } -bool Dispatcher::SendTo(const std::string& strIdentify, Actor* pSender) +bool Dispatcher::SendTo(const std::string& strIdentify, Actor* pSender, bool bPipeline) { LOG4_TRACE("%s", strIdentify.c_str()); if (nullptr == pSender) @@ -1109,12 +1109,13 @@ bool Dispatcher::SendTo(const std::string& strIdentify, Actor* pSender) LOG4_ERROR("pSender can't be nullptr!"); return(false); } - auto ctx_iter = m_mapNamedRedisChannel.find(strIdentify); - if (ctx_iter != m_mapNamedRedisChannel.end()) + if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) { - return(SendTo(*(ctx_iter->second.begin()), pSender)); + LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); + return(false); } - else + auto named_iter = m_mapNamedRedisChannel.find(strIdentify); + if (named_iter == m_mapNamedRedisChannel.end()) { size_t iPosIpPortSeparator = strIdentify.rfind(':'); if (iPosIpPortSeparator == std::string::npos) @@ -1130,17 +1131,47 @@ bool Dispatcher::SendTo(const std::string& strIdentify, Actor* pSender) LOG4_ERROR("invalid node identify \"%s\"", strIdentify.c_str()); return(false); } - if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) + std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); + return(AutoRedisCmd(strHost, iPort, pRedisStep)); + } + else + { + if (named_iter->second.empty()) { - LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); + size_t iPosIpPortSeparator = strIdentify.rfind(':'); + if (iPosIpPortSeparator == std::string::npos) + { + LOG4_ERROR("invalid node identify \"%s\"", strIdentify.c_str()); + return(false); + } + std::string strHost = strIdentify.substr(0, iPosIpPortSeparator); + std::string strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); + int iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + LOG4_ERROR("invalid node identify \"%s\"", strIdentify.c_str()); + return(false); + } + std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); + return(AutoRedisCmd(strHost, iPort, pRedisStep)); + } + else + { + auto channel_iter = named_iter->second.begin(); + if (SendTo((*channel_iter), pSender)) + { + if (!bPipeline) + { + named_iter->second.erase(channel_iter); + } + return(true); + } return(false); } - std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep)); } } -bool Dispatcher::SendTo(const std::string& strHost, int iPort, Actor* pSender) +bool Dispatcher::SendTo(const std::string& strHost, int iPort, Actor* pSender, bool bPipeline) { LOG4_TRACE("%s, %d", strHost.c_str(), iPort); if (nullptr == pSender) @@ -1148,23 +1179,40 @@ bool Dispatcher::SendTo(const std::string& strHost, int iPort, Actor* pSender) LOG4_ERROR("pSender can't be nullptr!"); return(false); } + if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) + { + LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); + return(false); + } std::ostringstream oss; oss << strHost << ":" << iPort; std::string strIdentify = std::move(oss.str()); - auto ctx_iter = m_mapNamedRedisChannel.find(strIdentify); - if (ctx_iter != m_mapNamedRedisChannel.end()) + auto named_iter = m_mapNamedRedisChannel.find(strIdentify); + if (named_iter != m_mapNamedRedisChannel.end()) { - return(SendTo(*(ctx_iter->second.begin()), pSender)); + std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); + return(AutoRedisCmd(strHost, iPort, pRedisStep)); } else { - if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) + if (named_iter->second.empty()) + { + std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); + return(AutoRedisCmd(strHost, iPort, pRedisStep)); + } + else { - LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); + auto channel_iter = named_iter->second.begin(); + if (SendTo((*channel_iter), pSender)) + { + if (!bPipeline) + { + named_iter->second.erase(channel_iter); + } + return(true); + } return(false); } - std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep)); } } @@ -1363,7 +1411,7 @@ bool Dispatcher::AutoSend(const std::string& strHost, int iPort, const std::stri } } -bool Dispatcher::AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep) +bool Dispatcher::AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep, bool bPipeline) { LOG4_TRACE("redisAsyncConnect(%s, %d)", strHost.c_str(), iPort); redisAsyncContext *c = redisAsyncConnect(strHost.c_str(), iPort); @@ -1389,6 +1437,7 @@ bool Dispatcher::AutoRedisCmd(const std::string& strHost, int iPort, std::shared } pRedisChannel->listPipelineStep.push_back(pRedisStep); pRedisStep->SetLabor(m_pLabor); + pRedisChannel->SetPipeline(bPipeline); m_mapRedisChannel.insert(std::make_pair(c, pRedisChannel)); redisLibevAttach(m_loop, c); redisAsyncSetConnectCallback(c, RedisConnectCallback); @@ -1398,6 +1447,11 @@ bool Dispatcher::AutoRedisCmd(const std::string& strHost, int iPort, std::shared oss << strHost << ":" << iPort; std::string strIdentify = std::move(oss.str()); pRedisChannel->SetIdentify(strIdentify); + if (bPipeline) + { + // if(bPipeline == false) the channel should not add to named connection pool, because there is an uncompleted request. + AddNamedRedisChannel(strIdentify, pRedisChannel); + } AddNamedRedisChannel(strIdentify, pRedisChannel); return(true); } @@ -1923,7 +1977,7 @@ bool Dispatcher::AddIoReadEvent(std::shared_ptr pChannel) { LOG4_TRACE("fd[%d], seq[%u]", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); - if (NULL == io_watcher) + if (NULL == io_watcher || pChannel->GetFd() < 0) { return(false); } @@ -1948,7 +2002,7 @@ bool Dispatcher::AddIoWriteEvent(std::shared_ptr pChannel) { LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); - if (NULL == io_watcher) + if (NULL == io_watcher || pChannel->GetFd() < 0) { return(false); } @@ -1972,7 +2026,7 @@ bool Dispatcher::RemoveIoWriteEvent(std::shared_ptr pChannel) { LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); - if (NULL == io_watcher) + if (NULL == io_watcher || pChannel->GetFd() < 0) { return(false); } diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 1cb52252..f3399a40 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -138,9 +138,9 @@ class Dispatcher // SendTo() for redis bool SendTo(std::shared_ptr pRedisChannel, Actor* pSender); - bool SendTo(const std::string& strIdentify, Actor* pSender); - bool SendTo(const std::string& strHost, int iPort, Actor* pSender); - bool AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep); + bool SendTo(const std::string& strIdentify, Actor* pSender, bool bPipeline = true); + bool SendTo(const std::string& strHost, int iPort, Actor* pSender, bool bPipeline = true); + bool AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep, bool bPipeline = true); // SendTo() for unix domain socket bool SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender); @@ -210,7 +210,7 @@ class Dispatcher /* named Channel */ std::unordered_map > > m_mapNamedSocketChannel; ///< key为Identify,连接存在时,if(http连接)list.size()>=1;else list.size()==1; - std::unordered_map > > m_mapNamedRedisChannel; ///< key为identify,list.size()==1 + std::unordered_map > > m_mapNamedRedisChannel; ///< key为identify,连接存在时,if(pipeline)list.size()==1;else list.size()>=1; std::unordered_map > m_mapLoaderAndWorkerChannel; ///< Loader和Worker之间通信通道 std::unordered_map >::iterator m_iterLoaderAndWorkerChannel; diff --git a/src/labor/Loader.cpp b/src/labor/Loader.cpp index c2266074..b581f57a 100644 --- a/src/labor/Loader.cpp +++ b/src/labor/Loader.cpp @@ -10,6 +10,7 @@ #include "Loader.hpp" #include "actor/ActorBuilder.hpp" +#include "actor/Actor.hpp" namespace neb { @@ -19,6 +20,12 @@ Loader::Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int { } +Loader::Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex, const std::vector& vecWorkerThreadId) + : Worker(strWorkPath, iControlFd, iDataFd, iWorkerIndex, Labor::LABOR_LOADER), + m_vecWorkerThreadId(vecWorkerThreadId) +{ +} + Loader::~Loader() { } @@ -28,6 +35,8 @@ bool Loader::InitActorBuilder() LOG4_TRACE(""); if (NewActorBuilder()) { + std::string strSessionId = "neb::SessionWorkerThreadInfo"; + GetActorBuilder()->MakeSharedActor(nullptr, "neb::SessionWorkerThreadInfo", strSessionId, m_vecWorkerThreadId); return(GetActorBuilder()->Init( (const_cast(GetNodeConf()))["load_config"]["loader"]["boot_load"], (const_cast(GetNodeConf()))["load_config"]["loader"]["dynamic_loading"])); diff --git a/src/labor/Loader.hpp b/src/labor/Loader.hpp index 117a47ae..5fa7c51d 100644 --- a/src/labor/Loader.hpp +++ b/src/labor/Loader.hpp @@ -20,10 +20,14 @@ class Loader: public Worker { public: Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex); + Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex, const std::vector& vecWorkerThreadId); virtual ~Loader(); protected: virtual bool InitActorBuilder(); + +private: + std::vector m_vecWorkerThreadId; ///< worker线程ID(线程模式下) }; } /* namespace neb */ diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 7a31275d..bcb7ced2 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -58,8 +58,8 @@ Manager::Manager(const std::string& strConfFile) CreateEvents(); if (m_stNodeInfo.bThreadMode) { + CreateWorkerThread(); // Loader可能需要用到Worker线程ID,故先创建Worker CreateLoaderThread(); - CreateWorkerThread(); } else { @@ -452,6 +452,7 @@ void Manager::CreateLoader() m_pDispatcher->AddIoReadEvent(pChannelData); m_pDispatcher->AddIoReadEvent(pChannelControl); m_pSessionManager->SendOnlineNodesToWorker(); + m_pSessionManager->NewSocketWhenLoaderCreated(); } else { @@ -503,7 +504,9 @@ void Manager::CreateLoaderThread() m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->AddIoReadEvent(pChannelData); m_pDispatcher->AddIoReadEvent(pChannelControl); + m_pSessionManager->SetLoaderActorBuilder(m_pLoaderActorBuilder); m_pSessionManager->SendOnlineNodesToWorker(); + m_pSessionManager->NewSocketWhenLoaderCreated(); } void Manager::CreateWorker() @@ -558,6 +561,7 @@ void Manager::CreateWorker() m_pDispatcher->AddIoReadEvent(pChannelData); m_pDispatcher->AddIoReadEvent(pChannelControl); m_pSessionManager->NewSocketWhenWorkerCreated(iDataFds[0]); + m_pSessionManager->SendOnlineNodesToWorker(); // optional } else { @@ -598,6 +602,9 @@ void Manager::CreateWorkerThread() pWorker->SetLoaderActorBuilder(m_pLoaderActorBuilder); std::thread t(&Worker::Run, pWorker); t.detach(); + std::ostringstream ossThreadId; + ossThreadId << t.get_id(); + m_pSessionManager->AddWorkerThreadId(strtoull(ossThreadId.str().c_str(), NULL, 10)); m_pSessionManager->AddWorkerInfo(i, getpid(), iControlFds[0], iDataFds[0]); std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); @@ -605,6 +612,7 @@ void Manager::CreateWorkerThread() m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->AddIoReadEvent(pChannelData); m_pDispatcher->AddIoReadEvent(pChannelControl); + m_pSessionManager->SendOnlineNodesToWorker(); // optional m_pSessionManager->NewSocketWhenWorkerCreated(iDataFds[0]); } } diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index bc5a348d..bab3604a 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -152,7 +152,7 @@ bool Worker::Init(CJsonObject& oJsonConf) bool bCpuAffinity = false; oJsonConf.Get("cpu_affinity", bCpuAffinity); - if (bCpuAffinity) + if (bCpuAffinity && !m_stNodeInfo.bThreadMode) { #ifndef __CYGWIN__ /* get logical cpu number */ From 9822fbea4424ca4a68b72dd49aed031c1ffa91d1 Mon Sep 17 00:00:00 2001 From: nebim Date: Wed, 4 Nov 2020 12:05:17 +0800 Subject: [PATCH 103/176] =?UTF-8?q?=E8=A1=A5=E5=85=85CodecResp.cpp?= =?UTF-8?q?=E9=81=97=E6=BC=8F=E7=9A=84=E9=83=A8=E5=88=86=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/codec/CodecResp.cpp | 335 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index 0072afe3..5d1f5fa8 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -120,5 +120,340 @@ E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply) return(eStatus); } +E_CODEC_STATUS CodecResp::EncodeSimpleString(const RedisReply& oReply, CBuffer* pBuff) +{ + pBuff->WriteByte(RESP_SIMPLE_STRING); + pBuff->Write(oReply.str().c_str(), oReply.str().size()); + pBuff->WriteByte('\r'); + pBuff->WriteByte('\n'); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecResp::EncodeError(const RedisReply& oReply, CBuffer* pBuff) +{ + pBuff->WriteByte(RESP_ERROR); + pBuff->Write(oReply.str().c_str(), oReply.str().size()); + pBuff->WriteByte('\r'); + pBuff->WriteByte('\n'); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecResp::EncodeInteger(const RedisReply& oReply, CBuffer* pBuff) +{ + char szInteger[64] = {0}; + snprintf(szInteger, 64, "%ld", oReply.integer()); + pBuff->WriteByte(RESP_INTEGER); + pBuff->Write(&szInteger, strlen(szInteger)); + pBuff->WriteByte('\r'); + pBuff->WriteByte('\n'); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecResp::EncodeBulkString(const RedisReply& oReply, CBuffer* pBuff) +{ + char szStringLen[64] = {0}; + snprintf(szStringLen, 64, "%lu", oReply.str().size()); + pBuff->WriteByte(RESP_BULK_STRING); + pBuff->Write(szStringLen, strlen(szStringLen)); + pBuff->WriteByte('\r'); + pBuff->WriteByte('\n'); + if (oReply.str().size() > 0) + { + pBuff->Write(oReply.str().c_str(), oReply.str().size()); + } + pBuff->WriteByte('\r'); + pBuff->WriteByte('\n'); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecResp::EncodeArray(const RedisReply& oReply, CBuffer* pBuff) +{ + char szArraySize[64] = {0}; + snprintf(szArraySize, 64, "%d", oReply.element_size()); + pBuff->WriteByte(RESP_ARRAY); + pBuff->Write(szArraySize, strlen(szArraySize)); + pBuff->WriteByte('\r'); + pBuff->WriteByte('\n'); + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + for (int i = 0; i < oReply.element_size(); ++i) + { + auto& rElement = oReply.element(i); + switch (rElement.type()) + { + case REDIS_REPLY_STRING: + eStatus = EncodeBulkString(rElement, pBuff); + break; + case REDIS_REPLY_ARRAY: + eStatus = EncodeArray(rElement, pBuff); + break; + case REDIS_REPLY_INTEGER: + eStatus = EncodeInteger(rElement, pBuff); + break; + case REDIS_REPLY_STATUS: + eStatus = EncodeSimpleString(rElement, pBuff); + break; + case REDIS_REPLY_ERROR: + eStatus = EncodeError(rElement, pBuff); + break; + case REDIS_REPLY_NIL: + eStatus = EncodeNull(rElement, pBuff); + break; + default: + LOG4_ERROR("invalid redis reply type %d", oReply.type()); + return(CODEC_STATUS_ERR); + } + if (CODEC_STATUS_OK != eStatus) + { + return(eStatus); + } + } + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecResp::EncodeNull(const RedisReply& oReply, CBuffer* pBuff) +{ + char szInteger[64] = {0}; + snprintf(szInteger, 64, "%d", -1); + pBuff->WriteByte(RESP_BULK_STRING); + pBuff->Write(&szInteger, strlen(szInteger)); + pBuff->WriteByte('\r'); + pBuff->WriteByte('\n'); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecResp::DecodeSimpleString(CBuffer* pBuff, RedisReply& oReply) +{ + size_t uiReadableBytes = pBuff->ReadableBytes(); + char cLastChar = 0; + uint32 uiStringLen = 0; + const char* pData = pBuff->GetRawReadBuffer(); + for (size_t i = 0; i < uiReadableBytes; ++i) + { + switch (pData[i]) + { + case '\n': + if ('\r' == cLastChar) + { + cLastChar = '\n'; + oReply.set_type(REDIS_REPLY_STATUS); + oReply.set_str(pBuff->GetRawReadBuffer(), uiStringLen); + pBuff->AdvanceReadIndex(i + 1); + return(CODEC_STATUS_OK); + } + else + { + return(CODEC_STATUS_ERR); + } + break; + case '\r': + cLastChar = '\r'; + break; + default: + ++uiStringLen; + } + } + return(CODEC_STATUS_PAUSE); +} + +E_CODEC_STATUS CodecResp::DecodeError(CBuffer* pBuff, RedisReply& oReply) +{ + size_t uiReadableBytes = pBuff->ReadableBytes(); + char cLastChar = 0; + uint32 uiStringLen = 0; + const char* pData = pBuff->GetRawReadBuffer(); + for (size_t i = 0; i < uiReadableBytes; ++i) + { + switch (pData[i]) + { + case '\n': + if ('\r' == cLastChar) + { + cLastChar = '\n'; + oReply.set_type(REDIS_REPLY_ERROR); + oReply.set_str(pBuff->GetRawReadBuffer(), uiStringLen); + pBuff->AdvanceReadIndex(i + 1); + return(CODEC_STATUS_OK); + } + else + { + return(CODEC_STATUS_ERR); + } + break; + case '\r': + cLastChar = '\r'; + break; + default: + ++uiStringLen; + } + } + return(CODEC_STATUS_PAUSE); +} + +E_CODEC_STATUS CodecResp::DecodeInteger(CBuffer* pBuff, RedisReply& oReply) +{ + size_t uiReadableBytes = pBuff->ReadableBytes(); + char cLastChar = 0; + const char* pData = pBuff->GetRawReadBuffer(); + for (size_t i = 0; i < uiReadableBytes; ++i) + { + switch (*pData) + { + case '\n': + if ('\r' == cLastChar) + { + cLastChar = '\n'; + oReply.set_type(REDIS_REPLY_INTEGER); + oReply.set_integer(StringConverter::RapidAtoi(pBuff->GetRawReadBuffer())); + pBuff->AdvanceReadIndex(i + 1); + return(CODEC_STATUS_OK); + } + else + { + return(CODEC_STATUS_ERR); + } + break; + case '\r': + cLastChar = '\r'; + break; + default: + ; + } + } + return(CODEC_STATUS_PAUSE); +} + +E_CODEC_STATUS CodecResp::DecodeBulkString(CBuffer* pBuff, RedisReply& oReply) +{ + size_t uiReadableBytes = pBuff->ReadableBytes(); + char cLastChar = 0; + int32 iStringLen = 0; + bool bGotStringLen = false; + const char* pData = pBuff->GetRawReadBuffer(); + for (size_t i = 0; i <= uiReadableBytes; ++i) + { + switch (pData[i]) + { + case '\n': + if ('\r' == cLastChar) + { + cLastChar = '\n'; + if (bGotStringLen) + { + if (iStringLen == -1) + { + oReply.set_type(REDIS_REPLY_NIL); + } + else + { + oReply.set_type(REDIS_REPLY_STRING); + oReply.set_str(pBuff->GetRawReadBuffer(), iStringLen); + } + pBuff->AdvanceReadIndex(i + 1); + return(CODEC_STATUS_OK); + } + else + { + iStringLen = StringConverter::RapidAtoi(pBuff->GetRawReadBuffer()); + bGotStringLen = true; + if (uiReadableBytes - i < (uint32)iStringLen) + { + return(CODEC_STATUS_PAUSE); + } + else + { + i += (uint32)iStringLen; + } + } + } + else + { + return(CODEC_STATUS_ERR); + } + break; + case '\r': + cLastChar = '\r'; + break; + default: + ; + } + } + return(CODEC_STATUS_PAUSE); +} + +E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) +{ + size_t uiReadableBytes = pBuff->ReadableBytes(); + char cLastChar = 0; + int32 iArraySize = 0; + const char* pData = pBuff->GetRawReadBuffer(); + for (size_t i = 0; i < uiReadableBytes; ++i) + { + switch (pData[i]) + { + case '\n': + if ('\r' == cLastChar) + { + cLastChar = '\n'; + iArraySize = StringConverter::RapidAtoi(pBuff->GetRawReadBuffer()); + pBuff->AdvanceReadIndex(i + 1); + + if (iArraySize == -1) + { + oReply.set_type(REDIS_REPLY_NIL); + } + else + { + oReply.set_type(REDIS_REPLY_ARRAY); + for (int32 i = 0; i < iArraySize; ++i) + { + char cFirstByte = 0; + auto pElement = oReply.add_element(); + pBuff->ReadByte(cFirstByte); + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + switch (cFirstByte) + { + case RESP_SIMPLE_STRING: + eStatus = DecodeSimpleString(pBuff, *pElement); + break; + case RESP_INTEGER: + eStatus = DecodeInteger(pBuff, *pElement); + break; + case RESP_BULK_STRING: + eStatus = DecodeBulkString(pBuff, *pElement); + break; + case RESP_ARRAY: + eStatus = DecodeArray(pBuff, *pElement); + break; + case RESP_ERROR: + eStatus = DecodeError(pBuff, *pElement); + break; + default: + oReply.set_type(REDIS_REPLY_ERROR); + oReply.set_integer(REDIS_ERR_PROTOCOL); + return(CODEC_STATUS_ERR); + } + if (CODEC_STATUS_OK != eStatus) + { + return(eStatus); + } + } + return(CODEC_STATUS_OK); + } + } + else + { + return(CODEC_STATUS_ERR); + } + break; + case '\r': + cLastChar = '\r'; + break; + default: + ; + } + } + return(CODEC_STATUS_PAUSE); +} + } From 0a064270628ba17eb443b35585d341984535f1fa Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 7 Nov 2020 21:35:47 +0800 Subject: [PATCH 104/176] add cityhash; update CJsonObject; add RedisChannel no pipeline; bug fixed. --- src/actor/ActorBuilder.cpp | 12 +- .../sys_session/manager/SessionManager.cpp | 14 +- .../sys_session/manager/SessionManager.hpp | 4 +- src/channel/SocketChannelImpl.cpp | 10 +- src/codec/Codec.hpp | 4 +- src/ios/Dispatcher.cpp | 8 +- src/ios/Nodes.cpp | 30 +- src/ios/Nodes.hpp | 3 +- src/logger/NetLogger.hpp | 1 + src/util/encrypt/city.cc | 646 ++++++++++++++++++ src/util/encrypt/city.h | 112 +++ src/util/encrypt/citycrc.h | 43 ++ src/util/encrypt/config.h | 114 ++++ src/util/json/CJsonObject.cpp | 92 ++- src/util/json/CJsonObject.hpp | 5 +- 15 files changed, 1059 insertions(+), 39 deletions(-) create mode 100644 src/util/encrypt/city.cc create mode 100644 src/util/encrypt/city.h create mode 100644 src/util/encrypt/citycrc.h create mode 100644 src/util/encrypt/config.h diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index ec36aad1..9b3f8467 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -1218,12 +1218,20 @@ void ActorBuilder::BootLoadCmd(CJsonObject& oCmdConf) for (int i = 0; i < oCmdConf["cmd"].GetArraySize(); ++i) { oCmdConf["cmd"][i].Get("cmd", iCmd); - MakeSharedCmd(nullptr, oCmdConf["cmd"][i]("class"), iCmd); + auto pCmd = MakeSharedCmd(nullptr, oCmdConf["cmd"][i]("class"), iCmd); + if (pCmd != nullptr) + { + LOG4_INFO("succeed in loading %s with cmd %d", oCmdConf["cmd"][i]("class").c_str(), iCmd); + } } for (int j = 0; j < oCmdConf["module"].GetArraySize(); ++j) { oCmdConf["module"][j].Get("path", strUrlPath); - MakeSharedModule(nullptr, oCmdConf["module"][j]("class"), strUrlPath); + auto pModule = MakeSharedModule(nullptr, oCmdConf["module"][j]("class"), strUrlPath); + if (pModule != nullptr) + { + LOG4_INFO("succeed in loading %s with module %s", oCmdConf["module"][j]("class").c_str(), strUrlPath.c_str()); + } } } diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 47b2ae90..dfbf69e2 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -186,12 +186,12 @@ Worker* SessionManager::MutableWorker(int iWorkerIndex, const std::string& strWo } } -Worker* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd) +Loader* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd) { auto iter = m_mapWorker.find(iWorkerIndex); if (iter == m_mapWorker.end()) { - Worker* pLoader = nullptr; + Loader* pLoader = nullptr; try { pLoader = new Loader(strWorkPath, iControlFd, iDataFd, iWorkerIndex, m_vecWorkerThreadId); @@ -201,12 +201,12 @@ Worker* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWo LOG4_ERROR("new Worker error: %s", e.what()); return(nullptr); } - m_mapWorker.insert(std::make_pair(iWorkerIndex, pLoader)); + m_mapWorker.insert(std::make_pair(iWorkerIndex, (Worker*)pLoader)); return(pLoader); } else { - return(iter->second); + return((Loader*)iter->second); } } @@ -295,11 +295,15 @@ int SessionManager::GetNextWorkerDataFd() { m_iterWorkerInfo = m_mapWorkerInfo.begin(); } - else if (m_iterWorkerInfo->second->iDataFd == m_iLoaderDataFd) + if (m_iterWorkerInfo->second->iDataFd == m_iLoaderDataFd) { ++m_iterWorkerInfo; if (m_iterWorkerInfo == m_mapWorkerInfo.end()) { + if (m_mapWorkerInfo.size() == 1) + { + return(-1); + } m_iterWorkerInfo = m_mapWorkerInfo.begin(); } } diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index a05ab976..1ef65d5e 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -18,6 +18,8 @@ namespace neb { +class Loader; + class SessionManager : public Session, public DynamicCreator, public ActorSys { @@ -35,7 +37,7 @@ class SessionManager : public Session, void AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); void AddLoaderInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); Worker* MutableWorker(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd); - Worker* MutableLoader(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd); + Loader* MutableLoader(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd); const WorkerInfo* GetWorkerInfo(int32 iWorkerIndex) const; bool SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad); void SetLoaderActorBuilder(ActorBuilder* pActorBuilder); diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 6cdf2212..83ee292b 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -166,7 +166,7 @@ E_CODEC_STATUS SocketChannelImpl::Send() } else { - if (EAGAIN == m_iErrno && EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); @@ -280,7 +280,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& } else { - if (EAGAIN == m_iErrno && EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); @@ -358,7 +358,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq } else { - if (EAGAIN == m_iErrno && EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); @@ -458,7 +458,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) } else { - if (EAGAIN == m_iErrno && EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); @@ -530,7 +530,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) } else { - if (EAGAIN == m_iErrno && EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 18a007d9..446293b9 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -60,7 +60,7 @@ enum E_CODEC_STATUS CODEC_STATUS_WANT_READ = 4, ///< 等待对端握手信息(此时,应该不再监听FD的可写事件,直到对端握手信息到达。此状态用于SSL连接握手) CODEC_STATUS_WANT_WRITE = 5, ///< 等待己端握手信息(此时,应发起握手信息,并添加FD的可写事件监听。此状态用于SSL连接握手)或握手成功等待数据发送 CODEC_STATUS_INVALID = 6, ///< 使用了错误的编解码方式(此时为调用错误,应修改调用方式) - CODEC_STATUS_ERR = 7, ///< 编解码失败 + CODEC_STATUS_ERR = 7, ///< 编解码失败,需关闭连接 CODEC_STATUS_EOF = 8, ///< 连接正常关闭 CODEC_STATUS_INT = 9, ///< 连接非正常关闭 }; @@ -135,7 +135,7 @@ class Codec int32 m_iErrno; E_CODEC_TYPE m_eCodecType; std::string m_strKey; // 密钥 - static std::vector m_vecAutoSwitchCodecType; + static std::vector m_vecAutoSwitchCodecType; // 自动转换有效的编解码类型 friend class SocketChannel; }; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index ee198fa5..30e5e41f 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -1132,7 +1132,7 @@ bool Dispatcher::SendTo(const std::string& strIdentify, Actor* pSender, bool bPi return(false); } std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep)); + return(AutoRedisCmd(strHost, iPort, pRedisStep, bPipeline)); } else { @@ -1153,7 +1153,7 @@ bool Dispatcher::SendTo(const std::string& strIdentify, Actor* pSender, bool bPi return(false); } std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep)); + return(AutoRedisCmd(strHost, iPort, pRedisStep, bPipeline)); } else { @@ -1191,14 +1191,14 @@ bool Dispatcher::SendTo(const std::string& strHost, int iPort, Actor* pSender, b if (named_iter != m_mapNamedRedisChannel.end()) { std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep)); + return(AutoRedisCmd(strHost, iPort, pRedisStep, bPipeline)); } else { if (named_iter->second.empty()) { std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep)); + return(AutoRedisCmd(strHost, iPort, pRedisStep, bPipeline)); } else { diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index 3dbbbd41..b4aa1912 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -13,6 +13,8 @@ #define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 #include "cryptopp/md5.h" #include "cryptopp/hex.h" +#include "cryptopp/filters.h" +#include "util/encrypt/city.h" namespace neb { @@ -30,17 +32,19 @@ Nodes::~Nodes() bool Nodes::GetNode(const std::string& strNodeType, const std::string& strHashKey, std::string& strNodeIdentify) { uint32 uiKeyHash = 0; - if (HASH_fnv1_64 == m_iHashAlgorithm) + switch (m_iHashAlgorithm) { - uiKeyHash = hash_fnv1_64(strHashKey.c_str(), strHashKey.size()); - } - else if (HASH_murmur3_32 == m_iHashAlgorithm) - { - uiKeyHash = murmur3_32(strHashKey.c_str(), strHashKey.size(), 0x000001b3); - } - else - { - uiKeyHash = hash_fnv1a_64(strHashKey.c_str(), strHashKey.size()); + case HASH_cityhash_32: + uiKeyHash = CityHash32(strHashKey.c_str(), strHashKey.size()); + break; + case HASH_fnv1_64: + uiKeyHash = hash_fnv1_64(strHashKey.c_str(), strHashKey.size()); + break; + case HASH_murmur3_32: + uiKeyHash = murmur3_32(strHashKey.c_str(), strHashKey.size(), 0x000001b3); + break; + default: + uiKeyHash = hash_fnv1a_64(strHashKey.c_str(), strHashKey.size()); } auto node_type_iter = m_mapNode.find(strNodeType); @@ -220,7 +224,11 @@ void Nodes::DelNode(const std::string& strNodeType, const std::string& strNodeId for (std::vector::iterator hash_iter = node_iter->second.begin(); hash_iter != node_iter->second.end(); ++hash_iter) { - node_type_iter->second->mapHash2Node.erase(node_type_iter->second->mapHash2Node.find(*hash_iter)); + auto it = node_type_iter->second->mapHash2Node.find(*hash_iter); + if (it != node_type_iter->second->mapHash2Node.end()) + { + node_type_iter->second->mapHash2Node.erase(it); + } } node_type_iter->second->mapNode2Hash.erase(node_iter); node_type_iter->second->itPollingNode = node_type_iter->second->mapNode2Hash.begin(); diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index 1b3bb238..261caad7 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -31,6 +31,7 @@ enum E_HASH_ALGORITHM HASH_fnv1a_64 = 0, HASH_fnv1_64 = 1, HASH_murmur3_32 = 2, + HASH_cityhash_32 = 3, }; /** @@ -44,7 +45,7 @@ class Nodes * @param iVirtualNodeNum 每个实体节点对应的虚拟节点数量 * @param dSessionTimeout 超时时间,0表示永不超时 */ - Nodes(int iHashAlgorithm = HASH_fnv1a_64, int iVirtualNodeNum = 200); + Nodes(int iHashAlgorithm = HASH_cityhash_32, int iVirtualNodeNum = 200); virtual ~Nodes(); /* 实体节点hash信息 diff --git a/src/logger/NetLogger.hpp b/src/logger/NetLogger.hpp index 3732f3c3..828532e6 100644 --- a/src/logger/NetLogger.hpp +++ b/src/logger/NetLogger.hpp @@ -41,6 +41,7 @@ class NetLogger: public Logger virtual void SetLogLevel(int iLev) { m_iLogLevel = iLev; + m_pLog->SetLogLevel(iLev); } virtual void SetNetLogLevel(int iLev) diff --git a/src/util/encrypt/city.cc b/src/util/encrypt/city.cc new file mode 100644 index 00000000..c0bc0d55 --- /dev/null +++ b/src/util/encrypt/city.cc @@ -0,0 +1,646 @@ +// Copyright (c) 2011 Google, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// CityHash, by Geoff Pike and Jyrki Alakuijala +// +// This file provides CityHash64() and related functions. +// +// It's probably possible to create even faster hash functions by +// writing a program that systematically explores some of the space of +// possible hash functions, by using SIMD instructions, or by +// compromising on hash quality. + +#include "config.h" +#include "city.h" + +#include +#include // for memcpy and memset + +using namespace std; + +static uint64 UNALIGNED_LOAD64(const char *p) { + uint64 result; + memcpy(&result, p, sizeof(result)); + return result; +} + +static uint32 UNALIGNED_LOAD32(const char *p) { + uint32 result; + memcpy(&result, p, sizeof(result)); + return result; +} + +#ifdef _MSC_VER + +#include +#define bswap_32(x) _byteswap_ulong(x) +#define bswap_64(x) _byteswap_uint64(x) + +#elif defined(__APPLE__) + +// Mac OS X / Darwin features +#include +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) + +#elif defined(__sun) || defined(sun) + +#include +#define bswap_32(x) BSWAP_32(x) +#define bswap_64(x) BSWAP_64(x) + +#elif defined(__FreeBSD__) + +#include +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) + +#elif defined(__OpenBSD__) + +#include +#define bswap_32(x) swap32(x) +#define bswap_64(x) swap64(x) + +#elif defined(__NetBSD__) + +#include +#include +#if defined(__BSWAP_RENAME) && !defined(__bswap_32) +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) +#endif + +#else + +#include + +#endif + +#ifdef WORDS_BIGENDIAN +#define uint32_in_expected_order(x) (bswap_32(x)) +#define uint64_in_expected_order(x) (bswap_64(x)) +#else +#define uint32_in_expected_order(x) (x) +#define uint64_in_expected_order(x) (x) +#endif + +#if !defined(LIKELY) +#if HAVE_BUILTIN_EXPECT +#define LIKELY(x) (__builtin_expect(!!(x), 1)) +#else +#define LIKELY(x) (x) +#endif +#endif + +static uint64 Fetch64(const char *p) { + return uint64_in_expected_order(UNALIGNED_LOAD64(p)); +} + +static uint32 Fetch32(const char *p) { + return uint32_in_expected_order(UNALIGNED_LOAD32(p)); +} + +// Some primes between 2^63 and 2^64 for various uses. +static const uint64 k0 = 0xc3a5c85c97cb3127ULL; +static const uint64 k1 = 0xb492b66fbe98f273ULL; +static const uint64 k2 = 0x9ae16a3b2f90404fULL; + +// Magic numbers for 32-bit hashing. Copied from Murmur3. +static const uint32 c1 = 0xcc9e2d51; +static const uint32 c2 = 0x1b873593; + +// A 32-bit to 32-bit integer hash copied from Murmur3. +static uint32 fmix(uint32 h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + return h; +} + +static uint32 Rotate32(uint32 val, int shift) { + // Avoid shifting by 32: doing so yields an undefined result. + return shift == 0 ? val : ((val >> shift) | (val << (32 - shift))); +} + +#undef PERMUTE3 +#define PERMUTE3(a, b, c) do { std::swap(a, b); std::swap(a, c); } while (0) + +static uint32 Mur(uint32 a, uint32 h) { + // Helper from Murmur3 for combining two 32-bit values. + a *= c1; + a = Rotate32(a, 17); + a *= c2; + h ^= a; + h = Rotate32(h, 19); + return h * 5 + 0xe6546b64; +} + +static uint32 Hash32Len13to24(const char *s, size_t len) { + uint32 a = Fetch32(s - 4 + (len >> 1)); + uint32 b = Fetch32(s + 4); + uint32 c = Fetch32(s + len - 8); + uint32 d = Fetch32(s + (len >> 1)); + uint32 e = Fetch32(s); + uint32 f = Fetch32(s + len - 4); + uint32 h = len; + + return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h))))))); +} + +static uint32 Hash32Len0to4(const char *s, size_t len) { + uint32 b = 0; + uint32 c = 9; + for (size_t i = 0; i < len; i++) { + signed char v = s[i]; + b = b * c1 + v; + c ^= b; + } + return fmix(Mur(b, Mur(len, c))); +} + +static uint32 Hash32Len5to12(const char *s, size_t len) { + uint32 a = len, b = len * 5, c = 9, d = b; + a += Fetch32(s); + b += Fetch32(s + len - 4); + c += Fetch32(s + ((len >> 1) & 4)); + return fmix(Mur(c, Mur(b, Mur(a, d)))); +} + +uint32 CityHash32(const char *s, size_t len) { + if (len <= 24) { + return len <= 12 ? + (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) : + Hash32Len13to24(s, len); + } + + // len > 24 + uint32 h = len, g = c1 * len, f = g; + uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2; + uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2; + uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2; + uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2; + uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2; + h ^= a0; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + h ^= a2; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + g ^= a1; + g = Rotate32(g, 19); + g = g * 5 + 0xe6546b64; + g ^= a3; + g = Rotate32(g, 19); + g = g * 5 + 0xe6546b64; + f += a4; + f = Rotate32(f, 19); + f = f * 5 + 0xe6546b64; + size_t iters = (len - 1) / 20; + do { + uint32 a0 = Rotate32(Fetch32(s) * c1, 17) * c2; + uint32 a1 = Fetch32(s + 4); + uint32 a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2; + uint32 a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2; + uint32 a4 = Fetch32(s + 16); + h ^= a0; + h = Rotate32(h, 18); + h = h * 5 + 0xe6546b64; + f += a1; + f = Rotate32(f, 19); + f = f * c1; + g += a2; + g = Rotate32(g, 18); + g = g * 5 + 0xe6546b64; + h ^= a3 + a1; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + g ^= a4; + g = bswap_32(g) * 5; + h += a4 * 5; + h = bswap_32(h); + f += a0; + PERMUTE3(f, h, g); + s += 20; + } while (--iters != 0); + g = Rotate32(g, 11) * c1; + g = Rotate32(g, 17) * c1; + f = Rotate32(f, 11) * c1; + f = Rotate32(f, 17) * c1; + h = Rotate32(h + g, 19); + h = h * 5 + 0xe6546b64; + h = Rotate32(h, 17) * c1; + h = Rotate32(h + f, 19); + h = h * 5 + 0xe6546b64; + h = Rotate32(h, 17) * c1; + return h; +} + +// Bitwise right rotate. Normally this will compile to a single +// instruction, especially if the shift is a manifest constant. +static uint64 Rotate(uint64 val, int shift) { + // Avoid shifting by 64: doing so yields an undefined result. + return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); +} + +static uint64 ShiftMix(uint64 val) { + return val ^ (val >> 47); +} + +static uint64 HashLen16(uint64 u, uint64 v) { + return Hash128to64(uint128(u, v)); +} + +static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { + // Murmur-inspired hashing. + uint64 a = (u ^ v) * mul; + a ^= (a >> 47); + uint64 b = (v ^ a) * mul; + b ^= (b >> 47); + b *= mul; + return b; +} + +static uint64 HashLen0to16(const char *s, size_t len) { + if (len >= 8) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch64(s) + k2; + uint64 b = Fetch64(s + len - 8); + uint64 c = Rotate(b, 37) * mul + a; + uint64 d = (Rotate(a, 25) + b) * mul; + return HashLen16(c, d, mul); + } + if (len >= 4) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch32(s); + return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); + } + if (len > 0) { + uint8 a = s[0]; + uint8 b = s[len >> 1]; + uint8 c = s[len - 1]; + uint32 y = static_cast(a) + (static_cast(b) << 8); + uint32 z = len + (static_cast(c) << 2); + return ShiftMix(y * k2 ^ z * k0) * k2; + } + return k2; +} + +// This probably works well for 16-byte strings as well, but it may be overkill +// in that case. +static uint64 HashLen17to32(const char *s, size_t len) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch64(s) * k1; + uint64 b = Fetch64(s + 8); + uint64 c = Fetch64(s + len - 8) * mul; + uint64 d = Fetch64(s + len - 16) * k2; + return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, + a + Rotate(b + k2, 18) + c, mul); +} + +// Return a 16-byte hash for 48 bytes. Quick and dirty. +// Callers do best to use "random-looking" values for a and b. +static pair WeakHashLen32WithSeeds( + uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) { + a += w; + b = Rotate(b + a + z, 21); + uint64 c = a; + a += x; + a += y; + b += Rotate(a, 44); + return make_pair(a + z, b + c); +} + +// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. +static pair WeakHashLen32WithSeeds( + const char* s, uint64 a, uint64 b) { + return WeakHashLen32WithSeeds(Fetch64(s), + Fetch64(s + 8), + Fetch64(s + 16), + Fetch64(s + 24), + a, + b); +} + +// Return an 8-byte hash for 33 to 64 bytes. +static uint64 HashLen33to64(const char *s, size_t len) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch64(s) * k2; + uint64 b = Fetch64(s + 8); + uint64 c = Fetch64(s + len - 24); + uint64 d = Fetch64(s + len - 32); + uint64 e = Fetch64(s + 16) * k2; + uint64 f = Fetch64(s + 24) * 9; + uint64 g = Fetch64(s + len - 8); + uint64 h = Fetch64(s + len - 16) * mul; + uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9; + uint64 v = ((a + g) ^ d) + f + 1; + uint64 w = bswap_64((u + v) * mul) + h; + uint64 x = Rotate(e + f, 42) + c; + uint64 y = (bswap_64((v + w) * mul) + g) * mul; + uint64 z = e + f + c; + a = bswap_64((x + z) * mul + y) + b; + b = ShiftMix((z + a) * mul + d + h) * mul; + return b + x; +} + +uint64 CityHash64(const char *s, size_t len) { + if (len <= 32) { + if (len <= 16) { + return HashLen0to16(s, len); + } else { + return HashLen17to32(s, len); + } + } else if (len <= 64) { + return HashLen33to64(s, len); + } + + // For strings over 64 bytes we hash the end first, and then as we + // loop we keep 56 bytes of state: v, w, x, y, and z. + uint64 x = Fetch64(s + len - 40); + uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); + uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); + pair v = WeakHashLen32WithSeeds(s + len - 64, len, z); + pair w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); + x = x * k1 + Fetch64(s); + + // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. + len = (len - 1) & ~static_cast(63); + do { + x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; + x ^= w.second; + y += v.first + Fetch64(s + 40); + z = Rotate(z + w.first, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); + std::swap(z, x); + s += 64; + len -= 64; + } while (len != 0); + return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z, + HashLen16(v.second, w.second) + x); +} + +uint64 CityHash64WithSeed(const char *s, size_t len, uint64 seed) { + return CityHash64WithSeeds(s, len, k2, seed); +} + +uint64 CityHash64WithSeeds(const char *s, size_t len, + uint64 seed0, uint64 seed1) { + return HashLen16(CityHash64(s, len) - seed0, seed1); +} + +// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings +// of any length representable in signed long. Based on City and Murmur. +static uint128 CityMurmur(const char *s, size_t len, uint128 seed) { + uint64 a = Uint128Low64(seed); + uint64 b = Uint128High64(seed); + uint64 c = 0; + uint64 d = 0; + signed long l = len - 16; + if (l <= 0) { // len <= 16 + a = ShiftMix(a * k1) * k1; + c = b * k1 + HashLen0to16(s, len); + d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); + } else { // len > 16 + c = HashLen16(Fetch64(s + len - 8) + k1, a); + d = HashLen16(b + len, c + Fetch64(s + len - 16)); + a += d; + do { + a ^= ShiftMix(Fetch64(s) * k1) * k1; + a *= k1; + b ^= a; + c ^= ShiftMix(Fetch64(s + 8) * k1) * k1; + c *= k1; + d ^= c; + s += 16; + l -= 16; + } while (l > 0); + } + a = HashLen16(a, c); + b = HashLen16(d, b); + return uint128(a ^ b, HashLen16(b, a)); +} + +uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) { + if (len < 128) { + return CityMurmur(s, len, seed); + } + + // We expect len >= 128 to be the common case. Keep 56 bytes of state: + // v, w, x, y, and z. + pair v, w; + uint64 x = Uint128Low64(seed); + uint64 y = Uint128High64(seed); + uint64 z = len * k1; + v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s); + v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8); + w.first = Rotate(y + z, 35) * k1 + x; + w.second = Rotate(x + Fetch64(s + 88), 53) * k1; + + // This is the same inner loop as CityHash64(), manually unrolled. + do { + x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; + x ^= w.second; + y += v.first + Fetch64(s + 40); + z = Rotate(z + w.first, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); + std::swap(z, x); + s += 64; + x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; + x ^= w.second; + y += v.first + Fetch64(s + 40); + z = Rotate(z + w.first, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); + std::swap(z, x); + s += 64; + len -= 128; + } while (LIKELY(len >= 128)); + x += Rotate(v.first + z, 49) * k0; + y = y * k0 + Rotate(w.second, 37); + z = z * k0 + Rotate(w.first, 27); + w.first *= 9; + v.first *= k0; + // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. + for (size_t tail_done = 0; tail_done < len; ) { + tail_done += 32; + y = Rotate(x + y, 42) * k0 + v.second; + w.first += Fetch64(s + len - tail_done + 16); + x = x * k0 + w.first; + z += w.second + Fetch64(s + len - tail_done); + w.second += v.first; + v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second); + v.first *= k0; + } + // At this point our 56 bytes of state should contain more than + // enough information for a strong 128-bit hash. We use two + // different 56-byte-to-8-byte hashes to get a 16-byte final result. + x = HashLen16(x, v.first); + y = HashLen16(y + z, w.first); + return uint128(HashLen16(x + v.second, w.second) + y, + HashLen16(x + w.second, y + v.second)); +} + +uint128 CityHash128(const char *s, size_t len) { + return len >= 16 ? + CityHash128WithSeed(s + 16, len - 16, + uint128(Fetch64(s), Fetch64(s + 8) + k0)) : + CityHash128WithSeed(s, len, uint128(k0, k1)); +} + +#ifdef __SSE4_2__ +#include +#include + +// Requires len >= 240. +static void CityHashCrc256Long(const char *s, size_t len, + uint32 seed, uint64 *result) { + uint64 a = Fetch64(s + 56) + k0; + uint64 b = Fetch64(s + 96) + k0; + uint64 c = result[0] = HashLen16(b, len); + uint64 d = result[1] = Fetch64(s + 120) * k0 + len; + uint64 e = Fetch64(s + 184) + seed; + uint64 f = 0; + uint64 g = 0; + uint64 h = c + d; + uint64 x = seed; + uint64 y = 0; + uint64 z = 0; + + // 240 bytes of input per iter. + size_t iters = len / 240; + len -= iters * 240; + do { +#undef CHUNK +#define CHUNK(r) \ + PERMUTE3(x, z, y); \ + b += Fetch64(s); \ + c += Fetch64(s + 8); \ + d += Fetch64(s + 16); \ + e += Fetch64(s + 24); \ + f += Fetch64(s + 32); \ + a += b; \ + h += f; \ + b += c; \ + f += d; \ + g += e; \ + e += z; \ + g += x; \ + z = _mm_crc32_u64(z, b + g); \ + y = _mm_crc32_u64(y, e + h); \ + x = _mm_crc32_u64(x, f + a); \ + e = Rotate(e, r); \ + c += e; \ + s += 40 + + CHUNK(0); PERMUTE3(a, h, c); + CHUNK(33); PERMUTE3(a, h, f); + CHUNK(0); PERMUTE3(b, h, f); + CHUNK(42); PERMUTE3(b, h, d); + CHUNK(0); PERMUTE3(b, h, e); + CHUNK(33); PERMUTE3(a, h, e); + } while (--iters > 0); + + while (len >= 40) { + CHUNK(29); + e ^= Rotate(a, 20); + h += Rotate(b, 30); + g ^= Rotate(c, 40); + f += Rotate(d, 34); + PERMUTE3(c, h, g); + len -= 40; + } + if (len > 0) { + s = s + len - 40; + CHUNK(33); + e ^= Rotate(a, 43); + h += Rotate(b, 42); + g ^= Rotate(c, 41); + f += Rotate(d, 40); + } + result[0] ^= h; + result[1] ^= g; + g += h; + a = HashLen16(a, g + z); + x += y << 32; + b += x; + c = HashLen16(c, z) + h; + d = HashLen16(d, e + result[0]); + g += e; + h += HashLen16(x, f); + e = HashLen16(a, d) + g; + z = HashLen16(b, c) + a; + y = HashLen16(g, h) + c; + result[0] = e + z + y + x; + a = ShiftMix((a + y) * k0) * k0 + b; + result[1] += a + result[0]; + a = ShiftMix(a * k0) * k0 + c; + result[2] = a + result[1]; + a = ShiftMix((a + e) * k0) * k0; + result[3] = a + result[2]; +} + +// Requires len < 240. +static void CityHashCrc256Short(const char *s, size_t len, uint64 *result) { + char buf[240]; + memcpy(buf, s, len); + memset(buf + len, 0, 240 - len); + CityHashCrc256Long(buf, 240, ~static_cast(len), result); +} + +void CityHashCrc256(const char *s, size_t len, uint64 *result) { + if (LIKELY(len >= 240)) { + CityHashCrc256Long(s, len, 0, result); + } else { + CityHashCrc256Short(s, len, result); + } +} + +uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) { + if (len <= 900) { + return CityHash128WithSeed(s, len, seed); + } else { + uint64 result[4]; + CityHashCrc256(s, len, result); + uint64 u = Uint128High64(seed) + result[0]; + uint64 v = Uint128Low64(seed) + result[1]; + return uint128(HashLen16(u, v + result[2]), + HashLen16(Rotate(v, 32), u * k0 + result[3])); + } +} + +uint128 CityHashCrc128(const char *s, size_t len) { + if (len <= 900) { + return CityHash128(s, len); + } else { + uint64 result[4]; + CityHashCrc256(s, len, result); + return uint128(result[2], result[3]); + } +} + +#endif diff --git a/src/util/encrypt/city.h b/src/util/encrypt/city.h new file mode 100644 index 00000000..94499ce4 --- /dev/null +++ b/src/util/encrypt/city.h @@ -0,0 +1,112 @@ +// Copyright (c) 2011 Google, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// CityHash, by Geoff Pike and Jyrki Alakuijala +// +// http://code.google.com/p/cityhash/ +// +// This file provides a few functions for hashing strings. All of them are +// high-quality functions in the sense that they pass standard tests such +// as Austin Appleby's SMHasher. They are also fast. +// +// For 64-bit x86 code, on short strings, we don't know of anything faster than +// CityHash64 that is of comparable quality. We believe our nearest competitor +// is Murmur3. For 64-bit x86 code, CityHash64 is an excellent choice for hash +// tables and most other hashing (excluding cryptography). +// +// For 64-bit x86 code, on long strings, the picture is more complicated. +// On many recent Intel CPUs, such as Nehalem, Westmere, Sandy Bridge, etc., +// CityHashCrc128 appears to be faster than all competitors of comparable +// quality. CityHash128 is also good but not quite as fast. We believe our +// nearest competitor is Bob Jenkins' Spooky. We don't have great data for +// other 64-bit CPUs, but for long strings we know that Spooky is slightly +// faster than CityHash on some relatively recent AMD x86-64 CPUs, for example. +// Note that CityHashCrc128 is declared in citycrc.h. +// +// For 32-bit x86 code, we don't know of anything faster than CityHash32 that +// is of comparable quality. We believe our nearest competitor is Murmur3A. +// (On 64-bit CPUs, it is typically faster to use the other CityHash variants.) +// +// Functions in the CityHash family are not suitable for cryptography. +// +// Please see CityHash's README file for more details on our performance +// measurements and so on. +// +// WARNING: This code has been only lightly tested on big-endian platforms! +// It is known to work well on little-endian platforms that have a small penalty +// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. +// It should work on all 32-bit and 64-bit platforms that allow unaligned reads; +// bug reports are welcome. +// +// By the way, for some hash functions, given strings a and b, the hash +// of a+b is easily derived from the hashes of a and b. This property +// doesn't hold for any hash functions in this file. + +#ifndef CITY_HASH_H_ +#define CITY_HASH_H_ + +#include // for size_t. +#include +#include + +typedef uint8_t uint8; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef std::pair uint128; + +inline uint64 Uint128Low64(const uint128& x) { return x.first; } +inline uint64 Uint128High64(const uint128& x) { return x.second; } + +// Hash function for a byte array. +uint64 CityHash64(const char *buf, size_t len); + +// Hash function for a byte array. For convenience, a 64-bit seed is also +// hashed into the result. +uint64 CityHash64WithSeed(const char *buf, size_t len, uint64 seed); + +// Hash function for a byte array. For convenience, two seeds are also +// hashed into the result. +uint64 CityHash64WithSeeds(const char *buf, size_t len, + uint64 seed0, uint64 seed1); + +// Hash function for a byte array. +uint128 CityHash128(const char *s, size_t len); + +// Hash function for a byte array. For convenience, a 128-bit seed is also +// hashed into the result. +uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed); + +// Hash function for a byte array. Most useful in 32-bit binaries. +uint32 CityHash32(const char *buf, size_t len); + +// Hash 128 input bits down to 64 bits of output. +// This is intended to be a reasonably good hash function. +inline uint64 Hash128to64(const uint128& x) { + // Murmur-inspired hashing. + const uint64 kMul = 0x9ddfea08eb382d69ULL; + uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; + a ^= (a >> 47); + uint64 b = (Uint128High64(x) ^ a) * kMul; + b ^= (b >> 47); + b *= kMul; + return b; +} + +#endif // CITY_HASH_H_ diff --git a/src/util/encrypt/citycrc.h b/src/util/encrypt/citycrc.h new file mode 100644 index 00000000..318e3917 --- /dev/null +++ b/src/util/encrypt/citycrc.h @@ -0,0 +1,43 @@ +// Copyright (c) 2011 Google, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// CityHash, by Geoff Pike and Jyrki Alakuijala +// +// This file declares the subset of the CityHash functions that require +// _mm_crc32_u64(). See the CityHash README for details. +// +// Functions in the CityHash family are not suitable for cryptography. + +#ifndef CITY_HASH_CRC_H_ +#define CITY_HASH_CRC_H_ + +#include + +// Hash function for a byte array. +uint128 CityHashCrc128(const char *s, size_t len); + +// Hash function for a byte array. For convenience, a 128-bit seed is also +// hashed into the result. +uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed); + +// Hash function for a byte array. Sets result[0] ... result[3]. +void CityHashCrc256(const char *s, size_t len, uint64 *result); + +#endif // CITY_HASH_CRC_H_ diff --git a/src/util/encrypt/config.h b/src/util/encrypt/config.h new file mode 100644 index 00000000..278a2e12 --- /dev/null +++ b/src/util/encrypt/config.h @@ -0,0 +1,114 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define to 1 if the compiler supports __builtin_expect. */ +#define HAVE_BUILTIN_EXPECT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "cityhash-discuss@googlegroups.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "CityHash" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "CityHash 1.1.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "cityhash" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.1.1" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define for Solaris 2.5.1 so the uint32_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +/* #undef _UINT32_T */ + +/* Define for Solaris 2.5.1 so the uint64_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +/* #undef _UINT64_T */ + +/* Define for Solaris 2.5.1 so the uint8_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +/* #undef _UINT8_T */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to `int' if does not define. */ +/* #undef ssize_t */ + +/* Define to the type of an unsigned integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +/* #undef uint32_t */ + +/* Define to the type of an unsigned integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +/* #undef uint64_t */ + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +/* #undef uint8_t */ diff --git a/src/util/json/CJsonObject.cpp b/src/util/json/CJsonObject.cpp index 7728b743..b45d22f0 100644 --- a/src/util/json/CJsonObject.cpp +++ b/src/util/json/CJsonObject.cpp @@ -9,7 +9,6 @@ ******************************************************************************/ #include "CJsonObject.hpp" -#include #ifdef _WIN32 #define snprintf _snprintf_s @@ -308,9 +307,30 @@ std::string CJsonObject::operator()(const std::string& strKey) const } else if (pJsonStruct->type == cJSON_Int) { - std::ostringstream ossNumber; - ossNumber << pJsonStruct->valueint; - return(ossNumber.str()); + char szNumber[128] = {0}; + if (pJsonStruct->sign == -1) + { + if (pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) + { + snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%ld", (int64)pJsonStruct->valueint); + } + } + else + { + if ((uint64)pJsonStruct->valueint <= (uint64)UINT_MAX) + { + snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%lu", pJsonStruct->valueint); + } + } + return(std::string(szNumber)); } else if (pJsonStruct->type == cJSON_Double) { @@ -363,9 +383,30 @@ std::string CJsonObject::operator()(unsigned int uiWhich) const } else if (pJsonStruct->type == cJSON_Int) { - std::ostringstream ossNumber; - ossNumber << pJsonStruct->valueint; - return(ossNumber.str()); + char szNumber[128] = {0}; + if (pJsonStruct->sign == -1) + { + if (pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) + { + snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%ld", (int64)pJsonStruct->valueint); + } + } + else + { + if ((uint64)pJsonStruct->valueint <= (uint64)UINT_MAX) + { + snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%lu", pJsonStruct->valueint); + } + } + return(std::string(szNumber)); } else if (pJsonStruct->type == cJSON_Double) { @@ -515,6 +556,29 @@ std::string CJsonObject::ToFormattedString() const return(strJsonData); } +bool CJsonObject::KeyExist(const std::string& strKey) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + return(true); +} bool CJsonObject::Get(const std::string& strKey, CJsonObject& oJsonObject) const { @@ -1264,6 +1328,20 @@ bool CJsonObject::Add(const std::string& strKey, double dValue) return(true); } +bool CJsonObject::ReplaceAdd(const std::string& strKey,const CJsonObject& oJsonObject) +{ + if(Replace(strKey,oJsonObject) == false) + return Add(strKey,oJsonObject); + return true; +} + +bool CJsonObject::ReplaceAdd(const std::string& strKey,const std::string& strValue) +{ + if(Replace(strKey,strValue) == false) + return Add(strKey,strValue); + return true; +} + bool CJsonObject::AddNull(const std::string& strKey) { cJSON* pFocusData = NULL; diff --git a/src/util/json/CJsonObject.hpp b/src/util/json/CJsonObject.hpp index 812d440c..ef3da13d 100644 --- a/src/util/json/CJsonObject.hpp +++ b/src/util/json/CJsonObject.hpp @@ -61,6 +61,7 @@ class CJsonObject void ResetTraversing(); CJsonObject& operator[](const std::string& strKey); std::string operator()(const std::string& strKey) const; + bool KeyExist(const std::string& strKey) const; bool Get(const std::string& strKey, CJsonObject& oJsonObject) const; bool Get(const std::string& strKey, std::string& strValue) const; bool Get(const std::string& strKey, int32& iValue) const; @@ -92,7 +93,9 @@ class CJsonObject bool Replace(const std::string& strKey, float fValue); bool Replace(const std::string& strKey, double dValue); bool ReplaceWithNull(const std::string& strKey); // replace value with null - template bool ReplaceAdd(const std::string& strKey,T& value) + bool ReplaceAdd(const std::string& strKey,const CJsonObject& oJsonObject); + bool ReplaceAdd(const std::string& strKey,const std::string& strValue); + template bool ReplaceAdd(const std::string& strKey,T value) { if(Replace(strKey,value) == false) return Add(strKey,value); From d98db06fa86cd2181c4b3fcdcad0e76de6dd62c0 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 21 Nov 2020 20:44:43 +0800 Subject: [PATCH 105/176] adjust identify splice --- src/actor/cmd/sys_cmd/CmdNodeNotice.cpp | 14 +++++----- src/ios/Dispatcher.cpp | 35 +++++++++++++------------ src/ios/Dispatcher.hpp | 4 +-- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp b/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp index 853113cb..50eb5442 100644 --- a/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp +++ b/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp @@ -43,7 +43,7 @@ bool CmdNodeNotice::AnyMessage( std::string strNodeHost; int iNodePort = 0; int iWorkerNum = 0; - char szIdentify[64] = {0}; + std::ostringstream ossIdentify; for (int i = 0; i < oJson["del_nodes"].GetArraySize(); ++i) { if (oJson["del_nodes"][i].Get("node_type",strNodeType) @@ -53,9 +53,9 @@ bool CmdNodeNotice::AnyMessage( { for(int j = 1; j <= iWorkerNum; ++j) { - snprintf(szIdentify, sizeof(szIdentify), "%s:%d.%d", strNodeHost.c_str(), iNodePort, j); - GetLabor(this)->GetDispatcher()->DelNodeIdentify(strNodeType, std::string(szIdentify)); - LOG4_DEBUG("DelNodeIdentify(%s,%s)", strNodeType.c_str(), szIdentify); + ossIdentify << strNodeHost << ":" << iNodePort << "." << j; + GetLabor(this)->GetDispatcher()->DelNodeIdentify(strNodeType, ossIdentify.str()); + LOG4_DEBUG("DelNodeIdentify(%s,%s)", strNodeType.c_str(), ossIdentify.str().c_str()); } } } @@ -69,9 +69,9 @@ bool CmdNodeNotice::AnyMessage( { for(int j = 1; j <= iWorkerNum; ++j) { - snprintf(szIdentify, sizeof(szIdentify), "%s:%d.%d", strNodeHost.c_str(), iNodePort, j); - GetLabor(this)->GetDispatcher()->AddNodeIdentify(strNodeType, std::string(szIdentify)); - LOG4_DEBUG("AddNodeIdentify(%s,%s)", strNodeType.c_str(), szIdentify); + ossIdentify << strNodeHost << ":" << iNodePort << "." << j; + GetLabor(this)->GetDispatcher()->AddNodeIdentify(strNodeType, ossIdentify.str()); + LOG4_DEBUG("AddNodeIdentify(%s,%s)", strNodeType.c_str(), ossIdentify.str().c_str()); } } } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 30e5e41f..8c902f8f 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -1013,13 +1013,14 @@ bool Dispatcher::SendTo(std::shared_ptr pChannel, const HttpMsg& bool Dispatcher::SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) { - char szIdentify[256] = {0}; - snprintf(szIdentify, sizeof(szIdentify), "%s:%d", strHost.c_str(), iPort); - LOG4_TRACE("identify: %s", szIdentify); - auto named_iter = m_mapNamedSocketChannel.find(szIdentify); + std::ostringstream ossIdentify; + ossIdentify << strHost << ":" << iPort; + std::string strIdentify = std::move(ossIdentify.str()); + LOG4_TRACE("identify: %s", strIdentify.c_str()); + auto named_iter = m_mapNamedSocketChannel.find(strIdentify); if (named_iter == m_mapNamedSocketChannel.end()) { - LOG4_TRACE("no channel match %s.", szIdentify); + LOG4_TRACE("no channel match %s.", strIdentify.c_str()); return(AutoSend(strHost, iPort, strUrlPath, oHttpMsg, uiHttpStepSeq)); } else @@ -1383,12 +1384,12 @@ bool Dispatcher::AutoSend(const std::string& strHost, int iPort, const std::stri { connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); freeaddrinfo(pAddrResult); /* No longer needed */ - char szIdentify[32] = {0}; AddIoTimeout(pChannel, 1.5); AddIoReadEvent(pChannel); AddIoWriteEvent(pChannel); - snprintf(szIdentify, sizeof(szIdentify), "%s:%d", strHost.c_str(), iPort); - pChannel->m_pImpl->SetIdentify(szIdentify); + std::ostringstream ossIdentify; + ossIdentify << strHost << ":" << iPort; + pChannel->m_pImpl->SetIdentify(ossIdentify.str()); pChannel->m_pImpl->SetRemoteAddr(strHost); E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); if (CODEC_STATUS_OK != eCodecStatus @@ -1519,7 +1520,7 @@ bool Dispatcher::Disconnect(const std::string& strIdentify, bool bChannelNotice) auto named_iter = m_mapNamedSocketChannel.find(strIdentify); if (named_iter != m_mapNamedSocketChannel.end()) { - std::list>::iterator channel_iter; + std::unordered_set>::iterator channel_iter; while (named_iter->second.size() > 1) { channel_iter = named_iter->second.begin(); @@ -1565,13 +1566,13 @@ bool Dispatcher::AddNamedSocketChannel(const std::string& strIdentify, std::shar auto named_iter = m_mapNamedSocketChannel.find(strIdentify); if (named_iter == m_mapNamedSocketChannel.end()) { - std::list> listChannel; - listChannel.push_back(pChannel); - m_mapNamedSocketChannel.insert(std::make_pair(strIdentify, listChannel)); + std::unordered_set> setChannel; + setChannel.insert(pChannel); + m_mapNamedSocketChannel.insert(std::make_pair(strIdentify, std::move(setChannel))); } else { - named_iter->second.push_back(pChannel); + named_iter->second.insert(pChannel); } pChannel->m_pImpl->SetIdentify(strIdentify); return(true); @@ -1600,14 +1601,14 @@ bool Dispatcher::AddNamedRedisChannel(const std::string& strIdentify, std::share auto named_iter = m_mapNamedRedisChannel.find(strIdentify); if (named_iter == m_mapNamedRedisChannel.end()) { - std::list > listChannel; - listChannel.push_back(pChannel); - m_mapNamedRedisChannel.insert(std::make_pair(strIdentify, listChannel)); + std::unordered_set > setChannel; + setChannel.insert(pChannel); + m_mapNamedRedisChannel.insert(std::make_pair(strIdentify, std::move(setChannel))); return(true); } else { - named_iter->second.push_back(pChannel); + named_iter->second.insert(pChannel); } pChannel->SetIdentify(strIdentify); return(true); diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index f3399a40..50dd2716 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -209,8 +209,8 @@ class Dispatcher std::unordered_map > m_mapRedisChannel; /* named Channel */ - std::unordered_map > > m_mapNamedSocketChannel; ///< key为Identify,连接存在时,if(http连接)list.size()>=1;else list.size()==1; - std::unordered_map > > m_mapNamedRedisChannel; ///< key为identify,连接存在时,if(pipeline)list.size()==1;else list.size()>=1; + std::unordered_map > > m_mapNamedSocketChannel; ///< key为Identify,连接存在时,if(http连接)set.size()>=1;else set.size()==1; + std::unordered_map > > m_mapNamedRedisChannel; ///< key为identify,连接存在时,if(pipeline)set.size()==1;else set.size()>=1; std::unordered_map > m_mapLoaderAndWorkerChannel; ///< Loader和Worker之间通信通道 std::unordered_map >::iterator m_iterLoaderAndWorkerChannel; From 4cb8fcd9346e36fa26484725490a584c7ceae079 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 29 Nov 2020 16:48:48 +0800 Subject: [PATCH 106/176] add log for dynamic actor creation. --- src/actor/ActorBuilder.hpp | 6 +++--- src/actor/ActorFactory.hpp | 16 ++++++++++------ src/actor/DynamicCreator.hpp | 4 ++-- src/channel/SocketChannelImpl.hpp | 4 ++-- src/codec/Codec.hpp | 4 ++-- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 6cd9a874..2a21ce33 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -213,7 +213,7 @@ void ActorBuilder::Logger(const std::string& strTraceId, int iLogLevel, const ch template std::shared_ptr ActorBuilder::MakeSharedActor(Actor* pCreator, const std::string& strActorName, Targs&&... args) { - Actor* pActor = ActorFactory::Instance()->Create(strActorName, std::forward(args)...); + Actor* pActor = ActorFactory::Instance()->Create(m_pLogger, strActorName, std::forward(args)...); if (nullptr == pActor) { /** @@ -282,8 +282,8 @@ std::shared_ptr ActorBuilder::MakeSharedChain(Actor* pCreator, const std: template Actor* ActorBuilder::NewActor(const std::string& strActorName, Targs... args) { - LOG4_TRACE("\"%s\" created by NewActor().", strActorName.c_str()); - return(ActorFactory::Instance()->Create(strActorName, std::forward(args)...)); + LOG4_INFO("\"%s\" created by NewActor() instead.", strActorName.c_str()); + return(ActorFactory::Instance()->Create(m_pLogger, strActorName, std::forward(args)...)); } } /* namespace neb */ diff --git a/src/actor/ActorFactory.hpp b/src/actor/ActorFactory.hpp index 3e64076a..4ae5af72 100644 --- a/src/actor/ActorFactory.hpp +++ b/src/actor/ActorFactory.hpp @@ -13,6 +13,7 @@ #include #include #include +#include "logger/NetLogger.hpp" namespace neb { @@ -34,14 +35,14 @@ class ActorFactory virtual ~ActorFactory(){}; - bool Regist(const std::string& strTypeName, std::function pFunc); + bool Regist(const std::string& strTypeName, std::function, Targs&&... args)> pFunc); bool UnRegist(const std::string& strTypeName); - Actor* Create(const std::string& strTypeName, Targs&&... args); + Actor* Create(std::shared_ptr pLogger, const std::string& strTypeName, Targs&&... args); private: ActorFactory(){}; static ActorFactory* s_pActorFactory; - std::unordered_map > m_mapCreateFunction; + std::unordered_map, Targs&&...)> > m_mapCreateFunction; }; @@ -49,7 +50,7 @@ template ActorFactory* ActorFactory::s_pActorFactory = nullptr; template -bool ActorFactory::Regist(const std::string& strTypeName, std::function pFunc) +bool ActorFactory::Regist(const std::string& strTypeName, std::function, Targs&&... args)> pFunc) { if (nullptr == pFunc) { @@ -76,16 +77,19 @@ bool ActorFactory::UnRegist(const std::string& strTypeName) } template -Actor* ActorFactory::Create(const std::string& strTypeName, Targs&&... args) +Actor* ActorFactory::Create(std::shared_ptr pLogger, + const std::string& strTypeName, Targs&&... args) { auto iter = m_mapCreateFunction.find(strTypeName); if (iter == m_mapCreateFunction.end()) { + pLogger->WriteLog(Logger::WARNING, __FILE__, __LINE__, __FUNCTION__, + "no CreateObject found for \"%s\"", strTypeName.c_str()); return (nullptr); } else { - return (iter->second(std::forward(args)...)); + return (iter->second(pLogger, std::forward(args)...)); } } diff --git a/src/actor/DynamicCreator.hpp b/src/actor/DynamicCreator.hpp index 3e04e3b6..96b909d5 100644 --- a/src/actor/DynamicCreator.hpp +++ b/src/actor/DynamicCreator.hpp @@ -66,7 +66,7 @@ class DynamicCreator } virtual ~DynamicCreator(){}; - static T* CreateObject(Targs&&... args) + static T* CreateObject(std::shared_ptr pLogger, Targs&&... args) { T* pT = nullptr; try @@ -75,7 +75,7 @@ class DynamicCreator } catch(std::bad_alloc& e) { - //std::cerr << e.what() << std::endl; // TODO write log + pLogger->WriteLog(Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "%s", e.what()); return(nullptr); } return(pT); diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 848def9c..b87be237 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -57,7 +57,7 @@ class SocketChannelImpl: public Channel E_CODEC_STATUS Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody); E_CODEC_STATUS Fetch(HttpMsg& oHttpMsg); - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); public: int GetFd() const @@ -228,7 +228,7 @@ class SocketChannelImpl: public Channel }; template -void SocketChannelImpl::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) +void SocketChannelImpl::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 446293b9..13d97977 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -107,7 +107,7 @@ class Codec static const std::vector& GetAutoSwitchCodecType(); static void AddAutoSwitchCodecType(E_CODEC_TYPE eCodecType); - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args); + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); inline void SetErrno(int32 iErrno) { @@ -141,7 +141,7 @@ class Codec }; template -void Codec::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs... args) +void Codec::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } From 7bf544a374bb464f1e04dbb7f09463af0cab8a5e Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 29 Nov 2020 17:05:19 +0800 Subject: [PATCH 107/176] v1.3 release --- README.md | 7 +++++++ README_cn.md | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/README.md b/README.md index df3dd60f..817054bc 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,13 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v1.3 + - add non-pipeline mode support for redis connection + - the worker thread to start before the Loader thread in thread mode, and bring the worker thread ID to the Loader + - merge CodecResp and CodecHttp2 written by @nebim + - add third-party cityhash + - update CJsonObject + - bug fixed #### v1.2 - delay start when loading a large amount of data #### v1.1 diff --git a/README_cn.md b/README_cn.md index 91cc1712..51f31b80 100644 --- a/README_cn.md +++ b/README_cn.md @@ -114,6 +114,13 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 +#### v1.3 + - 增加redis连接的非pipeline模式支持 + - 增加线程模式下Worker线程比Loader线程先启动,并把worker线程Id带到Loader + - 初步合并@nebim写的CodecResp和CodecHttp2 + - 增加第三方cityhash + - 更新CJsonObject + - bug修复 #### v1.2 - 增加延迟启动功能:因loader加载大量本地数据文件而延迟绑定端口提供服务并向Beacon注册。 - 线程模式下Manager不再监控和重启不健康的worker和loader服务。 From ea356bef8e3593e54125273fb8b7d110b3e40e8e Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 2 Dec 2020 23:18:10 +0800 Subject: [PATCH 108/176] CodecResp fixed --- src/codec/Codec.hpp | 1 + src/codec/CodecResp.cpp | 41 +++++++++++++++++------------------------ src/codec/CodecResp.hpp | 1 - src/labor/Manager.cpp | 2 +- 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 13d97977..e9efb3f8 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -43,6 +43,7 @@ enum E_CODEC_TYPE CODEC_WS_EXTEND_JSON = 6, ///< 带Extension data的websocket协议扩展,Application data为json CODEC_WS_EXTEND_PB = 7, ///< 带Extension data的websocket协议扩展,Application data为pb CODEC_NEBULA_IN_NODE = 8, ///< 节点各进程间通信协议,与CODEC_NEBULA协议相同,使用的编解码类也相同,只为区别节点内部连接与外部连接 + CODEC_RESP = 9, ///< redis数据传输协议resp }; /** diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index 5d1f5fa8..95ac540e 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -9,7 +9,6 @@ ******************************************************************************/ #include "CodecResp.hpp" #include "util/StringConverter.hpp" -#include "actor/step/RedisStep.hpp" // TODO move to dispatcher namespace neb { @@ -33,23 +32,6 @@ E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMs return(CODEC_STATUS_INVALID); } -E_CODEC_STATUS CodecResp::Encode(std::shared_ptr pRedisStep, CBuffer* pBuff) -{ - RedisReply oReply; - oReply.set_type(REDIS_REPLY_ARRAY); - auto pElement = oReply.add_element(); - pElement->set_type(REDIS_REPLY_STRING); - pElement->set_str(pRedisStep->GetCmd()); - auto& rArgs = pRedisStep->GetCmdArguments(); - for (size_t i = 0; i < rArgs.size(); ++i) - { - pElement = oReply.add_element(); - pElement->set_type(REDIS_REPLY_STRING); - pElement->set_str(rArgs[i].first); - } - return(Encode(oReply, pBuff)); -} - E_CODEC_STATUS CodecResp::Encode(const RedisReply& oReply, CBuffer* pBuff) { int iHadWriteLen = 0; @@ -87,6 +69,10 @@ E_CODEC_STATUS CodecResp::Encode(const RedisReply& oReply, CBuffer* pBuff) E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply) { + if (pBuff->ReadableBytes() == 0) + { + return(CODEC_STATUS_PAUSE); + } char cFirstByte = 0; size_t uiReadIndex = pBuff->GetReadIndex(); pBuff->ReadByte(cFirstByte); @@ -227,6 +213,7 @@ E_CODEC_STATUS CodecResp::DecodeSimpleString(CBuffer* pBuff, RedisReply& oReply) char cLastChar = 0; uint32 uiStringLen = 0; const char* pData = pBuff->GetRawReadBuffer(); + const char* pPartBegin = pData; for (size_t i = 0; i < uiReadableBytes; ++i) { switch (pData[i]) @@ -236,7 +223,7 @@ E_CODEC_STATUS CodecResp::DecodeSimpleString(CBuffer* pBuff, RedisReply& oReply) { cLastChar = '\n'; oReply.set_type(REDIS_REPLY_STATUS); - oReply.set_str(pBuff->GetRawReadBuffer(), uiStringLen); + oReply.set_str(pPartBegin, uiStringLen); pBuff->AdvanceReadIndex(i + 1); return(CODEC_STATUS_OK); } @@ -261,6 +248,7 @@ E_CODEC_STATUS CodecResp::DecodeError(CBuffer* pBuff, RedisReply& oReply) char cLastChar = 0; uint32 uiStringLen = 0; const char* pData = pBuff->GetRawReadBuffer(); + const char* pPartBegin = pData; for (size_t i = 0; i < uiReadableBytes; ++i) { switch (pData[i]) @@ -270,7 +258,7 @@ E_CODEC_STATUS CodecResp::DecodeError(CBuffer* pBuff, RedisReply& oReply) { cLastChar = '\n'; oReply.set_type(REDIS_REPLY_ERROR); - oReply.set_str(pBuff->GetRawReadBuffer(), uiStringLen); + oReply.set_str(pPartBegin, uiStringLen); pBuff->AdvanceReadIndex(i + 1); return(CODEC_STATUS_OK); } @@ -294,6 +282,7 @@ E_CODEC_STATUS CodecResp::DecodeInteger(CBuffer* pBuff, RedisReply& oReply) size_t uiReadableBytes = pBuff->ReadableBytes(); char cLastChar = 0; const char* pData = pBuff->GetRawReadBuffer(); + const char* pPartBegin = pData; for (size_t i = 0; i < uiReadableBytes; ++i) { switch (*pData) @@ -303,7 +292,7 @@ E_CODEC_STATUS CodecResp::DecodeInteger(CBuffer* pBuff, RedisReply& oReply) { cLastChar = '\n'; oReply.set_type(REDIS_REPLY_INTEGER); - oReply.set_integer(StringConverter::RapidAtoi(pBuff->GetRawReadBuffer())); + oReply.set_integer(StringConverter::RapidAtoi(pPartBegin)); pBuff->AdvanceReadIndex(i + 1); return(CODEC_STATUS_OK); } @@ -329,6 +318,7 @@ E_CODEC_STATUS CodecResp::DecodeBulkString(CBuffer* pBuff, RedisReply& oReply) int32 iStringLen = 0; bool bGotStringLen = false; const char* pData = pBuff->GetRawReadBuffer(); + const char* pPartBegin = pData; for (size_t i = 0; i <= uiReadableBytes; ++i) { switch (pData[i]) @@ -346,14 +336,15 @@ E_CODEC_STATUS CodecResp::DecodeBulkString(CBuffer* pBuff, RedisReply& oReply) else { oReply.set_type(REDIS_REPLY_STRING); - oReply.set_str(pBuff->GetRawReadBuffer(), iStringLen); + oReply.set_str(pPartBegin, iStringLen); } pBuff->AdvanceReadIndex(i + 1); return(CODEC_STATUS_OK); } else { - iStringLen = StringConverter::RapidAtoi(pBuff->GetRawReadBuffer()); + iStringLen = StringConverter::RapidAtoi(pPartBegin); + pPartBegin = pData + i + 1; bGotStringLen = true; if (uiReadableBytes - i < (uint32)iStringLen) { @@ -386,6 +377,7 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) char cLastChar = 0; int32 iArraySize = 0; const char* pData = pBuff->GetRawReadBuffer(); + const char* pPartBegin = pData; for (size_t i = 0; i < uiReadableBytes; ++i) { switch (pData[i]) @@ -394,12 +386,13 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) if ('\r' == cLastChar) { cLastChar = '\n'; - iArraySize = StringConverter::RapidAtoi(pBuff->GetRawReadBuffer()); + iArraySize = StringConverter::RapidAtoi(pPartBegin); pBuff->AdvanceReadIndex(i + 1); if (iArraySize == -1) { oReply.set_type(REDIS_REPLY_NIL); + return(CODEC_STATUS_OK); } else { diff --git a/src/codec/CodecResp.hpp b/src/codec/CodecResp.hpp index 7d8915a6..0a5e5911 100644 --- a/src/codec/CodecResp.hpp +++ b/src/codec/CodecResp.hpp @@ -33,7 +33,6 @@ class CodecResp: public neb::Codec virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); - virtual E_CODEC_STATUS Encode(std::shared_ptr pRedisStep, CBuffer* pBuff); // TODO move to dispatcher virtual E_CODEC_STATUS Encode(const RedisReply& oReply, CBuffer* pBuff); virtual E_CODEC_STATUS Decode(CBuffer* pBuff, RedisReply& oReply); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index bcb7ced2..5f9bf576 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -391,7 +391,7 @@ bool Manager::CreateEvents() bool bDirectToLoader = false; m_oCurrentConf.Get("new_client_to_loader", bDirectToLoader); m_pSessionManager = std::dynamic_pointer_cast( - m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", bDirectToLoader)); + m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", (bool)bDirectToLoader)); AddPeriodicTaskEvent(); return(true); From 83359c0c08092c11b2930830a97255f6f63d7973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Here=C3=B1=C3=BA?= Date: Wed, 2 Dec 2020 22:21:46 -0300 Subject: [PATCH 109/176] Minor fix (line 11) * plus minor proposals --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 817054bc..c2392afa 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ English | [中文](/README_cn.md)         /_/ |_/\___/_.___/\__,_/_/\__,_/ one-click installation ``` -# Nebula : a powerful framwork for building highly concurrent, distributed, and resilient message-driven applications for C++. +# Nebula : a powerful framework for building highly concurrent, distributed, and resilient message-driven applications for C++. [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)
1. [Overview](#Overview) @@ -46,7 +46,7 @@ Nebula can be used as a single high-performance TCP server, but building a clust ## Getting Start - Nebula was developed with C++11/C++14 standard, requires a compiler capable of the C++11-standard and at least gcc4.8(some C++14 features are replaced by C++11 standard when encountering a lower version of the compiler). + Nebula was developed with C++11/C++14 standard, requires a compiler capable of the C++11-standard and at least gcc4.8 (some C++14 features are replaced by C++11 standard when encountering a lower version of the compiler). We provides [NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap), which allows developers to build and deploy Nebula quickly. Nebula will be a framework that be widely used. A distributed solution based on NebulaBootstrap will make it easy to develop micro-service applications in C++. All dependencies will be automatically resolved in the following build steps. @@ -66,7 +66,7 @@ Nebula can be used as a single high-performance TCP server, but building a clust + plugins       plugins path. - logic       plugins for logic server(optional). + script       script path. deploy.sh, startup.sh and shutdown.sh were depend on this path. - + temp        temp file path(optional). + + temp        temp file path (optional). - configure.sh     run configure.sh for a simple configuration when deploy for the first time. - deploy.sh      auto build and deploy. - shutdown.sh      shutdown one or more server. @@ -159,7 +159,7 @@ A simple testing can be start with a NebulaInterface only, and also can be start - add cpu affinity inorder to support cpu binding. - add dynamic library unload. #### v0.7 - - add configuration management(check [Nebcli](https://github.com/Bwar/Nebcli) for detail). + - add configuration management (check [Nebcli](https://github.com/Bwar/Nebcli) for detail). #### v0.6 - NebulaBeacon adds node status information query, registration center leader-fllower election. - NebulaInterface adds hello demo. From 675a7f6462515ed9c2c99fd34ef8402aaaf037b6 Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 7 Dec 2020 00:24:31 +0800 Subject: [PATCH 110/176] replace hiredis with CodecResp; add RawStep --- src/actor/Actor.cpp | 95 ++++- src/actor/Actor.hpp | 105 +++--- src/actor/ActorBuilder.cpp | 265 +++++++------- src/actor/ActorBuilder.hpp | 14 +- src/actor/step/HttpStep.cpp | 4 +- src/actor/step/PbStep.hpp | 3 - src/actor/step/RawStep.cpp | 24 ++ src/actor/step/RawStep.hpp | 46 +++ src/actor/step/RedisStep.cpp | 21 +- src/actor/step/RedisStep.hpp | 28 +- src/channel/SocketChannel.cpp | 18 +- src/channel/SocketChannel.hpp | 18 +- src/channel/SocketChannelImpl.cpp | 448 ++++++++++++++++++++++-- src/channel/SocketChannelImpl.hpp | 48 ++- src/ios/Dispatcher.cpp | 556 ------------------------------ src/ios/Dispatcher.hpp | 228 +++++++++++- src/labor/Worker.hpp | 1 - 17 files changed, 1086 insertions(+), 836 deletions(-) create mode 100644 src/actor/step/RawStep.cpp create mode 100644 src/actor/step/RawStep.hpp diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 390046d6..985a29af 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -149,64 +149,132 @@ bool Actor::SendTo(std::shared_ptr pChannel) bool Actor::SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { + (const_cast(oMsgBody)).set_trace_id(GetTraceId()); return(m_pLabor->GetDispatcher()->SendTo(pChannel, iCmd, uiSeq, oMsgBody, this)); } bool Actor::SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) { - return(m_pLabor->GetDispatcher()->SendTo(pChannel, oHttpMsg)); + (const_cast(oHttpMsg)).mutable_headers()->insert({"x-trace-id", GetTraceId()}); + return(m_pLabor->GetDispatcher()->SendTo(pChannel, oHttpMsg, 0)); +} + +bool Actor::SendTo(std::shared_ptr pChannel, const RedisReply& oRedisReply) +{ + return(m_pLabor->GetDispatcher()->SendTo(pChannel, oRedisReply, 0)); +} + +bool Actor::SendTo(std::shared_ptr pChannel, const char* pRawData, uint32 uiRawDataSize) +{ + return(m_pLabor->GetDispatcher()->SendTo(pChannel, pRawData, uiRawDataSize, 0)); } bool Actor::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { + if (Actor::ACT_PB_STEP != GetActorType()) + { + LOG4_ERROR("the actor whick send MsgBody request must be a PbStep."); + return(false); + } + (const_cast(oMsgBody)).set_trace_id(GetTraceId()); return(m_pLabor->GetDispatcher()->SendTo(strIdentify, iCmd, uiSeq, oMsgBody, eCodecType, this)); } -bool Actor::SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg) +bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg) { - return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, strUrlPath, oHttpMsg, this->GetSequence())); + if (Actor::ACT_HTTP_STEP != GetActorType()) + { + LOG4_ERROR("the actor whick send HttpMsg request must be a HttpStep."); + return(false); + } + bool bWithSsl = false; + bool bPipeline = false; + if (oHttpMsg.headers().find("x-trace-id") == oHttpMsg.headers().end()) + { + (const_cast(oHttpMsg)).mutable_headers()->insert({"x-trace-id", GetTraceId()}); + } + if (oHttpMsg.http_major() >= 2) + { + bPipeline = true; + } + std::string strSchema = oHttpMsg.url().substr(0, HttpMsg.url().find_first_of(":")); + std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c)->unsigned char {return std::tolower(c);}); + if (strSchema == std::string("https")) + { + bWithSsl = true; + } + return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, GetSequence())); } -bool Actor::SendTo(std::shared_ptr pChannel) +bool Actor::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(m_pLabor->GetDispatcher()->SendTo(pChannel, this)); + if (Actor::ACT_REDIS_STEP != GetActorType()) + { + LOG4_ERROR("the actor which send RedisMsg must be a RedisStep."); + return(false); + } + return(m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); } -bool Actor::SendTo(const std::string& strIdentify, bool bPipeline) +bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(m_pLabor->GetDispatcher()->SendTo(strIdentify, this, bPipeline)); + if (Actor::ACT_REDIS_STEP != GetActorType()) + { + LOG4_ERROR("the actor which send RedisMsg must be a RedisStep."); + return(false); + } + return(m_pLabor->GetActorBuilder()->SendToCluster(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); } -bool Actor::SendTo(const std::string& strHost, int iPort, bool bPipeline) +bool Actor::SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline) { - return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, this, bPipeline)); + if (Actor::ACT_RAW_STEP != GetActorType()) + { + LOG4_ERROR("the actor which send raw data must be a RawStep."); + return(false); + } + return(m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_UNKNOW, bWithSslt, bPipeline, pRawData, uiRawDataSize, GetSequence())); } bool Actor::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { + (const_cast(oMsgBody)).set_trace_id(GetTraceId()); return(m_pLabor->GetDispatcher()->SendTo(iCmd, uiSeq, oMsgBody, this)); } bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { + (const_cast(oMsgBody)).set_trace_id(GetTraceId()); return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType, this)); } bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { + (const_cast(oMsgBody)).set_trace_id(GetTraceId()); return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, uiFactor, iCmd, uiSeq, oMsgBody, eCodecType, this)); } bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { + (const_cast(oMsgBody)).set_trace_id(GetTraceId()); return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType, this)); } bool Actor::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { + (const_cast(oMsgBody)).set_trace_id(GetTraceId()); return(m_pLabor->GetDispatcher()->SendDataReport(iCmd, uiSeq, oMsgBody, this)); } +bool Actor::CloseRawChannel(std::shared_ptr pChannel) +{ + if (CODEC_UNKNOW == pChannel->GetCodecType()) + { + m_pLabor->GetDispatcher()->Disconnect(pChannel, false); + } + return(false); +} + int32 Actor::GetStepNum() const { return(m_pLabor->GetActorBuilder()->GetStepNum()); @@ -217,6 +285,15 @@ void Actor::SetLabor(Labor* pLabor) m_pLabor = pLabor; } +uint32 Actor::ForceNewSequence() +{ + if (nullptr != m_pLabor) + { + m_uiSequence = m_pLabor->GetSequence(); + } + return(m_uiSequence); +} + ev_timer* Actor::MutableTimerWatcher() { if (NULL == m_pTimerWatcher) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 1e9c5173..d1ac2b05 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -26,6 +26,7 @@ #include "Definition.hpp" #include "pb/msg.pb.h" #include "pb/http.pb.h" +#include "pb/redis.pb.h" #include "util/json/CJsonObject.hpp" #include "channel/Channel.hpp" #include "labor/Labor.hpp" @@ -35,13 +36,14 @@ namespace neb { +typedef RedisReply RedisMsg; + class Labor; class Dispatcher; class ActorBuilder; class ActorSys; class SocketChannel; -class RedisChannel; class Actor; class Cmd; class Module; @@ -66,8 +68,9 @@ class Actor: public std::enable_shared_from_this ACT_PB_STEP = 6, ///< Step步骤对象,处理pb请求或响应 ACT_HTTP_STEP = 7, ///< Step步骤对象,处理http请求或响应 ACT_REDIS_STEP = 8, ///< Step步骤对象,处理redis请求或响应 - ACT_MODEL = 9, ///< Model模型对象,Model(IO无关)与Step(异步IO相关)共同构成功能链 - ACT_CHAIN = 10, ///< Chain链对象,用于将Model和Step组合成功能链 + ACT_RAW_STEP = 9, ///< Step步骤对象,处理未经编解码的裸数据 + ACT_MODEL = 10, ///< Model模型对象,Model(IO无关)与Step(异步IO相关)共同构成功能链 + ACT_CHAIN = 11, ///< Chain链对象,用于将Model和Step组合成功能链 }; public: @@ -134,30 +137,46 @@ class Actor: public std::enable_shared_from_this * @param stCtx 消息通道上下文 * @return 是否发送成功 */ - bool SendTo(std::shared_ptr pChannel); + virtual bool SendTo(std::shared_ptr pChannel); /** - * @brief 发送数据 - * @note 使用指定连接将数据发送。如果能直接得知stCtx(比如刚从该连接接收到数据,欲回确认包),就 - * 应调用此函数发送。此函数是SendTo()函数中最高效的一个。 - * @param stCtx 消息通道上下文 + * @brief 发送PB响应数据 + * @note 使用指定连接将数据发送 + * @param pChannel 消息通道 * @param iCmd 发送的命令字 * @param uiSeq 发送的数据包seq * @param oMsgBody 数据包体 * @return 是否发送成功 */ - bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + virtual bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); /** - * @brief 发送数据 - * @param stCtx 消息通道上下文 + * @brief 发送HTTP响应 + * @param pChannel 消息通道 * @param oHttpMsg http消息 * @return 是否发送成功 */ - bool SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + virtual bool SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + + /** + * @brief 发送redis响应 + * @param pChannel 消息通道 + * @param oRedisReply redis消息 + * @return 是否发送成功 + */ + virtual bool SendTo(std::shared_ptr pChannel, const RedisReply& oRedisReply); /** - * @brief 发送数据 + * @brief 发送raw响应 + * @param pChannel 消息通道 + * @param pRawData raw消息 + * @param uiRawDataSize raw消息长度 + * @return 是否发送成功 + */ + virtual bool SendTo(std::shared_ptr pChannel, const char* pRawData, uint32 uiRawDataSize); + + /** + * @brief 发送请求 * @note 指定连接标识符将数据发送。此函数先查找与strIdentify匹配的Channel,如果找到就调用 * SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) * 发送,如果未找到则调用SendToWithAutoConnect(const std::string& strIdentify, @@ -169,44 +188,40 @@ class Actor: public std::enable_shared_from_this * @param oCodecType 编解码方式 * @return 是否发送成功 */ - bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); /** - * @brief 发送数据 + * @brief 发送http请求 * @param strHost IP地址 * @param iPort 端口 - * @param strUrlPath 路径 * @param oHttpMsg http消息 * @return 是否发送成功 */ - bool SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg); + virtual bool SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg); /** - * @brief 发送到redis channel - * @note 只有RedisStep及其派生类才能调用此方法,调用此方法时将调用者自身(RedisStep)添加到 - * RedisChannel的pipeline中。 - * @param pChannel RedisChannel指针 - */ - bool SendTo(std::shared_ptr pChannel); - - /** - * @brief 发送到redis channel - * @note 只有RedisStep及其派生类才能调用此方法,调用此方法时将调用者自身(RedisStep)添加到 - * RedisChannel的pipeline中。 - * @param strIdentify RedisChannel通道标识 + * @brief 发送redis请求 + * @note 只有RedisStep及其派生类才能调用此方法 + * @param strIdentify RedisChannel通道标识,格式如: 192.168.125.53:6379 + * @param oRedisMsg redis消息 + * @param bWithSsl 是否需要SSL * @param bPipeline 是否支持pipeline + * @param uiStepSeq 应用层无用参数,框架层的系统Actor会用到 + * @return 是否发送成功 */ - bool SendTo(const std::string& strIdentify, bool bPipeline = true); + bool SendTo(const std::string& strIdentify, const oRedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); + bool SendToCluster(const std::string& strIdentify, const oRedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); /** - * @brief 发送到redis channel - * @note 只有RedisStep及其派生类才能调用此方法,调用此方法时将调用者自身(RedisStep)添加到 - * RedisChannel的pipeline中。 - * @param strHost RedisChannel地址 - * @param iPort RedisChannel端口 + * @brief 发送raw请求 + * @note 只有RawStep及其派生类才能调用此方法。 + * @param strIdentify Channel通道标识 + * @param pRawData裸数据 + * @param bWithSsl 是否需要SSL * @param bPipeline 是否支持pipeline + * @return 是否发送成功 */ - bool SendTo(const std::string& strHost, int iPort, bool bPipeline = true); + virtual bool SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl = false, bool bPipeline = false); /** * @brief 从worker发送到loader或从loader发送到worker @@ -215,7 +230,7 @@ class Actor: public std::enable_shared_from_this * @param oMsgBody 数据包体 * @return 是否发送成功 */ - bool SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + virtual bool SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); /** * @brief 发送到下一个同一类型的节点 @@ -227,7 +242,7 @@ class Actor: public std::enable_shared_from_this * @param oCodecType 编解码方式 * @return 是否发送成功 */ - bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + virtual bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); /** * @brief 以取模方式选择发送到同一类型节点 @@ -240,11 +255,18 @@ class Actor: public std::enable_shared_from_this * @param oCodecType 编解码方式 * @return 是否发送成功 */ - bool SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + virtual bool SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + + virtual bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); - bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + virtual bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + /** + * @brief 关闭raw数据通道 + * @note 当且仅当raw数据传输的无编解码通道才可以由Actor的应用层关闭,对于http连接 + * 等只应由框架层管理的通道,调用此函数不能关闭连接,也不应该尝试自己去关闭。 + */ + virtual bool CloseRawChannel(std::shared_ptr pChannel); int32 GetStepNum() const; @@ -266,6 +288,7 @@ class Actor: public std::enable_shared_from_this private: void SetLabor(Labor* pLabor); + uint32 ForceNewSequence(); ev_timer* MutableTimerWatcher(); void SetActorName(const std::string& strActorName); void SetTraceId(const std::string& strTraceId); diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 9b3f8467..64c355cf 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -19,7 +19,9 @@ #include "step/HttpStep.hpp" #include "step/PbStep.hpp" #include "step/RedisStep.hpp" -#include "step/Step.hpp" +#include "step/RawStep.hpp" +#include "cmd/CmdRedis.hpp" +#include "cmd/CmdRaw.hpp" #include "model/Model.hpp" #include "chain/Chain.hpp" #include "actor/session/sys_session/SessionLogger.hpp" @@ -127,7 +129,7 @@ bool ActorBuilder::OnStepTimeout(std::shared_ptr pStep) bool ActorBuilder::OnSessionTimeout(std::shared_ptr pSession) { ev_timer* watcher = pSession->MutableTimerWatcher(); - LOG4_TRACE("CHECK watchar = 0x%x", watcher); + //LOG4_TRACE("CHECK watchar = 0x%x", watcher); ev_tstamp after = pSession->GetActiveTime() - m_pLabor->GetNowTime() + pSession->GetTimeout(); if (after > 0) // 定时时间内被重新刷新过,重新设置定时器 { @@ -136,7 +138,7 @@ bool ActorBuilder::OnSessionTimeout(std::shared_ptr pSession) } else // 会话已超时 { - LOG4_TRACE("session_id: %s", pSession->GetSessionId().c_str()); + //LOG4_TRACE("session_id: %s", pSession->GetSessionId().c_str()); if (CMD_STATUS_RUNNING == pSession->Timeout()) { m_pLabor->GetDispatcher()->RefreshEvent(watcher, pSession->GetTimeout()); @@ -298,6 +300,7 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH } else // 回调 { + pChannel->m_pImpl->PopStepSeq(); auto step_iter = m_mapCallbackStep.find(oMsgHead.seq()); if (step_iter != m_mapCallbackStep.end()) // 步骤回调 { @@ -404,12 +407,14 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http } else { - auto http_step_iter = m_mapCallbackStep.find(pChannel->GetStepSeq()); + auto http_step_iter = m_mapCallbackStep.find(pChannel->m_pImpl->PopStepSeq()); + if (!pChannel->IsPipeline() && pChannel->m_pImpl->GetPipelineStepSeq().empty()) + { + m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); + } if (http_step_iter == m_mapCallbackStep.end()) { LOG4_TRACE("no callback for http response from %s!", oHttpMsg.url().c_str()); - m_pLabor->GetDispatcher()->SetChannelStatus(pChannel, CHANNEL_STATUS_ESTABLISHED, 0); - m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. } else { @@ -420,8 +425,6 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http { uint32 uiChainId = http_step_iter->second->GetChainId(); RemoveStep(http_step_iter->second); - m_pLabor->GetDispatcher()->SetChannelStatus(pChannel, CHANNEL_STATUS_ESTABLISHED, 0); - m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); @@ -441,20 +444,38 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http return(true); } -bool ActorBuilder::OnError(std::shared_ptr pChannel, uint32 uiStepSeq, int iErrno, const std::string& strErrMsg) +bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const RedisMsg& oRedisMsg, uint32 uiFinalStepSeq) { - auto step_iter = m_mapCallbackStep.find(uiStepSeq); - if (step_iter != m_mapCallbackStep.end()) + if (pChannel->IsClient()) { - if (step_iter->second != nullptr) + std::unordered_map>::iterator step_iter; + if (uiFinalStepSeq == 0) // callback from SocketChannel by redis msg + { + step_iter = m_mapCallbackStep.find(pChannel->m_pImpl->PopStepSeq()); + } + else // callback from StepRedisCluster + { + step_iter = m_mapCallbackStep.find(uiFinalStepSeq); + } + if (!pChannel->IsPipeline() && pChannel->m_pImpl->GetPipelineStepSeq().empty()) + { + m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. + } + if (step_iter == m_mapCallbackStep.end()) + { + LOG4_TRACE("no callback for redis reply from %s!", pChannel->GetIdentify().c_str()); + return(false); + } + else { E_CMD_STATUS eResult; - step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = step_iter->second->ErrBack(pChannel, iErrno, strErrMsg); + http_step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + LOG4_TRACE("callback of redis cmd: %s", (std::dynamic_pointer_cast(step_iter->second))->CmdToString().c_str()); + eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oRedisMsg); if (CMD_STATUS_RUNNING != eResult) { - uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); + uint32 uiChainId = http_step_iter->second->GetChainId(); + RemoveStep(http_step_iter->second); if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); @@ -469,146 +490,100 @@ bool ActorBuilder::OnError(std::shared_ptr pChannel, uint32 uiSte } } } + return(eResult); } - ExecAssemblyLine(pChannel, iErrno, strErrMsg); - return(true); } else { - snprintf(m_pErrBuff, gc_iErrBuffLen, "no callback or the callback for seq %u had been timeout!", uiStepSeq); - LOG4_WARNING(m_pErrBuff); + auto cmd_iter = m_mapCmd.find(CMD_REQ_REDIS_PROXY); + if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) + { + auto pCmdRedis = std::dynamic_pointer_cast(cmd_iter->second); + if (pCmdRedis == nullptr) + { + LOG4_ERROR("cmd %d is not a CmdRedis instance!", CMD_REQ_REDIS_PROXY); + return(false); + } + return(pCmdRedis->AnyMessage(pChannel, oRedisMsg)); + } + LOG4_ERROR("no instance of CmdRedis or derived class of CmdRedis found for cmd %d", CMD_REQ_REDIS_PROXY); return(false); } } -bool ActorBuilder::OnRedisConnected(std::shared_ptr pChannel, const redisAsyncContext *c, int status) +bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const CBuffer& oBuffer) { - if (status == REDIS_OK) + if (pChannel->IsClient()) { - pChannel->bIsReady = true; - int iCmdStatus; - for (auto step_iter = pChannel->listPipelineStep.begin(); - step_iter != pChannel->listPipelineStep.end(); ) + auto step_iter = m_mapCallbackStep.find(pChannel->m_pImpl->PopStepSeq()); + if (!pChannel->IsPipeline() && pChannel->m_pImpl->GetPipelineStepSeq().empty()) { - size_t args_size = (*step_iter)->GetCmdArguments().size() + 1; - const char* argv[args_size]; - size_t arglen[args_size]; - argv[0] = (*step_iter)->GetCmd().c_str(); - arglen[0] = (*step_iter)->GetCmd().size(); - std::vector >::const_iterator c_iter = (*step_iter)->GetCmdArguments().begin(); - for (size_t i = 1; c_iter != (*step_iter)->GetCmdArguments().end(); ++c_iter, ++i) - { - argv[i] = c_iter->first.c_str(); - arglen[i] = c_iter->first.size(); - } - iCmdStatus = redisAsyncCommandArgv((redisAsyncContext*)c, Dispatcher::RedisCmdCallback, nullptr, args_size, argv, arglen); - if (iCmdStatus == REDIS_OK) - { - LOG4_TRACE("succeed in sending redis cmd: %s", (*step_iter)->CmdToString().c_str()); - ++step_iter; - } - else + m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. + } + if (step_iter == m_mapCallbackStep.end()) + { + LOG4_TRACE("no callback for raw data reply from %s!", pChannel->GetIdentify().c_str()); + return(false); + } + else + { + E_CMD_STATUS eResult; + http_step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes()); + if (CMD_STATUS_RUNNING != eResult) { - E_CMD_STATUS eResult; - uint32 uiChainId; - auto interrupt_step_iter = step_iter; - for (; step_iter != pChannel->listPipelineStep.end(); ++step_iter) + uint32 uiChainId = http_step_iter->second->GetChainId(); + RemoveStep(http_step_iter->second); + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) { - eResult = (*step_iter)->Callback(c, status, nullptr); - uiChainId = (*step_iter)->GetChainId(); - RemoveStep(*step_iter); - if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) { - if (0 != uiChainId) + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } + RemoveChain(uiChainId); } } - else - { - RemoveChain(uiChainId); - } - } - for (auto erase_step_iter = interrupt_step_iter; - erase_step_iter != pChannel->listPipelineStep.end();) - { - RemoveChain((*erase_step_iter)->GetChainId()); } - pChannel->listPipelineStep.clear(); - return(false); } + return(eResult); } } else { - for (auto step_iter = pChannel->listPipelineStep.begin(); - step_iter != pChannel->listPipelineStep.end(); ++step_iter) + auto cmd_iter = m_mapCmd.find(CMD_REQ_RAW_DATA); + if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) { - (*step_iter)->Callback(c, status, nullptr); - RemoveChain((*step_iter)->GetChainId()); - RemoveStep(*step_iter); + auto pCmdRaw = std::dynamic_pointer_cast(cmd_iter->second); + if (pCmdRaw == nullptr) + { + LOG4_ERROR("cmd %d is not a CmdRaw instance!", CMD_REQ_RAW_DATA); + return(false); + } + return(pCmdRaw->AnyMessage(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes())); } - pChannel->listPipelineStep.clear(); + LOG4_ERROR("no instance of CmdRaw or derived class of CmdRaw found for cmd %d", CMD_REQ_RAW_DATA); return(false); } - return(true); -} - -void ActorBuilder::OnRedisDisconnected(std::shared_ptr pChannel, const redisAsyncContext *c, int status) -{ - for (auto step_iter = pChannel->listPipelineStep.begin(); - step_iter != pChannel->listPipelineStep.end(); ++step_iter) - { - LOG4_ERROR("RedisDisconnect callback error %d of redis cmd: %s", - c->err, (*step_iter)->CmdToString().c_str()); - (*step_iter)->Callback(c, c->err, nullptr); - RemoveChain((*step_iter)->GetChainId()); - RemoveStep(std::dynamic_pointer_cast(*step_iter)); - } - pChannel->listPipelineStep.clear(); } -bool ActorBuilder::OnRedisCmdResult(std::shared_ptr pChannel, redisAsyncContext *c, void *reply, void *privdata) +bool ActorBuilder::OnError(std::shared_ptr pChannel, uint32 uiStepSeq, int iErrno, const std::string& strErrMsg) { - if (pChannel->listPipelineStep.empty()) - { - LOG4_ERROR("no redis step!"); - return(false); - } - - auto step_iter = pChannel->listPipelineStep.begin(); - if (nullptr == reply) - { - LOG4_ERROR("redis %s error %d: %s", pChannel->GetIdentify().c_str(), c->err, c->errstr); - for ( ; step_iter != pChannel->listPipelineStep.end(); ++step_iter) - { - LOG4_ERROR("callback error %d of redis cmd: %s", c->err, (*step_iter)->CmdToString().c_str()); - (*step_iter)->Callback(c, c->err, (redisReply*)reply); - RemoveChain((*step_iter)->GetChainId()); - RemoveStep(*step_iter); - } - pChannel->listPipelineStep.clear(); - } - else + auto step_iter = m_mapCallbackStep.find(uiStepSeq); + if (step_iter != m_mapCallbackStep.end()) { - if (step_iter != pChannel->listPipelineStep.end()) + if (step_iter->second != nullptr) { - LOG4_TRACE("callback of redis cmd: %s", (*step_iter)->CmdToString().c_str()); - E_CMD_STATUS eResult = (*step_iter)->Callback(c, REDIS_OK, (redisReply*)reply); - pChannel->listPipelineStep.erase(step_iter); - if (CMD_STATUS_RUNNING != eResult && CMD_STATUS_FAULT != eResult) + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = step_iter->second->ErrBack(pChannel, iErrno, strErrMsg); + if (CMD_STATUS_RUNNING != eResult) { - uint32 uiChainId = (*step_iter)->GetChainId(); - if (0 != uiChainId) + uint32 uiChainId = step_iter->second->GetChainId(); + RemoveStep(step_iter->second); + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); if (chain_iter != m_mapChain.end()) @@ -622,23 +597,16 @@ bool ActorBuilder::OnRedisCmdResult(std::shared_ptr pChannel, redi } } } - if (CMD_STATUS_RUNNING != eResult) - { - RemoveStep(std::dynamic_pointer_cast(*step_iter)); - } - //freeReplyObject(reply); - } - else - { - LOG4_ERROR("no redis callback data found!"); } + ExecAssemblyLine(pChannel, iErrno, strErrMsg); + return(true); } - - if (pChannel->listPipelineStep.size() == 0 && !pChannel->IsPipeline()) + else { - m_pLabor->GetDispatcher()->AddNamedRedisChannel(pChannel->GetIdentify(), pChannel); + snprintf(m_pErrBuff, gc_iErrBuffLen, "no callback or the callback for seq %u had been timeout!", uiStepSeq); + LOG4_WARNING(m_pErrBuff); + return(false); } - return(true); } void ActorBuilder::AddAssemblyLine(std::shared_ptr pSession) @@ -878,6 +846,7 @@ std::shared_ptr ActorBuilder::InitializeSharedActor(Actor* pCreator, std: case Actor::ACT_PB_STEP: case Actor::ACT_HTTP_STEP: case Actor::ACT_REDIS_STEP: + case Actor::ACT_RAW_STEP: if (TransformToSharedStep(pCreator, pSharedActor)) { return(pSharedActor); @@ -940,6 +909,10 @@ bool ActorBuilder::TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor->SetTraceId(pCreator->GetTraceId()); } + while (m_mapCallbackStep.find(pSharedActor->GetSequence()) != m_mapCallbackStep.end()) + { + pSharedActor->ForceNewSequence(); + } std::shared_ptr pSharedStep = std::dynamic_pointer_cast(pSharedActor); for (auto iter = pSharedStep->m_setNextStepSeq.begin(); iter != pSharedStep->m_setNextStepSeq.end(); ++iter) { @@ -1120,6 +1093,26 @@ bool ActorBuilder::TransformToSharedChain(Actor* pCreator, std::shared_ptrSendTo(strIdentify, oRedisMsg, bWithSsl, bPipeline, uiStepSeq); + } + } + else + { + bSendResult = iter->second->SendTo(strIdentify, oRedisMsg, bWithSsl, bPipeline, uiStepSeq); + } + return(bSendResult); +} + std::shared_ptr ActorBuilder::GetSession(uint32 uiSessionId) { std::ostringstream oss; diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 2a21ce33..19c5bb53 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -27,24 +27,25 @@ #pragma GCC diagnostic ignored "-Wunused-function" #endif #include "ev.h" -#include "hiredis/hiredis.h" -#include "hiredis/async.h" -#include "hiredis/adapters/libev.h" #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #include "pb/msg.pb.h" #include "pb/http.pb.h" +#include "pb/redis.pb.h" #include "pb/neb_sys.pb.h" #include "Definition.hpp" #include "Error.hpp" +#include "util/CBuffer.hpp" #include "ActorFactory.hpp" #include "logger/NetLogger.hpp" namespace neb { +typedef RedisReply RedisMsg; + class Manager; class Worker; @@ -94,10 +95,9 @@ class ActorBuilder bool OnChainTimeout(std::shared_ptr pChain); bool OnMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); bool OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + bool OnMessage(std::shared_ptr pChannel, const RedisMsg& oRedisMsg, uint32 uiFinalStepSeq = 0); + bool OnMessage(std::shared_ptr pChannel, const CBuffer& oBuffer); bool OnError(std::shared_ptr pChannel, uint32 uiStepSeq, int iErrno, const std::string& strErrMsg); - bool OnRedisConnected(std::shared_ptr pChannel, const redisAsyncContext *c, int status); - void OnRedisDisconnected(std::shared_ptr pChannel, const redisAsyncContext *c, int status); - bool OnRedisCmdResult(std::shared_ptr pChannel, redisAsyncContext *c, void *reply, void *privdata); public: template @@ -139,6 +139,7 @@ class ActorBuilder bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); public: + bool SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const ReidsMsg& oRedisMsg, uint32 uiStepSeq); virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); @@ -189,6 +190,7 @@ class ActorBuilder // Step and Session std::unordered_map > m_mapCallbackStep; + std::unordered_map > m_mapClusterChannelStep; //集群回调,发往集群的请求和响应都会经由ClusterChannelStep截获再收发 std::unordered_map > m_mapCallbackSession; std::unordered_set > m_setAssemblyLine; ///< 资源就绪后执行队列 diff --git a/src/actor/step/HttpStep.cpp b/src/actor/step/HttpStep.cpp index a27a94b0..40db8a16 100644 --- a/src/actor/step/HttpStep.cpp +++ b/src/actor/step/HttpStep.cpp @@ -117,10 +117,12 @@ bool HttpStep::HttpRequest(const HttpMsg& oHttpMsg) strHost = oHttpMsg.url().substr(stUrl.field_data[UF_HOST].off, stUrl.field_data[UF_HOST].len); } + /* if(stUrl.field_set & (1 << UF_PATH)) { strPath = oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off, stUrl.field_data[UF_PATH].len); } + */ if (iPort == 80) { @@ -131,7 +133,7 @@ bool HttpStep::HttpRequest(const HttpMsg& oHttpMsg) iPort = 443; } } - return(SendTo(strHost, iPort, strPath, oHttpMsg)); + return(SendTo(strHost, iPort, oHttpMsg)); } else { diff --git a/src/actor/step/PbStep.hpp b/src/actor/step/PbStep.hpp index 6c88e153..24723c56 100644 --- a/src/actor/step/PbStep.hpp +++ b/src/actor/step/PbStep.hpp @@ -40,9 +40,6 @@ class PbStep: public Step const MsgHead& oMsgHead, const MsgBody& oMsgBody, void* data = NULL) = 0; - -private: - friend class ActorBuilder; }; } /* namespace neb */ diff --git a/src/actor/step/RawStep.cpp b/src/actor/step/RawStep.cpp new file mode 100644 index 00000000..ddb37435 --- /dev/null +++ b/src/actor/step/RawStep.cpp @@ -0,0 +1,24 @@ +/******************************************************************************* + * Project: Nebula + * @file RawStep.cpp + * @brief + * @author Bwar + * @date: 2020年12月6日 + * @note + * Modify history: + ******************************************************************************/ +#include "actor/step/RawStep.hpp" + +namespace neb +{ + +RawStep::RawStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) + : Step(ACT_REDIS_STEP, pNextStep, dTimeout) +{ +} + +RawStep::~RawStep() +{ +} + +} /* namespace neb */ diff --git a/src/actor/step/RawStep.hpp b/src/actor/step/RawStep.hpp new file mode 100644 index 00000000..2748adc2 --- /dev/null +++ b/src/actor/step/RawStep.hpp @@ -0,0 +1,46 @@ +/******************************************************************************* + * Project: Nebula + * @file RawStep.hpp + * @brief + * @author Bwar + * @date: 2020年12月6日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_STEP_RAWSTEP_HPP_ +#define SRC_ACTOR_STEP_RAWSTEP_HPP_ + +#include +#include +#include "actor/step/Step.hpp" +#include "pb/redis.pb.h" + +namespace neb +{ + +class RawStep: public Step +{ +public: + RawStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + RawStep(const RawStep&) = delete; + RawStep& operator=(const RawStep&) = delete; + virtual ~RawStep(); + + /** + * @brief 步骤回调 + * @note 回调的是裸数据,需要由RawStep派生类自行在Callback里进行解码(包括组包和拆包)。 + * 一次接收到uiRawDataSize大小的pRawData可能是一个完整的数据包,也可能只是部分数据包或 + * 多个数据包。用户自定义通信协议可以通过RawStep来实现收发和业务逻辑。 + * @param pChannel 消息来源通信通道 + * @param pRawData 数据指针 + * @param uiRawDataSize 数据长度 + */ + virtual E_CMD_STATUS Callback( + std::shared_ptr pChannel, + const char* pRawData, + uint32 uiRawDataSize) = 0; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_STEP_RAWSTEP_HPP_ */ diff --git a/src/actor/step/RedisStep.cpp b/src/actor/step/RedisStep.cpp index b5de3911..4b627568 100644 --- a/src/actor/step/RedisStep.cpp +++ b/src/actor/step/RedisStep.cpp @@ -12,8 +12,8 @@ namespace neb { -RedisStep::RedisStep(std::shared_ptr pNextStep) - : Step(ACT_REDIS_STEP, pNextStep, gc_dNoTimeout) +RedisStep::RedisStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) + : Step(ACT_REDIS_STEP, pNextStep, dTimeout) { } @@ -30,6 +30,7 @@ void RedisStep::SetCmd(const std::string& strCmd) { m_vecCmdArguments.clear(); m_strCmd = strCmd; + std::transform(m_strCmd.begin(), m_strCmd.end(), m_strCmd.begin(), [](unsigned char c)->unsigned char{return std::toupper(c);}); } void RedisStep::Append(const std::string& strArgument, bool bIsBinaryArg) @@ -55,4 +56,20 @@ std::string RedisStep::CmdToString() const return strCmd; } +const RedisRequest& RedisStep::GenrateRedisRequest() +{ + m_oRedisRequest.Clear(); + m_oRedisRequest.set_type(REDIS_REPLY_ARRAY); + auto pElement = m_oRedisRequest.add_element(); + pElement->set_type(REDIS_REPLY_STRING); + pElement->set_str(m_strCmd); + for (size_t i = 0; i < m_vecCmdArguments.size(); ++i) + { + pElement = m_oRedisRequest.add_element(); + pElement->set_type(REDIS_REPLY_STRING); + pElement->set_str(m_vecCmdArguments[i].first); + } + return(m_oRedisRequest); +} + } /* namespace neb */ diff --git a/src/actor/step/RedisStep.hpp b/src/actor/step/RedisStep.hpp index 0899d65e..10245218 100644 --- a/src/actor/step/RedisStep.hpp +++ b/src/actor/step/RedisStep.hpp @@ -13,40 +13,32 @@ #include #include #include "actor/step/Step.hpp" +#include "pb/redis.pb.h" namespace neb { +typedef RedisReply RedisRequest; + /** * @brief StepRedis在回调后一定会被删除 */ class RedisStep: public Step { public: - RedisStep(std::shared_ptr pNextStep = nullptr); + RedisStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); RedisStep(const RedisStep&) = delete; RedisStep& operator=(const RedisStep&) = delete; virtual ~RedisStep(); /** * @brief redis步骤回调 - * @param c redis连接上下文 - * @param status 回调状态 - * @param pReply 执行结果集 + * @param pChannel 数据来源通道 + * @param oRedisReply redis响应 */ virtual E_CMD_STATUS Callback( - const redisAsyncContext *c, - int status, - redisReply* pReply) = 0; - - /** - * @brief redis超时 - * @note 此函数暂时无用,不会有redis超时回调 - */ - virtual E_CMD_STATUS Timeout() - { - return(CMD_STATUS_FAULT); - } + std::shared_ptr pChannel, + const RedisReply& oRedisReply) = 0; public: /** @@ -88,11 +80,15 @@ class RedisStep: public Step return(m_vecCmdArguments); } +protected: + const RedisRequest& GenrateRedisRequest(); + private: std::string m_strErr; std::string m_strHashKey; std::string m_strCmd; std::vector > m_vecCmdArguments; + RedisRequest m_oRedisRequest; }; } /* namespace neb */ diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index 3d222669..8f037e4d 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -21,7 +21,7 @@ namespace neb { SocketChannel::SocketChannel(std::shared_ptr pLogger, int iFd, uint32 ulSeq, bool bWithSsl, ev_tstamp dKeepAlive) - : m_pImpl(nullptr), m_pLogger(pLogger) + : m_bIsClientConnection(false), m_pImpl(nullptr), m_pLogger(pLogger) { if (bWithSsl) { @@ -45,11 +45,22 @@ SocketChannel::~SocketChannel() m_pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ""); } +bool SocketChannel::Init(E_CODEC_TYPE eCodecType, bool bIsClient) +{ + m_bIsClientConnection = bIsClient; + return(m_pImpl->Init(eCodecType, bIsClient)); +} + int SocketChannel::GetFd() const { return(m_pImpl->GetFd()); } +bool SocketChannel::IsPipeline() const +{ + return(m_pImpl->IsPipeline()); +} + const std::string& SocketChannel::GetIdentify() const { return(m_pImpl->GetIdentify()); @@ -70,11 +81,6 @@ E_CODEC_TYPE SocketChannel::GetCodecType() const return(m_pImpl->GetCodecType()); } -uint32 SocketChannel::GetStepSeq() const -{ - return(m_pImpl->GetStepSeq()); -} - int SocketChannel::SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, std::shared_ptr pLogger) { ssize_t n; diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index 37739e4e..c5488538 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -36,25 +36,29 @@ class SocketChannel: public Channel, public std::enable_shared_from_this pLogger); static int RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, int& iCodecType, std::shared_ptr pLogger); + virtual bool Init(E_CODEC_TYPE eCodecType, bool bIsClient = false); + int GetFd() const; + bool IsClient() const + { + return(m_bIsClientConnection); + } + bool IsPipeline() const; const std::string& GetIdentify() const; const std::string& GetRemoteAddr() const; const std::string& GetClientData() const; E_CODEC_TYPE GetCodecType() const; - uint32 GetStepSeq() const; - - // 设置对称加密密钥 - void SetSecretKey(const std::string& strKey) - { - m_pImpl->SetSecretKey(strKey); - } private: + bool m_bIsClientConnection; + // Hide most of the channel implementation for Actors + // 以m_pImpl对Actor及其派生类隐藏框架层才需要的Channel大部分实现 //std::unique_ptr m_pImpl; std::shared_ptr m_pImpl; std::shared_ptr m_pLogger; friend class Dispatcher; + friend class ActorBuilder; }; } /* namespace neb */ diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 83ee292b..bcf758ca 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -11,6 +11,7 @@ #include "codec/CodecProto.hpp" #include "codec/CodecPrivate.hpp" #include "codec/CodecHttp.hpp" +#include "codec/CodecResp.hpp" #include "labor/Labor.hpp" #include "labor/Manager.hpp" #include "logger/NetLogger.hpp" @@ -21,7 +22,7 @@ namespace neb SocketChannelImpl::SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) : m_ucChannelStatus(CHANNEL_STATUS_INIT), - m_unRemoteWorkerIdx(0), m_iFd(iFd), m_uiSeq(ulSeq), m_uiForeignSeq(0), m_uiStepSeq(0), + m_unRemoteWorkerIdx(0), m_iFd(iFd), m_uiSeq(ulSeq), m_uiForeignSeq(0), m_bPipeline(true), m_uiUnitTimeMsgNum(0), m_uiMsgNum(0), m_dActiveTime(0.0), m_dKeepAlive(dKeepAlive), m_pIoWatcher(NULL), m_pTimerWatcher(NULL), @@ -34,7 +35,7 @@ SocketChannelImpl::SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ SocketChannelImpl::~SocketChannelImpl() { LOG4_DEBUG("SocketChannelImpl::~SocketChannelImpl() fd %d, seq %u", m_iFd, m_uiSeq); - m_vecStepWaitForConnected.clear(); + m_listPipelineStepSeq.clear(); if (CHANNEL_STATUS_CLOSED != m_ucChannelStatus) { Close(); @@ -52,9 +53,22 @@ bool SocketChannelImpl::Init(E_CODEC_TYPE eCodecType, bool bIsClient) LOG4_TRACE("fd[%d], codec_type[%d]", m_iFd, eCodecType); try { - m_pRecvBuff = new CBuffer(); - m_pSendBuff = new CBuffer(); - m_pWaitForSendBuff = new CBuffer(); + if (m_pRecvBuff == nullptr) + { + m_pRecvBuff = new CBuffer(); + } + if (m_pSendBuff == nullptr) + { + m_pSendBuff = new CBuffer(); + } + if (m_pWaitForSendBuff == nullptr) + { + m_pWaitForSendBuff = new CBuffer(); + } + if (m_pCodec != nullptr) + { + DELETE(m_pCodec); + } switch (eCodecType) { case CODEC_NEBULA: @@ -71,6 +85,12 @@ bool SocketChannelImpl::Init(E_CODEC_TYPE eCodecType, bool bIsClient) m_pCodec = new CodecHttp(m_pLogger, eCodecType); m_pCodec->SetKey(m_strKey); break; + case CODEC_RESP: + m_pCodec = new CodecResp(m_pLogger, eCodecType); + m_pCodec->SetKey(m_strKey); + break; + case CODEC_UNKNOW: + break; default: LOG4_ERROR("no codec defined for code type %d", eCodecType); break; @@ -90,6 +110,17 @@ E_CODEC_TYPE SocketChannelImpl::GetCodecType() const return(m_pCodec->GetCodecType()); } +uint32 SocketChannelImpl::PopStepSeq() +{ + if (m_listPipelineStepSeq.empty()) + { + return(0); + } + uint32 uiStepSeq = m_listPipelineStepSeq.front(); + m_listPipelineStepSeq.pop_front(); + return(uiStepSeq); +} + ev_tstamp SocketChannelImpl::GetKeepAlive() { if (CODEC_HTTP == m_pCodec->GetCodecType()) @@ -123,8 +154,13 @@ E_CODEC_STATUS SocketChannelImpl::Send() { return(CODEC_STATUS_PAUSE); } + if (m_pCodec == nullptr) + { + LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); + return(CODEC_STATUS_ERR); + } int iNeedWriteLen = 0; - int iWriteLen = 0; + int iWrittenLen = 0; iNeedWriteLen = m_pSendBuff->ReadableBytes(); if (0 == iNeedWriteLen) { @@ -144,9 +180,9 @@ E_CODEC_STATUS SocketChannelImpl::Send() } m_dActiveTime = m_pLabor->GetNowTime(); - iWriteLen = Write(m_pSendBuff, m_iErrno); - LOG4_TRACE("iNeedWriteLen = %d, iWriteLen = %d", iNeedWriteLen, iWriteLen); - if (iWriteLen >= 0) + iWrittenLen = Write(m_pSendBuff, m_iErrno); + LOG4_TRACE("iNeedWriteLen = %d, iWrittenLen = %d", iNeedWriteLen, iWrittenLen); + if (iWrittenLen >= 0) { m_vecStepWaitForConnected.clear(); if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ @@ -155,7 +191,7 @@ E_CODEC_STATUS SocketChannelImpl::Send() m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); } m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iWriteLen && 0 == m_pWaitForSendBuff->ReadableBytes()) + if (iNeedWriteLen == iWrittenLen && 0 == m_pWaitForSendBuff->ReadableBytes()) { return(CODEC_STATUS_OK); } @@ -181,10 +217,10 @@ E_CODEC_STATUS SocketChannelImpl::Send() E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { LOG4_TRACE("channel_fd[%d], channel_seq[%d], cmd[%u], seq[%u]", m_iFd, m_uiSeq, iCmd, uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + if (m_pCodec == nullptr) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); - return(CODEC_STATUS_EOF); + LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); + return(CODEC_STATUS_ERR); } E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; int32 iMsgBodyLen = oMsgBody.ByteSize(); @@ -198,6 +234,9 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& case CHANNEL_STATUS_ESTABLISHED: eCodecStatus = m_pCodec->Encode(oMsgHead, oMsgBody, m_pSendBuff); break; + case CHANNEL_STATUS_CLOSED: + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + return(CODEC_STATUS_EOF); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: case CHANNEL_STATUS_TRANSFER_TO_WORKER: @@ -226,10 +265,10 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& break; default: eCodecStatus = m_pCodec->Encode(oMsgHead, oMsgBody, m_pWaitForSendBuff); - if (CODEC_STATUS_OK == eCodecStatus) + if (CODEC_STATUS_OK == eCodecStatus && (gc_uiCmdReq & iCmd)) { eCodecStatus = CODEC_STATUS_PAUSE; - m_vecStepWaitForConnected.push_back(uiSeq); + m_listPipelineStepSeq.push_back(uiSeq); } break; } @@ -252,9 +291,9 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& } errno = 0; - int iWriteLen = Write(m_pSendBuff, m_iErrno); - LOG4_TRACE("iNeedWriteLen = %d, iWriteLen = %d", iNeedWriteLen, iWriteLen); - if (iWriteLen >= 0) + int iWrittenLen = Write(m_pSendBuff, m_iErrno); + LOG4_TRACE("iNeedWriteLen = %d, iWrittenLen = %d", iNeedWriteLen, iWrittenLen); + if (iWrittenLen >= 0) { if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) @@ -262,7 +301,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); } m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iWriteLen) + if (iNeedWriteLen == iWrittenLen) { if (CMD_RSP_TELL_WORKER == iCmd) { @@ -295,10 +334,10 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq) { LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + if (m_pCodec == nullptr) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); - return(CODEC_STATUS_EOF); + LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); + return(CODEC_STATUS_ERR); } E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; switch (m_ucChannelStatus) @@ -306,6 +345,9 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq case CHANNEL_STATUS_ESTABLISHED: eCodecStatus = ((CodecHttp*)m_pCodec)->Encode(oHttpMsg, m_pSendBuff); break; + case CHANNEL_STATUS_CLOSED: + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + return(CODEC_STATUS_EOF); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: case CHANNEL_STATUS_TRANSFER_TO_WORKER: @@ -313,10 +355,94 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq case CHANNEL_STATUS_TRY_CONNECT: case CHANNEL_STATUS_INIT: eCodecStatus = ((CodecHttp*)m_pCodec)->Encode(oHttpMsg, m_pWaitForSendBuff); - if (CODEC_STATUS_OK == eCodecStatus) + if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) + { + eCodecStatus = CODEC_STATUS_PAUSE; + m_listPipelineStepSeq.push_back(uiStepSeq); + } + break; + default: + LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), m_ucChannelStatus); + return(CODEC_STATUS_OK); + } + + if (CODEC_STATUS_OK != eCodecStatus) + { + return(eCodecStatus); + } + + int iNeedWriteLen = m_pSendBuff->ReadableBytes(); + if (iNeedWriteLen <= 0) + { + return(eCodecStatus); + } + + int iWrittenLen = Write(m_pSendBuff, m_iErrno); + LOG4_TRACE("fd[%d], channel_seq[%u] iWrittenLen = %d, m_iErrno = %d", + GetFd(), GetSequence(), iWrittenLen, m_iErrno); + if (iWrittenLen >= 0) + { + if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ + && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) + { + m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); + } + if (uiStepSeq > 0) + { + m_listPipelineStepSeq.push_back(uiStepSeq); + } + m_dActiveTime = m_pLabor->GetNowTime(); + if (iNeedWriteLen == iWrittenLen) + { + return(CODEC_STATUS_OK); + } + else + { + return(CODEC_STATUS_PAUSE); + } + } + else + { + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + { + m_dActiveTime = m_pLabor->GetNowTime(); + return(CODEC_STATUS_PAUSE); + } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + return(CODEC_STATUS_INT); + } +} + +E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepSeq) +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); + if (m_pCodec == nullptr) + { + LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); + return(CODEC_STATUS_ERR); + } + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + switch (m_ucChannelStatus) + { + case CHANNEL_STATUS_ESTABLISHED: + eCodecStatus = ((CodecResp*)m_pCodec)->Encode(oRedisMsg, m_pSendBuff); + break; + case CHANNEL_STATUS_CLOSED: + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + return(CODEC_STATUS_EOF); + case CHANNEL_STATUS_TELL_WORKER: + case CHANNEL_STATUS_WORKER: + case CHANNEL_STATUS_TRANSFER_TO_WORKER: + case CHANNEL_STATUS_CONNECTED: + case CHANNEL_STATUS_TRY_CONNECT: + case CHANNEL_STATUS_INIT: + eCodecStatus = ((CodecResp*)m_pCodec)->Encode(oRedisMsg, m_pWaitForSendBuff); + if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) { eCodecStatus = CODEC_STATUS_PAUSE; - m_vecStepWaitForConnected.push_back(uiStepSeq); + m_listPipelineStepSeq.push_back(uiStepSeq); } break; default: @@ -335,19 +461,97 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq return(eCodecStatus); } - int iWriteLen = Write(m_pSendBuff, m_iErrno); - LOG4_TRACE("fd[%d], channel_seq[%u] iWriteLen = %d, m_iErrno = %d", - GetFd(), GetSequence(), iWriteLen, m_iErrno); - if (iWriteLen >= 0) + int iWrittenLen = Write(m_pSendBuff, m_iErrno); + LOG4_TRACE("fd[%d], channel_seq[%u] iWrittenLen = %d, m_iErrno = %d", + GetFd(), GetSequence(), iWrittenLen, m_iErrno); + if (iWrittenLen >= 0) + { + if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ + && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) + { + m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); + } + if (uiStepSeq > 0) + { + m_listPipelineStepSeq.push_back(uiStepSeq); + } + m_dActiveTime = m_pLabor->GetNowTime(); + if (iNeedWriteLen == iWrittenLen) + { + return(CODEC_STATUS_OK); + } + else + { + return(CODEC_STATUS_PAUSE); + } + } + else + { + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + { + m_dActiveTime = m_pLabor->GetNowTime(); + return(CODEC_STATUS_PAUSE); + } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + return(CODEC_STATUS_INT); + } +} + +E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint32 uiStepSeq) +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + switch (m_ucChannelStatus) + { + case CHANNEL_STATUS_ESTABLISHED: + m_pSendBuff->Write(pRaw, uiRawSize); + break; + case CHANNEL_STATUS_CLOSED: + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + return(CODEC_STATUS_EOF); + case CHANNEL_STATUS_TELL_WORKER: + case CHANNEL_STATUS_WORKER: + case CHANNEL_STATUS_TRANSFER_TO_WORKER: + case CHANNEL_STATUS_CONNECTED: + case CHANNEL_STATUS_TRY_CONNECT: + case CHANNEL_STATUS_INIT: + m_pWaitForSendBuff->Write(pRaw, uiRawSize); + eCodecStatus = CODEC_STATUS_PAUSE; + break; + default: + LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), m_ucChannelStatus); + return(CODEC_STATUS_OK); + } + + if (CODEC_STATUS_OK != eCodecStatus) + { + return(eCodecStatus); + } + + int iNeedWriteLen = m_pSendBuff->ReadableBytes(); + if (iNeedWriteLen <= 0) + { + return(eCodecStatus); + } + + int iWrittenLen = Write(m_pSendBuff, m_iErrno); + LOG4_TRACE("fd[%d], channel_seq[%u] iWrittenLen = %d, m_iErrno = %d", + GetFd(), GetSequence(), iWrittenLen, m_iErrno); + if (iWrittenLen >= 0) { if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) { m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); } - m_uiStepSeq = uiStepSeq; + if (uiStepSeq > 0) + { + m_listPipelineStepSeq.push_back(uiStepSeq); + } m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iWriteLen) + if (iNeedWriteLen == iWrittenLen) { return(CODEC_STATUS_OK); } @@ -373,10 +577,10 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) { LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + if (m_pCodec == nullptr) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); - return(CODEC_STATUS_EOF); + LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid.") + return(CODEC_STATUS_ERR); } int iReadLen = 0; iReadLen = Read(m_pRecvBuff, m_iErrno); @@ -407,6 +611,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) } } break; + case CHANNEL_STATUS_CLOSED: + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + return(CODEC_STATUS_EOF); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: case CHANNEL_STATUS_TRANSFER_TO_WORKER: @@ -478,6 +685,11 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); return(CODEC_STATUS_EOF); } + if (m_pCodec == nullptr) + { + LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); + return(CODEC_STATUS_ERR); + } int iReadLen = 0; iReadLen = Read(m_pRecvBuff, m_iErrno); LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", @@ -542,6 +754,132 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) } } +E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + { + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + return(CODEC_STATUS_EOF); + } + if (m_pCodec == nullptr) + { + LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); + return(CODEC_STATUS_ERR); + } + int iReadLen = 0; + iReadLen = Read(m_pRecvBuff, m_iErrno); + LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", + m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); + if (iReadLen > 0) + { + if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ + && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) + { + m_pRecvBuff->Compact(m_pRecvBuff->ReadableBytes() * 2); + } + m_dActiveTime = m_pLabor->GetNowTime(); + E_CODEC_STATUS eCodecStatus = ((CodecResp*)m_pCodec)->Decode(m_pRecvBuff, oRedisReply); + if (CODEC_STATUS_OK == eCodecStatus) + { + ++m_uiUnitTimeMsgNum; + ++m_uiMsgNum; + if (m_uiMsgNum == 1) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + } + } + else + { + if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) + { + return(CODEC_STATUS_INVALID); + } + } + return(eCodecStatus); + } + else if (iReadLen == 0) + { + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_TRACE("fd %d closed by peer, error %d %s!", + m_iFd, m_iErrno, m_strErrMsg.c_str()); + if (m_pRecvBuff->ReadableBytes() > 0) + { + ((CodecResp*)m_pCodec)->Decode(m_pRecvBuff, oRedisReply); + } + return(CODEC_STATUS_EOF); + } + else + { + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + { + m_dActiveTime = m_pLabor->GetNowTime(); + return(CODEC_STATUS_PAUSE); + } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + return(CODEC_STATUS_INT); + } +} + +E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + { + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + return(CODEC_STATUS_EOF); + } + int iReadLen = 0; + iReadLen = Read(m_pRecvBuff, m_iErrno); + LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", + m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); + if (iReadLen > 0) + { + if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ + && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) + { + m_pRecvBuff->Compact(m_pRecvBuff->ReadableBytes() * 2); + } + m_dActiveTime = m_pLabor->GetNowTime(); + if (oRawBuff.Write(m_pRecvBuff, m_pRecvBuff->ReadableBytes()) > 0) + { + ++m_uiUnitTimeMsgNum; + ++m_uiMsgNum; + if (m_uiMsgNum == 1) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + } + return(CODEC_STATUS_OK); + } + return(CODEC_STATUS_PAUSE); + } + else if (iReadLen == 0) + { + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_TRACE("fd %d closed by peer, error %d %s!", + m_iFd, m_iErrno, m_strErrMsg.c_str()); + if (m_pRecvBuff->ReadableBytes() > 0) + { + oRawBuff->Write(m_pRecvBuff, m_pRecvBuff->ReadableBytes()); + } + return(CODEC_STATUS_EOF); + } + else + { + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + { + m_dActiveTime = m_pLabor->GetNowTime(); + return(CODEC_STATUS_PAUSE); + } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + return(CODEC_STATUS_INT); + } +} + E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) { LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); @@ -573,15 +911,59 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) } // 当http1.0响应包未带Content-Length头时,m_pRecvBuff可读字节数为0,以关闭连接表示数据发送完毕。 E_CODEC_STATUS eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); + if (CODEC_STATUS_OK == eCodecStatus) + { + ++m_uiUnitTimeMsgNum; + ++m_uiMsgNum; + } + return(eCodecStatus); +} + +E_CODEC_STATUS SocketChannelImpl::Fetch(RedisReply& oRedisReply) +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + { + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + return(CODEC_STATUS_EOF); + } + E_CODEC_STATUS eCodecStatus = ((CodecResp*)m_pCodec)->Decode(m_pRecvBuff, oRedisReply); + if (CODEC_STATUS_OK == eCodecStatus) + { + ++m_uiUnitTimeMsgNum; + ++m_uiMsgNum; + } return(eCodecStatus); } +E_CODEC_STATUS SocketChannelImpl::Fetch(CBuffer& oRawBuff) +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + { + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + return(CODEC_STATUS_EOF); + } + if (oRawBuff.Write(m_pRecvBuff, m_pRecvBuff->ReadableBytes()) > 0) + { + ++m_uiUnitTimeMsgNum; + ++m_uiMsgNum; + return(CODEC_STATUS_OK); + } + return(CODEC_STATUS_PAUSE); +} + void SocketChannelImpl::SetSecretKey(const std::string& strKey) { m_strKey = strKey; m_pCodec->SetKey(m_strKey); } +void SocketChannelImpl::SetRemoteWorkerIndex(uint16 unRemoteWorkerIndex) +{ + m_unRemoteWorkerIdx = unRemoteWorkerIndex; +} + bool SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive) { LOG4_TRACE("channel_fd[%d], channel_seq[%d], codec_type[%d], new_codec_type[%d]", diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index b87be237..1a56f266 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -26,6 +26,7 @@ #include "util/json/CJsonObject.hpp" #include "pb/http.pb.h" +#include "pb/redis.pb.h" #include "codec/Codec.hpp" #include "Channel.hpp" #include "Definition.hpp" @@ -34,8 +35,9 @@ namespace neb { +typedef RedisReply RedisMsg; + class Labor; -class Dispatcher; class NetLogger; class SocketChannel; @@ -52,10 +54,16 @@ class SocketChannelImpl: public Channel virtual E_CODEC_STATUS Send(); virtual E_CODEC_STATUS Send(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); virtual E_CODEC_STATUS Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq); + virtual E_CODEC_STATUS Send(const RedisMsg& oRedisMsg, uint32 uiStepSeq); + virtual E_CODEC_STATUS Send(const char* pRaw, uint32 uiRawSize, uint32 uiStepSeq); virtual E_CODEC_STATUS Recv(MsgHead& oMsgHead, MsgBody& oMsgBody); virtual E_CODEC_STATUS Recv(HttpMsg& oHttpMsg); + virtual E_CODEC_STATUS Recv(RedisReply& oRedisReply); + virtual E_CODEC_STATUS Recv(CBuffer& oRawBuff); E_CODEC_STATUS Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody); E_CODEC_STATUS Fetch(HttpMsg& oHttpMsg); + E_CODEC_STATUS Fetch(RedisReply& oRedisReply); + E_CODEC_STATUS Fetch(CBuffer& oRawBuff); template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); @@ -70,16 +78,18 @@ class SocketChannelImpl: public Channel return(m_uiSeq); } - uint32 GetStepSeq() const - { - return(m_uiStepSeq); - } + uint32 PopStepSeq(); ev_tstamp GetActiveTime() const { return(m_dActiveTime); } + bool IsPipeline() const + { + return(m_bPipeline); + } + ev_tstamp GetKeepAlive(); uint8 GetChannelStatus() const @@ -97,6 +107,11 @@ class SocketChannelImpl: public Channel return(m_strRemoteAddr); } + uint16 GetRemoteWorkerIndex() const + { + return(m_unRemoteWorkerIdx); + } + const std::string& GetClientData() const { return(m_strClientData); @@ -119,9 +134,14 @@ class SocketChannelImpl: public Channel return(m_uiUnitTimeMsgNum); } - const std::vector& GetStepWaitForConnected() const + const std::list& GetPipelineStepSeq() const + { + return(m_listPipelineStepSeq); + } + + Labor* GetLabor() { - return(m_vecStepWaitForConnected); + return(m_pLabor); } int GetErrno() const @@ -157,10 +177,9 @@ class SocketChannelImpl: public Channel m_ucChannelStatus = (uint8)eStatus; } - void SetChannelStatus(E_CHANNEL_STATUS eStatus, uint32 uiStepSeq) + void SetPipeline(bool bPipeline) { - m_ucChannelStatus = (uint8)eStatus; - m_uiStepSeq = uiStepSeq; + m_bPipeline = bPipeline; } void SetClientData(const std::string& strClientData) @@ -180,6 +199,8 @@ class SocketChannelImpl: public Channel void SetSecretKey(const std::string& strKey); + void SetRemoteWorkerIndex(uint16 unRemoteWorkerIndex); + bool SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive); bool AutoSwitchCodec(); @@ -200,7 +221,7 @@ class SocketChannelImpl: public Channel int32 m_iFd; ///< 文件描述符 uint32 m_uiSeq; ///< 文件描述符创建时对应的序列号 uint32 m_uiForeignSeq; ///< 外来的seq,每个连接的包都是有序的,用作接入Server数据包检查,防止篡包 - uint32 m_uiStepSeq; ///< 正在等待回调的Step seq(比如发出一个HttpPost或HttpGet请求,m_uiStepSeq即为正在等待响应的HttpStep的seq) + uint32 m_bPipeline; ///< 是否支持pipeline uint32 m_uiUnitTimeMsgNum; ///< 统计单位时间内接收消息数量 uint32 m_uiMsgNum; ///< 接收消息数量 ev_tstamp m_dActiveTime; ///< 最后一次访问时间 @@ -217,14 +238,11 @@ class SocketChannelImpl: public Channel std::string m_strErrMsg; std::string m_strIdentify; ///< 连接标识(可以为空,不为空时用于标识业务层与连接的关系) std::string m_strRemoteAddr; ///< 对端IP地址(不是客户端地址,但可能跟客户端地址相同) - std::vector m_vecStepWaitForConnected; ///< 等待连接成功的Step + std::list m_listPipelineStepSeq; ///< 等待回调的Step seq std::set m_setSkipCodecType; ///< Codec转换需跳过的CodecType Labor* m_pLabor; SocketChannel* m_pSocketChannel; std::shared_ptr m_pLogger; - - - friend class Dispatcher; }; template diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 8c902f8f..7b21c886 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -674,93 +674,6 @@ bool Dispatcher::AddIoTimeout(std::shared_ptr pChannel, ev_tstamp } } -bool Dispatcher::SendTo(std::shared_ptr pChannel) -{ - LOG4_TRACE("channel %d", pChannel->m_pImpl->GetFd()); - E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(); - if (CODEC_STATUS_OK == eStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) - { - AddIoWriteEvent(pChannel); - return(true); - } - else if (CODEC_STATUS_WANT_READ == eStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - return(false); -} - -bool Dispatcher::SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) -{ - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - if (CODEC_STATUS_OK == eStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) - { - AddIoWriteEvent(pChannel); - return(true); - } - else if (CODEC_STATUS_WANT_READ == eStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - return(false); -} - -bool Dispatcher::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) -{ - LOG4_TRACE("identify: %s", strIdentify.c_str()); - auto named_iter = m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == m_mapNamedSocketChannel.end()) - { - LOG4_TRACE("no channel match %s.", strIdentify.c_str()); - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - return(AutoSend(strIdentify, iCmd, uiSeq, oMsgBody, eCodecType)); - } - else - { - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - E_CODEC_STATUS eStatus = (*named_iter->second.begin())->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - if (CODEC_STATUS_OK == eStatus) - { - RemoveIoWriteEvent(*named_iter->second.begin()); - return(true); - } - else if (CODEC_STATUS_PAUSE == eStatus) // || CODEC_STATUS_WANT_WRITE == eCodecStatus) - { - AddIoWriteEvent(*named_iter->second.begin()); - return(true); - } - else if (CODEC_STATUS_WANT_READ == eStatus) - { - RemoveIoWriteEvent(*named_iter->second.begin()); - return(true); - } - DiscardSocketChannel(*named_iter->second.begin()); - return(false); - } -} - bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); @@ -988,475 +901,6 @@ std::shared_ptr Dispatcher::StressSend(const std::string& strIden } } -bool Dispatcher::SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) -{ - E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); - switch (eStatus) - { - case CODEC_STATUS_OK: - return(true); - case CODEC_STATUS_PAUSE: - case CODEC_STATUS_WANT_WRITE: - AddIoWriteEvent(pChannel); - return(true); - case CODEC_STATUS_WANT_READ: - RemoveIoWriteEvent(pChannel); - return(true); - case CODEC_STATUS_EOF: // http1.0 respone and close - DiscardSocketChannel(pChannel); - return(true); - default: - DiscardSocketChannel(pChannel); - return(false); - } -} - -bool Dispatcher::SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) -{ - std::ostringstream ossIdentify; - ossIdentify << strHost << ":" << iPort; - std::string strIdentify = std::move(ossIdentify.str()); - LOG4_TRACE("identify: %s", strIdentify.c_str()); - auto named_iter = m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == m_mapNamedSocketChannel.end()) - { - LOG4_TRACE("no channel match %s.", strIdentify.c_str()); - return(AutoSend(strHost, iPort, strUrlPath, oHttpMsg, uiHttpStepSeq)); - } - else - { - if (named_iter->second.empty()) - { - return(AutoSend(strHost, iPort, strUrlPath, oHttpMsg, uiHttpStepSeq)); - } - else - { - auto channel_iter = named_iter->second.begin(); - E_CODEC_STATUS eStatus = (*channel_iter)->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); - named_iter->second.erase(channel_iter); // erase from named channel pool, the channel remain in m_mapSocketChannel. - if (CODEC_STATUS_OK == eStatus) - { - return(true); - } - else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) - { - AddIoWriteEvent(*channel_iter); - return(true); - } - else if (CODEC_STATUS_WANT_READ == eStatus) - { - RemoveIoWriteEvent(*channel_iter); - return(true); - } - DiscardSocketChannel(*channel_iter); - return(false); - } - } -} - -bool Dispatcher::SendTo(std::shared_ptr pRedisChannel, Actor* pSender) -{ - LOG4_TRACE(" "); - if (nullptr == pSender) - { - LOG4_ERROR("pSender can't be nullptr!"); - return(false); - } - if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) - { - LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); - return(false); - } - std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - if (pRedisChannel->bIsReady) - { - int status; - size_t args_size = pRedisStep->GetCmdArguments().size() + 1; - const char* argv[args_size]; - size_t arglen[args_size]; - argv[0] = pRedisStep->GetCmd().c_str(); - arglen[0] = pRedisStep->GetCmd().size(); - std::vector >::const_iterator c_iter = pRedisStep->GetCmdArguments().begin(); - for (size_t i = 1; c_iter != pRedisStep->GetCmdArguments().end(); ++c_iter, ++i) - { - argv[i] = c_iter->first.c_str(); - arglen[i] = c_iter->first.size(); - } - status = redisAsyncCommandArgv((redisAsyncContext*)pRedisChannel->RedisContext(), RedisCmdCallback, nullptr, args_size, argv, arglen); - if (status == REDIS_OK) - { - LOG4_DEBUG("succeed in sending redis cmd: %s", pRedisStep->CmdToString().c_str()); - pRedisChannel->listPipelineStep.push_back(pRedisStep); - return(true); - } - else - { - LOG4_ERROR("redis status %d!", status); - return(false); - } - } - else - { - pRedisChannel->listPipelineStep.push_back(pRedisStep); - return(true); - } -} - -bool Dispatcher::SendTo(const std::string& strIdentify, Actor* pSender, bool bPipeline) -{ - LOG4_TRACE("%s", strIdentify.c_str()); - if (nullptr == pSender) - { - LOG4_ERROR("pSender can't be nullptr!"); - return(false); - } - if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) - { - LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); - return(false); - } - auto named_iter = m_mapNamedRedisChannel.find(strIdentify); - if (named_iter == m_mapNamedRedisChannel.end()) - { - size_t iPosIpPortSeparator = strIdentify.rfind(':'); - if (iPosIpPortSeparator == std::string::npos) - { - LOG4_ERROR("invalid node identify \"%s\"", strIdentify.c_str()); - return(false); - } - std::string strHost = strIdentify.substr(0, iPosIpPortSeparator); - std::string strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); - int iPort = atoi(strPort.c_str()); - if (iPort == 0) - { - LOG4_ERROR("invalid node identify \"%s\"", strIdentify.c_str()); - return(false); - } - std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep, bPipeline)); - } - else - { - if (named_iter->second.empty()) - { - size_t iPosIpPortSeparator = strIdentify.rfind(':'); - if (iPosIpPortSeparator == std::string::npos) - { - LOG4_ERROR("invalid node identify \"%s\"", strIdentify.c_str()); - return(false); - } - std::string strHost = strIdentify.substr(0, iPosIpPortSeparator); - std::string strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); - int iPort = atoi(strPort.c_str()); - if (iPort == 0) - { - LOG4_ERROR("invalid node identify \"%s\"", strIdentify.c_str()); - return(false); - } - std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep, bPipeline)); - } - else - { - auto channel_iter = named_iter->second.begin(); - if (SendTo((*channel_iter), pSender)) - { - if (!bPipeline) - { - named_iter->second.erase(channel_iter); - } - return(true); - } - return(false); - } - } -} - -bool Dispatcher::SendTo(const std::string& strHost, int iPort, Actor* pSender, bool bPipeline) -{ - LOG4_TRACE("%s, %d", strHost.c_str(), iPort); - if (nullptr == pSender) - { - LOG4_ERROR("pSender can't be nullptr!"); - return(false); - } - if (Actor::ACT_REDIS_STEP != pSender->GetActorType()) - { - LOG4_ERROR("The actor which send data to the RedisChannel must be a RedisStep."); - return(false); - } - std::ostringstream oss; - oss << strHost << ":" << iPort; - std::string strIdentify = std::move(oss.str()); - auto named_iter = m_mapNamedRedisChannel.find(strIdentify); - if (named_iter != m_mapNamedRedisChannel.end()) - { - std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep, bPipeline)); - } - else - { - if (named_iter->second.empty()) - { - std::shared_ptr pRedisStep = std::dynamic_pointer_cast(pSender->shared_from_this()); - return(AutoRedisCmd(strHost, iPort, pRedisStep, bPipeline)); - } - else - { - auto channel_iter = named_iter->second.begin(); - if (SendTo((*channel_iter), pSender)) - { - if (!bPipeline) - { - named_iter->second.erase(channel_iter); - } - return(true); - } - return(false); - } - } -} - -bool Dispatcher::AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) -{ - LOG4_TRACE("%s", strIdentify.c_str()); - size_t iPosIpPortSeparator = strIdentify.rfind(':'); - size_t iPosPortWorkerIndexSeparator = strIdentify.rfind('.'); - if (iPosIpPortSeparator == std::string::npos) - { - return(false); - } - std::string strHost = strIdentify.substr(0, iPosIpPortSeparator); - std::string strPort; - int iPort = 0; - int iWorkerIndex = 0; - if (iPosPortWorkerIndexSeparator != std::string::npos) - { - strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); - iPort = atoi(strPort.c_str()); - if (iPort == 0) - { - return(false); - } - std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); - iWorkerIndex = atoi(strWorkerIndex.c_str()); - if (iWorkerIndex > 200) - { - LOG4_ERROR("worker index must smaller than 200"); - return(false); - } - } - else - { - strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); - iPort = atoi(strPort.c_str()); - if (iPort == 0) - { - return(false); - } - } - - struct addrinfo stAddrHints; - struct addrinfo* pAddrResult; - struct addrinfo* pAddrCurrent; - memset(&stAddrHints, 0, sizeof(struct addrinfo)); - stAddrHints.ai_family = AF_UNSPEC; - stAddrHints.ai_socktype = SOCK_STREAM; - stAddrHints.ai_protocol = IPPROTO_IP; - int iCode = getaddrinfo(strHost.c_str(), strPort.c_str(), &stAddrHints, &pAddrResult); - if (0 != iCode) - { - LOG4_ERROR("getaddrinfo(\"%s\", \"%s\") error %d: %s", - strHost.c_str(), strPort.c_str(), iCode, gai_strerror(iCode)); - return(false); - } - int iFd = -1; - for (pAddrCurrent = pAddrResult; - pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) - { - iFd = socket(pAddrCurrent->ai_family, - pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); - if (iFd == -1) - { - continue; - } - - break; - } - - /* No address succeeded */ - if (pAddrCurrent == NULL) - { - LOG4_ERROR("Could not connect to \"%s:%s\"", strHost.c_str(), strPort.c_str()); - freeaddrinfo(pAddrResult); /* No longer needed */ - return(false); - } - - x_sock_set_block(iFd, 0); - int nREUSEADDR = 1; - setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); - std::shared_ptr pChannel = CreateSocketChannel(iFd, eCodecType); - if (nullptr != pChannel) - { - connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); - freeaddrinfo(pAddrResult); /* No longer needed */ - AddIoTimeout(pChannel, 1.5); - AddIoReadEvent(pChannel); - AddIoWriteEvent(pChannel); - pChannel->m_pImpl->SetIdentify(strIdentify); - pChannel->m_pImpl->SetRemoteAddr(strHost); - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - if (CODEC_STATUS_OK != eCodecStatus - && CODEC_STATUS_PAUSE != eCodecStatus - && CODEC_STATUS_WANT_WRITE != eCodecStatus - && CODEC_STATUS_WANT_READ != eCodecStatus) - { - DiscardSocketChannel(pChannel); - } - - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); - pChannel->m_pImpl->m_unRemoteWorkerIdx = iWorkerIndex; - AddNamedSocketChannel(strIdentify, pChannel); - return(true); - } - else // 没有足够资源分配给新连接,直接close掉 - { - freeaddrinfo(pAddrResult); /* No longer needed */ - close(iFd); - return(false); - } -} - -bool Dispatcher::AutoSend(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq) -{ - LOG4_TRACE("%s, %d, %s", strHost.c_str(), iPort, strUrlPath.c_str()); - struct addrinfo stAddrHints; - struct addrinfo* pAddrResult; - struct addrinfo* pAddrCurrent; - memset(&stAddrHints, 0, sizeof(struct addrinfo)); - stAddrHints.ai_family = AF_UNSPEC; - stAddrHints.ai_socktype = SOCK_STREAM; - stAddrHints.ai_protocol = IPPROTO_IP; - int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); - if (0 != iCode) - { - LOG4_ERROR("getaddrinfo(\"%s\", \"%d\") error %d: %s", - strHost.c_str(), iPort, iCode, gai_strerror(iCode)); - return(false); - } - int iFd = -1; - for (pAddrCurrent = pAddrResult; - pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) - { - iFd = socket(pAddrCurrent->ai_family, - pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); - if (iFd == -1) - { - continue; - } - - break; - } - - /* No address succeeded */ - if (pAddrCurrent == NULL) - { - LOG4_ERROR("Could not connect to \"%s:%d\"", strHost.c_str(), iPort); - freeaddrinfo(pAddrResult); /* No longer needed */ - return(false); - } - - x_sock_set_block(iFd, 0); - int nREUSEADDR = 1; - setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); - std::shared_ptr pChannel = nullptr; - std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(':')); - std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c) -> unsigned char { return std::tolower(c); }); - if (strSchema == std::string("https")) - { - pChannel = CreateSocketChannel(iFd, CODEC_HTTP, true, true); - } - else - { - pChannel = CreateSocketChannel(iFd, CODEC_HTTP, true, false); - } - if (nullptr != pChannel) - { - connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); - freeaddrinfo(pAddrResult); /* No longer needed */ - AddIoTimeout(pChannel, 1.5); - AddIoReadEvent(pChannel); - AddIoWriteEvent(pChannel); - std::ostringstream ossIdentify; - ossIdentify << strHost << ":" << iPort; - pChannel->m_pImpl->SetIdentify(ossIdentify.str()); - pChannel->m_pImpl->SetRemoteAddr(strHost); - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(oHttpMsg, uiHttpStepSeq); - if (CODEC_STATUS_OK != eCodecStatus - && CODEC_STATUS_PAUSE != eCodecStatus - && CODEC_STATUS_WANT_WRITE != eCodecStatus - && CODEC_STATUS_WANT_READ != eCodecStatus) - { - DiscardSocketChannel(pChannel); - } - - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT, uiHttpStepSeq); - // AddNamedSocketChannel(szIdentify, pChannel); the channel should not add to named connection pool, because there is an uncompleted http request. - return(true); - } - else // 没有足够资源分配给新连接,直接close掉 - { - freeaddrinfo(pAddrResult); /* No longer needed */ - close(iFd); - return(false); - } -} - -bool Dispatcher::AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep, bool bPipeline) -{ - LOG4_TRACE("redisAsyncConnect(%s, %d)", strHost.c_str(), iPort); - redisAsyncContext *c = redisAsyncConnect(strHost.c_str(), iPort); - if (c->err) - { - LOG4_ERROR("error: %s", c->errstr); - // If the onConnect callback is called with REDIS_ERROR the context will - // be disconnected (and the inner context freed) after that callback anyway. - // No need to call it yourself - // redisAsyncFree(c); - return(false); - } - c->data = m_pLabor; - std::shared_ptr pRedisChannel = nullptr; - try - { - pRedisChannel = std::make_shared(c); - } - catch(std::bad_alloc& e) - { - LOG4_ERROR("new RedisChannel error: %s", e.what()); - return(false); - } - pRedisChannel->listPipelineStep.push_back(pRedisStep); - pRedisStep->SetLabor(m_pLabor); - pRedisChannel->SetPipeline(bPipeline); - m_mapRedisChannel.insert(std::make_pair(c, pRedisChannel)); - redisLibevAttach(m_loop, c); - redisAsyncSetConnectCallback(c, RedisConnectCallback); - redisAsyncSetDisconnectCallback(c, RedisDisconnectCallback); - - std::ostringstream oss; - oss << strHost << ":" << iPort; - std::string strIdentify = std::move(oss.str()); - pRedisChannel->SetIdentify(strIdentify); - if (bPipeline) - { - // if(bPipeline == false) the channel should not add to named connection pool, because there is an uncompleted request. - AddNamedRedisChannel(strIdentify, pRedisChannel); - } - AddNamedRedisChannel(strIdentify, pRedisChannel); - return(true); -} - bool Dispatcher::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) { if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 50dd2716..badba2d8 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -119,10 +119,14 @@ class Dispatcher public: bool AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout = 1.0); - // SendTo() for nebula socket - bool SendTo(std::shared_ptr pChannel); - bool SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender = nullptr); - bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); + template + bool SendTo(std::shared_ptr pChannel, Targs&&... args); + template + bool SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); + template + bool SendTo(const std::string& strHost, int iPort, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); + template + bool Dispatcher::AutoSend(const std::string& strIdentify, const std::string& strHost, int iPort, int iRemoteWorkerIndex, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); @@ -228,6 +232,222 @@ void Dispatcher::Logger(int iLogLevel, const char* szFileName, unsigned int uiFi m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } +template +bool Dispatcher::SendTo(std::shared_ptr pChannel, Targs&&... args) +{ + E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(std::forward(args)...); + switch (eStatus) + { + case CODEC_STATUS_OK: + return(true); + case CODEC_STATUS_PAUSE: + case CODEC_STATUS_WANT_WRITE: + AddIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_WANT_READ: + RemoveIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_EOF: // a case: http1.0 respone and close + DiscardSocketChannel(pChannel); + return(true); + default: + DiscardSocketChannel(pChannel); + return(false); + } +} + +template +bool Dispatcher::SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + LOG4_TRACE("identify: %s", strIdentify.c_str()); + // 将strIdentify分割的功能只在此SendTo()函数内两处调用,定义为Dispatcher的成员函数语义上不太合适,故定义lambda表达式 + auto split = [](const std::string& strIdentify, std::string& strHost, int& iPort, int& iWorkerIndex, std::string& strError)->bool + { + size_t iPosIpPortSeparator = strIdentify.rfind(':'); + size_t iPosPortWorkerIndexSeparator = strIdentify.rfind('.'); + if (iPosIpPortSeparator == std::string::npos) + { + return(false); + } + strHost = strIdentify.substr(0, iPosIpPortSeparator); + std::string strPort; + if (iPosPortWorkerIndexSeparator != std::string::npos) + { + strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); + iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + return(false); + } + std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); + iWorkerIndex = atoi(strWorkerIndex.c_str()); + if (iWorkerIndex > 200) + { + strError = "worker index must smaller than 200"; + return(false); + } + } + else + { + strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); + iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + return(false); + } + } + return(true); + }; + + auto named_iter = m_mapNamedSocketChannel.find(strIdentify); + if (named_iter == m_mapNamedSocketChannel.end()) + { + LOG4_TRACE("no channel match %s.", strIdentify.c_str()); + std::string strError; + std::string strHost; + int iPort = 0; + int iWorkerIndex = 0; + if (!split(strIdentify, strHost, iPort, iWorkerIndex, strError)) + { + LOG4_ERROR("%s", strError.c_str()); + return(false); + } + return(AutoSend(strIdentify, strHost, iPort, iWorkerIndex, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + if (named_iter->second.empty()) + { + std::string strError; + std::string strHost; + int iPort = 0; + int iWorkerIndex = 0; + if (!split(strIdentify, strHost, iPort, iWorkerIndex, strError)) + { + LOG4_ERROR("%s", strError.c_str()); + return(false); + } + return(AutoSend(strIdentify, strHost, iPort, iWorkerIndex, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + auto channel_iter = named_iter->second.begin(); + bool bResult = SendTo((*channel_iter), std::forward(args)...); + if (!bPipeline && bResult) + { + named_iter->second.erase(channel_iter); + } + return(bResult); + } + } +} + +template +bool Dispatcher::SendTo(const std::string& strHost, int iPort, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + LOG4_TRACE("host %s port %d", strHost.c_str(), iPort); + std::ostringstream ossIdentify; + ossIdentify << strHost << ":" << iPort; + auto named_iter = m_mapNamedSocketChannel.find(ossIdentify.str()); + if (named_iter == m_mapNamedSocketChannel.end()) + { + LOG4_TRACE("no channel match %s.", ossIdentify.str().c_str()); + return(AutoSend(ossIdentify.str(), strHost, iPort, 0, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + auto channel_iter = named_iter->second.begin(); + bool bResult = SendTo((*channel_iter), std::forward(args)...); + if (!bPipeline && bResult) + { + named_iter->second.erase(channel_iter); + } + return(bResult); + } +} + +template +bool Dispatcher::AutoSend( + const std::string& strIdentify, const std::string& strHost, int iPort, + int iRemoteWorkerIndex, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + LOG4_TRACE("%s", strIdentify.c_str()); + struct addrinfo stAddrHints; + struct addrinfo* pAddrResult; + struct addrinfo* pAddrCurrent; + memset(&stAddrHints, 0, sizeof(struct addrinfo)); + stAddrHints.ai_family = AF_UNSPEC; + stAddrHints.ai_socktype = SOCK_STREAM; + stAddrHints.ai_protocol = IPPROTO_IP; + int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); + if (0 != iCode) + { + LOG4_ERROR("getaddrinfo(\"%s\", \"%d\") error %d: %s", + strHost.c_str(), iPort, iCode, gai_strerror(iCode)); + return(false); + } + int iFd = -1; + for (pAddrCurrent = pAddrResult; + pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) + { + iFd = socket(pAddrCurrent->ai_family, + pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); + if (iFd == -1) + { + continue; + } + + break; + } + + /* No address succeeded */ + if (pAddrCurrent == NULL) + { + LOG4_ERROR("Could not connect to \"%s:%d\"", strHost.c_str(), iPort); + freeaddrinfo(pAddrResult); /* No longer needed */ + return(false); + } + + x_sock_set_block(iFd, 0); + int nREUSEADDR = 1; + setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); + std::shared_ptr pChannel = CreateSocketChannel(iFd, eCodecType, true, bWithSsl); + if (nullptr != pChannel) + { + connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); + freeaddrinfo(pAddrResult); /* No longer needed */ + AddIoTimeout(pChannel, 1.5); + AddIoReadEvent(pChannel); + AddIoWriteEvent(pChannel); + pChannel->m_pImpl->SetIdentify(strIdentify); + pChannel->m_pImpl->SetRemoteAddr(strHost); + pChannel->m_pImpl->SetPipeline(bPipeline); + E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(std::forward(args)...); + if (CODEC_STATUS_OK != eCodecStatus + && CODEC_STATUS_PAUSE != eCodecStatus + && CODEC_STATUS_WANT_WRITE != eCodecStatus + && CODEC_STATUS_WANT_READ != eCodecStatus) + { + DiscardSocketChannel(pChannel); + } + + pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); + pChannel->m_pImpl->SetRemoteWorkerIndex(iRemoteWorkerIndex); + if (bPipeline) + { + AddNamedSocketChannel(strIdentify, pChannel); + } + return(true); + } + else // 没有足够资源分配给新连接,直接close掉 + { + freeaddrinfo(pAddrResult); /* No longer needed */ + close(iFd); + return(false); + } +} + + } /* namespace neb */ #endif /* SRC_IOS_DISPATCHER_HPP_ */ diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index ab59c993..95318203 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -30,7 +30,6 @@ extern "C" { #include "util/CBuffer.hpp" #include "labor/Labor.hpp" #include "channel/SocketChannel.hpp" -#include "channel/RedisChannel.hpp" #include "codec/Codec.hpp" #include "logger/NetLogger.hpp" #include "NodeInfo.hpp" From 75c036e47a2c9004855ac24065dd7d1c6fd58483 Mon Sep 17 00:00:00 2001 From: Bwar Date: Thu, 10 Dec 2020 00:57:16 +0800 Subject: [PATCH 111/176] replace hiredis with CodecResp; add RawStep --- src/ios/Dispatcher.cpp | 501 +++++++++++++++++++---------------------- src/ios/Dispatcher.hpp | 32 +-- 2 files changed, 236 insertions(+), 297 deletions(-) diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 7b21c886..1a0782e8 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -10,7 +10,6 @@ #include "Dispatcher.hpp" #include -#include "util/process_helper.h" #include "Definition.hpp" #include "labor/Labor.hpp" #include "labor/Manager.hpp" @@ -48,7 +47,7 @@ void Dispatcher::IoCallback(struct ev_loop* loop, struct ev_io* watcher, int rev if (watcher->data != NULL) { SocketChannel* pChannel = static_cast(watcher->data); - Dispatcher* pDispatcher = pChannel->m_pImpl->m_pLabor->GetDispatcher(); + Dispatcher* pDispatcher = pChannel->m_pImpl->GetLabor()->GetDispatcher(); std::shared_ptr pSharedChannel = pChannel->shared_from_this(); if (revents & EV_READ) { @@ -70,7 +69,7 @@ void Dispatcher::IoTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int if (watcher->data != NULL) { SocketChannel* pChannel = static_cast(watcher->data); - Dispatcher* pDispatcher = pChannel->m_pImpl->m_pLabor->GetDispatcher(); + Dispatcher* pDispatcher = pChannel->m_pImpl->GetLabor()->GetDispatcher(); if (pChannel->m_pImpl->GetFd() < 3) // TODO 这个判断是不得已的做法,需查找fd为0回调到这里的原因 { return; @@ -79,33 +78,6 @@ void Dispatcher::IoTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int } } -void Dispatcher::RedisConnectCallback(const redisAsyncContext *c, int status) -{ - if (c->data != NULL) - { - Labor* pLabor = (Labor*)c->data; - pLabor->GetDispatcher()->OnRedisConnected(c, status); - } -} - -void Dispatcher::RedisDisconnectCallback(const redisAsyncContext *c, int status) -{ - if (c->data != NULL) - { - Labor* pLabor = (Labor*)c->data; - pLabor->GetDispatcher()->OnRedisDisconnected(c, status); - } -} - -void Dispatcher::RedisCmdCallback(redisAsyncContext *c, void *reply, void *privdata) -{ - if (c->data != NULL) - { - Labor* pLabor = (Labor*)c->data; - pLabor->GetDispatcher()->OnRedisCmdResult(c, reply, privdata); - } -} - void Dispatcher::PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, int revents) { if (watcher->data != NULL) @@ -192,89 +164,146 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) { LOG4_TRACE(" "); E_CODEC_STATUS eCodecStatus; - if (CODEC_HTTP == pChannel->m_pImpl->GetCodecType()) + switch(pChannel->GetCodecType()) { - for (int i = 0; ; ++i) - { - HttpMsg oHttpMsg; - if (0 == i) + case CODEC_HTTP: + for (int i = 0; ; ++i) { - eCodecStatus = pChannel->m_pImpl->Recv(oHttpMsg); + HttpMsg oHttpMsg; + if (0 == i) + { + eCodecStatus = pChannel->m_pImpl->Recv(oHttpMsg); + } + else + { + eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); + } + + if (CODEC_STATUS_OK == eCodecStatus) + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + } + else if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + } + else + { + break; + } } - else + break; + case CODEC_RESP: + for (int i = 0; ; ++i) { - eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); - } + RedisMsg oRedisMsg; + if (0 == i) + { + eCodecStatus = pChannel->m_pImpl->Recv(oRedisMsg); + } + else + { + eCodecStatus = pChannel->m_pImpl->Fetch(oRedisMsg); + } - if (CODEC_STATUS_OK == eCodecStatus) - { - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + if (CODEC_STATUS_OK == eCodecStatus) + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oRedisMsg); + } + else + { + break; + } } - else if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close + break; + case CODEC_UNKNOW: + CBuffer oBuff; + for (int i = 0; ; ++i) { - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + RedisMsg oRedisMsg; + if (0 == i) + { + eCodecStatus = pChannel->m_pImpl->Recv(oBuff); + } + else + { + eCodecStatus = pChannel->m_pImpl->Fetch(oBuff); + } + + if (CODEC_STATUS_OK == eCodecStatus) + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oBuff); + } + else + { + break; + } } - else + break; + default: + for (int i = 0; ; ++i) { - break; + MsgHead oMsgHead; + MsgBody oMsgBody; + if (0 == i) + { + eCodecStatus = pChannel->m_pImpl->Recv(oMsgHead, oMsgBody); + } + else + { + eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); + } + + if (CODEC_STATUS_OK == eCodecStatus) + { + if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) + { + if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 + { + LOG4_DEBUG("invalid request, please login first!"); + DiscardSocketChannel(pChannel); + return(false); + } + } + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); + } + else + { + break; + } } - } } - else + + switch (eCodecStatus) { - MsgHead oMsgHead; - MsgBody oMsgBody; - eCodecStatus = pChannel->m_pImpl->Recv(oMsgHead, oMsgBody); - while (CODEC_STATUS_OK == eCodecStatus) - { - if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) + case CODEC_STATUS_PAUSE: + return(true); + case CODEC_STATUS_WANT_WRITE: + return(SendTo(pChannel)); + case CODEC_STATUS_WANT_READ: + RemoveIoWriteEvent(pChannel); + return(true); + default: // 编解码出错或连接关闭或连接中断 + if (CODEC_STATUS_INVALID == eCodecStatus) { - if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 + if (pChannel->m_pImpl->AutoSwitchCodec()) { - LOG4_DEBUG("invalid request, please login first!"); - DiscardSocketChannel(pChannel); - return(false); + return(DataFetchAndHandle(pChannel)); } } - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); - oMsgHead.Clear(); // 注意protobuf的Clear()使用不当容易造成内存泄露 - oMsgBody.Clear(); - eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); - } - } - - if (CODEC_STATUS_PAUSE == eCodecStatus) - { - return(true); - } - else if (CODEC_STATUS_WANT_WRITE == eCodecStatus) - { - return(SendTo(pChannel)); - } - else if (CODEC_STATUS_WANT_READ == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - else // 编解码出错或连接关闭或连接中断 - { - if (CODEC_STATUS_INVALID == eCodecStatus) - { - if (pChannel->m_pImpl->AutoSwitchCodec()) + LOG4_TRACE("codec error or connection closed!"); + if (CHANNEL_STATUS_ESTABLISHED != pChannel->m_pImpl->GetChannelStatus()) { - return(DataFetchAndHandle(pChannel)); + auto& listUncompletedStep = pChannel->m_pImpl->GetPipelineStepSeq(); + for (auto it = listUncompletedStep.begin(); + it != listUncompletedStep.end(); ++it) + { + m_pLabor->GetActorBuilder()->OnError(pChannel, *it, + pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); + } } - } - LOG4_TRACE("codec error or connection closed!"); - auto& vecUncompletedStep = pChannel->m_pImpl->GetStepWaitForConnected(); - for (auto it = vecUncompletedStep.begin(); - it != vecUncompletedStep.end(); ++it) - { - m_pLabor->GetActorBuilder()->OnError(pChannel, *it, - pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); - } - DiscardSocketChannel(pChannel); - return(false); + DiscardSocketChannel(pChannel); + return(false); } } @@ -282,75 +311,110 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) { LOG4_TRACE(" "); E_CODEC_STATUS eCodecStatus; - if (CODEC_HTTP == pChannel->m_pImpl->GetCodecType()) + switch(pChannel->GetCodecType()) { - HttpMsg oHttpMsg; - eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); - while (CODEC_STATUS_OK == eCodecStatus) - { - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); - eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); - } - if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close - { - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); - } - } - else - { - MsgHead oMsgHead; - MsgBody oMsgBody; - eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); - while (CODEC_STATUS_OK == eCodecStatus) - { - if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) + case CODEC_HTTP: { - if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 + HttpMsg oHttpMsg; + eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); + while (CODEC_STATUS_OK == eCodecStatus) { - LOG4_DEBUG("invalid request, please login first!"); - DiscardSocketChannel(pChannel); - return(false); + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); + } + if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + } + } + break; + case CODEC_RESP: + for (int i = 0; ; ++i) + { + RedisMsg oRedisMsg; + eCodecStatus = pChannel->m_pImpl->Fetch(oRedisMsg); + if (CODEC_STATUS_OK == eCodecStatus) + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oRedisMsg); + } + else + { + break; + } + } + break; + case CODEC_UNKNOW: + CBuffer oBuff; + for (int i = 0; ; ++i) + { + RedisMsg oRedisMsg; + eCodecStatus = pChannel->m_pImpl->Fetch(oBuff); + if (CODEC_STATUS_OK == eCodecStatus) + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oBuff); + } + else + { + break; + } + } + break; + default: + for (int i = 0; ; ++i) + { + MsgHead oMsgHead; + MsgBody oMsgBody; + eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); + if (CODEC_STATUS_OK == eCodecStatus) + { + if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) + { + if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 + { + LOG4_DEBUG("invalid request, please login first!"); + DiscardSocketChannel(pChannel); + return(false); + } + } + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); + } + else + { + break; } } - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); - oMsgHead.Clear(); // 注意protobuf的Clear()使用不当容易造成内存泄露 - oMsgBody.Clear(); - eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); - } } - if (CODEC_STATUS_PAUSE == eCodecStatus) - { - return(true); - } - else if (CODEC_STATUS_WANT_WRITE == eCodecStatus) - { - return(SendTo(pChannel)); - } - else if (CODEC_STATUS_WANT_READ == eCodecStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - else // 编解码出错或连接关闭或连接中断 + switch (eCodecStatus) { - if (CODEC_STATUS_INVALID == eCodecStatus) - { - if (pChannel->m_pImpl->AutoSwitchCodec()) + case CODEC_STATUS_PAUSE: + return(true); + case CODEC_STATUS_WANT_WRITE: + return(SendTo(pChannel)); + case CODEC_STATUS_WANT_READ: + RemoveIoWriteEvent(pChannel); + return(true); + default: // 编解码出错或连接关闭或连接中断 + if (CODEC_STATUS_INVALID == eCodecStatus) { - return(DataFetchAndHandle(pChannel)); + if (pChannel->m_pImpl->AutoSwitchCodec()) + { + return(DataFetchAndHandle(pChannel)); + } } - } - LOG4_TRACE("codec error or connection closed!"); - auto& vecUncompletedStep = pChannel->m_pImpl->GetStepWaitForConnected(); - for (auto it = vecUncompletedStep.begin(); - it != vecUncompletedStep.end(); ++it) - { - m_pLabor->GetActorBuilder()->OnError(pChannel, *it, - pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); - } - DiscardSocketChannel(pChannel); - return(false); + LOG4_TRACE("codec error or connection closed!"); + if (CHANNEL_STATUS_ESTABLISHED != pChannel->m_pImpl->GetChannelStatus()) + { + auto& listUncompletedStep = pChannel->m_pImpl->GetPipelineStepSeq(); + for (auto it = listUncompletedStep.begin(); + it != listUncompletedStep.end(); ++it) + { + m_pLabor->GetActorBuilder()->OnError(pChannel, *it, + pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); + } + } + DiscardSocketChannel(pChannel); + return(false); } } @@ -488,7 +552,7 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) if (CHANNEL_STATUS_TRY_CONNECT == pChannel->m_pImpl->GetChannelStatus()) // connect之后的第一个写事件 { std::shared_ptr pStepConnectWorker = m_pLabor->GetActorBuilder()->MakeSharedStep( - nullptr, "neb::StepConnectWorker", pChannel, pChannel->m_pImpl->m_unRemoteWorkerIdx); + nullptr, "neb::StepConnectWorker", pChannel, pChannel->m_pImpl->GetRemoteWorkerIndex()); if (nullptr == pStepConnectWorker) { LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); @@ -576,51 +640,6 @@ bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) return(true); } -bool Dispatcher::OnRedisConnected(const redisAsyncContext *c, int status) -{ - LOG4_TRACE(" "); - auto channel_iter = m_mapRedisChannel.find((redisAsyncContext*)c); - if (channel_iter != m_mapRedisChannel.end()) - { - if (!m_pLabor->GetActorBuilder()->OnRedisConnected(channel_iter->second, c, status)) - { - DelNamedRedisChannel(channel_iter->second->GetIdentify()); - m_mapRedisChannel.erase(channel_iter); - } - } - return(true); -} - -bool Dispatcher::OnRedisDisconnected(const redisAsyncContext *c, int status) -{ - LOG4_TRACE(" "); - auto channel_iter = m_mapRedisChannel.find((redisAsyncContext*)c); - if (channel_iter != m_mapRedisChannel.end()) - { - m_pLabor->GetActorBuilder()->OnRedisDisconnected(channel_iter->second, c, status); - DelNamedRedisChannel(channel_iter->second->GetIdentify()); - m_mapRedisChannel.erase(channel_iter); - } - //redisAsyncDisconnect(const_cast(c)); //被动断开连接不需要 - return(true); -} - -bool Dispatcher::OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privdata) -{ - LOG4_TRACE(" "); - auto channel_iter = m_mapRedisChannel.find((redisAsyncContext*)c); - if (channel_iter != m_mapRedisChannel.end()) - { - m_pLabor->GetActorBuilder()->OnRedisCmdResult(channel_iter->second, c, reply, privdata); - if (nullptr == reply) - { - DelNamedRedisChannel(channel_iter->second->GetIdentify()); - m_mapRedisChannel.erase(channel_iter); - } - } - return(true); -} - bool Dispatcher::OnClientConnFrequencyTimeout(tagClientConnWatcherData* pData, ev_timer* watcher) { bool bRes = false; @@ -674,17 +693,13 @@ bool Dispatcher::AddIoTimeout(std::shared_ptr pChannel, ev_tstamp } } -bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) +bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; if (m_pSessionNode->GetNode(strNodeType, strOnlineNode)) { - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody, eCodecType)); + return(SendTo(strOnlineNode, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); } else { @@ -693,17 +708,13 @@ bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint } } -bool Dispatcher::SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) +bool Dispatcher::SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { LOG4_TRACE("nody_type: %s, factor: %d", strNodeType.c_str(), uiFactor); std::string strOnlineNode; if (m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) { - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody, eCodecType)); + return(SendTo(strOnlineNode, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); } else { @@ -712,7 +723,7 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, unsigned int uiFac } } -bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) +bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { LOG4_TRACE("nody_type: %s", strNodeType.c_str()); if (oMsgBody.has_req_target()) @@ -726,11 +737,7 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 std::string strOnlineNode; if (m_pSessionNode->GetNode(strNodeType, oMsgBody.req_target().route(), strOnlineNode)) { - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } - return(SendTo(strOnlineNode, iCmd, uiSeq, oMsgBody, eCodecType)); + return(SendTo(strOnlineNode, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); } else { @@ -750,20 +757,16 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 } }; -bool Dispatcher::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType, Actor* pSender) +bool Dispatcher::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); std::unordered_set setOnlineNodes; if (m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) { - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } bool bSendResult = false; for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) { - bSendResult |= SendTo(*node_iter, iCmd, uiSeq, oMsgBody, eCodecType); + bSendResult |= SendTo(*node_iter, eCodecType, false, true, iCmd, uiSeq, oMsgBody); } return(bSendResult); } @@ -781,11 +784,11 @@ bool Dispatcher::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 ui } } -bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) { - return(Broadcast("BEACON", iCmd, uiSeq, oMsgBody, CODEC_NEBULA, pSender)); + return(Broadcast("BEACON", iCmd, uiSeq, oMsgBody, CODEC_NEBULA)); } else { @@ -901,17 +904,13 @@ std::shared_ptr Dispatcher::StressSend(const std::string& strIden } } -bool Dispatcher::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender) +bool Dispatcher::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) { LOG4_ERROR("this function can not be called by manager process!"); return(false); } - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } if (m_iterLoaderAndWorkerChannel == m_mapLoaderAndWorkerChannel.end()) { m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); @@ -1040,37 +1039,6 @@ void Dispatcher::SetChannelIdentify(std::shared_ptr pChannel, con pChannel->m_pImpl->SetIdentify(strIdentify); } -bool Dispatcher::AddNamedRedisChannel(const std::string& strIdentify, std::shared_ptr pChannel) -{ - auto named_iter = m_mapNamedRedisChannel.find(strIdentify); - if (named_iter == m_mapNamedRedisChannel.end()) - { - std::unordered_set > setChannel; - setChannel.insert(pChannel); - m_mapNamedRedisChannel.insert(std::make_pair(strIdentify, std::move(setChannel))); - return(true); - } - else - { - named_iter->second.insert(pChannel); - } - pChannel->SetIdentify(strIdentify); - return(true); -} - -void Dispatcher::DelNamedRedisChannel(const std::string& strIdentify) -{ - auto named_iter = m_mapNamedRedisChannel.find(strIdentify); - if (named_iter == m_mapNamedRedisChannel.end()) - { - ; - } - else - { - m_mapNamedRedisChannel.erase(named_iter); - } -} - void Dispatcher::AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify) { LOG4_TRACE("%s, %s", strNodeType.c_str(), strIdentify.c_str()); @@ -1292,9 +1260,7 @@ bool Dispatcher::Init() void Dispatcher::Destroy() { m_mapSocketChannel.clear(); - m_mapRedisChannel.clear(); m_mapNamedSocketChannel.clear(); - m_mapNamedRedisChannel.clear(); if (m_loop != NULL) { ev_loop_destroy(m_loop); @@ -1326,7 +1292,7 @@ std::shared_ptr Dispatcher::CreateSocketChannel(int iFd, E_CODEC_ return(nullptr); } pChannel->m_pImpl->SetLabor(m_pLabor); - bool bInitResult = pChannel->m_pImpl->Init(eCodecType, bIsClient); + bool bInitResult = pChannel->Init(eCodecType, bIsClient); if (bInitResult) { m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); @@ -1489,11 +1455,6 @@ void Dispatcher::SetChannelStatus(std::shared_ptr pChannel, E_CHA pChannel->m_pImpl->SetChannelStatus(eStatus); } -void Dispatcher::SetChannelStatus(std::shared_ptr pChannel, E_CHANNEL_STATUS eStatus, uint32 ulStepSeq) -{ - pChannel->m_pImpl->SetChannelStatus(eStatus, ulStepSeq); -} - bool Dispatcher::AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout) { LOG4_TRACE(" "); diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index badba2d8..d130226a 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -32,7 +32,6 @@ extern "C" { #pragma GCC diagnostic ignored "-Wstrict-aliasing" #endif #include "ev.h" -#include "hiredis/async.h" #ifdef __GNUC__ #pragma GCC diagnostic pop #endif @@ -43,11 +42,13 @@ extern "C" { #include #include +#include #include +#include "util/process_helper.h" #include "pb/msg.pb.h" #include "channel/SocketChannel.hpp" -#include "channel/RedisChannel.hpp" +#include "logger/NetLogger.hpp" namespace neb { @@ -92,9 +93,6 @@ class Dispatcher public: static void IoCallback(struct ev_loop* loop, struct ev_io* watcher, int revents); static void IoTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); - static void RedisConnectCallback(const redisAsyncContext *c, int status); - static void RedisDisconnectCallback(const redisAsyncContext *c, int status); - static void RedisCmdCallback(redisAsyncContext *c, void *reply, void *privdata); static void PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, int revents); static void SignalCallback(struct ev_loop* loop, struct ev_signal* watcher, int revents); static void ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); @@ -106,9 +104,6 @@ class Dispatcher bool OnIoWrite(std::shared_ptr pChannel); bool OnIoError(std::shared_ptr pChannel); bool OnIoTimeout(std::shared_ptr pChannel); - bool OnRedisConnected(const redisAsyncContext *c, int status); - bool OnRedisDisconnected(const redisAsyncContext *c, int status); - bool OnRedisCmdResult(redisAsyncContext *c, void *reply, void *privdata); bool OnClientConnFrequencyTimeout(tagClientConnWatcherData* pData, ev_timer* watcher); template @@ -118,7 +113,6 @@ class Dispatcher public: bool AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout = 1.0); - template bool SendTo(std::shared_ptr pChannel, Targs&&... args); template @@ -135,19 +129,8 @@ class Dispatcher bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender); std::shared_ptr StressSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); - // SendTo() for http - bool SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); - bool SendTo(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); - bool AutoSend(const std::string& strHost, int iPort, const std::string& strUrlPath, const HttpMsg& oHttpMsg, uint32 uiHttpStepSeq = 0); - - // SendTo() for redis - bool SendTo(std::shared_ptr pRedisChannel, Actor* pSender); - bool SendTo(const std::string& strIdentify, Actor* pSender, bool bPipeline = true); - bool SendTo(const std::string& strHost, int iPort, Actor* pSender, bool bPipeline = true); - bool AutoRedisCmd(const std::string& strHost, int iPort, std::shared_ptr pRedisStep, bool bPipeline = true); - // SendTo() for unix domain socket - bool SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender); + bool SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); bool SendTo(int iFd, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); bool Disconnect(std::shared_ptr pChannel, bool bChannelNotice = true); @@ -159,8 +142,6 @@ class Dispatcher void SetChannelIdentify(std::shared_ptr pChannel, const std::string& strIdentify); bool AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel); void DelNamedSocketChannel(const std::string& strIdentify); - bool AddNamedRedisChannel(const std::string& strIdentify, std::shared_ptr pChannel); - void DelNamedRedisChannel(const std::string& strIdentify); void AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); void DelNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); void SetClientData(std::shared_ptr pChannel, const std::string& strClientData); @@ -194,7 +175,6 @@ class Dispatcher int32 GetConnectionNum() const; int32 GetClientNum() const; void SetChannelStatus(std::shared_ptr pChannel, E_CHANNEL_STATUS eStatus); - void SetChannelStatus(std::shared_ptr pChannel, E_CHANNEL_STATUS eStatus, uint32 ulStepSeq); bool AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout = 60.0); bool AcceptFdAndTransfer(int iFd, int iFamily = AF_INET); bool AcceptServerConn(int iFd); @@ -210,11 +190,9 @@ class Dispatcher // Channel std::unordered_map > m_mapSocketChannel; - std::unordered_map > m_mapRedisChannel; /* named Channel */ std::unordered_map > > m_mapNamedSocketChannel; ///< key为Identify,连接存在时,if(http连接)set.size()>=1;else set.size()==1; - std::unordered_map > > m_mapNamedRedisChannel; ///< key为identify,连接存在时,if(pipeline)set.size()==1;else set.size()>=1; std::unordered_map > m_mapLoaderAndWorkerChannel; ///< Loader和Worker之间通信通道 std::unordered_map >::iterator m_iterLoaderAndWorkerChannel; @@ -271,7 +249,7 @@ bool Dispatcher::SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, } strHost = strIdentify.substr(0, iPosIpPortSeparator); std::string strPort; - if (iPosPortWorkerIndexSeparator != std::string::npos) + if (iPosPortWorkerIndexSeparator != std::string::npos && iPosPortWorkerIndexSeparator > iPosIpPortSeparator) { strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); iPort = atoi(strPort.c_str()); From f7c1651e31bb2932d75ae4da4abacc71dfe95656 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 12 Dec 2020 17:29:54 +0800 Subject: [PATCH 112/176] add RedisCmd and RawCmd, replace hiredis with CodecResp, compile passing. --- src/Makefile | 1 - src/actor/Actor.cpp | 46 ++++++------------------ src/actor/Actor.hpp | 4 +-- src/actor/ActorBuilder.cpp | 58 ++++++++++++++++++++----------- src/actor/ActorBuilder.hpp | 2 +- src/actor/cmd/CW.hpp | 4 +++ src/actor/cmd/RawCmd.hpp | 54 ++++++++++++++++++++++++++++ src/actor/cmd/RedisCmd.hpp | 54 ++++++++++++++++++++++++++++ src/actor/step/RedisStep.cpp | 1 + src/actor/step/Step.cpp | 44 +++++++++++++++++++++++ src/actor/step/Step.hpp | 9 +++++ src/channel/SocketChannelImpl.cpp | 31 +++++++++++++++-- src/ios/Dispatcher.cpp | 12 +++---- src/ios/Dispatcher.hpp | 14 ++++---- src/util/CBuffer.hpp | 4 +-- src/util/StringCoder.cpp | 21 +++++++++++ src/util/StringCoder.hpp | 2 ++ 17 files changed, 284 insertions(+), 77 deletions(-) create mode 100644 src/actor/cmd/RawCmd.hpp create mode 100644 src/actor/cmd/RedisCmd.hpp diff --git a/src/Makefile b/src/Makefile index 5932dfd3..a1c3111f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -33,7 +33,6 @@ INC := $(INC) \ LDFLAGS := $(LDFLAGS) -D_LINUX_OS_ \ - -L$(LIB3RD_PATH)/lib -lhiredis \ -L$(LIB3RD_PATH)/lib -lcryptopp \ -L$(LIB3RD_PATH)/lib -lev \ -L$(LIB3RD_PATH)/lib -lprotobuf \ diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 985a29af..d06d311e 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -9,6 +9,7 @@ ******************************************************************************/ #include "actor/Actor.hpp" +#include #include "ios/Dispatcher.hpp" #include "actor/session/Session.hpp" #include "actor/step/Step.hpp" @@ -150,7 +151,7 @@ bool Actor::SendTo(std::shared_ptr pChannel) bool Actor::SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendTo(pChannel, iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendTo(pChannel, iCmd, uiSeq, oMsgBody)); } bool Actor::SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) @@ -171,22 +172,12 @@ bool Actor::SendTo(std::shared_ptr pChannel, const char* pRawData bool Actor::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { - if (Actor::ACT_PB_STEP != GetActorType()) - { - LOG4_ERROR("the actor whick send MsgBody request must be a PbStep."); - return(false); - } (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendTo(strIdentify, iCmd, uiSeq, oMsgBody, eCodecType, this)); + return(m_pLabor->GetDispatcher()->SendTo(strIdentify, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); } bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg) { - if (Actor::ACT_HTTP_STEP != GetActorType()) - { - LOG4_ERROR("the actor whick send HttpMsg request must be a HttpStep."); - return(false); - } bool bWithSsl = false; bool bPipeline = false; if (oHttpMsg.headers().find("x-trace-id") == oHttpMsg.headers().end()) @@ -197,7 +188,7 @@ bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMs { bPipeline = true; } - std::string strSchema = oHttpMsg.url().substr(0, HttpMsg.url().find_first_of(":")); + std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(":")); std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c)->unsigned char {return std::tolower(c);}); if (strSchema == std::string("https")) { @@ -208,62 +199,47 @@ bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMs bool Actor::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - if (Actor::ACT_REDIS_STEP != GetActorType()) - { - LOG4_ERROR("the actor which send RedisMsg must be a RedisStep."); - return(false); - } return(m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); } bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - if (Actor::ACT_REDIS_STEP != GetActorType()) - { - LOG4_ERROR("the actor which send RedisMsg must be a RedisStep."); - return(false); - } - return(m_pLabor->GetActorBuilder()->SendToCluster(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); + return(m_pLabor->GetActorBuilder()->SendToCluster(strIdentify, bWithSsl, bPipeline, oRedisMsg, GetSequence())); } bool Actor::SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline) { - if (Actor::ACT_RAW_STEP != GetActorType()) - { - LOG4_ERROR("the actor which send raw data must be a RawStep."); - return(false); - } - return(m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_UNKNOW, bWithSslt, bPipeline, pRawData, uiRawDataSize, GetSequence())); + return(m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_UNKNOW, bWithSsl, bPipeline, pRawData, uiRawDataSize, GetSequence())); } bool Actor::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendTo(iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendTo(iCmd, uiSeq, oMsgBody)); } bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType, this)); + return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); } bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, uiFactor, iCmd, uiSeq, oMsgBody, eCodecType, this)); + return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, uiFactor, iCmd, uiSeq, oMsgBody, eCodecType)); } bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType, this)); + return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); } bool Actor::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendDataReport(iCmd, uiSeq, oMsgBody, this)); + return(m_pLabor->GetDispatcher()->SendDataReport(iCmd, uiSeq, oMsgBody)); } bool Actor::CloseRawChannel(std::shared_ptr pChannel) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index d1ac2b05..9bd9a69a 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -209,8 +209,8 @@ class Actor: public std::enable_shared_from_this * @param uiStepSeq 应用层无用参数,框架层的系统Actor会用到 * @return 是否发送成功 */ - bool SendTo(const std::string& strIdentify, const oRedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); - bool SendToCluster(const std::string& strIdentify, const oRedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); + bool SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); + bool SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); /** * @brief 发送raw请求 diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 64c355cf..76cc74ed 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -20,8 +20,8 @@ #include "step/PbStep.hpp" #include "step/RedisStep.hpp" #include "step/RawStep.hpp" -#include "cmd/CmdRedis.hpp" -#include "cmd/CmdRaw.hpp" +#include "cmd/RedisCmd.hpp" +#include "cmd/RawCmd.hpp" #include "model/Model.hpp" #include "chain/Chain.hpp" #include "actor/session/sys_session/SessionLogger.hpp" @@ -469,13 +469,13 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Redi else { E_CMD_STATUS eResult; - http_step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); LOG4_TRACE("callback of redis cmd: %s", (std::dynamic_pointer_cast(step_iter->second))->CmdToString().c_str()); - eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oRedisMsg); + eResult = step_iter->second->Callback(pChannel, oRedisMsg); if (CMD_STATUS_RUNNING != eResult) { - uint32 uiChainId = http_step_iter->second->GetChainId(); - RemoveStep(http_step_iter->second); + uint32 uiChainId = step_iter->second->GetChainId(); + RemoveStep(step_iter->second); if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); @@ -498,15 +498,15 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Redi auto cmd_iter = m_mapCmd.find(CMD_REQ_REDIS_PROXY); if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) { - auto pCmdRedis = std::dynamic_pointer_cast(cmd_iter->second); - if (pCmdRedis == nullptr) + auto pRedisCmd = std::dynamic_pointer_cast(cmd_iter->second); + if (pRedisCmd == nullptr) { - LOG4_ERROR("cmd %d is not a CmdRedis instance!", CMD_REQ_REDIS_PROXY); + LOG4_ERROR("cmd %d is not a RedisCmd instance!", CMD_REQ_REDIS_PROXY); return(false); } - return(pCmdRedis->AnyMessage(pChannel, oRedisMsg)); + return(pRedisCmd->AnyMessage(pChannel, oRedisMsg)); } - LOG4_ERROR("no instance of CmdRedis or derived class of CmdRedis found for cmd %d", CMD_REQ_REDIS_PROXY); + LOG4_ERROR("no instance of RedisCmd or derived class of RedisCmd found for cmd %d", CMD_REQ_REDIS_PROXY); return(false); } } @@ -528,12 +528,12 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const CBuf else { E_CMD_STATUS eResult; - http_step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes()); + step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = step_iter->second->Callback(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes()); if (CMD_STATUS_RUNNING != eResult) { - uint32 uiChainId = http_step_iter->second->GetChainId(); - RemoveStep(http_step_iter->second); + uint32 uiChainId = step_iter->second->GetChainId(); + RemoveStep(step_iter->second); if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); @@ -556,15 +556,15 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const CBuf auto cmd_iter = m_mapCmd.find(CMD_REQ_RAW_DATA); if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) { - auto pCmdRaw = std::dynamic_pointer_cast(cmd_iter->second); - if (pCmdRaw == nullptr) + auto pRawCmd = std::dynamic_pointer_cast(cmd_iter->second); + if (pRawCmd == nullptr) { - LOG4_ERROR("cmd %d is not a CmdRaw instance!", CMD_REQ_RAW_DATA); + LOG4_ERROR("cmd %d is not a RawCmd instance!", CMD_REQ_RAW_DATA); return(false); } - return(pCmdRaw->AnyMessage(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes())); + return(pRawCmd->AnyMessage(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes())); } - LOG4_ERROR("no instance of CmdRaw or derived class of CmdRaw found for cmd %d", CMD_REQ_RAW_DATA); + LOG4_ERROR("no instance of RawCmd or derived class of RawCmd found for cmd %d", CMD_REQ_RAW_DATA); return(false); } } @@ -941,6 +941,7 @@ bool ActorBuilder::TransformToSharedStep(Actor* pCreator, std::shared_ptr } else { + LOG4_ERROR("step %u exist.", pSharedStep->GetSequence()); return(false); } } @@ -976,6 +977,7 @@ bool ActorBuilder::TransformToSharedSession(Actor* pCreator, std::shared_ptrGetSessionId().c_str()); return(false); } } @@ -1000,6 +1002,10 @@ bool ActorBuilder::TransformToSharedCmd(Actor* pCreator, std::shared_ptr return(true); } } + else + { + LOG4_ERROR("cmd %d exist.", pSharedCmd->GetCmd()); + } return(false); } @@ -1024,6 +1030,10 @@ bool ActorBuilder::TransformToSharedModule(Actor* pCreator, std::shared_ptrGetModulePath().c_str()); + } return(false); } @@ -1038,6 +1048,10 @@ bool ActorBuilder::TransformToSharedModel(Actor* pCreator, std::shared_ptrGetActorName().c_str()); + } return(false); } @@ -1090,6 +1104,10 @@ bool ActorBuilder::TransformToSharedChain(Actor* pCreator, std::shared_ptrGetSequence()); + } return(false); } diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 19c5bb53..c4c012cf 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -139,7 +139,7 @@ class ActorBuilder bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); public: - bool SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const ReidsMsg& oRedisMsg, uint32 uiStepSeq); + bool SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq); virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); diff --git a/src/actor/cmd/CW.hpp b/src/actor/cmd/CW.hpp index b4ed7d12..d3580a74 100644 --- a/src/actor/cmd/CW.hpp +++ b/src/actor/cmd/CW.hpp @@ -73,6 +73,10 @@ enum E_CMD CMD_REQ_LOG4_TRACE = 401, ///< 分布式网络日志请求 CMD_RSP_LOG4_TRACE = 402, ///< 分布式网络日志响应(无须响应) + CMD_REQ_REDIS_PROXY = 403, ///< redis代理固定虚Cmd,不会在网络中传输,redis代理插件加载时必须配置成CMD_REQ_REDIS_PROXY + CMD_RSP_REDIS_PROXY = 404, ///< 不使用 + CMD_REQ_RAW_DATA = 405, ///< 裸数据传输固定虚Cmd,不会在网络中传输,处理raw数据的插件加载时必须配置成CMD_REQ_RAW_DATA + CMD_RSP_RAW_DATA = 406, ///< 不使用 // 接入层转发命令字,如客户端数据转发给Logic,Logic数据转发给客户端等 CMD_REQ_FROM_CLIENT = 501, ///< 客户端发送过来需由接入层转发的数据,传输的MsgHead里的Cmd不会被改变(无业务逻辑直接转发的场景,如登录等接入层有业务逻辑的场景不适用) diff --git a/src/actor/cmd/RawCmd.hpp b/src/actor/cmd/RawCmd.hpp new file mode 100644 index 00000000..ad6ea70d --- /dev/null +++ b/src/actor/cmd/RawCmd.hpp @@ -0,0 +1,54 @@ +/******************************************************************************* + * Project: Nebula + * @file RawCmd.hpp + * @brief raw请求处理入口 + * @author Bwar + * @date: 2020年12月12日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_RAWCMD_HPP_ +#define SRC_ACTOR_CMD_RAWCMD_HPP_ + +#include "Cmd.hpp" + +namespace neb +{ + +/** + * @brief raw请求处理入口 + * @note 使用Nebula框架开发自定义服务时会用到RawCmd, + * 其他场景不会用到。使用RawCmd在加载插件时,配置文件里的cmd应配置成CMD_REQ_RAW_DATA + * 值为405RedisCmd.,否则框架不能正确调用该插件。 + */ +class RawCmd: public Cmd +{ +public: + RawCmd(int32 iCmd) + : Cmd(iCmd) + { + } + RawCmd(const RawCmd&) = delete; + RawCmd& operator=(const RawCmd&) = delete; + virtual ~RawCmd(){}; + + virtual bool Init() + { + return(true); + } + + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, const MsgBody& oMsgBody) + { + return(false); + } + + virtual bool AnyMessage( + std::shared_ptr pChannel, + const char* pRawData, uint32 uiRawDataSize) = 0; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_RAWCMD_HPP_ */ diff --git a/src/actor/cmd/RedisCmd.hpp b/src/actor/cmd/RedisCmd.hpp new file mode 100644 index 00000000..3f415334 --- /dev/null +++ b/src/actor/cmd/RedisCmd.hpp @@ -0,0 +1,54 @@ +/******************************************************************************* + * Project: Nebula + * @file RedisCmd.hpp + * @brief redis请求处理入口 + * @author Bwar + * @date: 2020年12月12日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_REDISCMD_HPP_ +#define SRC_ACTOR_CMD_REDISCMD_HPP_ + +#include "Cmd.hpp" + +namespace neb +{ + +/** + * @brief redis请求处理入口 + * @note 使用Nebula框架开发redis数据代理或开发基于redis协议的存储服务时会用到RedisCmd, + * 其他场景不会用到。使用RedisCmd在加载插件时,配置文件里的cmd应配置成CMD_REQ_REDIS_PROXY + * 值为403,否则框架不能正确调用该插件。 + */ +class RedisCmd: public Cmd +{ +public: + RedisCmd(int32 iCmd) + : Cmd(iCmd) + { + } + RedisCmd(const RedisCmd&) = delete; + RedisCmd& operator=(const RedisCmd&) = delete; + virtual ~RedisCmd(){}; + + virtual bool Init() + { + return(true); + } + + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, const MsgBody& oMsgBody) + { + return(false); + } + + virtual bool AnyMessage( + std::shared_ptr pChannel, + const RedisMsg& oRedisMsg) = 0; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_REDISCMD_HPP_ */ diff --git a/src/actor/step/RedisStep.cpp b/src/actor/step/RedisStep.cpp index 4b627568..2c16cddf 100644 --- a/src/actor/step/RedisStep.cpp +++ b/src/actor/step/RedisStep.cpp @@ -8,6 +8,7 @@ * Modify history: ******************************************************************************/ #include "actor/step/RedisStep.hpp" +#include namespace neb { diff --git a/src/actor/step/Step.cpp b/src/actor/step/Step.cpp index 347d1978..a4e3d89d 100644 --- a/src/actor/step/Step.cpp +++ b/src/actor/step/Step.cpp @@ -28,6 +28,50 @@ Step::~Step() m_setPreStepSeq.clear(); } +E_CMD_STATUS Step::Callback(std::shared_ptr pChannel, + const MsgHead& oMsgHead, const MsgBody& oMsgBody, void* data) +{ + if (Actor::ACT_PB_STEP != GetActorType()) + { + LOG4_WARNING("got a PbStep callback, but this step is not a PbStep, " + "you need to implement a PbStep callback"); + } + return(CMD_STATUS_FAULT); +} + +E_CMD_STATUS Step::Callback(std::shared_ptr pChannel, + const HttpMsg& oHttpMsg, void* data) +{ + if (Actor::ACT_HTTP_STEP != GetActorType()) + { + LOG4_WARNING("got a HttpStep callback, but this step is not a HttpStep, " + "you need to implement a HttpStep callback"); + } + return(CMD_STATUS_FAULT); +} + +E_CMD_STATUS Step::Callback(std::shared_ptr pChannel, + const RedisReply& oRedisReply) +{ + if (Actor::ACT_REDIS_STEP != GetActorType()) + { + LOG4_WARNING("got a RedisStep callback, but this step is not a RedisStep, " + "you need to implement a RedisStep callback"); + } + return(CMD_STATUS_FAULT); +} + +E_CMD_STATUS Step::Callback(std::shared_ptr pChannel, + const char* pRawData, uint32 uiRawDataSize) +{ + if (Actor::ACT_RAW_STEP != GetActorType()) + { + LOG4_WARNING("got a RawStep callback, but this step is not a RawStep, " + "you need to implement a RawStep callback"); + } + return(CMD_STATUS_FAULT); +} + void Step::NextStep(int iErrno, const std::string& strErrMsg, void* data) { if (iErrno != ERR_OK) diff --git a/src/actor/step/Step.hpp b/src/actor/step/Step.hpp index 7e9b3dec..407154ee 100644 --- a/src/actor/step/Step.hpp +++ b/src/actor/step/Step.hpp @@ -51,6 +51,15 @@ class Step: public Actor return(CMD_STATUS_FAULT); } + virtual E_CMD_STATUS Callback(std::shared_ptr pChannel, + const MsgHead& oMsgHead, const MsgBody& oMsgBody, void* data = NULL); + virtual E_CMD_STATUS Callback(std::shared_ptr pChannel, + const HttpMsg& oHttpMsg, void* data = NULL); + virtual E_CMD_STATUS Callback(std::shared_ptr pChannel, + const RedisReply& oRedisReply); + virtual E_CMD_STATUS Callback(std::shared_ptr pChannel, + const char* pRawData, uint32 uiRawDataSize); + protected: /** * @brief 执行当前步骤接下来的步骤 diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index bcf758ca..42871865 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -184,7 +184,6 @@ E_CODEC_STATUS SocketChannelImpl::Send() LOG4_TRACE("iNeedWriteLen = %d, iWrittenLen = %d", iNeedWriteLen, iWrittenLen); if (iWrittenLen >= 0) { - m_vecStepWaitForConnected.clear(); if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) { @@ -579,7 +578,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); if (m_pCodec == nullptr) { - LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid.") + LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); return(CODEC_STATUS_ERR); } int iReadLen = 0; @@ -637,6 +636,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) m_ucChannelStatus = CHANNEL_STATUS_TRANSFER_TO_WORKER; break; default: + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[from %d to %d] may be a fault.", + m_iFd, m_uiSeq, m_ucChannelStatus, CHANNEL_STATUS_ESTABLISHED); + m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; break; } } @@ -710,6 +712,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) if (m_uiMsgNum == 1) { m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; } } else @@ -787,6 +790,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) if (m_uiMsgNum == 1) { m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; } } else @@ -850,6 +854,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) if (m_uiMsgNum == 1) { m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; } return(CODEC_STATUS_OK); } @@ -862,7 +867,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) m_iFd, m_iErrno, m_strErrMsg.c_str()); if (m_pRecvBuff->ReadableBytes() > 0) { - oRawBuff->Write(m_pRecvBuff, m_pRecvBuff->ReadableBytes()); + oRawBuff.Write(m_pRecvBuff, m_pRecvBuff->ReadableBytes()); } return(CODEC_STATUS_EOF); } @@ -896,6 +901,11 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) m_uiForeignSeq = oMsgHead.seq(); ++m_uiUnitTimeMsgNum; ++m_uiMsgNum; + if (m_uiMsgNum == 1) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; + } LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_uiSeq, oMsgHead.cmd(), oMsgHead.seq()); } return(eCodecStatus); @@ -915,6 +925,11 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) { ++m_uiUnitTimeMsgNum; ++m_uiMsgNum; + if (m_uiMsgNum == 1) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; + } } return(eCodecStatus); } @@ -932,6 +947,11 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(RedisReply& oRedisReply) { ++m_uiUnitTimeMsgNum; ++m_uiMsgNum; + if (m_uiMsgNum == 1) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; + } } return(eCodecStatus); } @@ -948,6 +968,11 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(CBuffer& oRawBuff) { ++m_uiUnitTimeMsgNum; ++m_uiMsgNum; + if (m_uiMsgNum == 1) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; + } return(CODEC_STATUS_OK); } return(CODEC_STATUS_PAUSE); diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 1a0782e8..3d879dfb 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -217,6 +217,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) } break; case CODEC_UNKNOW: + { CBuffer oBuff; for (int i = 0; ; ++i) { @@ -239,6 +240,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) break; } } + } break; default: for (int i = 0; ; ++i) @@ -344,6 +346,7 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) } break; case CODEC_UNKNOW: + { CBuffer oBuff; for (int i = 0; ; ++i) { @@ -358,6 +361,7 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) break; } } + } break; default: for (int i = 0; ; ++i) @@ -730,7 +734,7 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 { if (0 != oMsgBody.req_target().route_id()) { - return(SendOriented(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, eCodecType, pSender)); + return(SendOriented(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, eCodecType)); } else if (oMsgBody.req_target().route().length() > 0) { @@ -747,7 +751,7 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 } else { - return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType, pSender)); + return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); } } else @@ -798,10 +802,6 @@ bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBod LOG4_ERROR("no connected channel to manager!"); return(false); } - if (nullptr != pSender) - { - (const_cast(oMsgBody)).set_trace_id(pSender->GetTraceId()); - } E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); if (CODEC_STATUS_OK == eStatus) { diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index d130226a..3c78dc77 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -120,13 +120,13 @@ class Dispatcher template bool SendTo(const std::string& strHost, int iPort, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); template - bool Dispatcher::AutoSend(const std::string& strIdentify, const std::string& strHost, int iPort, int iRemoteWorkerIndex, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); - bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); - bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); - bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); - bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); - bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA, Actor* pSender = nullptr); - bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, Actor* pSender); + bool AutoSend(const std::string& strIdentify, const std::string& strHost, int iPort, int iRemoteWorkerIndex, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); + bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); std::shared_ptr StressSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); // SendTo() for unix domain socket diff --git a/src/util/CBuffer.hpp b/src/util/CBuffer.hpp index e7b3c5dc..d154b943 100644 --- a/src/util/CBuffer.hpp +++ b/src/util/CBuffer.hpp @@ -77,7 +77,7 @@ class CBuffer { m_write_idx += step; } - inline bool Readable() + inline bool Readable() const { return m_write_idx > m_read_idx; } @@ -85,7 +85,7 @@ class CBuffer { return m_buffer_len > m_write_idx; } - inline size_t ReadableBytes() + inline size_t ReadableBytes() const { return Readable() ? m_write_idx - m_read_idx : 0; } diff --git a/src/util/StringCoder.cpp b/src/util/StringCoder.cpp index 38881688..cbd21086 100644 --- a/src/util/StringCoder.cpp +++ b/src/util/StringCoder.cpp @@ -180,4 +180,25 @@ void DecodeParameter(const std::string& strParameter, std::map& vecDest) +{ + vecDest.clear(); + size_t uiBegin = 0; + size_t uiEnd = 0; + std::string strSub; + while (uiBegin < strSrc.size()) + { + uiEnd = strSrc.find_first_of(strPattern, uiBegin); + if (std::string::npos == uiEnd) + { + strSub = strSrc.substr(uiBegin); + vecDest.push_back(strSub); + break; + } + strSub = strSrc.substr(uiBegin, uiEnd - uiBegin); + vecDest.push_back(strSub); + uiBegin = uiEnd + 1; + } +} + } /* namespace neb */ diff --git a/src/util/StringCoder.hpp b/src/util/StringCoder.hpp index 81d0efde..7b18bdd8 100644 --- a/src/util/StringCoder.hpp +++ b/src/util/StringCoder.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace neb { @@ -37,6 +38,7 @@ void EncodeParameter(const std::map& mapParameters, st void DecodeParameter(const std::string& strParameter, std::map& mapParameters); +void Split(const std::string& strSrc, const std::string& strPattern, std::vector& vecDest); } /* namespace neb */ From 75acaec23416bcd62bf882febc8f0a58257cca6e Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 20 Dec 2020 13:25:14 +0800 Subject: [PATCH 113/176] add log flush config; new version test passing --- conf/nebula.json | 1 + src/actor/Actor.cpp | 5 +++ src/actor/Actor.hpp | 5 +-- src/actor/ActorBuilder.cpp | 4 +-- src/ios/Dispatcher.cpp | 12 +++++++ src/ios/Dispatcher.hpp | 12 +++++++ src/ios/Nodes.cpp | 73 ++++++++++++++++++++++++++++++++++++++ src/ios/Nodes.hpp | 7 ++-- src/labor/Manager.cpp | 2 ++ src/labor/Worker.cpp | 9 ++++- src/logger/FileLogger.cpp | 19 +++++++--- src/logger/FileLogger.hpp | 4 ++- src/logger/NetLogger.cpp | 6 ++-- src/logger/NetLogger.hpp | 1 + 14 files changed, 144 insertions(+), 16 deletions(-) diff --git a/conf/nebula.json b/conf/nebula.json index c1ad0b0b..fd2e328c 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -44,6 +44,7 @@ "max_log_file_num": 5, "//max_log_file_size": "单个日志文件大小限制", "max_log_file_size": 20480000, + "always_flush_log":true, "//permission": "限制。addr_permit为连接限制,限制每个IP在统计时间内连接次数;uin_permit为消息数量限制,限制每个用户在单位统计时间内发送消息数量。", "permission": { "addr_permit": { "stat_interval": 60.0, "permit_num": 1000000000 }, diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index d06d311e..7ae830dd 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -70,6 +70,11 @@ const std::string& Actor::GetNodeIdentify() const return(m_pLabor->GetNodeInfo().strNodeIdentify); } +const NodeInfo& Actor::GetNodeInfo() const +{ + return(m_pLabor->GetNodeInfo()); +} + ev_tstamp Actor::GetDataReportInterval() const { return(m_pLabor->GetNodeInfo().dDataReportInterval); diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 9bd9a69a..2e250f21 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -109,6 +109,7 @@ class Actor: public std::enable_shared_from_this const std::string& GetNodeType() const; const std::string& GetWorkPath() const; const std::string& GetNodeIdentify() const; + const NodeInfo& GetNodeInfo() const; time_t GetNowTime() const; long GetNowTimeMs() const; ev_tstamp GetDataReportInterval() const; @@ -209,8 +210,8 @@ class Actor: public std::enable_shared_from_this * @param uiStepSeq 应用层无用参数,框架层的系统Actor会用到 * @return 是否发送成功 */ - bool SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); - bool SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); + virtual bool SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); + virtual bool SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); /** * @brief 发送raw请求 diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 76cc74ed..db299a2e 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -347,10 +347,10 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) { - LOG4_DEBUG("oInHttpMsg.type() = %d, oInHttpMsg.path() = %s", - oHttpMsg.type(), oHttpMsg.path().c_str()); if (HTTP_REQUEST == oHttpMsg.type()) // 新请求 { + LOG4_DEBUG("oInHttpMsg.type() = %d, oInHttpMsg.path() = %s", + oHttpMsg.type(), oHttpMsg.path().c_str()); auto module_iter = m_mapModule.find(oHttpMsg.path()); if (module_iter == m_mapModule.end()) { diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 3d879dfb..cdbf62ca 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -875,7 +875,19 @@ std::shared_ptr Dispatcher::StressSend(const std::string& strIden x_sock_set_block(iFd, 0); int nREUSEADDR = 1; + int iKeepAlive = 1; + int iKeepIdle = 60; + int iKeepInterval = 5; + int iKeepCount = 3; + int iTcpNoDelay = 1; + int iTcpQuickAck = 1; setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); + setsockopt(iFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)); + setsockopt(iFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)); + setsockopt(iFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)); + setsockopt(iFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)); + setsockopt(iFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)); + setsockopt(iFd, IPPROTO_TCP, TCP_QUICKACK, (void*)&iTcpQuickAck, sizeof(iTcpQuickAck)); std::shared_ptr pChannel = CreateSocketChannel(iFd, eCodecType); if (nullptr != pChannel) { diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 3c78dc77..39ad8508 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -388,7 +388,19 @@ bool Dispatcher::AutoSend( x_sock_set_block(iFd, 0); int nREUSEADDR = 1; + int iKeepAlive = 1; + int iKeepIdle = 60; + int iKeepInterval = 5; + int iKeepCount = 3; + int iTcpNoDelay = 1; + int iTcpQuickAck = 1; setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); + setsockopt(iFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)); + setsockopt(iFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)); + setsockopt(iFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)); + setsockopt(iFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)); + setsockopt(iFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)); + setsockopt(iFd, IPPROTO_TCP, TCP_QUICKACK, (void*)&iTcpQuickAck, sizeof(iTcpQuickAck)); std::shared_ptr pChannel = CreateSocketChannel(iFd, eCodecType, true, bWithSsl); if (nullptr != pChannel) { diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index b4aa1912..2c8072d3 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -147,6 +147,79 @@ bool Nodes::GetNode(const std::string& strNodeType, std::unordered_set pNode = std::make_shared(); + m_mapNode.insert(std::make_pair(strNodeType, pNode)); + std::string strHashKey; + char szVirtualNodeIdentify[40] = {0}; + uint32 uiKeyHash = 0; + std::vector vecHash; + for (int i = 0; i < m_iVirtualNodeNum; ++i) + { + snprintf(szVirtualNodeIdentify, 40, "%d@%s#%d", m_iVirtualNodeNum - i, strNodeIdentify.c_str(), i); + strHashKey = szVirtualNodeIdentify; + switch (m_iHashAlgorithm) + { + case HASH_cityhash_32: + uiKeyHash = CityHash32(strHashKey.c_str(), strHashKey.size()); + break; + case HASH_fnv1_64: + uiKeyHash = hash_fnv1_64(strHashKey.c_str(), strHashKey.size()); + break; + case HASH_murmur3_32: + uiKeyHash = murmur3_32(strHashKey.c_str(), strHashKey.size(), 0x000001b3); + break; + default: + uiKeyHash = hash_fnv1a_64(strHashKey.c_str(), strHashKey.size()); + } + vecHash.push_back(uiKeyHash); + pNode->mapHash2Node.insert(std::make_pair(uiKeyHash, strNodeIdentify)); + } + pNode->mapNode2Hash.insert(std::make_pair(strNodeIdentify, std::move(vecHash))); + pNode->itPollingNode = pNode->mapNode2Hash.begin(); + pNode->itHashRing = pNode->mapHash2Node.begin(); + } + else + { + auto node_iter = node_type_iter->second->mapNode2Hash.find(strNodeIdentify); + if (node_iter == node_type_iter->second->mapNode2Hash.end()) + { + std::string strHashKey; + char szVirtualNodeIdentify[40] = {0}; + uint32 uiKeyHash = 0; + std::vector vecHash; + for (int i = 0; i < m_iVirtualNodeNum; ++i) + { + snprintf(szVirtualNodeIdentify, 40, "%d@%s#%d", m_iVirtualNodeNum - i, strNodeIdentify.c_str(), i); + strHashKey = szVirtualNodeIdentify; + switch (m_iHashAlgorithm) + { + case HASH_cityhash_32: + uiKeyHash = CityHash32(strHashKey.c_str(), strHashKey.size()); + break; + case HASH_fnv1_64: + uiKeyHash = hash_fnv1_64(strHashKey.c_str(), strHashKey.size()); + break; + case HASH_murmur3_32: + uiKeyHash = murmur3_32(strHashKey.c_str(), strHashKey.size(), 0x000001b3); + break; + default: + uiKeyHash = hash_fnv1a_64(strHashKey.c_str(), strHashKey.size()); + } + vecHash.push_back(uiKeyHash); + node_type_iter->second->mapHash2Node.insert(std::make_pair(uiKeyHash, strNodeIdentify)); + } + node_type_iter->second->mapNode2Hash.insert(std::make_pair(strNodeIdentify, std::move(vecHash))); + node_type_iter->second->itPollingNode = node_type_iter->second->mapNode2Hash.begin(); + node_type_iter->second->itHashRing = node_type_iter->second->mapHash2Node.begin(); + } + } +} + +void Nodes::AddNodeKetama(const std::string& strNodeType, const std::string& strNodeIdentify) { auto node_type_iter = m_mapNode.find(strNodeType); if (node_type_iter == m_mapNode.end()) diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index 261caad7..6d310fd1 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -42,10 +42,10 @@ class Nodes public: /** * @note 节点管理Session构造函数 + * @param iHashAlgorithm hash算法 * @param iVirtualNodeNum 每个实体节点对应的虚拟节点数量 - * @param dSessionTimeout 超时时间,0表示永不超时 */ - Nodes(int iHashAlgorithm = HASH_cityhash_32, int iVirtualNodeNum = 200); + Nodes(int iHashAlgorithm = HASH_murmur3_32, int iVirtualNodeNum = 200); virtual ~Nodes(); /* 实体节点hash信息 @@ -88,13 +88,16 @@ class Nodes /** * @brief 添加节点 * @note 添加节点信息,每个节点均有一个主节点一个被节点构成。 + * @param strNodeType 节点类型 * @param strNodeIdentify 节点标识 */ void AddNode(const std::string& strNodeType, const std::string& strNodeIdentify); + void AddNodeKetama(const std::string& strNodeType, const std::string& strNodeIdentify); /** * @brief 删除节点 * @note 删除节点信息,每个节点均有一个主节点一个被节点构成。 + * @param strNodeType 节点类型 * @param strNodeIdentify 节点标识 */ void DelNode(const std::string& strNodeType, const std::string& strNodeIdentify); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 5f9bf576..4f620c3d 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -134,6 +134,7 @@ bool Manager::InitLogger(const CJsonObject& oJsonConf) int32 iMaxLogFileSize = 0; int32 iMaxLogFileNum = 0; int32 iMaxLogLineLen = 1024; + bool bAlwaysFlushLog = true; std::string strLoggingHost; std::string strLogname = m_stNodeInfo.strWorkPath + std::string("/") + oJsonConf("log_path") + std::string("/") + getproctitle() + std::string(".log"); @@ -144,6 +145,7 @@ bool Manager::InitLogger(const CJsonObject& oJsonConf) oJsonConf.Get("max_log_file_num", iMaxLogFileNum); oJsonConf.Get("log_max_line_len", iMaxLogLineLen); oJsonConf.Get("log_level", iLogLevel); + oJsonConf.Get("always_flush_log", bAlwaysFlushLog); m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, iMaxLogLineLen, this); m_pLogger->SetNetLogLevel(iNetLogLevel); LOG4_NOTICE("%s program begin, and work path %s...", oJsonConf("server_name").c_str(), m_stNodeInfo.strWorkPath.c_str()); diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index bab3604a..84293e9a 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -129,10 +129,15 @@ bool Worker::Init(CJsonObject& oJsonConf) { m_stNodeInfo.dStepTimeout = 0.5; } + m_stNodeInfo.uiWorkerNum = strtoul(oJsonConf("worker_num").c_str(), NULL, 10); + oJsonConf.Get("data_report", m_stNodeInfo.dDataReportInterval); oJsonConf.Get("node_type", m_stNodeInfo.strNodeType); oJsonConf.Get("host", m_stNodeInfo.strHostForServer); oJsonConf.Get("port", m_stNodeInfo.iPortForServer); - oJsonConf.Get("data_report", m_stNodeInfo.dDataReportInterval); + oJsonConf.Get("access_host", m_stNodeInfo.strHostForClient); + oJsonConf.Get("access_port", m_stNodeInfo.iPortForClient); + oJsonConf.Get("gateway", m_stNodeInfo.strGateway); + oJsonConf.Get("gateway_port", m_stNodeInfo.iGatewayPort); m_oNodeConf = oJsonConf; m_oCustomConf = oJsonConf["custom"]; std::ostringstream oss; @@ -239,6 +244,7 @@ bool Worker::InitLogger(const CJsonObject& oJsonConf, const std::string& strLogN int32 iMaxLogLineLen = 1024; int32 iLogLevel = 0; int32 iNetLogLevel = 0; + bool bAlwaysFlushLog = true; std::string strLogname = m_stNodeInfo.strWorkPath + std::string("/") + oJsonConf("log_path") + std::string("/") + strLogNameBase + std::string(".log"); std::string strParttern = "[%D,%d{%q}][%p] [%l] %m%n"; @@ -247,6 +253,7 @@ bool Worker::InitLogger(const CJsonObject& oJsonConf, const std::string& strLogN oJsonConf.Get("log_max_line_len", iMaxLogLineLen); oJsonConf.Get("net_log_level", iNetLogLevel); oJsonConf.Get("log_level", iLogLevel); + oJsonConf.Get("always_flush_log", bAlwaysFlushLog); m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, iMaxLogLineLen, this); m_pLogger->SetNetLogLevel(iNetLogLevel); LOG4_NOTICE("%s program begin...", getproctitle()); diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp index df84af9e..629986aa 100644 --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -27,9 +27,9 @@ namespace neb FileLogger* FileLogger::s_pInstance = nullptr; FileLogger::FileLogger(const std::string& strLogFile, int iLogLev, - unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex) + unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex, bool bAlwaysFlush) : m_iLogLevel(iLogLev), m_uiLogNum(0), m_uiMaxFileSize(uiMaxFileSize), - m_uiMaxRollFileIndex(uiMaxRollFileIndex), m_strLogFileBase(strLogFile) + m_uiMaxRollFileIndex(uiMaxRollFileIndex), m_bAlwaysFlush(bAlwaysFlush), m_strLogFileBase(strLogFile) { #if __GNUC__ < 5 m_szTime = (char*)malloc(20); @@ -70,7 +70,10 @@ int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLi fprintf(m_fp, "\n"); - fflush(m_fp); + if (m_bAlwaysFlush) + { + fflush(m_fp); + } ++m_uiLogNum; return 0; @@ -97,7 +100,10 @@ int FileLogger::WriteLog(const std::string& strTraceId, int iLev, const char* sz fprintf(m_fp, "\n"); ++m_uiLogNum; - fflush(m_fp); + if (m_bAlwaysFlush) + { + fflush(m_fp); + } return 0; } @@ -233,7 +239,10 @@ int FileLogger::Vappend(const std::string& strTraceId, int iLev, const char* szF fprintf(m_fp, oss.str().c_str()); //fprintf(m_fp, "[%s] [%s,%03d] ", std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S").c_str(), duration_in_ms.count() % 1000, Logger::LogLevMsg[iLev].c_str()); vfprintf(m_fp, szLogStr, ap); - fflush(m_fp); + if (m_bAlwaysFlush) + { + fflush(m_fp); + } return 0; } diff --git a/src/logger/FileLogger.hpp b/src/logger/FileLogger.hpp index 96da4cd4..bb6ab072 100644 --- a/src/logger/FileLogger.hpp +++ b/src/logger/FileLogger.hpp @@ -24,7 +24,8 @@ class FileLogger: public Logger const std::string& strLogFile, int iLogLev = Logger::INFO, unsigned int uiMaxFileSize = neb::gc_uiMaxLogFileSize, - unsigned int uiMaxRollFileIndex = neb::gc_uiMaxRollLogFileIndex); + unsigned int uiMaxRollFileIndex = neb::gc_uiMaxRollLogFileIndex, + bool bAlwaysFlush = true); virtual ~FileLogger() { #if __GNUC__ < 5 @@ -70,6 +71,7 @@ class FileLogger: public Logger unsigned int m_uiLogNum; unsigned int m_uiMaxFileSize; // 日志文件大小 unsigned int m_uiMaxRollFileIndex; // 滚动日志文件数量 + bool m_bAlwaysFlush; std::string m_strLogFileBase; // 日志文件基本名(如 log/program_name.log) }; diff --git a/src/logger/NetLogger.cpp b/src/logger/NetLogger.cpp index 16660848..095e67ab 100644 --- a/src/logger/NetLogger.cpp +++ b/src/logger/NetLogger.cpp @@ -21,15 +21,15 @@ namespace neb { NetLogger::NetLogger(const std::string strLogFile, int iLogLev, unsigned int uiMaxFileSize, - unsigned int uiMaxRollFileIndex, unsigned int uiMaxLogLineLen, Labor* pLabor) + unsigned int uiMaxRollFileIndex, unsigned int uiMaxLogLineLen, bool bAlwaysFlush, Labor* pLabor) : m_pLogBuff(NULL), m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), m_uiMaxLogLineLen(uiMaxFileSize), m_bEnableNetLogger(false), m_pLabor(pLabor), m_pLog(nullptr) { m_pLogBuff = (char*)malloc(m_uiMaxLogLineLen); #if __cplusplus >= 201401L - m_pLog = std::make_unique(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex); + m_pLog = std::make_unique(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex, bAlwaysFlush); #else - m_pLog = std::unique_ptr(new FileLogger(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex)); + m_pLog = std::unique_ptr(new FileLogger(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex, bAlwaysFlush)); #endif } diff --git a/src/logger/NetLogger.hpp b/src/logger/NetLogger.hpp index 828532e6..cad61b22 100644 --- a/src/logger/NetLogger.hpp +++ b/src/logger/NetLogger.hpp @@ -28,6 +28,7 @@ class NetLogger: public Logger unsigned int uiMaxFileSize = gc_uiMaxLogFileSize, unsigned int uiMaxRollFileIndex = gc_uiMaxRollLogFileIndex, unsigned int uiMaxLogLineLen = gc_uiMaxLogLineLen, + bool bAlwaysFlush = true, Labor* pLabor = nullptr); virtual ~NetLogger(); From 4a38f8a05a29b57068f1c3d1aba2f5025bec1775 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 26 Dec 2020 21:15:19 +0800 Subject: [PATCH 114/176] CodecResp bug fixed --- src/codec/CodecResp.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index 95ac540e..b215f84a 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -285,7 +285,7 @@ E_CODEC_STATUS CodecResp::DecodeInteger(CBuffer* pBuff, RedisReply& oReply) const char* pPartBegin = pData; for (size_t i = 0; i < uiReadableBytes; ++i) { - switch (*pData) + switch (pData[i]) { case '\n': if ('\r' == cLastChar) @@ -376,6 +376,7 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) size_t uiReadableBytes = pBuff->ReadableBytes(); char cLastChar = 0; int32 iArraySize = 0; + size_t uiReadIndex = pBuff->GetReadIndex(); const char* pData = pBuff->GetRawReadBuffer(); const char* pPartBegin = pData; for (size_t i = 0; i < uiReadableBytes; ++i) @@ -397,8 +398,9 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) else { oReply.set_type(REDIS_REPLY_ARRAY); - for (int32 i = 0; i < iArraySize; ++i) + for (int32 j = 0; j < iArraySize; ++j) { + uiReadIndex = pBuff->GetReadIndex(); char cFirstByte = 0; auto pElement = oReply.add_element(); pBuff->ReadByte(cFirstByte); @@ -429,6 +431,7 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) { return(eStatus); } + i += (pBuff->GetReadIndex() - uiReadIndex); } return(CODEC_STATUS_OK); } From a2e38c7952d9f8cc9520b9c8096449590cfde11f Mon Sep 17 00:00:00 2001 From: nebim Date: Fri, 1 Jan 2021 17:18:04 +0800 Subject: [PATCH 115/176] =?UTF-8?q?=E5=A2=9E=E5=8A=A0redis=20cluster?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actor/step/RedisStep.cpp | 16 + src/actor/step/RedisStep.hpp | 4 +- src/actor/step/sys_step/StepRedisCluster.cpp | 1029 ++++++++++++++++++ src/actor/step/sys_step/StepRedisCluster.hpp | 128 +++ src/util/encrypt/crc16.c | 88 ++ src/util/encrypt/crc16.h | 11 + 6 files changed, 1275 insertions(+), 1 deletion(-) create mode 100644 src/actor/step/sys_step/StepRedisCluster.cpp create mode 100644 src/actor/step/sys_step/StepRedisCluster.hpp create mode 100644 src/util/encrypt/crc16.c create mode 100644 src/util/encrypt/crc16.h diff --git a/src/actor/step/RedisStep.cpp b/src/actor/step/RedisStep.cpp index 2c16cddf..8cd79e1c 100644 --- a/src/actor/step/RedisStep.cpp +++ b/src/actor/step/RedisStep.cpp @@ -73,4 +73,20 @@ const RedisRequest& RedisStep::GenrateRedisRequest() return(m_oRedisRequest); } +std::shared_ptr RedisStep::MutableRedisRequest() +{ + auto pRequest = std::make_shared(); + pRequest->set_type(REDIS_REPLY_ARRAY); + auto pElement = pRequest->add_element(); + pElement->set_type(REDIS_REPLY_STRING); + pElement->set_str(m_strCmd); + for (size_t i = 0; i < m_vecCmdArguments.size(); ++i) + { + pElement = m_oRedisRequest.add_element(); + pElement->set_type(REDIS_REPLY_STRING); + pElement->set_str(m_vecCmdArguments[i].first); + } + return(pRequest); +} + } /* namespace neb */ diff --git a/src/actor/step/RedisStep.hpp b/src/actor/step/RedisStep.hpp index 10245218..d095e117 100644 --- a/src/actor/step/RedisStep.hpp +++ b/src/actor/step/RedisStep.hpp @@ -80,9 +80,11 @@ class RedisStep: public Step return(m_vecCmdArguments); } -protected: const RedisRequest& GenrateRedisRequest(); +protected: + std::shared_ptr MutableRedisRequest(); + private: std::string m_strErr; std::string m_strHashKey; diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp new file mode 100644 index 00000000..37345766 --- /dev/null +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -0,0 +1,1029 @@ +/******************************************************************************* + * Project: Nebula + * @file StepRedisCluster.hpp + * @brief + * @author nebim + * @date: 2020-12-12 + * @note + * Modify history: + ******************************************************************************/ + +#include "StepRedisCluster.hpp" +#include +#include "util/StringCoder.hpp" +#include "ios/Dispatcher.hpp" +#include "util/StringCoder.hpp" +#include "util/encrypt/crc16.h" + +namespace neb +{ + +const uint16 StepRedisCluster::sc_unClusterSlots = 16384; + +const std::unordered_set StepRedisCluster::s_setSupportExtractCmd = +{ + // strings + "APPEND","BITCOUNT","BITFIELD","BITPOS","DECR","DECRBY","GET", + "GETBIT","GETRANGE","GETSET","INCR","INCRBY","INCRBYFLOAT","MGET", + "MSET","MSETNX","PSETEX","SET","SETBIT","SETEX","SETNX","SETRANGE", + "STRLEN", + // hashes + "HDEL","HEXISTS","HGET","HGETALL","HINCRBY","HINCRBYFLOAT","HKEYS", + "HLEN","HMGET","HMSET","HSET","HSETNX","HSTRLEN","HVALS","HSCAN", + // lists + "LINDEX","LINSERT","LLEN","LPOP","LPOS","LPUSH","LPUSHX","LRANGE", + "LREM","LSET","LTRIM","RPOP","PROPLPUSH","RPUSH","RPUSHX", + // sets + "SADD","SCARD","SISMEMBER","SMISMEMBER","SMEMBERS","SPOP", + "SRANDMEMBER","SREM","SSCAN", + // sorted sets + "ZADD","ZCARD","ZCOUNT","ZINCRBY","ZLEXCOUNT","ZPOPMAX","ZPOPMIN", + "ZRANGE","ZRANGEBYLEX","ZREVRANGEBYLEX","ZRANGEBYSCORE","ZRANK", + "ZREM","ZREMRANGEBYLEX","ZREMRANGEBYRANK","ZREMRANGEBYSCORE", + "ZREVRANGE","ZREVRANGEBYSCORE","ZREVRANK","ZSCORE","ZMSCORE","ZSCAN", + // keys + "DEL","DUMP","EXISTS","EXPIRE","EXPIREAT","MOVE","PERSIST","PEXPIRE", + "PEXPIREAT","PTTL","RANDOMKEY","RESTORE","SORT","TOUCH","TTL","TYPE", + "UNLINK" +}; + +const std::unordered_set StepRedisCluster::s_setReadCmd = +{ + // strings + "BITCOUNT","BITPOS","GET","GETBIT","GETRANGE","MGET","STRLEN", + // hashes + "HEXISTS","HGET","HGETALL","HKEYS","HLEN","HMGET","HSTRLEN","HVALS","HSCAN", + // lists + "LINDEX","LLEN","LRANGE", + // sets + "SCARD","SISMEMBER","SMISMEMBER","SMEMBERS","SRANDMEMBER","SSCAN", + // sorted sets + "ZCARD","ZCOUNT","ZLEXCOUNT","ZRANGE","ZRANGEBYLEX","ZRANGEBYSCORE","ZRANK", + "ZREVRANGE","ZREVRANGEBYLEX","ZREVRANGEBYSCORE","ZREVRANK", + "ZSCORE","ZMSCORE","ZSCAN", + // keys + "DUMP","EXISTS","PTTL","RANDOMKEY","TTL","TYPE" +}; + +const std::unordered_set StepRedisCluster::s_setWriteCmd = +{ + // strings + "APPEND","BITFIELD","DECR","DECRBY","GETSET","INCR","INCRBY","INCRBYFLOAT", + "MSET","MSETNX","PSETEX","SET","SETBIT","SETEX","SETNX","SETRANGE", + // hashes + "HDEL","HINCRBY","HINCRBYFLOAT","HMSET","HSET","HSETNX", + // lists + "LINSERT","LPOS","LPUSH","LPUSHX","LREM","LSET","LTRIM","RPOP", + "PROPLPUSH","RPUSH","RPUSHX", + // sets + "SADD","SPOP","SREM", + // sorted sets + "ZADD","ZINCRBY","ZPOPMAX","ZPOPMIN", + "ZREM","ZREMRANGEBYLEX","ZREMRANGEBYRANK","ZREMRANGEBYSCORE", + // keys + "DEL","EXPIREAT","MOVE","PERSIST","PEXPIRE","PEXPIREAT", + "RESTORE","SORT","TOUCH","UNLINK" +}; + +const std::unordered_set StepRedisCluster::s_setMultipleKeyCmd = +{ + "MGET","DEL","EXISTS","TOUCH","UNLINK" +}; + +const std::unordered_set StepRedisCluster::s_setMultipleKeyValueCmd = +{ + "MSET","MSETNX" +}; + +StepRedisCluster::StepRedisCluster( + const std::string& strIdentify, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) + : RedisStep(nullptr, 10.0), + m_bWithSsl(bWithSsl), m_bPipeline(bPipeline), m_bEnableReadOnly(bEnableReadOnly), + m_uiAddressIndex(0), + m_strIdentify(strIdentify) +{ + Split(strIdentify, ",", m_vecAddress); +} + +StepRedisCluster::~StepRedisCluster() +{ +} + +E_CMD_STATUS StepRedisCluster::Emit(int iErrno, const std::string& strErrMsg, void* data) +{ + SendCmdClusterSlots(); + return(CMD_STATUS_RUNNING); +} + +E_CMD_STATUS StepRedisCluster::Callback( + std::shared_ptr pChannel, const RedisReply& oRedisReply) +{ + LOG4_TRACE("callback from %s:\n%s", pChannel->GetIdentify().c_str(), oRedisReply.DebugString().c_str()); + auto step_iter = m_mapPipelineRequest.find(pChannel->GetIdentify()); + if (step_iter == m_mapPipelineRequest.end() || step_iter->second.size() == 0) + { + LOG4_ERROR("no \"%s\" found in m_mapPipelineStep", pChannel->GetIdentify().c_str()); + } + auto pRedisRequest = step_iter->second.front(); + uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); + step_iter->second.pop(); + if (uiRealStepSeq == GetSequence()) + { + if (pRedisRequest->element(0).str() == "ASK") + { + CmdAskingCallback(pChannel, pChannel->GetIdentify(), oRedisReply); + } + else if (pRedisRequest->element(0).str() == "READONLY") + { + CmdReadOnlyCallback(pChannel, pChannel->GetIdentify(), oRedisReply); + } + else + { + CmdClusterSlotsCallback(oRedisReply); + SendWaittingRequest(); + } + } + else + { + auto num_iter = m_mapStepEmitNum.find(uiRealStepSeq); + if (num_iter == m_mapStepEmitNum.end()) // 单key请求的响应 + { + if (REDIS_REPLY_ERROR == oRedisReply.type()) + { + std::vector vecMsg; + Split(oRedisReply.str(), " ", vecMsg); + if (vecMsg.size() >= 3) + { + if (vecMsg[0] == "MOVED") + { + SendTo(vecMsg[2], pRedisRequest); + SendCmdClusterSlots(); + return(CMD_STATUS_RUNNING); + } + if (vecMsg[0] == "ASK") + { + AddToAskingQueue(vecMsg[2], pRedisRequest); + SendCmdAsking(vecMsg[2]); + return(CMD_STATUS_RUNNING); + } + } + } + GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oRedisReply, uiRealStepSeq); + } + else // 多key请求的响应 + { + if (REDIS_REPLY_ARRAY == oRedisReply.type()) + { + auto reply_iter = m_mapReply.find(uiRealStepSeq); + if (reply_iter == m_mapReply.end()) + { + LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); + return(CMD_STATUS_RUNNING); + } + std::vector vecElementIndex; + for (int i = 1; i < pRedisRequest->element_size(); ++i) // element(0).str() is cmd + { + if (pRedisRequest->element(i).integer() > 0 || i == 1) + { + vecElementIndex.push_back((uint32)pRedisRequest->element(i).integer()); + } + } + for (uint32 j = 0; j < vecElementIndex.size(); ++j) + { + if (vecElementIndex[j] < reply_iter->second.size()) + { + if (reply_iter->second[vecElementIndex[j]] == nullptr) + { + reply_iter->second[vecElementIndex[j]] = new RedisReply(); + } + reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply.element(j)); + } + else + { + LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); + } + } + uint32 uiReqReplyOdd = (pRedisRequest->element_size() - 1) % oRedisReply.element_size(); + if (uiReqReplyOdd != 0) + { + LOG4_ERROR("request and reply not match for %s", pRedisRequest->element(0).str().c_str()); + } + num_iter->second--; + if (num_iter->second == 0) + { + RedisReply oFinalReply; + oFinalReply.set_type(oRedisReply.type()); + for (size_t k = 0; k < reply_iter->second.size(); ++k) + { + oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); + reply_iter->second[k] = nullptr; + } + GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); + m_mapStepEmitNum.erase(num_iter); + m_mapReply.erase(reply_iter); + } + } + else + { + if (REDIS_REPLY_ERROR == oRedisReply.type()) + { + std::vector vecMsg; + Split(oRedisReply.str(), " ", vecMsg); + if (vecMsg.size() >= 3) + { + if (vecMsg[0] == "MOVED") + { + SendTo(vecMsg[2], pRedisRequest); + SendCmdClusterSlots(); + return(CMD_STATUS_RUNNING); + } + if (vecMsg[0] == "ASK") + { + AddToAskingQueue(vecMsg[2], pRedisRequest); + SendCmdAsking(vecMsg[2]); + return(CMD_STATUS_RUNNING); + } + if (vecMsg[0] == "CROSSSLOT") + { + SendCmdClusterSlots(); + } + } + } + auto reply_iter = m_mapReply.find(uiRealStepSeq); + if (reply_iter == m_mapReply.end()) + { + LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); + return(CMD_STATUS_RUNNING); + } + std::vector vecElementIndex; + for (int i = 1; i < pRedisRequest->element_size(); ++i) // element(0).str() is cmd + { + if (pRedisRequest->element(i).integer() > 0 || i == 1) + { + vecElementIndex.push_back((uint32)pRedisRequest->element(i).integer()); + } + } + for (uint32 j = 0; j < vecElementIndex.size(); ++j) + { + if (vecElementIndex[j] < reply_iter->second.size()) + { + if (reply_iter->second[vecElementIndex[j]] == nullptr) + { + reply_iter->second[vecElementIndex[j]] = new RedisReply(); + } + reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply); + } + else + { + LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); + } + } + num_iter->second--; + if (num_iter->second == 0) + { + RedisReply oFinalReply; + oFinalReply.set_type(oRedisReply.type()); + for (size_t k = 0; k < reply_iter->second.size(); ++k) + { + oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); + reply_iter->second[k] = nullptr; + } + GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); + m_mapStepEmitNum.erase(num_iter); + m_mapReply.erase(reply_iter); + } + } + } + } + return(CMD_STATUS_RUNNING); +} + +E_CMD_STATUS StepRedisCluster::Timeout() +{ + for (auto timeout_iter = m_mapTimeoutStep.begin(); timeout_iter != m_mapTimeoutStep.end(); ) + { + if (GetNowTime() - timeout_iter->first >= GetTimeout()) + { + for (uint32 i = 0; i < timeout_iter->second.size(); ++i) + { + auto reply_iter = m_mapReply.find(timeout_iter->second[i]); + if (reply_iter != m_mapReply.end()) + { + m_mapReply.erase(reply_iter); + } + auto num_iter = m_mapStepEmitNum.find(timeout_iter->second[i]); + if (num_iter != m_mapStepEmitNum.end()) + { + m_mapStepEmitNum.erase(num_iter); + } + } + timeout_iter->second.clear(); + m_mapTimeoutStep.erase(timeout_iter); + timeout_iter = m_mapTimeoutStep.begin(); + } + else + { + break; + } + } + return(CMD_STATUS_RUNNING); +} + +E_CMD_STATUS StepRedisCluster::ErrBack( + std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) +{ + LOG4_ERROR("error %d: %s", iErrno, strErrMsg.c_str()); + auto step_iter = m_mapPipelineRequest.find(pChannel->GetIdentify()); + if (step_iter == m_mapPipelineRequest.end() || step_iter->second.size() == 0) + { + LOG4_INFO("no \"%s\" found in m_mapPipelineStep", pChannel->GetIdentify().c_str()); + } + while (step_iter->second.size() > 0) + { + auto pRedisRequest = step_iter->second.front(); + uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); + step_iter->second.pop(); + if (uiRealStepSeq == GetSequence()) + { + SendCmdClusterSlots(); + } + else + { + auto num_iter = m_mapStepEmitNum.find(uiRealStepSeq); + if (num_iter == m_mapStepEmitNum.end()) // 单key请求的响应 + { + GetLabor(this)->GetActorBuilder()->OnError(pChannel, uiRealStepSeq, iErrno, strErrMsg); + } + else + { + auto reply_iter = m_mapReply.find(uiRealStepSeq); + if (reply_iter == m_mapReply.end()) + { + LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); + continue; + } + std::vector vecElementIndex; + for (int i = 1; i < pRedisRequest->element_size(); ++i) // element(0).str() is cmd + { + if (pRedisRequest->element(i).integer() > 0 || i == 1) + { + vecElementIndex.push_back((uint32)pRedisRequest->element(i).integer()); + } + } + RedisReply oRedisReply; + oRedisReply.set_type(REDIS_REPLY_ERROR); + oRedisReply.set_str(strErrMsg); + for (uint32 j = 0; j < vecElementIndex.size(); ++j) + { + if (vecElementIndex[j] < reply_iter->second.size()) + { + if (reply_iter->second[vecElementIndex[j]] == nullptr) + { + reply_iter->second[vecElementIndex[j]] = new RedisReply(); + } + reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply); + } + else + { + LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); + } + } + num_iter->second--; + if (num_iter->second == 0) + { + RedisReply oFinalReply; + oFinalReply.set_type(REDIS_REPLY_ARRAY); + for (size_t k = 0; k < reply_iter->second.size(); ++k) + { + oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); + reply_iter->second[k] = nullptr; + } + GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); + m_mapStepEmitNum.erase(num_iter); + m_mapReply.erase(reply_iter); + } + } + } + } + AskingQueueErrBack(pChannel, iErrno, strErrMsg); + return(CMD_STATUS_RUNNING); +} + +bool StepRedisCluster::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, + bool bWithSsl, bool bPipeline, uint32 uiStepSeq) +{ + if (m_mapSlot2Node.size() > 0) + { + return(Dispatch(oRedisMsg, uiStepSeq)); + } + else + { + m_vecWaittingRequest.push_back(std::make_pair(uiStepSeq, oRedisMsg)); + return(SendCmdClusterSlots()); + } +} + +bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptr pRedisMsg) +{ + LOG4_TRACE("%s", pRedisMsg->DebugString().c_str()); + bool bResult = GetLabor(this)->GetDispatcher()->SendTo(strIdentify, CODEC_RESP, m_bWithSsl, m_bPipeline, (*pRedisMsg.get()), GetSequence()); + if (bResult) + { + auto iter = m_mapPipelineRequest.find(strIdentify); + if (iter == m_mapPipelineRequest.end()) + { + std::queue> queStep; + queStep.push(pRedisMsg); + m_mapPipelineRequest.insert(std::make_pair(strIdentify, std::move(queStep))); + } + else + { + iter->second.push(pRedisMsg); + } + } + return(bResult); +} + +bool StepRedisCluster::ExtractCmd(const RedisMsg& oRedisMsg, + std::string& strCmd, std::vector& vecHashKeys, + int& iReadOrWrite, int& iKeyInterval) +{ + auto GetTag = [](const std::string& strKey, std::string& strTag)->void + { + size_t uiTagBegin = strKey.find_first_of("{"); + if (std::string::npos == uiTagBegin) + { + strTag = strKey; + } + else + { + size_t uiTagEnd = strKey.find_last_of("}"); + if (std::string::npos == uiTagEnd || uiTagEnd <= uiTagBegin + 1) + { + strTag = strKey; + } + else + { + ++uiTagBegin; + size_t uiTagLen = uiTagEnd - uiTagBegin; + if (uiTagLen > 0) + { + strTag = strKey.substr(uiTagBegin, uiTagLen); + } + else + { + strTag = strKey; + } + } + } + }; + + if (REDIS_REPLY_ARRAY == oRedisMsg.type()) + { + if (oRedisMsg.element_size() == 0) + { + LOG4_ERROR("cmd element size is 0, invalid redis cmd: %s", oRedisMsg.DebugString().c_str()); + return(false); + } + if (REDIS_REPLY_STRING != oRedisMsg.element(0).type() || oRedisMsg.element(0).str().size() == 0) + { + LOG4_ERROR("cmd element be a REDIS_REPLY_STRING and key length can not be 0, " + "invalid redis cmd: %s", oRedisMsg.DebugString().c_str()); + return(false); + } + strCmd = oRedisMsg.element(0).str(); + if (s_setSupportExtractCmd.find(strCmd) == s_setSupportExtractCmd.end()) + { + LOG4_ERROR("cmd %s not supported by StepRedisCluster", strCmd.c_str()); + return(false); + } + std::string strTag; + if (s_setMultipleKeyCmd.find(strCmd) != s_setMultipleKeyCmd.end()) + { + if (oRedisMsg.element_size() < 2) + { + LOG4_ERROR("param num fault of %s, invalid redis cmd: %s", + strCmd.c_str(), oRedisMsg.DebugString().c_str()); + return(false); + } + for (int i = 1; i < oRedisMsg.element_size(); ++i) + { + if (REDIS_REPLY_STRING != oRedisMsg.element(i).type() || oRedisMsg.element(i).str().size() == 0) + { + LOG4_ERROR("cmd element be a REDIS_REPLY_STRING and key length can not be 0, " + "invalid redis cmd: %s", oRedisMsg.DebugString().c_str()); + return(false); + } + GetTag(oRedisMsg.element(i).str(), strTag); + vecHashKeys.push_back(strTag); + } + iKeyInterval = 1; + if (s_setWriteCmd.find(strCmd) == s_setWriteCmd.end()) + { + iReadOrWrite = REDIS_CMD_READ; + } + else + { + iReadOrWrite = REDIS_CMD_WRITE; + } + return(true); + } + else if (s_setMultipleKeyValueCmd.find(strCmd) != s_setMultipleKeyValueCmd.end()) + { + if (!(oRedisMsg.element_size() & 0x0001)) // (oRedisMsg.element_size() % 2 == 0) + { + LOG4_ERROR("param num must be even of %s, invalid redis cmd: %s", + strCmd.c_str(), oRedisMsg.DebugString().c_str()); + return(false); + } + for (int i = 1; i < oRedisMsg.element_size(); ++i) + { + if (i & 0x0001) // (i % 2 == 1) + { + if (REDIS_REPLY_STRING != oRedisMsg.element(i).type() || oRedisMsg.element(i).str().size() == 0) + { + LOG4_ERROR("cmd element be a REDIS_REPLY_STRING and key length can not be 0, " + "invalid redis cmd: %s", oRedisMsg.DebugString().c_str()); + return(false); + } + GetTag(oRedisMsg.element(i).str(), strTag); + vecHashKeys.push_back(strTag); + } + } + iKeyInterval = 2; + if (s_setWriteCmd.find(strCmd) == s_setWriteCmd.end()) + { + iReadOrWrite = REDIS_CMD_READ; + } + else + { + iReadOrWrite = REDIS_CMD_WRITE; + } + LOG4_TRACE("oRedisMsg.element_size() = %d", oRedisMsg.element_size()); + return(true); + } + else + { + if (oRedisMsg.element_size() < 2) + { + LOG4_ERROR("param num fault of %s, invalid redis cmd: %s", + strCmd.c_str(), oRedisMsg.DebugString().c_str()); + return(false); + } + if (REDIS_REPLY_STRING != oRedisMsg.element(1).type() || oRedisMsg.element(1).str().size() == 0) + { + LOG4_ERROR("cmd element be a REDIS_REPLY_STRING, invalid redis cmd: %s", oRedisMsg.DebugString().c_str()); + return(false); + } + GetTag(oRedisMsg.element(1).str(), strTag); + vecHashKeys.emplace_back(std::move(strTag)); + if (s_setWriteCmd.find(strCmd) == s_setWriteCmd.end()) + { + iReadOrWrite = REDIS_CMD_WRITE; + } + else + { + iReadOrWrite = REDIS_CMD_READ; + } + return(true); + } + } + else + { + LOG4_ERROR("cmd must be a REDIS_REPLY_ARRAY, invalid redis cmd: %s", oRedisMsg.DebugString().c_str()); + return(false); + } +} + +bool StepRedisCluster::GetRedisNode(int iSlotId, int iReadOrWrite, std::string& strNodeIdentify, bool& bIsMaster) const +{ + auto slot_iter = m_mapSlot2Node.find(iSlotId); + if (slot_iter == m_mapSlot2Node.end()) + { + LOG4_ERROR("no redis node found for slot %d", iSlotId); + return(false); + } + if ((REDIS_CMD_WRITE == iReadOrWrite) + || (slot_iter->second->setFllower.size() == 0) + || (!m_bEnableReadOnly)) + { + strNodeIdentify = slot_iter->second->strMaster; + bIsMaster = true; + } + else + { + slot_iter->second->iterFllower++; + if (slot_iter->second->iterFllower == slot_iter->second->setFllower.end()) + { + slot_iter->second->iterFllower = slot_iter->second->setFllower.begin(); + } + strNodeIdentify = *(slot_iter->second->iterFllower); + bIsMaster = false; + } + return(true); +} + +bool StepRedisCluster::NeedSetReadOnly(const std::string& strNode) const +{ + auto iter = m_mapPipelineRequest.find(strNode); + if (iter == m_mapPipelineRequest.end() || iter->second.empty()) + { + return(true); + } + return(false); +} + +bool StepRedisCluster::SendCmdClusterSlots() +{ + SetCmd("CLUSTER"); + Append("SLOTS"); + if (m_vecAddress.size() == 0) + { + LOG4_ERROR("no redis node address!"); + return(false); + } + auto pRedisRequest = MutableRedisRequest(); + pRedisRequest->set_integer(GetSequence()); // 借用integer暂存seq + if (m_uiAddressIndex >= m_vecAddress.size()) + { + m_uiAddressIndex = 0; + } + bool bResult = SendTo(m_vecAddress[m_uiAddressIndex++], pRedisRequest); + m_uiAddressIndex++; + return(bResult); +} + +bool StepRedisCluster::CmdClusterSlotsCallback(const RedisReply& oRedisReply) +{ + if (REDIS_REPLY_ARRAY == oRedisReply.type()) + { + int iFromSlot = 0; + int iToSlot = 0; + for (int i = 0; i < oRedisReply.element_size(); ++i) + { + if (REDIS_REPLY_ARRAY == oRedisReply.element(i).type()) + { + if (oRedisReply.element(i).element_size() < 3) + { + LOG4_ERROR("invalid element num %d in element(%d)", oRedisReply.element(i).element_size(), i); + continue; + } + if (REDIS_REPLY_INTEGER != oRedisReply.element(i).element(0).type() + || REDIS_REPLY_INTEGER != oRedisReply.element(i).element(1).type()) + { + LOG4_ERROR("invalid element type %d or %d in element(%d)", + oRedisReply.element(i).element(0).type(), + oRedisReply.element(i).element(1).type(), i); + continue; + } + iFromSlot = oRedisReply.element(i).element(0).integer(); + iToSlot = oRedisReply.element(i).element(1).integer(); + auto pRedisNode = std::make_shared(); + for (int j = 2; j < oRedisReply.element(i).element_size(); ++j) + { + if (REDIS_REPLY_STRING != oRedisReply.element(i).element(j).element(0).type() + || REDIS_REPLY_INTEGER != oRedisReply.element(i).element(j).element(1).type()) + { + LOG4_ERROR("invalid element type %d or %d in element(%d).element(%d)", + oRedisReply.element(i).element(j).element(0).type(), + oRedisReply.element(i).element(j).element(1).type(), i, j); + break; + } + std::ostringstream oss; + oss << oRedisReply.element(i).element(j).element(0).str() + << ":" + << oRedisReply.element(i).element(j).element(1).integer(); + if (j == 2) + { + pRedisNode->strMaster = oss.str(); + } + else + { + pRedisNode->setFllower.insert(oss.str()); + } + } + pRedisNode->iterFllower = pRedisNode->setFllower.begin(); + for (int iSlotId = iFromSlot; iSlotId <= iToSlot; ++iSlotId) + { + auto it = m_mapSlot2Node.find(iSlotId); + if (it == m_mapSlot2Node.end()) + { + m_mapSlot2Node.insert(std::make_pair(iSlotId, pRedisNode)); + } + else + { + it->second = pRedisNode; + } + } + } + else + { + LOG4_ERROR("redis reply type %d is invalid for CLUSTER SLOTS in element(%d)", i); + } + } + return(true); + } + LOG4_ERROR("redis reply type %d is invalid for CLUSTER SLOTS"); + return(false); +} + +bool StepRedisCluster::SendCmdAsking(const std::string& strIdentify) +{ + SetCmd("ASKING"); + auto pRedisRequest = MutableRedisRequest(); + pRedisRequest->set_integer(GetSequence()); // 借用integer暂存seq + return(SendTo(strIdentify, pRedisRequest)); +} + +void StepRedisCluster::CmdAskingCallback(std::shared_ptr pChannel, + const std::string& strIdentify, const RedisReply& oRedisReply) +{ + if (REDIS_REPLY_STATUS == oRedisReply.type()) + { + if ("OK" == oRedisReply.str()) + { + auto step_iter = m_mapAskingRequest.find(strIdentify); + if (step_iter == m_mapAskingRequest.end() || step_iter->second.size() == 0) + { + LOG4_INFO("no \"%s\" found in m_mapPipelineStep", strIdentify.c_str()); + } + while (step_iter->second.size() > 0) + { + auto& oRedisRequest = step_iter->second.front(); + uint32 uiRealStepSeq = (uint32)oRedisRequest->integer(); + step_iter->second.pop(); + if (uiRealStepSeq == GetSequence()) + { + ; + } + else + { + SendTo(strIdentify, oRedisRequest); + } + } + } + else + { + AskingQueueErrBack(pChannel, oRedisReply.type(), "unexpected asking error"); + } + } + else if (REDIS_REPLY_ERROR == oRedisReply.type()) + { + AskingQueueErrBack(pChannel, oRedisReply.type(), oRedisReply.str()); + } + else + { + AskingQueueErrBack(pChannel, oRedisReply.type(), oRedisReply.str()); + } +} + +bool StepRedisCluster::SendCmdReadOnly(const std::string& strIdentify) +{ + SetCmd("READONLY"); + auto pRedisRequest = MutableRedisRequest(); + pRedisRequest->set_integer(GetSequence()); // 借用integer暂存seq + return(SendTo(strIdentify, pRedisRequest)); +} + +void StepRedisCluster::CmdReadOnlyCallback(std::shared_ptr pChannel, + const std::string& strIdentify, const RedisReply& oRedisReply) +{ + if (REDIS_REPLY_STATUS == oRedisReply.type()) + { + if ("OK" == oRedisReply.str()) + { + ; + } + else + { + LOG4_ERROR("set %s read only failed: %s", pChannel->GetIdentify().c_str(), oRedisReply.str().c_str()); + } + } + else if (REDIS_REPLY_ERROR == oRedisReply.type()) + { + LOG4_ERROR("set %s read only failed: %s", pChannel->GetIdentify().c_str(), oRedisReply.str().c_str()); + } + else + { + LOG4_ERROR("set %s read only failed: unexpected reply type %d", pChannel->GetIdentify().c_str(), oRedisReply.type()); + } +} + +void StepRedisCluster::AskingQueueErrBack( + std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) +{ + LOG4_ERROR("error %d: %s", iErrno, strErrMsg.c_str()); + auto step_iter = m_mapAskingRequest.find(pChannel->GetIdentify()); + if (step_iter == m_mapAskingRequest.end() || step_iter->second.size() == 0) + { + LOG4_INFO("no \"%s\" found in m_mapPipelineStep", pChannel->GetIdentify().c_str()); + } + while (step_iter->second.size() > 0) + { + auto pRedisRequest = step_iter->second.front(); + uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); + step_iter->second.pop(); + if (uiRealStepSeq == GetSequence()) + { + SendCmdClusterSlots(); + } + else + { + auto num_iter = m_mapStepEmitNum.find(uiRealStepSeq); + if (num_iter == m_mapStepEmitNum.end()) // 单key请求的响应 + { + GetLabor(this)->GetActorBuilder()->OnError(pChannel, uiRealStepSeq, iErrno, strErrMsg); + } + else + { + auto reply_iter = m_mapReply.find(uiRealStepSeq); + if (reply_iter == m_mapReply.end()) + { + LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); + continue; + } + std::vector vecElementIndex; + for (int i = 1; i < pRedisRequest->element_size(); ++i) // element(0).str() is cmd + { + if (pRedisRequest->element(i).integer() > 0 || i == 1) + { + vecElementIndex.push_back((uint32)pRedisRequest->element(i).integer()); + } + } + RedisReply oRedisReply; + oRedisReply.set_type(REDIS_REPLY_ERROR); + oRedisReply.set_str(strErrMsg); + for (uint32 j = 0; j < vecElementIndex.size(); ++j) + { + if (vecElementIndex[j] < reply_iter->second.size()) + { + if (reply_iter->second[vecElementIndex[j]] == nullptr) + { + reply_iter->second[vecElementIndex[j]] = new RedisReply(); + } + reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply); + } + else + { + LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); + } + } + num_iter->second--; + if (num_iter->second == 0) + { + RedisReply oFinalReply; + oFinalReply.set_type(REDIS_REPLY_ARRAY); + for (size_t k = 0; k < reply_iter->second.size(); ++k) + { + oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); + reply_iter->second[k] = nullptr; + } + GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); + m_mapStepEmitNum.erase(num_iter); + m_mapReply.erase(reply_iter); + } + } + } + } +} + +bool StepRedisCluster::Dispatch(const RedisMsg& oRedisMsg, uint32 uiStepSeq) +{ + std::string strCmd; + std::vector vecHashKey; + int iKeyInterval = 0; + int iReadOrWrite = 0; + if (!ExtractCmd(oRedisMsg, strCmd, vecHashKey, iReadOrWrite, iKeyInterval)) + { + return(false); + } + bool bIsMasterNode = false; + std::string strRedisNode; + if (vecHashKey.size() == 1) + { + uint16 unHash = crc16(vecHashKey[0].c_str(), vecHashKey[0].size()); + int iSlotId = (int)(unHash % sc_unClusterSlots); + if (!GetRedisNode(iSlotId, iReadOrWrite, strRedisNode, bIsMasterNode)) + { + return(false); + } + auto pRedisRequest = std::make_shared(oRedisMsg); + pRedisRequest->set_integer(uiStepSeq); // 借用integer暂存seq + if (!bIsMasterNode && NeedSetReadOnly(strRedisNode)) + { + SendCmdReadOnly(strRedisNode); + } + return(SendTo(strRedisNode, pRedisRequest)); + } + else if (vecHashKey.size() > 1) + { + if ((int)vecHashKey.size() * iKeyInterval >= oRedisMsg.element_size()) + { + LOG4_ERROR("element size error."); + return(false); + } + std::unordered_map> mapRedisRequest; + for (uint32 i = 0; i < vecHashKey.size(); ++i) + { + uint16 unHash = crc16(vecHashKey[i].c_str(), vecHashKey[i].size()); + int iSlotId = (int)(unHash % sc_unClusterSlots); + auto req_iter = mapRedisRequest.find(iSlotId); + if (req_iter == mapRedisRequest.end()) + { + auto pSubRequest = std::make_shared(); + pSubRequest->set_type(oRedisMsg.type()); + auto pElement = pSubRequest->add_element(); + pElement->CopyFrom(oRedisMsg.element(0)); // cmd + pElement = pSubRequest->add_element(); + pElement->CopyFrom(oRedisMsg.element(i * iKeyInterval + 1)); // key + pElement->set_integer(i); // 借用integer暂存key的序号 + for (int j = 2; j <= iKeyInterval; ++j) + { + pElement = pSubRequest->add_element(); + pElement->CopyFrom(oRedisMsg.element(i * iKeyInterval + j)); // value + } + pSubRequest->set_integer(uiStepSeq); // 借用integer暂存seq + mapRedisRequest.insert(std::make_pair(iSlotId, pSubRequest)); + } + else + { + auto pElement = req_iter->second->add_element(); + pElement->CopyFrom(oRedisMsg.element(i * iKeyInterval + 1)); // key + pElement->set_integer(i); // 借用integer暂存key的序号 + for (int j = 2; j <= iKeyInterval; ++j) + { + pElement = req_iter->second->add_element(); + pElement->CopyFrom(oRedisMsg.element(i * iKeyInterval + j)); // value + } + } + } + for (auto req_iter = mapRedisRequest.begin(); req_iter != mapRedisRequest.end(); ++req_iter) + { + if (!GetRedisNode(req_iter->first, iReadOrWrite, strRedisNode, bIsMasterNode)) + { + return(false); + } + if (!bIsMasterNode && NeedSetReadOnly(strRedisNode)) + { + SendCmdReadOnly(strRedisNode); + } + SendTo(strRedisNode, req_iter->second); + } + m_mapStepEmitNum[uiStepSeq] = mapRedisRequest.size(); + std::vector vecReply; + auto iter_bool = m_mapReply.insert(std::make_pair(uiStepSeq, vecReply)); + if (iter_bool.second == true) + { + iter_bool.first->second.resize(vecHashKey.size(), nullptr); + } + return(iter_bool.second); + } + else + { + LOG4_ERROR("no hash key for %s", strCmd.c_str()); + return(false); + } +} + +void StepRedisCluster::SendWaittingRequest() +{ + for (size_t i = 0; i < m_vecWaittingRequest.size(); ++i) + { + Dispatch(m_vecWaittingRequest[i].second, m_vecWaittingRequest[i].first); + } + m_vecWaittingRequest.clear(); +} + +void StepRedisCluster::RegisterStep(uint32 uiStepSeq) +{ + auto iter = m_mapTimeoutStep.find(uiStepSeq); + if (iter == m_mapTimeoutStep.end()) + { + std::vector vecStep; + vecStep.push_back(uiStepSeq); + m_mapTimeoutStep.insert(std::make_pair(GetNowTime(), std::move(vecStep))); + } + else + { + iter->second.push_back(uiStepSeq); + } +} + +void StepRedisCluster::AddToAskingQueue(const std::string& strIdentify, std::shared_ptr pRedisMsg) +{ + auto iter = m_mapAskingRequest.find(strIdentify); + if (iter == m_mapAskingRequest.end()) + { + std::queue> queStep; + queStep.push(pRedisMsg); + m_mapAskingRequest.insert(std::make_pair(strIdentify, std::move(queStep))); + } + else + { + iter->second.push(pRedisMsg); + } +} + +} /* namespace neb */ + + diff --git a/src/actor/step/sys_step/StepRedisCluster.hpp b/src/actor/step/sys_step/StepRedisCluster.hpp new file mode 100644 index 00000000..81f18006 --- /dev/null +++ b/src/actor/step/sys_step/StepRedisCluster.hpp @@ -0,0 +1,128 @@ +/******************************************************************************* + * Project: Nebula + * @file StepRedisCluster.hpp + * @brief + * @author nebim + * @date: 2020-12-12 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_STEP_SYS_STEP_STEPREDISCLUSTER_HPP_ +#define SRC_ACTOR_STEP_SYS_STEP_STEPREDISCLUSTER_HPP_ + +#include +#include +#include +#include +#include +#include "actor/step/RedisStep.hpp" +#include "actor/ActorSys.hpp" + +#define REDIS_COMMAND_ASKING "ASKING" +#define REDIS_COMMAND_PING "PING" + +namespace neb +{ + +class Dispatcher; + +enum E_REDIS_CMD_TYPE +{ + REDIS_CMD_READ = 0, + REDIS_CMD_WRITE = 1, +}; + +struct RedisNode +{ + std::string strMaster; + std::unordered_set setFllower; + std::unordered_set::iterator iterFllower; + RedisNode(){} + RedisNode(const RedisNode& stNode) = delete; + RedisNode(RedisNode&& stNode) + { + strMaster = std::move(stNode.strMaster); + setFllower = std::move(stNode.setFllower); + iterFllower = setFllower.begin(); + } + RedisNode& operator=(const RedisNode& stNode) = delete; + RedisNode& operator=(RedisNode&& stNode) + { + strMaster = std::move(stNode.strMaster); + setFllower = std::move(stNode.setFllower); + iterFllower = setFllower.begin(); + return(*this); + } +}; + +class StepRedisCluster: public RedisStep, + public DynamicCreator, + public ActorSys +{ +public: + StepRedisCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, bool bEnableReadOnly); + virtual ~StepRedisCluster(); + + virtual E_CMD_STATUS Emit(int iErrno = neb::ERR_OK, const std::string& strErrMsg = "", void* data = NULL); + + virtual E_CMD_STATUS Callback( + std::shared_ptr pChannel, + const RedisReply& oRedisReply); + + virtual E_CMD_STATUS Timeout(); + + virtual E_CMD_STATUS ErrBack( + std::shared_ptr pChannel, + int iErrno, const std::string& strErrMsg) override; + + + virtual bool SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, + bool bWithSsl, bool bPipeline, uint32 uiStepSeq) override; + +protected: + bool SendTo(const std::string& strIdentify, std::shared_ptr pRedisMsg); + bool ExtractCmd(const RedisMsg& oRedisMsg, std::string& strCmd, + std::vector& vecHashKeys, int& iReadOrWrite, int& iKeyInterval); + bool GetRedisNode(int iSlotId, int iReadOrWrite, std::string& strNodeIdentify, bool& bIsMaster) const; + bool NeedSetReadOnly(const std::string& strNode) const; + bool SendCmdClusterSlots(); + bool CmdClusterSlotsCallback(const RedisReply& oRedisReply); + bool SendCmdAsking(const std::string& strIdentify); + void CmdAskingCallback(std::shared_ptr pChannel, const std::string& strIdentify, const RedisReply& oRedisReply); + bool SendCmdReadOnly(const std::string& strIdentify); + void CmdReadOnlyCallback(std::shared_ptr pChannel, const std::string& strIdentify, const RedisReply& oRedisReply); + void AskingQueueErrBack(std::shared_ptr pChannel, + int iErrno, const std::string& strErrMsg); + bool Dispatch(const RedisMsg& oRedisMsg, uint32 uiStepSeq); + void SendWaittingRequest(); + void RegisterStep(uint32 uiStepSeq); + void AddToAskingQueue(const std::string& strIdentify, std::shared_ptr pRedisMsg); + +private: + bool m_bWithSsl; ///< 是否支持SSL + bool m_bPipeline; ///< 是否支持pipeline + bool m_bEnableReadOnly; ///< 是否对从节点启用只读 + uint32 m_uiAddressIndex; + + std::string m_strIdentify; ///< 地址标识,由m_vecAddress合并而成,形如 192.168.47.101:6379,198.168.47.102:6379,192.168.47.103:6379 + std::vector m_vecAddress; ///< 集群地址 + std::unordered_map> m_mapSlot2Node; // redis cluster slot对应的节点 + std::unordered_map>> m_mapPipelineRequest; ///< 等待回调的请求 + std::unordered_map>> m_mapAskingRequest; ///< 等待Asking的请求 + std::unordered_map m_mapStepEmitNum; // 每个step发出请求(等待响应)数量 + std::unordered_map> m_mapReply; + std::map> m_mapTimeoutStep; + std::vector> m_vecWaittingRequest; + + static const uint16 sc_unClusterSlots; ///< redis cluster槽位数 + static const std::unordered_set s_setSupportExtractCmd; + static const std::unordered_set s_setReadCmd; + static const std::unordered_set s_setWriteCmd; + static const std::unordered_set s_setMultipleKeyCmd; + static const std::unordered_set s_setMultipleKeyValueCmd; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_STEP_SYS_STEP_STEPREDISCLUSTER_HPP_ */ + diff --git a/src/util/encrypt/crc16.c b/src/util/encrypt/crc16.c new file mode 100644 index 00000000..23360c8c --- /dev/null +++ b/src/util/encrypt/crc16.c @@ -0,0 +1,88 @@ +/* + * Copyright 2001-2010 Georges Menie (www.menie.org) + * Copyright 2010-2012 Salvatore Sanfilippo (adapted to Redis coding style) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* CRC16 implementation according to CCITT standards. + * + * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the + * following parameters: + * + * Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN" + * Width : 16 bit + * Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1) + * Initialization : 0000 + * Reflect Input byte : False + * Reflect Output CRC : False + * Xor constant to output CRC : 0000 + * Output for "123456789" : 31C3 + */ +#include "crc16.h" + +static const uint16_t crc16tab[256]= { + 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, + 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, + 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, + 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, + 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, + 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, + 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, + 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, + 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, + 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, + 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, + 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, + 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, + 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, + 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, + 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, + 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, + 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, + 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, + 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, + 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, + 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, + 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, + 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, + 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, + 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, + 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, + 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, + 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, + 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, + 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, + 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 +}; + +uint16_t crc16(const char *buf, int len) { + int counter; + uint16_t crc = 0; + for (counter = 0; counter < len; counter++) + crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF]; + return crc; +} + diff --git a/src/util/encrypt/crc16.h b/src/util/encrypt/crc16.h new file mode 100644 index 00000000..fe3bd496 --- /dev/null +++ b/src/util/encrypt/crc16.h @@ -0,0 +1,11 @@ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +uint16_t crc16(const char *buf, int len); + +#ifdef __cplusplus +} +#endif From fed3adafa5fe6c44de78469f4ff1a013708c648c Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 4 Jan 2021 01:01:52 +0800 Subject: [PATCH 116/176] add send to one of; CodecResp bug fixed --- src/actor/Actor.cpp | 13 ++- src/actor/Actor.hpp | 25 +++++- src/actor/ActorBuilder.cpp | 24 +++++- src/actor/ActorBuilder.hpp | 3 +- .../session/sys_session/SessionOneOf.cpp | 83 +++++++++++++++++++ .../session/sys_session/SessionOneOf.hpp | 48 +++++++++++ src/codec/CodecResp.cpp | 23 ++--- src/ios/Dispatcher.cpp | 6 +- src/util/StringConverter.hpp | 21 +++-- 9 files changed, 214 insertions(+), 32 deletions(-) create mode 100644 src/actor/session/sys_session/SessionOneOf.cpp create mode 100644 src/actor/session/sys_session/SessionOneOf.hpp diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 7ae830dd..f1dd719a 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -181,7 +181,7 @@ bool Actor::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, con return(m_pLabor->GetDispatcher()->SendTo(strIdentify, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); } -bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg) +bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq) { bool bWithSsl = false; bool bPipeline = false; @@ -207,12 +207,17 @@ bool Actor::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bo return(m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); } -bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) +bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) { - return(m_pLabor->GetActorBuilder()->SendToCluster(strIdentify, bWithSsl, bPipeline, oRedisMsg, GetSequence())); + return(m_pLabor->GetActorBuilder()->SendToCluster(strIdentify, bWithSsl, bPipeline, oRedisMsg, GetSequence(), bEnableReadOnly)); } -bool Actor::SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline) +bool Actor::SendToOneOf(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) +{ + return(m_pLabor->GetActorBuilder()->SendToOneOf(strIdentify, bWithSsl, bPipeline, oRedisMsg, GetSequence())); +} + +bool Actor::SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { return(m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_UNKNOW, bWithSsl, bPipeline, pRawData, uiRawDataSize, GetSequence())); } diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 2e250f21..a4b497e0 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -196,9 +196,10 @@ class Actor: public std::enable_shared_from_this * @param strHost IP地址 * @param iPort 端口 * @param oHttpMsg http消息 + * @param uiStepSeq 应用层无用参数,框架层的系统Actor会用到 * @return 是否发送成功 */ - virtual bool SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg); + virtual bool SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq); /** * @brief 发送redis请求 @@ -211,7 +212,24 @@ class Actor: public std::enable_shared_from_this * @return 是否发送成功 */ virtual bool SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); - virtual bool SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); + + /** + * @brief 发送redis请求 + * @note 只有RedisStep及其派生类才能调用此方法 + * @param strIdentify RedisChannel通道标识,格式如: 192.168.125.53:6379 + * @param oRedisMsg redis消息 + * @param bWithSsl 是否需要SSL + * @param bPipeline 是否支持pipeline + * @param bEnableReadOnly redis cluster集群从节点,官方默认设置的是不分担读请求 + * 只作备份和故障转移用,当有请求读向从节点时,会被重定向对应的主节点来处理。 + * 这个readonly告诉redis cluster从节点客户端愿意读取可能过时的数据并对写请求不感兴趣 + * @return 是否发送成功 + */ + virtual bool SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, bool bEnableReadOnly = false); + /** + * @brief 发送redis请求到类似于codis proxy的服务 + */ + virtual bool SendToOneOf(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); /** * @brief 发送raw请求 @@ -220,9 +238,10 @@ class Actor: public std::enable_shared_from_this * @param pRawData裸数据 * @param bWithSsl 是否需要SSL * @param bPipeline 是否支持pipeline + * @param uiStepSeq 应用层无用参数,框架层的系统Actor会用到 * @return 是否发送成功 */ - virtual bool SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl = false, bool bPipeline = false); + virtual bool SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl = false, bool bPipeline = false, uint32 uiStepSeq = 0); /** * @brief 从worker发送到loader或从loader发送到worker diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index db299a2e..166483a6 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -470,7 +470,6 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Redi { E_CMD_STATUS eResult; step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - LOG4_TRACE("callback of redis cmd: %s", (std::dynamic_pointer_cast(step_iter->second))->CmdToString().c_str()); eResult = step_iter->second->Callback(pChannel, oRedisMsg); if (CMD_STATUS_RUNNING != eResult) { @@ -1111,13 +1110,13 @@ bool ActorBuilder::TransformToSharedChain(Actor* pCreator, std::shared_ptrSendTo(strIdentify, oRedisMsg, bWithSsl, bPipeline, uiStepSeq); + } + } + else + { + bSendResult = iter->second->SendTo(strIdentify, oRedisMsg, bWithSsl, bPipeline, uiStepSeq); + } + return(bSendResult); +} + std::shared_ptr ActorBuilder::GetSession(uint32 uiSessionId) { std::ostringstream oss; diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index c4c012cf..72609197 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -139,7 +139,8 @@ class ActorBuilder bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); public: - bool SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq); + bool SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq, bool bEnableReadOnly); + bool SendToOneOf(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq); virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); diff --git a/src/actor/session/sys_session/SessionOneOf.cpp b/src/actor/session/sys_session/SessionOneOf.cpp new file mode 100644 index 00000000..f1603a1c --- /dev/null +++ b/src/actor/session/sys_session/SessionOneOf.cpp @@ -0,0 +1,83 @@ +/******************************************************************************* + * Project: Nebula + * @file SessionOneOf.hpp + * @brief + * @author Bwar + * @date: 2020-12-26 + * @note + * Modify history: + ******************************************************************************/ +#include "SessionOneOf.hpp" +#include +#include "util/StringCoder.hpp" +#include "labor/Labor.hpp" +#include "ios/Dispatcher.hpp" + +namespace neb +{ + +SessionOneOf::SessionOneOf(const std::string& strIdentify, bool bWithSsl, bool bPipeline) + : Timer(strIdentify, 300.0), + m_bWithSsl(bWithSsl), m_bPipeline(bPipeline), m_uiAddressIndex(0) +{ + Split(strIdentify, ",", m_vecAddress); +} + +SessionOneOf::~SessionOneOf() +{ +} + +bool SessionOneOf::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) +{ + if (m_vecAddress.size() == 0) + { + return(false); + } + m_uiAddressIndex = (m_uiAddressIndex < m_vecAddress.size()) ? m_uiAddressIndex : 0; + return(GetLabor(this)->GetDispatcher()->SendTo(m_vecAddress[m_uiAddressIndex++], eCodecType, false, true, iCmd, uiSeq, oMsgBody)); +} + +bool SessionOneOf::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq) +{ + if (m_vecAddress.size() == 0) + { + return(false); + } + m_uiAddressIndex = (m_uiAddressIndex < m_vecAddress.size()) ? m_uiAddressIndex : 0; + bool bWithSsl = false; + bool bPipeline = false; + if (oHttpMsg.http_major() >= 2) + { + bPipeline = true; + } + std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(":")); + std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c)->unsigned char {return std::tolower(c);}); + if (strSchema == std::string("https")) + { + bWithSsl = true; + } + return(GetLabor(this)->GetDispatcher()->SendTo(m_vecAddress[m_uiAddressIndex++], CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, uiStepSeq)); +} + +bool SessionOneOf::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) +{ + if (m_vecAddress.size() == 0) + { + return(false); + } + m_uiAddressIndex = (m_uiAddressIndex < m_vecAddress.size()) ? m_uiAddressIndex : 0; + return(GetLabor(this)->GetDispatcher()->SendTo(m_vecAddress[m_uiAddressIndex++], CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, uiStepSeq)); +} + +bool SessionOneOf::SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) +{ + if (m_vecAddress.size() == 0) + { + return(false); + } + m_uiAddressIndex = (m_uiAddressIndex < m_vecAddress.size()) ? m_uiAddressIndex : 0; + return(GetLabor(this)->GetDispatcher()->SendTo(m_vecAddress[m_uiAddressIndex++], CODEC_RESP, bWithSsl, bPipeline, pRawData, uiRawDataSize, uiStepSeq)); +} + +} /* namespace neb */ + diff --git a/src/actor/session/sys_session/SessionOneOf.hpp b/src/actor/session/sys_session/SessionOneOf.hpp new file mode 100644 index 00000000..1eea92ef --- /dev/null +++ b/src/actor/session/sys_session/SessionOneOf.hpp @@ -0,0 +1,48 @@ +/******************************************************************************* + * Project: Nebula + * @file SessionOneOf.hpp + * @brief + * @author Bwar + * @date: 2020-12-26 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_SESSION_SYS_SESSION_SESSIONONEOF_H_ +#define SRC_ACTOR_SESSION_SYS_SESSION_SESSIONONEOF_H_ + +#include "actor/session/Timer.hpp" +#include "actor/ActorSys.hpp" + +namespace neb +{ + +class SessionOneOf: public Timer, + DynamicCreator, + public ActorSys +{ +public: + SessionOneOf(const std::string& strIdentify, bool bWithSsl, bool bPipeline); + virtual ~SessionOneOf(); + + virtual E_CMD_STATUS Timeout() + { + return(CMD_STATUS_COMPLETED); + } + + virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + virtual bool SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq = 0); + virtual bool SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); + virtual bool SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl = false, bool bPipeline = false, uint32 uiStepSeq = 0); + +private: + bool m_bWithSsl; ///< 是否支持SSL + bool m_bPipeline; ///< 是否支持pipeline + //std::string m_strIdentify; ///< 地址标识,由m_vecAddress合并而成,形如 192.168.47.101:6379,198.168.47.102:6379,192.168.47.103:6379 GetSessionId() + uint32 m_uiAddressIndex; + std::vector m_vecAddress; ///< 集群地址 +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_SESSION_SYS_SESSION_SESSIONONEOF_H_ */ + diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index b215f84a..db1cde74 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -69,7 +69,7 @@ E_CODEC_STATUS CodecResp::Encode(const RedisReply& oReply, CBuffer* pBuff) E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply) { - if (pBuff->ReadableBytes() == 0) + if (pBuff->ReadableBytes() < 0) { return(CODEC_STATUS_PAUSE); } @@ -329,15 +329,8 @@ E_CODEC_STATUS CodecResp::DecodeBulkString(CBuffer* pBuff, RedisReply& oReply) cLastChar = '\n'; if (bGotStringLen) { - if (iStringLen == -1) - { - oReply.set_type(REDIS_REPLY_NIL); - } - else - { - oReply.set_type(REDIS_REPLY_STRING); - oReply.set_str(pPartBegin, iStringLen); - } + oReply.set_type(REDIS_REPLY_STRING); + oReply.set_str(pPartBegin, iStringLen); pBuff->AdvanceReadIndex(i + 1); return(CODEC_STATUS_OK); } @@ -346,6 +339,12 @@ E_CODEC_STATUS CodecResp::DecodeBulkString(CBuffer* pBuff, RedisReply& oReply) iStringLen = StringConverter::RapidAtoi(pPartBegin); pPartBegin = pData + i + 1; bGotStringLen = true; + if (iStringLen == -1) + { + oReply.set_type(REDIS_REPLY_NIL); + pBuff->AdvanceReadIndex(i + 1); + return(CODEC_STATUS_OK); + } if (uiReadableBytes - i < (uint32)iStringLen) { return(CODEC_STATUS_PAUSE); @@ -400,6 +399,10 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) oReply.set_type(REDIS_REPLY_ARRAY); for (int32 j = 0; j < iArraySize; ++j) { + if (pBuff->ReadableBytes() == 0) + { + return(CODEC_STATUS_PAUSE); + } uiReadIndex = pBuff->GetReadIndex(); char cFirstByte = 0; auto pElement = oReply.add_element(); diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index cdbf62ca..f66a83dd 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -286,14 +286,13 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) RemoveIoWriteEvent(pChannel); return(true); default: // 编解码出错或连接关闭或连接中断 - if (CODEC_STATUS_INVALID == eCodecStatus) + if (CODEC_STATUS_INVALID == eCodecStatus && !pChannel->IsClient()) { if (pChannel->m_pImpl->AutoSwitchCodec()) { return(DataFetchAndHandle(pChannel)); } } - LOG4_TRACE("codec error or connection closed!"); if (CHANNEL_STATUS_ESTABLISHED != pChannel->m_pImpl->GetChannelStatus()) { auto& listUncompletedStep = pChannel->m_pImpl->GetPipelineStepSeq(); @@ -399,14 +398,13 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) RemoveIoWriteEvent(pChannel); return(true); default: // 编解码出错或连接关闭或连接中断 - if (CODEC_STATUS_INVALID == eCodecStatus) + if (CODEC_STATUS_INVALID == eCodecStatus && !pChannel->IsClient()) { if (pChannel->m_pImpl->AutoSwitchCodec()) { return(DataFetchAndHandle(pChannel)); } } - LOG4_TRACE("codec error or connection closed!"); if (CHANNEL_STATUS_ESTABLISHED != pChannel->m_pImpl->GetChannelStatus()) { auto& listUncompletedStep = pChannel->m_pImpl->GetPipelineStepSeq(); diff --git a/src/util/StringConverter.hpp b/src/util/StringConverter.hpp index 07ee212e..231c0cf3 100644 --- a/src/util/StringConverter.hpp +++ b/src/util/StringConverter.hpp @@ -25,25 +25,32 @@ class StringConverter template static inline TInt RapidAtoi(const char* str) { + int sign = 1; TInt res = 0; - for (int k = 0; ; k +=4) + int k = 0; + if (str[0] == '-') + { + sign = -1; + k = 1; + } + for (; ; k += 4) { if (not _IS_NUM(str[k])) { - return(res); + return(res * sign); } else if (not _IS_NUM(str[k + 1])) { - return(res * 10 + _TO_NUM(str[k])); + return((res * 10 + _TO_NUM(str[k])) * sign); } else if (not _IS_NUM(str[k + 2])) { - return(res * 100 + _TO_NUM(str[k]) * 10 + _TO_NUM(str[k + 1])); + return((res * 100 + _TO_NUM(str[k]) * 10 + _TO_NUM(str[k + 1])) * sign); } else if (not _IS_NUM(str[k + 3])) { - return(res * 1000 + _TO_NUM(str[k]) * 100 - + _TO_NUM(str[k + 1]) * 10 + _TO_NUM(str[k + 2])); + return((res * 1000 + _TO_NUM(str[k]) * 100 + + _TO_NUM(str[k + 1]) * 10 + _TO_NUM(str[k + 2])) * sign); } else { @@ -51,7 +58,7 @@ class StringConverter * 100 + _TO_NUM(str[k + 2]) * 10 + _TO_NUM(str[k + 3]); } } - return(res); + return(res * sign); } }; From 29a897a1a56de0bd866ebdf8b4eec65ad17622d6 Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 4 Jan 2021 22:45:33 +0800 Subject: [PATCH 117/176] build fixed --- src/actor/Actor.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index a4b497e0..b2f8f2c4 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -199,7 +199,7 @@ class Actor: public std::enable_shared_from_this * @param uiStepSeq 应用层无用参数,框架层的系统Actor会用到 * @return 是否发送成功 */ - virtual bool SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq); + virtual bool SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq = 0); /** * @brief 发送redis请求 From 8821e82c9cc2f4c33e298c40ac0ef0f69bff44bc Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 11 Jan 2021 01:23:16 +0800 Subject: [PATCH 118/176] replace SendToOneOf with SendRoundRobin --- src/actor/Actor.cpp | 29 +++- src/actor/Actor.hpp | 2 +- src/actor/ActorBuilder.cpp | 19 --- src/actor/ActorBuilder.hpp | 1 - .../sys_step/manager/StepReportToBeacon.cpp | 2 +- src/codec/CodecResp.cpp | 6 + src/codec/CodecResp.hpp | 13 +- src/ios/Dispatcher.cpp | 110 ++---------- src/ios/Dispatcher.hpp | 115 ++++++++++++- src/ios/Nodes.cpp | 158 ++++++++++++++++++ src/ios/Nodes.hpp | 32 +++- 11 files changed, 352 insertions(+), 135 deletions(-) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index f1dd719a..d6249b09 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -212,9 +212,9 @@ bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedis return(m_pLabor->GetActorBuilder()->SendToCluster(strIdentify, bWithSsl, bPipeline, oRedisMsg, GetSequence(), bEnableReadOnly)); } -bool Actor::SendToOneOf(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) +bool Actor::SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline) { - return(m_pLabor->GetActorBuilder()->SendToOneOf(strIdentify, bWithSsl, bPipeline, oRedisMsg, GetSequence())); + return(m_pLabor->GetActorBuilder()->SendRoundRobin(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); } bool Actor::SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) @@ -231,19 +231,38 @@ bool Actor::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); + return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); } bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, uiFactor, iCmd, uiSeq, oMsgBody, eCodecType)); + return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, eCodecType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); } bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); + if (oMsgBody.has_req_target()) + { + if (0 != oMsgBody.req_target().route_id()) + { + return(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, eCodecType); + } + else if (oMsgBody.req_target().route().length() > 0) + { + return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, eCodecType, false, true, oMsgBody.req_target().route(), iCmd, uiSeq, oMsgBody)); + } + else + { + return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); + } + } + else + { + LOG4_ERROR("MsgBody is not a request message."); + return(false); + } } bool Actor::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index b2f8f2c4..c97bee5c 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -229,7 +229,7 @@ class Actor: public std::enable_shared_from_this /** * @brief 发送redis请求到类似于codis proxy的服务 */ - virtual bool SendToOneOf(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); + virtual bool SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true); /** * @brief 发送raw请求 diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 166483a6..efcd5329 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -1130,25 +1130,6 @@ bool ActorBuilder::SendToCluster(const std::string& strIdentify, bool bWithSsl, return(bSendResult); } -bool ActorBuilder::SendToOneOf(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq) -{ - bool bSendResult = false; - auto iter = m_mapCallbackSession.find(strIdentify); - if (iter == m_mapCallbackSession.end()) - { - auto pSharedSession = MakeSharedSession(nullptr, "neb::SessionOneOf", strIdentify, (bool)bWithSsl, (bool)bPipeline); - if (pSharedSession != nullptr) - { - bSendResult = pSharedSession->SendTo(strIdentify, oRedisMsg, bWithSsl, bPipeline, uiStepSeq); - } - } - else - { - bSendResult = iter->second->SendTo(strIdentify, oRedisMsg, bWithSsl, bPipeline, uiStepSeq); - } - return(bSendResult); -} - std::shared_ptr ActorBuilder::GetSession(uint32 uiSessionId) { std::ostringstream oss; diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 72609197..47aa833a 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -140,7 +140,6 @@ class ActorBuilder public: bool SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq, bool bEnableReadOnly); - bool SendToOneOf(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq); virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp index 638e39c6..24640f4d 100644 --- a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp +++ b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp @@ -46,7 +46,7 @@ E_CMD_STATUS StepReportToBeacon::Emit( } m_pSessionManager->MakeReportData(oReportData); oMsgBody.set_data(oReportData.ToString()); - GetLabor(this)->GetDispatcher()->Broadcast("BEACON", + GetLabor(this)->GetDispatcher()->Broadcast("BEACON", false, true, (int32)CMD_REQ_NODE_STATUS_REPORT, GetSequence(), oMsgBody); return(CMD_STATUS_RUNNING); } diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index db1cde74..07bafa3e 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -13,6 +13,12 @@ namespace neb { +const char CodecResp::RESP_SIMPLE_STRING = '+'; +const char CodecResp::RESP_ERROR = '-'; +const char CodecResp::RESP_INTEGER = ':'; +const char CodecResp::RESP_BULK_STRING = '$'; +const char CodecResp::RESP_ARRAY = '*'; + CodecResp::CodecResp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) : Codec(pLogger, eCodecType) { diff --git a/src/codec/CodecResp.hpp b/src/codec/CodecResp.hpp index 0a5e5911..ad78917a 100644 --- a/src/codec/CodecResp.hpp +++ b/src/codec/CodecResp.hpp @@ -16,12 +16,6 @@ namespace neb { -const char RESP_SIMPLE_STRING = '+'; -const char RESP_ERROR = '-'; -const char RESP_INTEGER = ':'; -const char RESP_BULK_STRING = '$'; -const char RESP_ARRAY = '*'; - class RedisStep; class CodecResp: public neb::Codec @@ -48,6 +42,13 @@ class CodecResp: public neb::Codec E_CODEC_STATUS DecodeInteger(CBuffer* pBuff, RedisReply& oReply); E_CODEC_STATUS DecodeBulkString(CBuffer* pBuff, RedisReply& oReply); E_CODEC_STATUS DecodeArray(CBuffer* pBuff, RedisReply& oReply); + +private: + static const char RESP_SIMPLE_STRING; + static const char RESP_ERROR; + static const char RESP_INTEGER; + static const char RESP_BULK_STRING; + static const char RESP_ARRAY; }; } /* namespace neb */ diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index f66a83dd..6254da5f 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -18,7 +18,6 @@ #include "actor/step/Step.hpp" #include "actor/step/RedisStep.hpp" #include "actor/session/sys_session/manager/SessionManager.hpp" -#include "Nodes.hpp" namespace neb { @@ -30,11 +29,6 @@ Dispatcher::Dispatcher(Labor* pLabor, std::shared_ptr pLogger) m_pErrBuff = (char*)malloc(gc_iErrBuffLen); m_loop = ev_loop_new(EVFLAG_FORKCHECK | EVFLAG_SIGNALFD); -/*#if __cplusplus >= 201401L - m_pSessionNode = std::make_unique(); -#else - m_pSessionNode = std::unique_ptr(new Nodes()); -#endif*/ } Dispatcher::~Dispatcher() @@ -92,6 +86,7 @@ void Dispatcher::PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, i { ((Worker*)(pDispatcher->m_pLabor))->CheckParent(); } + pDispatcher->CheckFailedNode(); } ev_timer_stop (loop, watcher); ev_timer_set (watcher, NODE_BEAT + ev_time() - ev_now(loop), 0); @@ -295,6 +290,10 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) } if (CHANNEL_STATUS_ESTABLISHED != pChannel->m_pImpl->GetChannelStatus()) { + if (pChannel->IsClient()) + { + m_pSessionNode->NodeFailed(pChannel->GetIdentify()); + } auto& listUncompletedStep = pChannel->m_pImpl->GetPipelineStepSeq(); for (auto it = listUncompletedStep.begin(); it != listUncompletedStep.end(); ++it) @@ -407,6 +406,10 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) } if (CHANNEL_STATUS_ESTABLISHED != pChannel->m_pImpl->GetChannelStatus()) { + if (pChannel->IsClient()) + { + m_pSessionNode->NodeFailed(pChannel->GetIdentify()); + } auto& listUncompletedStep = pChannel->m_pImpl->GetPipelineStepSeq(); for (auto it = listUncompletedStep.begin(); it != listUncompletedStep.end(); ++it) @@ -575,6 +578,10 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) } } + if (pChannel->IsClient() && pChannel->m_pImpl->GetMsgNum() == 0) + { + m_pSessionNode->NodeRecover(pChannel->GetIdentify()); + } E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(); if (CODEC_STATUS_OK == eCodecStatus) { @@ -695,97 +702,6 @@ bool Dispatcher::AddIoTimeout(std::shared_ptr pChannel, ev_tstamp } } -bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) -{ - LOG4_TRACE("node_type: %s", strNodeType.c_str()); - std::string strOnlineNode; - if (m_pSessionNode->GetNode(strNodeType, strOnlineNode)) - { - return(SendTo(strOnlineNode, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); - } - else - { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } -} - -bool Dispatcher::SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) -{ - LOG4_TRACE("nody_type: %s, factor: %d", strNodeType.c_str(), uiFactor); - std::string strOnlineNode; - if (m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) - { - return(SendTo(strOnlineNode, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); - } - else - { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } -} - -bool Dispatcher::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) -{ - LOG4_TRACE("nody_type: %s", strNodeType.c_str()); - if (oMsgBody.has_req_target()) - { - if (0 != oMsgBody.req_target().route_id()) - { - return(SendOriented(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, eCodecType)); - } - else if (oMsgBody.req_target().route().length() > 0) - { - std::string strOnlineNode; - if (m_pSessionNode->GetNode(strNodeType, oMsgBody.req_target().route(), strOnlineNode)) - { - return(SendTo(strOnlineNode, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); - } - else - { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } - } - else - { - return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); - } - } - else - { - LOG4_ERROR("MsgBody is not a request message!"); - return(false); - } -}; - -bool Dispatcher::Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) -{ - LOG4_TRACE("node_type: %s", strNodeType.c_str()); - std::unordered_set setOnlineNodes; - if (m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) - { - bool bSendResult = false; - for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) - { - bSendResult |= SendTo(*node_iter, eCodecType, false, true, iCmd, uiSeq, oMsgBody); - } - return(bSendResult); - } - else - { - if ("BEACON" == strNodeType) - { - LOG4_WARNING("no beacon config."); - } - else - { - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - } - return(false); - } -} - bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 39ad8508..4790198e 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -49,6 +49,7 @@ extern "C" { #include "pb/msg.pb.h" #include "channel/SocketChannel.hpp" #include "logger/NetLogger.hpp" +#include "Nodes.hpp" namespace neb { @@ -121,10 +122,14 @@ class Dispatcher bool SendTo(const std::string& strHost, int iPort, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); template bool AutoSend(const std::string& strIdentify, const std::string& strHost, int iPort, int iRemoteWorkerIndex, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); - bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); - bool SendOriented(const std::string& strNodeType, unsigned int uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); - bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); - bool Broadcast(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + template + bool SendRoundRobin(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); + template + bool SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args); + template + bool SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args); + template + bool Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); std::shared_ptr StressSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); @@ -178,6 +183,7 @@ class Dispatcher bool AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout = 60.0); bool AcceptFdAndTransfer(int iFd, int iFamily = AF_INET); bool AcceptServerConn(int iFd); + void CheckFailedNode(); void EvBreak(); private: @@ -185,6 +191,7 @@ class Dispatcher Labor* m_pLabor; struct ev_loop* m_loop; int32 m_iClientNum; + time_t m_lLastCheckNodeTime; std::shared_ptr m_pLogger; std::unique_ptr m_pSessionNode; @@ -437,6 +444,106 @@ bool Dispatcher::AutoSend( } } +template +bool Dispatcher::SendRoundRobin(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + LOG4_TRACE("node_type: %s", strNodeType.c_str()); + std::string strOnlineNode; + if (m_pSessionNode->GetNode(strNodeType, strOnlineNode)) + { + return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + } + LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } +} + +template +bool Dispatcher::SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args) +{ + LOG4_TRACE("node_type: %s", strNodeType.c_str()); + std::string strOnlineNode; + if (m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) + { + return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + } + LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } +} + +template +bool Dispatcher::SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args) +{ + LOG4_TRACE("node_type: %s", strNodeType.c_str()); + std::string strOnlineNode; + if (m_pSessionNode->GetNode(strNodeType, strFactor, strOnlineNode)) + { + return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + } + LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } +} + +template +bool Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + LOG4_TRACE("node_type: %s", strNodeType.c_str()); + std::unordered_set setOnlineNodes; + if (m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) + { + bool bSendResult = false; + for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + { + bSendResult |= SendTo(*node_iter, eCodecType, bWithSsl, bPipeline, std::forward(args)...); + } + return(bSendResult); + } + else + { + if ("BEACON" == strNodeType) + { + LOG4_WARNING("no beacon config."); + } + else + { + LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + bool bSendResult = false; + for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + { + bSendResult |= SendTo(*node_iter, eCodecType, bWithSsl, bPipeline, std::forward(args)...); + } + return(bSendResult); + } + LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); + } + return(false); + } +} } /* namespace neb */ diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index 2c8072d3..0c11bdfc 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -15,6 +15,7 @@ #include "cryptopp/hex.h" #include "cryptopp/filters.h" #include "util/encrypt/city.h" +#include "util/StringCoder.hpp" namespace neb { @@ -27,6 +28,7 @@ Nodes::Nodes(int iHashAlgorithm, int iVirtualNodeNum) Nodes::~Nodes() { m_mapNode.clear(); + m_mapNodeType.clear(); } bool Nodes::GetNode(const std::string& strNodeType, const std::string& strHashKey, std::string& strNodeIdentify) @@ -54,6 +56,15 @@ bool Nodes::GetNode(const std::string& strNodeType, const std::string& strHashKe } else { + if (node_type_iter->second->bCheckFailedNode) + { + node_type_iter->second->bCheckFailedNode = false; + if (node_type_iter->second->setFailedNode.size() > 0) + { + strNodeIdentify = *(node_type_iter->second->setFailedNode.begin()); + return(true); + } + } auto c_iter = node_type_iter->second->mapHash2Node.lower_bound(uiKeyHash); if (c_iter == node_type_iter->second->mapHash2Node.end()) { @@ -77,6 +88,15 @@ bool Nodes::GetNode(const std::string& strNodeType, uint32 uiHash, std::string& } else { + if (node_type_iter->second->bCheckFailedNode) + { + node_type_iter->second->bCheckFailedNode = false; + if (node_type_iter->second->setFailedNode.size() > 0) + { + strNodeIdentify = *(node_type_iter->second->setFailedNode.begin()); + return(true); + } + } auto c_iter = node_type_iter->second->mapHash2Node.lower_bound(uiHash); if (c_iter == node_type_iter->second->mapHash2Node.end()) { @@ -119,6 +139,15 @@ bool Nodes::GetNode(const std::string& strNodeType, std::string& strNodeIdentify } else { + if (node_type_iter->second->bCheckFailedNode) + { + node_type_iter->second->bCheckFailedNode = false; + if (node_type_iter->second->setFailedNode.size() > 0) + { + strNodeIdentify = *(node_type_iter->second->setFailedNode.begin()); + return(true); + } + } node_type_iter->second->itPollingNode++; if (node_type_iter->second->itPollingNode == node_type_iter->second->mapNode2Hash.end()) { @@ -153,6 +182,7 @@ void Nodes::AddNode(const std::string& strNodeType, const std::string& strNodeId { std::shared_ptr pNode = std::make_shared(); m_mapNode.insert(std::make_pair(strNodeType, pNode)); + pNode->strNodeType = strNodeType; std::string strHashKey; char szVirtualNodeIdentify[40] = {0}; uint32 uiKeyHash = 0; @@ -217,6 +247,17 @@ void Nodes::AddNode(const std::string& strNodeType, const std::string& strNodeId node_type_iter->second->itHashRing = node_type_iter->second->mapHash2Node.begin(); } } + auto node_id_iter = m_mapNodeType.find(strNodeIdentify); + if (node_id_iter == m_mapNodeType.end()) + { + std::unordered_set setNodeType; + setNodeType.insert(strNodeType); + m_mapNodeType.insert(std::make_pair(strNodeIdentify, std::move(setNodeType))); + } + else + { + node_id_iter->second.insert(strNodeType); + } } void Nodes::AddNodeKetama(const std::string& strNodeType, const std::string& strNodeIdentify) @@ -226,6 +267,7 @@ void Nodes::AddNodeKetama(const std::string& strNodeType, const std::string& str { std::shared_ptr pNode = std::make_shared(); m_mapNode.insert(std::make_pair(strNodeType, pNode)); + pNode->strNodeType = strNodeType; std::string strHash; char szVirtualNodeIdentify[40] = {0}; int32 iPointPerHash = 4; @@ -284,6 +326,36 @@ void Nodes::AddNodeKetama(const std::string& strNodeType, const std::string& str node_type_iter->second->itHashRing = node_type_iter->second->mapHash2Node.begin(); } } + auto node_id_iter = m_mapNodeType.find(strNodeIdentify); + if (node_id_iter == m_mapNodeType.end()) + { + std::unordered_set setNodeType; + setNodeType.insert(strNodeType); + m_mapNodeType.insert(std::make_pair(strNodeIdentify, std::move(setNodeType))); + } + else + { + node_id_iter->second.insert(strNodeType); + } +} + +bool Nodes::SplitAddAndGetNode(const std::string& strNodeType, std::string& strNodeIdentify) +{ + std::vector vecAddress; + Split(strNodeType, ",", vecAddress); + if (vecAddress.size() <= 1) + { + return(false); + } + for (size_t i = 0; i < vecAddress.size(); ++i) + { + if (vecAddress[i].size() < 5) + { + continue; + } + AddNode(strNodeType, vecAddress[i]); + } + return(GetNode(strNodeType, strNodeIdentify)); } void Nodes::DelNode(const std::string& strNodeType, const std::string& strNodeIdentify) @@ -308,11 +380,89 @@ void Nodes::DelNode(const std::string& strNodeType, const std::string& strNodeId node_type_iter->second->itHashRing = node_type_iter->second->mapHash2Node.begin(); } + if (node_type_iter->second->setFailedNode.size() > 0) + { + auto it = node_type_iter->second->setFailedNode.find(strNodeIdentify); + if (it != node_type_iter->second->setFailedNode.end()) + { + node_type_iter->second->setFailedNode.erase(it); + } + } + if (node_type_iter->second->mapNode2Hash.empty()) { m_mapNode.erase(node_type_iter); } } + auto node_id_iter = m_mapNodeType.find(strNodeIdentify); + if (node_id_iter != m_mapNodeType.end()) + { + m_mapNodeType.erase(node_id_iter); + } +} + +void Nodes::NodeFailed(const std::string& strNodeIdentify) +{ + auto node_id_iter = m_mapNodeType.find(strNodeIdentify); + if (node_id_iter == m_mapNodeType.end()) + { + ; // not found + } + else + { + for (auto type_it = node_id_iter->second.begin(); type_it != node_id_iter->second.end(); ++type_it) + { + auto node_type_iter = m_mapNode.find(*type_it); + if (node_type_iter != m_mapNode.end()) + { + auto node_iter = node_type_iter->second->mapNode2Hash.find(strNodeIdentify); + if (node_iter != node_type_iter->second->mapNode2Hash.end()) + { + for (auto hash_iter = node_iter->second.begin(); + hash_iter != node_iter->second.end(); ++hash_iter) + { + auto it = node_type_iter->second->mapHash2Node.find(*hash_iter); + if (it != node_type_iter->second->mapHash2Node.end()) + { + node_type_iter->second->mapHash2Node.erase(it); + } + } + node_type_iter->second->mapNode2Hash.erase(node_iter); + node_type_iter->second->itPollingNode = node_type_iter->second->mapNode2Hash.begin(); + node_type_iter->second->itHashRing = node_type_iter->second->mapHash2Node.begin(); + } + node_type_iter->second->setFailedNode.insert(strNodeIdentify); + } + } + } +} + +void Nodes::NodeRecover(const std::string& strNodeIdentify) +{ + auto node_id_iter = m_mapNodeType.find(strNodeIdentify); + if (node_id_iter == m_mapNodeType.end()) + { + ; // not found + } + else + { + for (auto type_it = node_id_iter->second.begin(); type_it != node_id_iter->second.end(); ++type_it) + { + auto node_type_iter = m_mapNode.find(*type_it); + if (node_type_iter != m_mapNode.end()) + { + if (node_type_iter->second->setFailedNode.size() > 0) + { + auto it = node_type_iter->second->setFailedNode.find(strNodeIdentify); + if (it != node_type_iter->second->setFailedNode.end()) + { + node_type_iter->second->setFailedNode.erase(it); + } + } + AddNode(*type_it, strNodeIdentify); + } + } + } } bool Nodes::IsNodeType(const std::string& strNodeIdentify, const std::string& strNodeType) @@ -329,6 +479,14 @@ bool Nodes::IsNodeType(const std::string& strNodeIdentify, const std::string& st return(false); } +void Nodes::CheckFailedNode() +{ + for (auto iter = m_mapNode.begin(); iter != m_mapNode.end(); ++iter) + { + iter->second->bCheckFailedNode = true; + } +} + uint32 Nodes::hash_fnv1_64(const char *key, size_t key_length) { uint64_t hash = FNV_64_INIT; diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index 6d310fd1..f9e982ba 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -55,10 +55,13 @@ class Nodes struct tagNode { + bool bCheckFailedNode = false; + std::string strNodeType; T_NODE2HASH_MAP mapNode2Hash; T_NODE2HASH_MAP::iterator itPollingNode; std::map mapHash2Node; std::map::const_iterator itHashRing; + std::unordered_set setFailedNode; tagNode(){} ~tagNode(){} @@ -92,8 +95,19 @@ class Nodes * @param strNodeIdentify 节点标识 */ void AddNode(const std::string& strNodeType, const std::string& strNodeIdentify); + + /** @deprecate */ void AddNodeKetama(const std::string& strNodeType, const std::string& strNodeIdentify); + /** + * @brief 添加并获取节点 + * @note 将形如192.168.1.47:6379,192.168.1.53:6379,192.168.1.38:6379的 + * strNodeType分割后添加节点信息并且获取其中一个节点。 + * @param[in] strNodeType 节点类型 + * @param[out] strNodeIdentify 节点标识 + */ + bool SplitAddAndGetNode(const std::string& strNodeType, std::string& strNodeIdentify); + /** * @brief 删除节点 * @note 删除节点信息,每个节点均有一个主节点一个被节点构成。 @@ -102,8 +116,23 @@ class Nodes */ void DelNode(const std::string& strNodeType, const std::string& strNodeIdentify); + /** + * @brief 节点失败 + * @not 节点失败达到一定数量会熔断,待探测成功后会重新加入服务节点列表。 + * @param strNodeIdentify 节点标识 + */ + void NodeFailed(const std::string& strNodeIdentify); + + /** + * @brief 节点恢复 + * @param strNodeIdentify 节点标识 + */ + void NodeRecover(const std::string& strNodeIdentify); + bool IsNodeType(const std::string& strNodeIdentify, const std::string& strNodeType); + void CheckFailedNode(); + protected: uint32 hash_fnv1_64(const char *key, size_t key_length); uint32 hash_fnv1a_64(const char *key, size_t key_length); @@ -113,7 +142,8 @@ class Nodes const int m_iHashAlgorithm; const int m_iVirtualNodeNum; - std::unordered_map > m_mapNode; + std::unordered_map > m_mapNode; + std::unordered_map> m_mapNodeType; // key为节点标识 }; } /* namespace neb */ From 8b648b5ff0ff7ba130f20e0db5fe87a504a3d8f7 Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 12 Jan 2021 00:29:13 +0800 Subject: [PATCH 119/176] replace SendToOneOf with SendRoundRobin, build passing --- src/actor/Actor.cpp | 4 ++-- .../step/sys_step/manager/StepReportToBeacon.cpp | 2 +- src/ios/Dispatcher.cpp | 12 +++++++++++- src/ios/Dispatcher.hpp | 4 ++-- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index d6249b09..93fae4e6 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -214,7 +214,7 @@ bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedis bool Actor::SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline) { - return(m_pLabor->GetActorBuilder()->SendRoundRobin(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); + return(m_pLabor->GetDispatcher()->SendRoundRobin(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); } bool Actor::SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) @@ -247,7 +247,7 @@ bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSe { if (0 != oMsgBody.req_target().route_id()) { - return(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, eCodecType); + return(SendOriented(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, eCodecType)); } else if (oMsgBody.req_target().route().length() > 0) { diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp index 24640f4d..cfff4e18 100644 --- a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp +++ b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp @@ -46,7 +46,7 @@ E_CMD_STATUS StepReportToBeacon::Emit( } m_pSessionManager->MakeReportData(oReportData); oMsgBody.set_data(oReportData.ToString()); - GetLabor(this)->GetDispatcher()->Broadcast("BEACON", false, true, + GetLabor(this)->GetDispatcher()->Broadcast("BEACON", CODEC_NEBULA, false, true, (int32)CMD_REQ_NODE_STATUS_REPORT, GetSequence(), oMsgBody); return(CMD_STATUS_RUNNING); } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 6254da5f..4c42fe77 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -706,7 +706,7 @@ bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBod { if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) { - return(Broadcast("BEACON", iCmd, uiSeq, oMsgBody, CODEC_NEBULA)); + return(Broadcast("BEACON", CODEC_NEBULA, false, true, iCmd, uiSeq, oMsgBody)); } else { @@ -1179,6 +1179,7 @@ bool Dispatcher::Init() #endif Codec::AddAutoSwitchCodecType(CODEC_HTTP); Codec::AddAutoSwitchCodecType(CODEC_PROTO); + Codec::AddAutoSwitchCodecType(CODEC_RESP); Codec::AddAutoSwitchCodecType(CODEC_PRIVATE); return(true); } @@ -1525,6 +1526,15 @@ bool Dispatcher::AcceptServerConn(int iFd) return(false); } +void Dispatcher::CheckFailedNode() +{ + if (GetNowTime() - m_lLastCheckNodeTime >= 60) + { + m_pSessionNode->CheckFailedNode(); + m_lLastCheckNodeTime = GetNowTime(); + } +} + void Dispatcher::EvBreak() { ev_break (m_loop, EVBREAK_ALL); diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 4790198e..c576d6d5 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -60,7 +60,6 @@ class Worker; class LoadStress; // not in Nebula project class Actor; class ActorBuilder; -class Nodes; struct tagClientConnWatcherData; typedef void (*signal_callback)(struct ev_loop*,ev_signal*,int); @@ -508,7 +507,7 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCode } template -bool Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool Dispatcher::Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); std::unordered_set setOnlineNodes; @@ -530,6 +529,7 @@ bool Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWi else { LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + std::string strOnlineNode; if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) { bool bSendResult = false; From 493a30e6cbd9f8e20bc88048f1030cd92ac98b42 Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 12 Jan 2021 00:35:41 +0800 Subject: [PATCH 120/176] replace SendToOneOf with SendRoundRobin, build passing --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index c2392afa..8b99c411 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,11 @@ Nebula can be used as a single high-performance TCP server, but building a clust We provides [NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap), which allows developers to build and deploy Nebula quickly. Nebula will be a framework that be widely used. A distributed solution based on NebulaBootstrap will make it easy to develop micro-service applications in C++. All dependencies will be automatically resolved in the following build steps. + first install gcc and auto tools. centos: +``` + sudo yum install -y gcc gcc-c++ + sudo yum install -y autoconf automake libtool +``` build step: 1. wget https://github.com/Bwar/NebulaBootstrap/archive/master.zip 2. unzip master.zip; rm master.zip; mv NebulaBootstrap-master NebulaBootstrap; chmod u+x deploy.sh; chmod u+x deploy.sh From 4036a019920b23d6fd37853623a8652de69cc185 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 16 Jan 2021 09:34:59 +0800 Subject: [PATCH 121/176] test passing with http gateway --- src/actor/Actor.hpp | 2 +- .../session/sys_session/SessionOneOf.cpp | 83 ------------------- .../session/sys_session/SessionOneOf.hpp | 48 ----------- src/actor/step/RedisStep.cpp | 2 +- src/channel/RedisChannel.cpp | 29 ------- src/channel/RedisChannel.hpp | 71 ---------------- src/channel/SocketChannel.cpp | 2 +- src/channel/SocketChannelImpl.cpp | 2 +- src/codec/CodecResp.cpp | 2 +- src/codec/http2/CodecHttp2.cpp | 1 + src/ios/Dispatcher.cpp | 2 +- src/ios/Dispatcher.hpp | 24 ++++-- src/labor/Manager.cpp | 2 +- src/labor/Worker.cpp | 2 +- 14 files changed, 26 insertions(+), 246 deletions(-) delete mode 100644 src/actor/session/sys_session/SessionOneOf.cpp delete mode 100644 src/actor/session/sys_session/SessionOneOf.hpp delete mode 100644 src/channel/RedisChannel.cpp delete mode 100644 src/channel/RedisChannel.hpp diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index c97bee5c..0ed0c3e8 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -229,7 +229,7 @@ class Actor: public std::enable_shared_from_this /** * @brief 发送redis请求到类似于codis proxy的服务 */ - virtual bool SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true); + virtual bool SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = false); /** * @brief 发送raw请求 diff --git a/src/actor/session/sys_session/SessionOneOf.cpp b/src/actor/session/sys_session/SessionOneOf.cpp deleted file mode 100644 index f1603a1c..00000000 --- a/src/actor/session/sys_session/SessionOneOf.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file SessionOneOf.hpp - * @brief - * @author Bwar - * @date: 2020-12-26 - * @note - * Modify history: - ******************************************************************************/ -#include "SessionOneOf.hpp" -#include -#include "util/StringCoder.hpp" -#include "labor/Labor.hpp" -#include "ios/Dispatcher.hpp" - -namespace neb -{ - -SessionOneOf::SessionOneOf(const std::string& strIdentify, bool bWithSsl, bool bPipeline) - : Timer(strIdentify, 300.0), - m_bWithSsl(bWithSsl), m_bPipeline(bPipeline), m_uiAddressIndex(0) -{ - Split(strIdentify, ",", m_vecAddress); -} - -SessionOneOf::~SessionOneOf() -{ -} - -bool SessionOneOf::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) -{ - if (m_vecAddress.size() == 0) - { - return(false); - } - m_uiAddressIndex = (m_uiAddressIndex < m_vecAddress.size()) ? m_uiAddressIndex : 0; - return(GetLabor(this)->GetDispatcher()->SendTo(m_vecAddress[m_uiAddressIndex++], eCodecType, false, true, iCmd, uiSeq, oMsgBody)); -} - -bool SessionOneOf::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq) -{ - if (m_vecAddress.size() == 0) - { - return(false); - } - m_uiAddressIndex = (m_uiAddressIndex < m_vecAddress.size()) ? m_uiAddressIndex : 0; - bool bWithSsl = false; - bool bPipeline = false; - if (oHttpMsg.http_major() >= 2) - { - bPipeline = true; - } - std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(":")); - std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c)->unsigned char {return std::tolower(c);}); - if (strSchema == std::string("https")) - { - bWithSsl = true; - } - return(GetLabor(this)->GetDispatcher()->SendTo(m_vecAddress[m_uiAddressIndex++], CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, uiStepSeq)); -} - -bool SessionOneOf::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) -{ - if (m_vecAddress.size() == 0) - { - return(false); - } - m_uiAddressIndex = (m_uiAddressIndex < m_vecAddress.size()) ? m_uiAddressIndex : 0; - return(GetLabor(this)->GetDispatcher()->SendTo(m_vecAddress[m_uiAddressIndex++], CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, uiStepSeq)); -} - -bool SessionOneOf::SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) -{ - if (m_vecAddress.size() == 0) - { - return(false); - } - m_uiAddressIndex = (m_uiAddressIndex < m_vecAddress.size()) ? m_uiAddressIndex : 0; - return(GetLabor(this)->GetDispatcher()->SendTo(m_vecAddress[m_uiAddressIndex++], CODEC_RESP, bWithSsl, bPipeline, pRawData, uiRawDataSize, uiStepSeq)); -} - -} /* namespace neb */ - diff --git a/src/actor/session/sys_session/SessionOneOf.hpp b/src/actor/session/sys_session/SessionOneOf.hpp deleted file mode 100644 index 1eea92ef..00000000 --- a/src/actor/session/sys_session/SessionOneOf.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file SessionOneOf.hpp - * @brief - * @author Bwar - * @date: 2020-12-26 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_ACTOR_SESSION_SYS_SESSION_SESSIONONEOF_H_ -#define SRC_ACTOR_SESSION_SYS_SESSION_SESSIONONEOF_H_ - -#include "actor/session/Timer.hpp" -#include "actor/ActorSys.hpp" - -namespace neb -{ - -class SessionOneOf: public Timer, - DynamicCreator, - public ActorSys -{ -public: - SessionOneOf(const std::string& strIdentify, bool bWithSsl, bool bPipeline); - virtual ~SessionOneOf(); - - virtual E_CMD_STATUS Timeout() - { - return(CMD_STATUS_COMPLETED); - } - - virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); - virtual bool SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq = 0); - virtual bool SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); - virtual bool SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl = false, bool bPipeline = false, uint32 uiStepSeq = 0); - -private: - bool m_bWithSsl; ///< 是否支持SSL - bool m_bPipeline; ///< 是否支持pipeline - //std::string m_strIdentify; ///< 地址标识,由m_vecAddress合并而成,形如 192.168.47.101:6379,198.168.47.102:6379,192.168.47.103:6379 GetSessionId() - uint32 m_uiAddressIndex; - std::vector m_vecAddress; ///< 集群地址 -}; - -} /* namespace neb */ - -#endif /* SRC_ACTOR_SESSION_SYS_SESSION_SESSIONONEOF_H_ */ - diff --git a/src/actor/step/RedisStep.cpp b/src/actor/step/RedisStep.cpp index 8cd79e1c..a6ae4d86 100644 --- a/src/actor/step/RedisStep.cpp +++ b/src/actor/step/RedisStep.cpp @@ -82,7 +82,7 @@ std::shared_ptr RedisStep::MutableRedisRequest() pElement->set_str(m_strCmd); for (size_t i = 0; i < m_vecCmdArguments.size(); ++i) { - pElement = m_oRedisRequest.add_element(); + pElement = pRequest->add_element(); pElement->set_type(REDIS_REPLY_STRING); pElement->set_str(m_vecCmdArguments[i].first); } diff --git a/src/channel/RedisChannel.cpp b/src/channel/RedisChannel.cpp deleted file mode 100644 index 0cb1ca71..00000000 --- a/src/channel/RedisChannel.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file RedisChannel.cpp - * @brief - * @author bwar - * @date: Mar 25, 2018 - * @note - * Modify history: - ******************************************************************************/ -#include "RedisChannel.hpp" - -namespace neb -{ - -RedisChannel::RedisChannel(redisAsyncContext *c) - : m_bPipeline(false), bIsReady(false), m_pRedisCtx(c) -{ -} - -RedisChannel::~RedisChannel() -{ - // RedisChannel should not be active closed. - //if (NULL != m_pRedisCtx) - //{ - // redisAsyncFree(m_pRedisCtx); - //} -} - -} /* namespace neb */ diff --git a/src/channel/RedisChannel.hpp b/src/channel/RedisChannel.hpp deleted file mode 100644 index 2651ad4e..00000000 --- a/src/channel/RedisChannel.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file RedisChannel.hpp - * @brief - * @author bwar - * @date: Mar 25, 2018 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_CHANNEL_REDISCHANNEL_HPP_ -#define SRC_CHANNEL_REDISCHANNEL_HPP_ - -#include -#include -#include -#include "hiredis/async.h" -#include "Channel.hpp" -#include "Definition.hpp" - -namespace neb -{ - -class Dispatcher; -class ActorBuilder; -class RedisStep; - -class RedisChannel: public Channel -{ -public: - RedisChannel(redisAsyncContext *c); - virtual ~RedisChannel(); - - const std::string& GetIdentify() - { - return(m_strIdentify); - } - - void SetIdentify(const std::string& strIdentify) - { - m_strIdentify = strIdentify; - } - - redisAsyncContext* RedisContext() - { - return(m_pRedisCtx); - } - - bool IsPipeline() const - { - return(m_bPipeline); - } - -protected: - void SetPipeline(bool bPipeline) - { - m_bPipeline = bPipeline; - } - -private: - bool m_bPipeline; ///< 是否支持pipeline - bool bIsReady; ///< redis连接是否准备就绪 - redisAsyncContext* m_pRedisCtx; ///< redis连接上下文地址 - std::string m_strIdentify; ///< 连接标识(可以为空,不为空时用于标识业务层与连接的关系) - std::list< std::shared_ptr > listPipelineStep; ///< RedisStep*的创建和回收在WorkerImpl - friend Dispatcher; - friend ActorBuilder; -}; - -} /* namespace neb */ - -#endif /* SRC_CHANNEL_REDISCHANNEL_HPP_ */ diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index 8f037e4d..1e44946b 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -29,7 +29,7 @@ SocketChannel::SocketChannel(std::shared_ptr pLogger, int iFd, uint32 pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "with openssl, create SocekChannelSslImpl."); m_pImpl = std::dynamic_pointer_cast(std::make_shared(this, pLogger, iFd, ulSeq, dKeepAlive)); #else - pLogger->WriteLog(Logger::INFO, __FILE__, __LINE__, __FUNCTION__, "without openssl, SocekChannelImpl instead."); + pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "without openssl, SocekChannelImpl instead."); m_pImpl = std::make_shared(this, pLogger, iFd, ulSeq, dKeepAlive); #endif } diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 42871865..51eeb99c 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -1100,7 +1100,7 @@ bool SocketChannelImpl::Close() if (0 == close(m_iFd)) { m_ucChannelStatus = CHANNEL_STATUS_CLOSED; - LOG4_DEBUG("channel[%d], channel_seq[%u] close successfully.", m_iFd, GetSequence()); + LOG4_TRACE("channel[%d], channel_seq[%u] close successfully.", m_iFd, GetSequence()); return(true); } else diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index 07bafa3e..add63f9c 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -75,7 +75,7 @@ E_CODEC_STATUS CodecResp::Encode(const RedisReply& oReply, CBuffer* pBuff) E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply) { - if (pBuff->ReadableBytes() < 0) + if (pBuff->ReadableBytes() < 1) { return(CODEC_STATUS_PAUSE); } diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index cdcd0024..40fc9873 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -81,6 +81,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR if (m_uiGoawayLastStreamId > 0 && m_stFrameHead.uiStreamIdentifier > m_uiGoawayLastStreamId) { SetErrno(H2_ERR_CANCEL); + pBuff->SetReadIndex(iReadIdx); return(CODEC_STATUS_PART_ERR); } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 4c42fe77..a8c1da61 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -23,7 +23,7 @@ namespace neb { Dispatcher::Dispatcher(Labor* pLabor, std::shared_ptr pLogger) - : m_pErrBuff(NULL), m_pLabor(pLabor), m_loop(NULL), m_iClientNum(0), + : m_pErrBuff(NULL), m_pLabor(pLabor), m_loop(NULL), m_iClientNum(0), m_lLastCheckNodeTime(0), m_pLogger(pLogger), m_pSessionNode(nullptr) { m_pErrBuff = (char*)malloc(gc_iErrBuffLen); diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index c576d6d5..b15c6aa7 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -120,13 +120,16 @@ class Dispatcher template bool SendTo(const std::string& strHost, int iPort, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); template - bool AutoSend(const std::string& strIdentify, const std::string& strHost, int iPort, int iRemoteWorkerIndex, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); + bool AutoSend(const std::string& strIdentify, const std::string& strHost, + int iPort, int iRemoteWorkerIndex, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); template bool SendRoundRobin(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); template - bool SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args); + bool SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, + bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args); template - bool SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args); + bool SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, + bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args); template bool Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); @@ -341,6 +344,10 @@ bool Dispatcher::SendTo(const std::string& strHost, int iPort, E_CODEC_TYPE eCod else { auto channel_iter = named_iter->second.begin(); + if (channel_iter == named_iter->second.end()) + { + return(AutoSend(ossIdentify.str(), strHost, iPort, 0, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + } bool bResult = SendTo((*channel_iter), std::forward(args)...); if (!bPipeline && bResult) { @@ -532,12 +539,15 @@ bool Dispatcher::Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecTy std::string strOnlineNode; if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) { - bool bSendResult = false; - for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + if (m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) { - bSendResult |= SendTo(*node_iter, eCodecType, bWithSsl, bPipeline, std::forward(args)...); + bool bSendResult = false; + for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + { + bSendResult |= SendTo(*node_iter, eCodecType, bWithSsl, bPipeline, std::forward(args)...); + } + return(bSendResult); } - return(bSendResult); } LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); } diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 4f620c3d..41e14e67 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -146,7 +146,7 @@ bool Manager::InitLogger(const CJsonObject& oJsonConf) oJsonConf.Get("log_max_line_len", iMaxLogLineLen); oJsonConf.Get("log_level", iLogLevel); oJsonConf.Get("always_flush_log", bAlwaysFlushLog); - m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, iMaxLogLineLen, this); + m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, iMaxLogLineLen, bAlwaysFlushLog, this); m_pLogger->SetNetLogLevel(iNetLogLevel); LOG4_NOTICE("%s program begin, and work path %s...", oJsonConf("server_name").c_str(), m_stNodeInfo.strWorkPath.c_str()); return(true); diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 84293e9a..899c86e6 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -254,7 +254,7 @@ bool Worker::InitLogger(const CJsonObject& oJsonConf, const std::string& strLogN oJsonConf.Get("net_log_level", iNetLogLevel); oJsonConf.Get("log_level", iLogLevel); oJsonConf.Get("always_flush_log", bAlwaysFlushLog); - m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, iMaxLogLineLen, this); + m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, iMaxLogLineLen, bAlwaysFlushLog, this); m_pLogger->SetNetLogLevel(iNetLogLevel); LOG4_NOTICE("%s program begin...", getproctitle()); return(true); From fb078b3d69fa11fd88de732ed101fce886d56284 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 24 Jan 2021 16:21:55 +0800 Subject: [PATCH 122/176] v1.4 release --- README.md | 9 ++++++++- README_cn.md | 11 +++++++++-- docs/cn/install.md | 8 ++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8b99c411..92939393 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,6 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Depend on * [protobuf](https://github.com/google/protobuf) * [libev](http://software.schmorp.de/pkg/libev.html) or [libev](https://github.com/kindy/libev) - * [hiredis](https://github.com/redis/hiredis) * [crypto++](https://github.com/weidai11/cryptopp) * [http_parse](https://github.com/nodejs/http-parser) integrate into Nebula/src/util/http * [CJsonObject](https://github.com/Bwar/CJsonObject) integrate into Nebula/src/util/json @@ -130,6 +129,14 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v1.4 + - replace hiredis client with native CodecResp + - add redis cluster client + - add raw data transmission + - add log real-time flush configurability + - add support for IP address combination node type + - dispatcher optimization + - bug fixed #### v1.3 - add non-pipeline mode support for redis connection - the worker thread to start before the Loader thread in thread mode, and bring the worker thread ID to the Loader diff --git a/README_cn.md b/README_cn.md index 51f31b80..946bc92c 100644 --- a/README_cn.md +++ b/README_cn.md @@ -88,7 +88,6 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 依赖 * [protobuf](https://github.com/google/protobuf) * [libev](http://software.schmorp.de/pkg/libev.html) 或 [libev](https://github.com/kindy/libev) - * [hiredis](https://github.com/redis/hiredis) * [crypto++](https://github.com/weidai11/cryptopp) * [http_parse](https://github.com/nodejs/http-parser) 已集成到 Nebula/src/util/http * [CJsonObject](https://github.com/Bwar/CJsonObject) 已集成到 Nebula/src/util/json @@ -110,10 +109,18 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 开发任务 - 完成开发指南 - 支持http2 - - 原生支持redis cluster、codis、dubbo、grpc等协议 + - 原生支持dubbo、grpc等协议 ## 版本历史 +#### v1.4 + - 以原生的CodecResp替代hiredis客户端 + - 增加redis cluster支持 + - 增加裸数据(RawData)传输支持 + - 增加日志实时flush可配置化 + - 增加IP地址组合节点类型支持 + - Dispatcher分发优化 + - bug修复 #### v1.3 - 增加redis连接的非pipeline模式支持 - 增加线程模式下Worker线程比Loader线程先启动,并把worker线程Id带到Loader diff --git a/docs/cn/install.md b/docs/cn/install.md index e40c13e4..c587f5d2 100644 --- a/docs/cn/install.md +++ b/docs/cn/install.md @@ -23,6 +23,14 @@ ### 安装说明   NebulaBootstrap使原本依赖很少的Nebula部署起来更轻而易举,获取安装源->解压安装包,给脚本加可执行权限->一键安装。 +  如果系统缺少autotool工具请先安装一下,ubuntu使用apt-get安装,centos使用yum安装,下面以centos为例: +``` + sudo yum install -y gcc gcc-c++ + sudo yum install -y autoconf automake libtool +``` + +开始安装 + ```bash # 获取安装源 wget https://github.com/Bwar/NebulaBootstrap/archive/master.zip From 26c558da2cb72a577b281713856a477ce1200359 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 20 Feb 2021 23:59:34 +0800 Subject: [PATCH 123/176] adjust http2, http2 test; add grpc. --- proto/http.proto | 29 +- src/actor/Actor.cpp | 5 + src/actor/Actor.hpp | 5 + src/actor/ActorBuilder.cpp | 20 +- src/actor/ActorSender.cpp | 297 +++++++ src/actor/ActorSender.hpp | 77 ++ src/actor/ActorSys.hpp | 2 +- src/actor/cmd/GrpcModule.cpp | 73 ++ src/actor/cmd/GrpcModule.hpp | 44 + src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp | 74 +- src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp | 3 +- src/actor/step/GrpcStep.cpp | 86 ++ src/actor/step/GrpcStep.hpp | 38 + src/channel/SocketChannel.cpp | 8 +- src/channel/SocketChannel.hpp | 6 +- src/channel/SocketChannelImpl.cpp | 117 ++- src/channel/SocketChannelImpl.hpp | 9 +- src/codec/Codec.cpp | 89 +-- src/codec/Codec.hpp | 1 + src/codec/CodecHttp.cpp | 4 +- src/codec/CodecUtil.cpp | 95 +++ src/codec/CodecUtil.hpp | 16 + src/codec/grpc/Grpc.hpp | 135 ++++ src/codec/http2/CodecHttp2.cpp | 550 ++++++++++--- src/codec/http2/CodecHttp2.hpp | 30 +- src/codec/http2/H2Comm.hpp | 10 +- src/codec/http2/Http2Frame.cpp | 417 ++++++++-- src/codec/http2/Http2Frame.hpp | 32 +- src/codec/http2/Http2Header.cpp | 15 +- src/codec/http2/Http2Header.hpp | 7 + src/codec/http2/Http2Stream.cpp | 299 ++++--- src/codec/http2/Http2Stream.hpp | 16 +- src/ios/Dispatcher.cpp | 47 +- src/ios/Dispatcher.hpp | 7 +- src/pb/http.pb.cc | 819 ++++++++++++++++--- src/pb/http.pb.h | 349 +++++++- src/util/json/CJsonObject.cpp | 839 +++++++++++++++++++- src/util/json/CJsonObject.hpp | 78 +- src/util/json/cJSON.c | 56 +- src/util/json/cJSON.h | 5 +- 40 files changed, 4113 insertions(+), 696 deletions(-) create mode 100644 src/actor/ActorSender.cpp create mode 100644 src/actor/ActorSender.hpp create mode 100644 src/actor/cmd/GrpcModule.cpp create mode 100644 src/actor/cmd/GrpcModule.hpp create mode 100644 src/actor/step/GrpcStep.cpp create mode 100644 src/actor/step/GrpcStep.hpp create mode 100644 src/codec/grpc/Grpc.hpp diff --git a/proto/http.proto b/proto/http.proto index 6c409322..df2d2106 100644 --- a/proto/http.proto +++ b/proto/http.proto @@ -2,6 +2,11 @@ syntax = "proto3"; message HttpMsg { + message Header + { + string name = 1; + string value = 2; + } message Upgrade { bool is_upgrade = 1; @@ -28,15 +33,17 @@ message HttpMsg // http2 only uint32 stream_id = 20; ///< 流ID,请求发送时为0则自动分配新的流ID,响应或push时为0则视为错误 - string hpack_data = 21; ///< 压缩后的头部数据(解码过程中使用) - repeated string adding_without_index_headers = 22; ///< 添加不需要新增到动态表的http头 - repeated string deleting_without_index_headers = 23; ///< 删除不需要新增到动态表的http头 - repeated string adding_never_index_headers = 24; ///< 添加永不新增到动态表的http头 - repeated string deleting_never_index_headers = 25; ///< 删除永不新增到动态表的http头 - uint32 dynamic_table_update_size = 26; ///< 更改动态表size - bool with_huffman = 27; ///< 是否使用huffman编码 - string headers_frame_padding = 28; ///< 头部帧填充字节 - string data_frame_padding = 29; ///< 数据帧填充字节 - string push_promise_frame_padding = 30; ///< PUSH_PROMISE帧填充字节 - map settings = 31; ///< SETTINGS类型和参数值 + repeated Header pseudo_header = 21; ///< 伪头部 + repeated Header trailer_header = 22; ///< 跟踪消息,如grpc的Trailers + string hpack_data = 23; ///< 压缩后的头部数据(解码过程中使用) + repeated string adding_without_index_headers = 24; ///< 添加不需要新增到动态表的http头 + repeated string deleting_without_index_headers = 25; ///< 删除不需要新增到动态表的http头 + repeated string adding_never_index_headers = 26; ///< 添加永不新增到动态表的http头 + repeated string deleting_never_index_headers = 27; ///< 删除永不新增到动态表的http头 + uint32 dynamic_table_update_size = 28; ///< 更改动态表size + bool with_huffman = 29; ///< 是否使用huffman编码 + string headers_frame_padding = 30; ///< 头部帧填充字节 + string data_frame_padding = 31; ///< 数据帧填充字节 + string push_promise_frame_padding = 32; ///< PUSH_PROMISE帧填充字节 + map settings = 33; ///< SETTINGS类型和参数值 } diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 93fae4e6..aac50748 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -271,6 +271,11 @@ bool Actor::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) return(m_pLabor->GetDispatcher()->SendDataReport(iCmd, uiSeq, oMsgBody)); } +std::shared_ptr Actor::GetLastActivityChannel() +{ + return(m_pLabor->GetDispatcher()->GetLastActivityChannel()); +} + bool Actor::CloseRawChannel(std::shared_ptr pChannel) { if (CODEC_UNKNOW == pChannel->GetCodecType()) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 0ed0c3e8..9eddec59 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -32,6 +32,7 @@ #include "labor/Labor.hpp" #include "codec/Codec.hpp" #include "ActorBuilder.hpp" +#include "ActorSender.hpp" namespace neb { @@ -41,6 +42,7 @@ typedef RedisReply RedisMsg; class Labor; class Dispatcher; class ActorBuilder; +class ActorSender; class ActorSys; class SocketChannel; @@ -281,6 +283,8 @@ class Actor: public std::enable_shared_from_this virtual bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + std::shared_ptr GetLastActivityChannel(); + /** * @brief 关闭raw数据通道 * @note 当且仅当raw数据传输的无编解码通道才可以由Actor的应用层关闭,对于http连接 @@ -327,6 +331,7 @@ class Actor: public std::enable_shared_from_this friend class Dispatcher; friend class ActorBuilder; friend class ActorSys; + friend class ActorSender; friend class Chain; }; diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index efcd5329..74e4aeb5 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -351,21 +351,23 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http { LOG4_DEBUG("oInHttpMsg.type() = %d, oInHttpMsg.path() = %s", oHttpMsg.type(), oHttpMsg.path().c_str()); - auto module_iter = m_mapModule.find(oHttpMsg.path()); - if (module_iter == m_mapModule.end()) + if (oHttpMsg.has_upgrade() && oHttpMsg.upgrade().is_upgrade()) { - if (oHttpMsg.has_upgrade() && oHttpMsg.upgrade().is_upgrade()) + auto module_iter = m_mapModule.find("http_upgrade"); + if (module_iter != m_mapModule.end()) { - module_iter = m_mapModule.find("http_upgrade"); - if (module_iter != m_mapModule.end()) + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + module_iter->second->SetTraceId(oss.str()); + if (module_iter->second->AnyMessage(pChannel, oHttpMsg)) { - std::ostringstream oss; - oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); - module_iter->second->SetTraceId(oss.str()); - module_iter->second->AnyMessage(pChannel, oHttpMsg); return(true); } } + } + auto module_iter = m_mapModule.find(oHttpMsg.path()); + if (module_iter == m_mapModule.end()) + { module_iter = m_mapModule.find("/switch"); if (module_iter == m_mapModule.end()) { diff --git a/src/actor/ActorSender.cpp b/src/actor/ActorSender.cpp new file mode 100644 index 00000000..516e4aac --- /dev/null +++ b/src/actor/ActorSender.cpp @@ -0,0 +1,297 @@ +/******************************************************************************* + * Project: Nebula + * @file ActorSender.cpp + * @brief + * @author Bwar + * @date: 2021-02-13 + * @note + * Modify history: + ******************************************************************************/ +#include "ActorSender.hpp" +#include +#include "Actor.hpp" +#include "ios/Dispatcher.hpp" +#include "codec/CodecHttp.hpp" +//#include "actor/session/Session.hpp" +//#include "actor/step/Step.hpp" +//#include "labor/Worker.hpp" + +namespace neb +{ + +ActorSender::ActorSender() +{ +} + +ActorSender::~ActorSender() +{ +} + +bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel) +{ + return(pActor->m_pLabor->GetDispatcher()->SendTo(pChannel)); +} + +bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +{ + (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); + return(pActor->m_pLabor->GetDispatcher()->SendTo(pChannel, iCmd, uiSeq, oMsgBody)); +} + +bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +{ + (const_cast(oHttpMsg)).mutable_headers()->insert({"x-trace-id", pActor->GetTraceId()}); + return(pActor->m_pLabor->GetDispatcher()->SendTo(pChannel, oHttpMsg, 0)); +} + +bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, const RedisReply& oRedisReply) +{ + return(pActor->m_pLabor->GetDispatcher()->SendTo(pChannel, oRedisReply, 0)); +} + +bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, const char* pRawData, uint32 uiRawDataSize) +{ + return(pActor->m_pLabor->GetDispatcher()->SendTo(pChannel, pRawData, uiRawDataSize, 0)); +} + +bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) +{ + (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); + return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); +} + +bool ActorSender::SendTo(Actor* pActor, const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq) +{ + bool bWithSsl = false; + bool bPipeline = false; + if (oHttpMsg.headers().find("x-trace-id") == oHttpMsg.headers().end()) + { + (const_cast(oHttpMsg)).mutable_headers()->insert({"x-trace-id", pActor->GetTraceId()}); + } + if (oHttpMsg.http_major() >= 2) + { + bPipeline = true; + } + std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(":")); + std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c)->unsigned char {return std::tolower(c);}); + if (strSchema == std::string("https")) + { + bWithSsl = true; + } + return(pActor->m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, pActor->GetSequence())); +} + +bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) +{ + return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, pActor->GetSequence())); +} + +bool ActorSender::SendToCluster(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) +{ + return(pActor->m_pLabor->GetActorBuilder()->SendToCluster(strIdentify, bWithSsl, bPipeline, oRedisMsg, pActor->GetSequence(), bEnableReadOnly)); +} + +bool ActorSender::SendRoundRobin(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline) +{ + return(pActor->m_pLabor->GetDispatcher()->SendRoundRobin(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, pActor->GetSequence())); +} + +bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) +{ + return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_UNKNOW, bWithSsl, bPipeline, pRawData, uiRawDataSize, pActor->GetSequence())); +} + +bool ActorSender::SendTo(Actor* pActor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +{ + (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); + return(pActor->m_pLabor->GetDispatcher()->SendTo(iCmd, uiSeq, oMsgBody)); +} + +bool ActorSender::SendRoundRobin(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) +{ + (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); + return(pActor->m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); +} + +bool ActorSender::SendOriented(Actor* pActor, const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) +{ + (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); + return(pActor->m_pLabor->GetDispatcher()->SendOriented(strNodeType, eCodecType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); +} + +bool ActorSender::SendOriented(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) +{ + (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); + if (oMsgBody.has_req_target()) + { + if (0 != oMsgBody.req_target().route_id()) + { + return(SendOriented(pActor, strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, eCodecType)); + } + else if (oMsgBody.req_target().route().length() > 0) + { + return(pActor->m_pLabor->GetDispatcher()->SendOriented(strNodeType, eCodecType, false, true, oMsgBody.req_target().route(), iCmd, uiSeq, oMsgBody)); + } + else + { + return(pActor->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); + } + } + else + { + return(false); + } +} + +bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, + uint32 uiStreamId, const std::string& strGrpcResponse, + E_GRPC_STATUS_CODE eStatus, const std::string& strStatusMessage, + E_COMPRESSION eCompression) +{ + HttpMsg oHttpMsg; + oHttpMsg.set_type(HTTP_RESPONSE); + oHttpMsg.set_http_major(2); + oHttpMsg.set_http_minor(0); + oHttpMsg.set_status_code(200); + oHttpMsg.set_stream_id(uiStreamId); + oHttpMsg.mutable_headers()->insert({"content-type", "application/grpc"}); + oHttpMsg.add_adding_never_index_headers(":status"); + oHttpMsg.add_adding_never_index_headers("content-type"); + if (eStatus == GRPC_OK) + { + uint8 ucCompressedFlag = 0; + uint32 uiMessageLength = 0; + std::string strResponseData; + switch (eCompression) + { + case COMPRESS_GZIP: + if (CodecUtil::Gzip(strGrpcResponse, strResponseData)) + { + oHttpMsg.mutable_headers()->insert({"grpc-encoding", "gzip"}); + ucCompressedFlag = 1; + uiMessageLength = strResponseData.size(); + oHttpMsg.mutable_body()->append(1, ucCompressedFlag); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 24) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 16) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 8) & 0xFF)); + oHttpMsg.mutable_body()->append(1, (uiMessageLength & 0xFF)); + oHttpMsg.mutable_body()->append(strResponseData); + } + else + { + uiMessageLength = strGrpcResponse.size(); + oHttpMsg.mutable_body()->append(1, ucCompressedFlag); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 24) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 16) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 8) & 0xFF)); + oHttpMsg.mutable_body()->append(1, (uiMessageLength & 0xFF)); + oHttpMsg.mutable_body()->append(strGrpcResponse); + } + break; + default: + uiMessageLength = strGrpcResponse.size(); + oHttpMsg.mutable_body()->append(1, ucCompressedFlag); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 24) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 16) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 8) & 0xFF)); + oHttpMsg.mutable_body()->append(1, (uiMessageLength & 0xFF)); + oHttpMsg.mutable_body()->append(strGrpcResponse); + } + } + auto pHeader = oHttpMsg.add_trailer_header(); + pHeader->set_name("grpc-status"); + pHeader->set_value(std::to_string(eStatus)); + pHeader = oHttpMsg.add_trailer_header(); + pHeader->set_name("grpc-message"); + pHeader->set_value(strStatusMessage); + return(SendTo(pActor, pChannel, oHttpMsg)); +} + +bool ActorSender::SendTo(Actor* pActor, const std::string& strUrl, const std::string& strGrpcRequest, E_COMPRESSION eCompression) +{ + int iPort = 0; + std::string strHost; + std::string strPath; + HttpMsg oHttpMsg; + struct http_parser_url stUrl; + oHttpMsg.set_http_major(2); + oHttpMsg.set_http_minor(0); + oHttpMsg.set_type(HTTP_REQUEST); + oHttpMsg.set_method(HTTP_POST); + oHttpMsg.set_url(strUrl); + oHttpMsg.mutable_headers()->insert({"content-type", "application/grpc"}); + oHttpMsg.add_adding_never_index_headers("content-type"); + if(0 == http_parser_parse_url(strUrl.c_str(), strUrl.length(), 0, &stUrl)) + { + if(stUrl.field_set & (1 << UF_PORT)) + { + iPort = stUrl.port; + } + else + { + iPort = 80; + } + + if(stUrl.field_set & (1 << UF_HOST) ) + { + strHost = oHttpMsg.url().substr(stUrl.field_data[UF_HOST].off, stUrl.field_data[UF_HOST].len); + } + + if (iPort == 80) + { + std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(':')); + std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c) -> unsigned char { return std::tolower(c); }); + if (strSchema == std::string("https")) + { + iPort = 443; + } + } + + uint8 ucCompressedFlag = 0; + uint32 uiMessageLength = 0; + std::string strRequestData; + switch (eCompression) + { + case COMPRESS_GZIP: + if (CodecUtil::Gzip(strGrpcRequest, strRequestData)) + { + oHttpMsg.mutable_headers()->insert({"grpc-encoding", "gzip"}); + ucCompressedFlag = 1; + uiMessageLength = strRequestData.size(); + oHttpMsg.mutable_body()->append(1, ucCompressedFlag); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 24) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 16) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 8) & 0xFF)); + oHttpMsg.mutable_body()->append(1, (uiMessageLength & 0xFF)); + oHttpMsg.mutable_body()->append(strRequestData); + } + else + { + uiMessageLength = strGrpcRequest.size(); + oHttpMsg.mutable_body()->append(1, ucCompressedFlag); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 24) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 16) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 8) & 0xFF)); + oHttpMsg.mutable_body()->append(1, (uiMessageLength & 0xFF)); + oHttpMsg.mutable_body()->append(strGrpcRequest); + } + break; + default: + uiMessageLength = strGrpcRequest.size(); + oHttpMsg.mutable_body()->append(1, ucCompressedFlag); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 24) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 16) & 0xFF)); + oHttpMsg.mutable_body()->append(1, ((uiMessageLength >> 8) & 0xFF)); + oHttpMsg.mutable_body()->append(1, (uiMessageLength & 0xFF)); + oHttpMsg.mutable_body()->append(strGrpcRequest); + } + return(SendTo(pActor, strHost, iPort, oHttpMsg)); + } + else + { + return(false); + } +} + +} /* namespace neb */ + diff --git a/src/actor/ActorSender.hpp b/src/actor/ActorSender.hpp new file mode 100644 index 00000000..d20521f0 --- /dev/null +++ b/src/actor/ActorSender.hpp @@ -0,0 +1,77 @@ +/******************************************************************************* + * Project: Nebula + * @file ActorSender.hpp + * @brief + * @author Bwar + * @date: 2021-02-13 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_ACTORSENDER_HPP_ +#define SRC_ACTOR_ACTORSENDER_HPP_ + +#include +#include "Definition.hpp" +#include "pb/msg.pb.h" +#include "pb/http.pb.h" +#include "pb/redis.pb.h" +#include "channel/Channel.hpp" +#include "codec/Codec.hpp" +#include "codec/CodecUtil.hpp" +#include "codec/grpc/Grpc.hpp" + +namespace neb +{ + +typedef RedisReply RedisMsg; + +class Actor; +class SocketChannel; + +/** + * @brief Actor发送代理 + * @note 随着Nebula支持的通信协议的增加,Actor类的Send函数越来越多,Actor的可读性会 + * 变差,也不利于维护,故将独立到ActorSender中来,为保持兼容性,原Actor中的Send函数保留。 + */ +class ActorSender +{ +public: + ActorSender(); + virtual ~ActorSender(); + + static bool SendTo(Actor* pActor, std::shared_ptr pChannel); + + // send pb message + static bool SendTo(Actor* pActor, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + static bool SendTo(Actor* pActor, const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + static bool SendTo(Actor* pActor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + static bool SendRoundRobin(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + static bool SendOriented(Actor* pActor, const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + static bool SendOriented(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + + // send http message + static bool SendTo(Actor* pActor, std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + static bool SendTo(Actor* pActor, const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq = 0); + + // send redis message + static bool SendTo(Actor* pActor, std::shared_ptr pChannel, const RedisReply& oRedisReply); + static bool SendTo(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); + static bool SendRoundRobin(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = false); + static bool SendToCluster(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, bool bEnableReadOnly = false); + + // send raw message + static bool SendTo(Actor* pActor, std::shared_ptr pChannel, const char* pRawData, uint32 uiRawDataSize); + static bool SendTo(Actor* pActor, const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl = false, bool bPipeline = false, uint32 uiStepSeq = 0); + + // send grpc message + static bool SendTo(Actor* pActor, std::shared_ptr pChannel, + uint32 uiStreamId, const std::string& strGrpcResponse, + E_GRPC_STATUS_CODE eStatus, const std::string& strStatusMessage, + E_COMPRESSION eCompression = COMPRESS_NA); + static bool SendTo(Actor* pActor, const std::string& strUrl, const std::string& strGrpcRequest, E_COMPRESSION eCompression = COMPRESS_NA); +}; + +} /* namespace neb */ + +#endif + diff --git a/src/actor/ActorSys.hpp b/src/actor/ActorSys.hpp index 6de7c14f..bc81b6f2 100644 --- a/src/actor/ActorSys.hpp +++ b/src/actor/ActorSys.hpp @@ -1,6 +1,6 @@ /******************************************************************************* * Project: Nebula - * @file ActorFriend.hpp + * @file ActorSys.hpp * @brief Actor友元类,用于访问Actor的保护或私有成员 * @author Bwar * @date: 2019年9月21日 diff --git a/src/actor/cmd/GrpcModule.cpp b/src/actor/cmd/GrpcModule.cpp new file mode 100644 index 00000000..b3c4980e --- /dev/null +++ b/src/actor/cmd/GrpcModule.cpp @@ -0,0 +1,73 @@ +/******************************************************************************* + * Project: Nebula + * @file GrpcModule.cpp + * @brief + * @author Bwar + * @date: 2021-02-13 + * @note + * Modify history: + ******************************************************************************/ +#include "GrpcModule.hpp" +#include "util/StringConverter.hpp" + +namespace neb +{ + +GrpcModule::GrpcModule(const std::string& strModulePath) + : Module(strModulePath) +{ +} + +GrpcModule::~GrpcModule() +{ +} + +bool GrpcModule::AnyMessage( + std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +{ + char szLength[5] = {0}; + uint8 ucCompressedFlag = 0; + uint32 uiMessageLength = 0; + std::string strMessage; + ucCompressedFlag = oHttpMsg.body()[0]; + szLength[0] = oHttpMsg.body()[4]; + szLength[1] = oHttpMsg.body()[3]; + szLength[2] = oHttpMsg.body()[2]; + szLength[3] = oHttpMsg.body()[1]; + uiMessageLength = StringConverter::RapidAtoi(szLength); + if (ucCompressedFlag) + { + auto iter = oHttpMsg.headers().find("grpc-encoding"); + if (iter == oHttpMsg.headers().end()) + { + LOG4_ERROR("Compressed-Flag had been set, but no \"grpc-encoding\" found in header."); + ActorSender::SendTo(this, pChannel, oHttpMsg.stream_id(), + "", GRPC_UNAVAILABLE, "Compressed-Flag had been set, but no \"grpc-encoding\" found in header."); + return(false); + } + else + { + if (iter->second == "gzip") + { + if (!CodecUtil::Gunzip(oHttpMsg.body().substr(5, uiMessageLength), strMessage)) + { + LOG4_ERROR("failed to gunzip message."); + return(false); + } + } + else + { + LOG4_ERROR("compression algorithm not support."); + return(false); + } + } + } + else + { + strMessage.assign(oHttpMsg.body(), 5, uiMessageLength); + } + return(AnyMessage(pChannel, oHttpMsg.stream_id(), strMessage)); +} + +} /* namespace neb */ + diff --git a/src/actor/cmd/GrpcModule.hpp b/src/actor/cmd/GrpcModule.hpp new file mode 100644 index 00000000..2a012ff5 --- /dev/null +++ b/src/actor/cmd/GrpcModule.hpp @@ -0,0 +1,44 @@ +/******************************************************************************* + * Project: Nebula + * @file GrpcModule.hpp + * @brief + * @author Bwar + * @date: 2021-02-13 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_GRPCMODULE_HPP_ +#define SRC_ACTOR_CMD_GRPCMODULE_HPP_ + +#include "Module.hpp" +#include "codec/CodecUtil.hpp" +#include "codec/grpc/Grpc.hpp" + +namespace neb +{ + +class GrpcModule: public Module +{ +public: + GrpcModule(const std::string& strModulePath); + virtual ~GrpcModule(); + + virtual bool Init() + { + return(true); + } + + virtual bool AnyMessage( + std::shared_ptr pChannel, + const HttpMsg& oHttpMsg + ) override final; + + virtual bool AnyMessage( + std::shared_ptr pChannel, + uint32 uiStreamId, + const std::string& strGrpcRequest) = 0; +}; + +} + +#endif diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp index 467a3193..ba59a049 100644 --- a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp +++ b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp @@ -7,12 +7,16 @@ * @note * Modify history: ******************************************************************************/ -#include +#include "actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp" +#include #include #include #include #include #include "ios/Dispatcher.hpp" +#include "codec/http2/H2Comm.hpp" +#include "codec/http2/Http2Frame.hpp" +#include "codec/http2/CodecHttp2.hpp" namespace neb { @@ -32,9 +36,18 @@ bool ModuleHttpUpgrade::AnyMessage(std::shared_ptr pChannel, cons { if (oHttpMsg.has_upgrade() && oHttpMsg.upgrade().is_upgrade()) { + if (std::string("h2") == oHttpMsg.upgrade().protocol() + || std::string("h2c") == oHttpMsg.upgrade().protocol()) + { + auto h_iter = oHttpMsg.headers().find("HTTP2-Settings"); + if (h_iter != oHttpMsg.headers().end()) + { + return(UpgradeToHttp2(pChannel, oHttpMsg, h_iter->second)); + } + } if (std::string("websocket") == oHttpMsg.upgrade().protocol()) { - return(WebSocket(pChannel, oHttpMsg)); + return(UpgradeToWebSocket(pChannel, oHttpMsg)); } } HttpMsg oOutHttpMsg; @@ -47,11 +60,11 @@ bool ModuleHttpUpgrade::AnyMessage(std::shared_ptr pChannel, cons return(false); } -bool ModuleHttpUpgrade::WebSocket(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +bool ModuleHttpUpgrade::UpgradeToWebSocket(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) { HttpMsg oOutHttpMsg; oOutHttpMsg.set_type(HTTP_RESPONSE); - oOutHttpMsg.set_status_code(400); + oOutHttpMsg.set_status_code(101); oOutHttpMsg.set_http_major(oHttpMsg.http_major()); oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); if (1 == oHttpMsg.http_major() && 1 == oHttpMsg.http_minor() @@ -133,4 +146,57 @@ bool ModuleHttpUpgrade::WebSocket(std::shared_ptr pChannel, const return(false); } +bool ModuleHttpUpgrade::UpgradeToHttp2(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, const std::string& strHttp2Setting) +{ + HttpMsg oOutHttpMsg; + oOutHttpMsg.set_type(HTTP_RESPONSE); + oOutHttpMsg.set_status_code(101); + oOutHttpMsg.set_http_major(oHttpMsg.http_major()); + oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); + oOutHttpMsg.mutable_headers()->insert( + google::protobuf::MapPair("Connection", "Upgrade")); + oOutHttpMsg.mutable_headers()->insert( + google::protobuf::MapPair("Upgrade", oHttpMsg.upgrade().protocol())); + + std::string strHttp2SettingPayload; + std::string strSettingFrame; + CryptoPP::Base64Decoder oDecoder; + oDecoder.Put((CryptoPP::byte*)strHttp2Setting.data(), strHttp2Setting.size()); + oDecoder.MessageEnd(); + CryptoPP::word64 size = oDecoder.MaxRetrievable(); + if(size && size <= SIZE_MAX) + { + strHttp2SettingPayload.resize(size); + oDecoder.Get((CryptoPP::byte*)strHttp2SettingPayload.data(), strHttp2SettingPayload.size()); + } + if (strHttp2SettingPayload.size() % 6 != 0) + { + LOG4_ERROR("invalid strHttp2Setting \"%s\"", strHttp2Setting.c_str()); + return(false); + } + const char* pH2SettingPayload = strHttp2SettingPayload.data(); + uint32 uiSettingNum = strHttp2SettingPayload.size() / 6; + tagSetting stSetting; + std::vector vecSetting; + for (uint32 i = 0; i < uiSettingNum; ++i) + { + memcpy(&stSetting.unIdentifier, pH2SettingPayload + (i * 6), 2); + memcpy(&stSetting.uiValue, pH2SettingPayload + (i * 6) + 2, 4); + stSetting.unIdentifier = CodecUtil::N2H(stSetting.unIdentifier); + stSetting.uiValue = CodecUtil::N2H(stSetting.uiValue); + vecSetting.push_back(stSetting); + } + Http2Frame::EncodeSetting(vecSetting, strSettingFrame); + oOutHttpMsg.set_body(strSettingFrame); + SendTo(pChannel, oOutHttpMsg); + + auto pCodec = GetLabor(this)->GetDispatcher()->SwitchCodec(pChannel, CODEC_HTTP2, true); + if (pCodec == nullptr) + { + return(false); + } + ((CodecHttp2*)pCodec)->Setting(vecSetting); + return(true); +} + } /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp index 027f9f0d..0e7e444d 100644 --- a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp +++ b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp @@ -29,7 +29,8 @@ class ModuleHttpUpgrade: public Module, const HttpMsg& oHttpMsg); protected: - bool WebSocket(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + bool UpgradeToHttp2(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, const std::string& strHttp2Setting); + bool UpgradeToWebSocket(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); private: const std::string mc_strWebSocketMagicGuid; diff --git a/src/actor/step/GrpcStep.cpp b/src/actor/step/GrpcStep.cpp new file mode 100644 index 00000000..6a753da6 --- /dev/null +++ b/src/actor/step/GrpcStep.cpp @@ -0,0 +1,86 @@ +/******************************************************************************* + * Project: Nebula + * @file GrpcStep.cpp + * @brief + * @author Bwar + * @date: 2021-02-14 + * @note + * Modify history: + ******************************************************************************/ +#include "GrpcStep.hpp" +#include "util/StringConverter.hpp" + +namespace neb +{ + +GrpcStep::GrpcStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) + : Step(ACT_HTTP_STEP, pNextStep, dTimeout) +{ +} + +GrpcStep::~GrpcStep() +{ +} + +E_CMD_STATUS GrpcStep::Callback( + std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +{ + char szLength[5] = {0}; + uint8 ucCompressedFlag = 0; + uint32 uiMessageLength = 0; + std::string strResponseData; + ucCompressedFlag = oHttpMsg.body()[0]; + szLength[0] = oHttpMsg.body()[4]; + szLength[1] = oHttpMsg.body()[3]; + szLength[2] = oHttpMsg.body()[2]; + szLength[3] = oHttpMsg.body()[1]; + uiMessageLength = StringConverter::RapidAtoi(szLength); + if (ucCompressedFlag) + { + auto iter = oHttpMsg.headers().find("grpc-encoding"); + if (iter == oHttpMsg.headers().end()) + { + LOG4_ERROR("Compressed-Flag had been set, but no \"grpc-encoding\" found in header."); + return(Callback(pChannel, strResponseData, GRPC_INVALID_ARGUMENT, + "Compressed-Flag had been set, but no \"grpc-encoding\" found in header.")); + } + else + { + if (iter->second == "gzip") + { + if (!CodecUtil::Gunzip(oHttpMsg.body().substr(5, uiMessageLength), strResponseData)) + { + LOG4_ERROR("failed to gunzip message."); + return(Callback(pChannel, strResponseData, GRPC_INVALID_ARGUMENT, "failed to gunzip message.")); + } + } + else + { + LOG4_ERROR("compression algorithm not support."); + return(Callback(pChannel, strResponseData, GRPC_INVALID_ARGUMENT, "compression algorithm not support.")); + } + } + } + else + { + strResponseData.assign(oHttpMsg.body(), 5, uiMessageLength); + } + + int iStatus = 0; + std::string strStatusMsg; + for (int i = 0; i < oHttpMsg.trailer_header_size(); ++i) + { + if ("grpc-status" == oHttpMsg.trailer_header(i).name()) + { + iStatus = StringConverter::RapidAtoi(oHttpMsg.trailer_header(i).value().c_str()); + } + else if ("grpc-message" == oHttpMsg.trailer_header(i).name()) + { + strStatusMsg = oHttpMsg.trailer_header(i).value(); + } + } + return(Callback(pChannel, strResponseData, iStatus, strStatusMsg)); +} + +} /* namespace neb */ + diff --git a/src/actor/step/GrpcStep.hpp b/src/actor/step/GrpcStep.hpp new file mode 100644 index 00000000..b5e1c25e --- /dev/null +++ b/src/actor/step/GrpcStep.hpp @@ -0,0 +1,38 @@ +/******************************************************************************* + * Project: Nebula + * @file GrpcStep.hpp + * @brief + * @author Bwar + * @date: 2021-02-14 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_STEP_GRPCSTEP_HPP_ +#define SRC_ACTOR_STEP_GRPCSTEP_HPP_ + +#include "Step.hpp" + +namespace neb +{ + +class GrpcStep: public Step +{ +public: + GrpcStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + GrpcStep(const GrpcStep&) = delete; + GrpcStep& operator=(const GrpcStep&) = delete; + virtual ~GrpcStep(); + + virtual E_CMD_STATUS Callback( + std::shared_ptr pChannel, + const HttpMsg& oHttpMsg); + + virtual E_CMD_STATUS Callback( + std::shared_ptr pChannel, + const std::string& strGrpcResponse, + int iStatus, const std::string& strStatusMessage) = 0; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_STEP_GRPCSTEP_HPP_ */ diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index 1e44946b..c554cc79 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -21,7 +21,7 @@ namespace neb { SocketChannel::SocketChannel(std::shared_ptr pLogger, int iFd, uint32 ulSeq, bool bWithSsl, ev_tstamp dKeepAlive) - : m_bIsClientConnection(false), m_pImpl(nullptr), m_pLogger(pLogger) + : m_pImpl(nullptr), m_pLogger(pLogger) { if (bWithSsl) { @@ -47,7 +47,6 @@ SocketChannel::~SocketChannel() bool SocketChannel::Init(E_CODEC_TYPE eCodecType, bool bIsClient) { - m_bIsClientConnection = bIsClient; return(m_pImpl->Init(eCodecType, bIsClient)); } @@ -56,6 +55,11 @@ int SocketChannel::GetFd() const return(m_pImpl->GetFd()); } +bool SocketChannel::IsClient() const +{ + return(m_pImpl->GetFd()); +} + bool SocketChannel::IsPipeline() const { return(m_pImpl->IsPipeline()); diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index c5488538..de616d4f 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -39,10 +39,7 @@ class SocketChannel: public Channel, public std::enable_shared_from_this m_pImpl; diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 51eeb99c..2af4c568 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -11,6 +11,7 @@ #include "codec/CodecProto.hpp" #include "codec/CodecPrivate.hpp" #include "codec/CodecHttp.hpp" +#include "codec/http2/CodecHttp2.hpp" #include "codec/CodecResp.hpp" #include "labor/Labor.hpp" #include "labor/Manager.hpp" @@ -21,13 +22,13 @@ namespace neb { SocketChannelImpl::SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) - : m_ucChannelStatus(CHANNEL_STATUS_INIT), + : m_ucChannelStatus(CHANNEL_STATUS_INIT), m_bIsClientConnection(false), m_unRemoteWorkerIdx(0), m_iFd(iFd), m_uiSeq(ulSeq), m_uiForeignSeq(0), m_bPipeline(true), m_uiUnitTimeMsgNum(0), m_uiMsgNum(0), m_dActiveTime(0.0), m_dKeepAlive(dKeepAlive), m_pIoWatcher(NULL), m_pTimerWatcher(NULL), m_pRecvBuff(nullptr), m_pSendBuff(nullptr), m_pWaitForSendBuff(nullptr), - m_pCodec(nullptr), m_iErrno(0), m_pLabor(nullptr), m_pSocketChannel(pSocketChannel), m_pLogger(pLogger) + m_pCodec(nullptr), m_pHoldingHttpMsg(nullptr), m_iErrno(0), m_pLabor(nullptr), m_pSocketChannel(pSocketChannel), m_pLogger(pLogger) { memset(m_szErrBuff, 0, sizeof(m_szErrBuff)); } @@ -45,12 +46,14 @@ SocketChannelImpl::~SocketChannelImpl() DELETE(m_pRecvBuff); DELETE(m_pSendBuff); DELETE(m_pWaitForSendBuff); + DELETE(m_pHoldingHttpMsg); DELETE(m_pCodec); } bool SocketChannelImpl::Init(E_CODEC_TYPE eCodecType, bool bIsClient) { LOG4_TRACE("fd[%d], codec_type[%d]", m_iFd, eCodecType); + m_bIsClientConnection = bIsClient; try { if (m_pRecvBuff == nullptr) @@ -77,18 +80,23 @@ bool SocketChannelImpl::Init(E_CODEC_TYPE eCodecType, bool bIsClient) m_pCodec = new CodecProto(m_pLogger, eCodecType); m_pCodec->SetKey(m_strKey); break; - case CODEC_PRIVATE: - m_pCodec = new CodecPrivate(m_pLogger, eCodecType); - m_pCodec->SetKey(m_strKey); - break; case CODEC_HTTP: m_pCodec = new CodecHttp(m_pLogger, eCodecType); m_pCodec->SetKey(m_strKey); break; + case CODEC_HTTP2: + m_pCodec = new CodecHttp2(m_pLogger, eCodecType, m_bIsClientConnection); + ((CodecHttp2*)m_pCodec)->ConnectionSetting(m_pSendBuff); + m_pCodec->SetKey(m_strKey); + break; case CODEC_RESP: m_pCodec = new CodecResp(m_pLogger, eCodecType); m_pCodec->SetKey(m_strKey); break; + case CODEC_PRIVATE: + m_pCodec = new CodecPrivate(m_pLogger, eCodecType); + m_pCodec->SetKey(m_strKey); + break; case CODEC_UNKNOW: break; default: @@ -365,7 +373,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq return(CODEC_STATUS_OK); } - if (CODEC_STATUS_OK != eCodecStatus) + if (CODEC_STATUS_OK != eCodecStatus || CODEC_STATUS_PART_OK != eCodecStatus) { return(eCodecStatus); } @@ -393,7 +401,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq m_dActiveTime = m_pLabor->GetNowTime(); if (iNeedWriteLen == iWrittenLen) { - return(CODEC_STATUS_OK); + return(eCodecStatus); } else { @@ -704,7 +712,20 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) m_pRecvBuff->Compact(m_pRecvBuff->ReadableBytes() * 2); } m_dActiveTime = m_pLabor->GetNowTime(); - E_CODEC_STATUS eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + if (CODEC_HTTP == m_pCodec->GetCodecType()) + { + eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); + } + else + { + size_t uiSendBuffLen = m_pSendBuff->ReadableBytes(); + eCodecStatus = ((CodecHttp2*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg, m_pSendBuff); + if (m_pSendBuff->ReadableBytes() > uiSendBuffLen) + { + Send(); + } + } if (CODEC_STATUS_OK == eCodecStatus) { ++m_uiUnitTimeMsgNum; @@ -713,6 +734,26 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) { m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; + if (oHttpMsg.has_upgrade() && oHttpMsg.upgrade().is_upgrade()) + { + if (std::string("h2") == oHttpMsg.upgrade().protocol() + || std::string("h2c") == oHttpMsg.upgrade().protocol()) + { + auto h_iter = oHttpMsg.headers().find("HTTP2-Settings"); + if (h_iter != oHttpMsg.headers().end()) + { + try + { + m_pHoldingHttpMsg = new HttpMsg(oHttpMsg); + } + catch (std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + return(CODEC_STATUS_ERR); + } + } + } + } } } else @@ -920,7 +961,20 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) return(CODEC_STATUS_EOF); } // 当http1.0响应包未带Content-Length头时,m_pRecvBuff可读字节数为0,以关闭连接表示数据发送完毕。 - E_CODEC_STATUS eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + if (CODEC_HTTP == m_pCodec->GetCodecType()) + { + eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); + } + else + { + size_t uiSendBuffLen = m_pSendBuff->ReadableBytes(); + eCodecStatus = ((CodecHttp2*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg, m_pSendBuff); + if (m_pSendBuff->ReadableBytes() > uiSendBuffLen) + { + Send(); + } + } if (CODEC_STATUS_OK == eCodecStatus) { ++m_uiUnitTimeMsgNum; @@ -929,6 +983,26 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) { m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; + if (oHttpMsg.has_upgrade() && oHttpMsg.upgrade().is_upgrade()) + { + if (std::string("h2") == oHttpMsg.upgrade().protocol() + || std::string("h2c") == oHttpMsg.upgrade().protocol()) + { + auto h_iter = oHttpMsg.headers().find("HTTP2-Settings"); + if (h_iter != oHttpMsg.headers().end()) + { + try + { + m_pHoldingHttpMsg = new HttpMsg(oHttpMsg); + } + catch (std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + return(CODEC_STATUS_ERR); + } + } + } + } } } return(eCodecStatus); @@ -989,13 +1063,13 @@ void SocketChannelImpl::SetRemoteWorkerIndex(uint16 unRemoteWorkerIndex) m_unRemoteWorkerIdx = unRemoteWorkerIndex; } -bool SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive) +Codec* SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive, bool bIsUpgrade) { LOG4_TRACE("channel_fd[%d], channel_seq[%d], codec_type[%d], new_codec_type[%d]", m_iFd, m_uiSeq, m_pCodec->GetCodecType(), eCodecType); if (eCodecType == m_pCodec->GetCodecType()) { - return(true); + return(m_pCodec); } Codec* pNewCodec = NULL; @@ -1017,6 +1091,15 @@ bool SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAliv pNewCodec = new CodecHttp(m_pLogger, eCodecType, dKeepAlive); pNewCodec->SetKey(m_strKey); break; + case CODEC_HTTP2: + pNewCodec = new CodecHttp2(m_pLogger, eCodecType, m_bIsClientConnection); + if (bIsUpgrade) + { + ((CodecHttp2*)pNewCodec)->TransferHoldingMsg(m_pHoldingHttpMsg); + m_pHoldingHttpMsg = nullptr; + } + pNewCodec->SetKey(m_strKey); + break; default: LOG4_ERROR("no codec defined for code type %d", eCodecType); break; @@ -1025,13 +1108,13 @@ bool SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAliv catch(std::bad_alloc& e) { LOG4_ERROR("%s", e.what()); - return(false); + return(nullptr); } DELETE(m_pCodec); m_pCodec = pNewCodec; m_dKeepAlive = dKeepAlive; m_dActiveTime = m_pLabor->GetNowTime(); - return(true); + return(m_pCodec); } bool SocketChannelImpl::AutoSwitchCodec() @@ -1050,7 +1133,11 @@ bool SocketChannelImpl::AutoSwitchCodec() auto skip_iter = m_setSkipCodecType.find(*codec_iter); if (skip_iter == m_setSkipCodecType.end()) { - return(SwitchCodec(*codec_iter, -1.0)); + if(SwitchCodec(*codec_iter, -1.0) != nullptr) + { + return(true); + } + return(false); } else { diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 1a56f266..4aca3a06 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -90,6 +90,11 @@ class SocketChannelImpl: public Channel return(m_bPipeline); } + bool bIsClient() const + { + return(m_bIsClientConnection); + } + ev_tstamp GetKeepAlive(); uint8 GetChannelStatus() const @@ -201,7 +206,7 @@ class SocketChannelImpl: public Channel void SetRemoteWorkerIndex(uint16 unRemoteWorkerIndex); - bool SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive); + Codec* SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive, bool bIsUpgrade = false); bool AutoSwitchCodec(); ev_io* MutableIoWatcher(); @@ -217,6 +222,7 @@ class SocketChannelImpl: public Channel private: uint8 m_ucChannelStatus; char m_szErrBuff[256]; + bool m_bIsClientConnection; uint16 m_unRemoteWorkerIdx; ///< 对端Worker进程ID,若不涉及则无需关心 int32 m_iFd; ///< 文件描述符 uint32 m_uiSeq; ///< 文件描述符创建时对应的序列号 @@ -232,6 +238,7 @@ class SocketChannelImpl: public Channel CBuffer* m_pSendBuff; CBuffer* m_pWaitForSendBuff; ///< 等待发送的数据缓冲区(数据到达时,连接并未建立,等连接建立并且pSendBuff发送完毕后立即发送) Codec* m_pCodec; ///< 编解码器 + HttpMsg* m_pHoldingHttpMsg; // 如果有http协议转换 int m_iErrno; std::string m_strKey; ///< 密钥 std::string m_strClientData; ///< 客户端相关数据(例如IM里的用户昵称、头像等,登录或连接时保存起来,后续发消息或其他操作无须客户端再带上来) diff --git a/src/codec/Codec.cpp b/src/codec/Codec.cpp index 5e706b90..e5f3587a 100644 --- a/src/codec/Codec.cpp +++ b/src/codec/Codec.cpp @@ -9,10 +9,6 @@ ******************************************************************************/ #include "Codec.hpp" -#include "cryptopp/default.h" -#include "cryptopp/cryptlib.h" -#include "cryptopp/aes.h" -#include "cryptopp/gzip.h" #include "util/encrypt/hconv.h" #include "util/encrypt/rc5.h" @@ -28,7 +24,7 @@ Codec::Codec(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) Codec::~Codec() { - LOG4_TRACE(""); + LOG4_TRACE("codec_type %d", m_eCodecType); } const std::vector& Codec::GetAutoSwitchCodecType() @@ -160,47 +156,12 @@ bool Codec::Unzip(const std::string& strSrc, std::string& strDest) bool Codec::Gzip(const std::string& strSrc, std::string& strDest) { - try - { - CryptoPP::Gzip oGzipper; - oGzipper.Put((CryptoPP::byte*)strSrc.c_str(), strSrc.size()); - oGzipper.MessageEnd(); - - CryptoPP::word64 avail = oGzipper.MaxRetrievable(); - if(avail) - { - strDest.resize(avail); - oGzipper.Get((CryptoPP::byte*)&strDest[0], strDest.size()); - } - } - catch(CryptoPP::InvalidDataFormat& e) - { - LOG4_ERROR("%s", e.GetWhat().c_str()); - return(false); - } - return (true); + return(CodecUtil::Gzip(strSrc, strDest)); } bool Codec::Gunzip(const std::string& strSrc, std::string& strDest) { - try - { - CryptoPP::Gunzip oUnZipper; - oUnZipper.Put((CryptoPP::byte*)strSrc.c_str(), strSrc.size()); - oUnZipper.MessageEnd(); - CryptoPP::word64 avail = oUnZipper.MaxRetrievable(); - if(avail) - { - strDest.resize(avail); - oUnZipper.Get((CryptoPP::byte*)&strDest[0], strDest.size()); - } - } - catch(CryptoPP::InvalidDataFormat& e) - { - LOG4_ERROR("%s", e.GetWhat().c_str()); - return(false); - } - return (true); + return(CodecUtil::Gunzip(strSrc, strDest)); } bool Codec::Rc5Encrypt(const std::string& strSrc, std::string& strDest) @@ -276,52 +237,12 @@ bool Codec::Rc5Decrypt(const std::string& strSrc, std::string& strDest) bool Codec::AesEncrypt(const std::string& strSrc, std::string& strDest) { - try - { - CryptoPP::CBC_Mode::Encryption oAes; - oAes.SetKeyWithIV((const CryptoPP::byte*)GetKey().c_str(), 16, (const CryptoPP::byte*)"2015-08-10 08:53:47"); - CryptoPP::StreamTransformationFilter oEncryptor( - oAes, NULL, CryptoPP::BlockPaddingSchemeDef::PKCS_PADDING); - for (size_t i = 0; i < strSrc.size(); ++i) - { - oEncryptor.Put((CryptoPP::byte)strSrc[i]); - } - oEncryptor.MessageEnd(); - size_t length = oEncryptor.MaxRetrievable(); - strDest.resize(length, 0); - oEncryptor.Get((CryptoPP::byte*)&strDest[0], length); - } - catch(CryptoPP::InvalidDataFormat& e) - { - LOG4_ERROR("%s", e.GetWhat().c_str()); - return(false); - } - return(true); + return(CodecUtil::AesEncrypt(GetKey(), strSrc, strDest)); } bool Codec::AesDecrypt(const std::string& strSrc, std::string& strDest) { - try - { - CryptoPP::CBC_Mode::Decryption oAes; - oAes.SetKeyWithIV((const CryptoPP::byte*)GetKey().c_str(), 16, (const CryptoPP::byte*)"2015-08-10 08:53:47"); - CryptoPP::StreamTransformationFilter oDecryptor( - oAes, NULL, CryptoPP::BlockPaddingSchemeDef::PKCS_PADDING); - for (size_t i = 0; i < strSrc.size(); ++i) - { - oDecryptor.Put((CryptoPP::byte)strSrc[i]); - } - oDecryptor.MessageEnd(); - size_t length = oDecryptor.MaxRetrievable(); - strDest.resize(length, 0); - oDecryptor.Get((CryptoPP::byte*)&strDest[0], length); - } - catch(CryptoPP::InvalidDataFormat& e) - { - LOG4_ERROR("%s", e.GetWhat().c_str()); - return(false); - } - return(true); + return(CodecUtil::AesDecrypt(GetKey(), strSrc, strDest)); } } /* namespace neb */ diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index e9efb3f8..c8cbd4ab 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -44,6 +44,7 @@ enum E_CODEC_TYPE CODEC_WS_EXTEND_PB = 7, ///< 带Extension data的websocket协议扩展,Application data为pb CODEC_NEBULA_IN_NODE = 8, ///< 节点各进程间通信协议,与CODEC_NEBULA协议相同,使用的编解码类也相同,只为区别节点内部连接与外部连接 CODEC_RESP = 9, ///< redis数据传输协议resp + CODEC_HTTP2 = 10, ///< http2编解码 }; /** diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 13416787..fe569e85 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -764,7 +764,9 @@ int CodecHttp::OnHeaderValue(http_parser *parser, const char *at, size_t len) pHttpMsg->set_keep_alive(0.0); } - if (std::string("Upgrade") == strHeadValue) + size_t uiPos = 0; + uiPos = strHeadValue.find_first_of("Upgrade"); + if (0 == uiPos) { pHttpMsg->mutable_upgrade()->set_is_upgrade(true); } diff --git a/src/codec/CodecUtil.cpp b/src/codec/CodecUtil.cpp index fafe9dd1..0b6d1675 100644 --- a/src/codec/CodecUtil.cpp +++ b/src/codec/CodecUtil.cpp @@ -8,6 +8,10 @@ * Modify history: ******************************************************************************/ #include "CodecUtil.hpp" +#include +#include +#include +#include namespace neb { @@ -96,5 +100,96 @@ uint64 CodecUtil::H2N(uint64 ullValue) return(ullValue); } +bool CodecUtil::Gzip(const std::string& strSrc, std::string& strDest) +{ + try + { + CryptoPP::Gzip oGzipper; + oGzipper.Put((CryptoPP::byte*)strSrc.c_str(), strSrc.size()); + oGzipper.MessageEnd(); + + CryptoPP::word64 avail = oGzipper.MaxRetrievable(); + if(avail) + { + strDest.resize(avail); + oGzipper.Get((CryptoPP::byte*)&strDest[0], strDest.size()); + } + } + catch(CryptoPP::InvalidDataFormat& e) + { + return(false); + } + return (true); +} + +bool CodecUtil::Gunzip(const std::string& strSrc, std::string& strDest) +{ + try + { + CryptoPP::Gunzip oUnZipper; + oUnZipper.Put((CryptoPP::byte*)strSrc.c_str(), strSrc.size()); + oUnZipper.MessageEnd(); + CryptoPP::word64 avail = oUnZipper.MaxRetrievable(); + if(avail) + { + strDest.resize(avail); + oUnZipper.Get((CryptoPP::byte*)&strDest[0], strDest.size()); + } + } + catch(CryptoPP::InvalidDataFormat& e) + { + return(false); + } + return (true); +} + +bool CodecUtil::AesEncrypt(const std::string& strKey, const std::string& strSrc, std::string& strDest) +{ + try + { + CryptoPP::CBC_Mode::Encryption oAes; + oAes.SetKeyWithIV((const CryptoPP::byte*)strKey.c_str(), 16, (const CryptoPP::byte*)"2015-08-10 08:53:47"); + CryptoPP::StreamTransformationFilter oEncryptor( + oAes, NULL, CryptoPP::BlockPaddingSchemeDef::PKCS_PADDING); + for (size_t i = 0; i < strSrc.size(); ++i) + { + oEncryptor.Put((CryptoPP::byte)strSrc[i]); + } + oEncryptor.MessageEnd(); + size_t length = oEncryptor.MaxRetrievable(); + strDest.resize(length, 0); + oEncryptor.Get((CryptoPP::byte*)&strDest[0], length); + } + catch(CryptoPP::InvalidDataFormat& e) + { + return(false); + } + return(true); +} + +bool CodecUtil::AesDecrypt(const std::string& strKey, const std::string& strSrc, std::string& strDest) +{ + try + { + CryptoPP::CBC_Mode::Decryption oAes; + oAes.SetKeyWithIV((const CryptoPP::byte*)strKey.c_str(), 16, (const CryptoPP::byte*)"2015-08-10 08:53:47"); + CryptoPP::StreamTransformationFilter oDecryptor( + oAes, NULL, CryptoPP::BlockPaddingSchemeDef::PKCS_PADDING); + for (size_t i = 0; i < strSrc.size(); ++i) + { + oDecryptor.Put((CryptoPP::byte)strSrc[i]); + } + oDecryptor.MessageEnd(); + size_t length = oDecryptor.MaxRetrievable(); + strDest.resize(length, 0); + oDecryptor.Get((CryptoPP::byte*)&strDest[0], length); + } + catch(CryptoPP::InvalidDataFormat& e) + { + return(false); + } + return(true); +} + } diff --git a/src/codec/CodecUtil.hpp b/src/codec/CodecUtil.hpp index f66cf59e..5fe26d73 100644 --- a/src/codec/CodecUtil.hpp +++ b/src/codec/CodecUtil.hpp @@ -10,11 +10,20 @@ #ifndef SRC_CODEC_CODECUTIL_HPP_ #define SRC_CODEC_CODECUTIL_HPP_ +#include #include "Definition.hpp" namespace neb { +enum E_COMPRESSION +{ + COMPRESS_NA = 0, // not compress + COMPRESS_GZIP = 1, + COMPRESS_DEFLATE = 2, + COMPRESS_SNAPPY = 3, +}; + class CodecUtil { public: @@ -41,6 +50,13 @@ class CodecUtil static uint64 N2H(uint64 ullValue); static uint64 H2N(uint64 ullValue); + static bool Gzip(const std::string& strSrc, std::string& strDest); + static bool Gunzip(const std::string& strSrc, std::string& strDest); + //static bool Deflate(const std::string& strSrc, std::string& strDest); + //static bool Undeflate(const std::string& strSrc, std::string& strDest); + static bool AesEncrypt(const std::string& strKey, const std::string& strSrc, std::string& strDest); + static bool AesDecrypt(const std::string& strKey, const std::string& strSrc, std::string& strDest); + protected: static inline bool IsLittleEndian() { diff --git a/src/codec/grpc/Grpc.hpp b/src/codec/grpc/Grpc.hpp new file mode 100644 index 00000000..751a0153 --- /dev/null +++ b/src/codec/grpc/Grpc.hpp @@ -0,0 +1,135 @@ +/******************************************************************************* + * Project: Nebula + * @file Grpc.hpp + * @brief + * @author Bwar + * @date: 2021-02-15 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_GRPC_GRPC_HPP_ +#define SRC_CODEC_GRPC_GRPC_HPP_ + +namespace neb +{ + +enum E_GRPC_STATUS_CODE +{ + GRPC_OK = 0, + + /// The operation was cancelled (typically by the caller). + GRPC_CANCELLED = 1, + + /// Unknown error. An example of where this error may be returned is if a + /// Status value received from another address space belongs to an error-space + /// that is not known in this address space. Also errors raised by APIs that + /// do not return enough error information may be converted to this error. + GRPC_UNKNOWN = 2, + + /// Client specified an invalid argument. Note that this differs from + /// FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are + /// problematic regardless of the state of the system (e.g., a malformed file + /// name). + GRPC_INVALID_ARGUMENT = 3, + + /// Deadline expired before operation could complete. For operations that + /// change the state of the system, this error may be returned even if the + /// operation has completed successfully. For example, a successful response + /// from a server could have been delayed long enough for the deadline to + /// expire. + GRPC_DEADLINE_EXCEEDED = 4, + + /// Some requested entity (e.g., file or directory) was not found. + GRPC_NOT_FOUND = 5, + + /// Some entity that we attempted to create (e.g., file or directory) already + /// exists. + GRPC_ALREADY_EXISTS = 6, + + /// The caller does not have permission to execute the specified operation. + /// PERMISSION_DENIED must not be used for rejections caused by exhausting + /// some resource (use RESOURCE_EXHAUSTED instead for those errors). + /// PERMISSION_DENIED must not be used if the caller can not be identified + /// (use UNAUTHENTICATED instead for those errors). + GRPC_PERMISSION_DENIED = 7, + + /// The request does not have valid authentication credentials for the + /// operation. + GRPC_UNAUTHENTICATED = 16, + + /// Some resource has been exhausted, perhaps a per-user quota, or perhaps the + /// entire file system is out of space. + GRPC_RESOURCE_EXHAUSTED = 8, + + /// Operation was rejected because the system is not in a state required for + /// the operation's execution. For example, directory to be deleted may be + /// non-empty, an rmdir operation is applied to a non-directory, etc. + /// + /// A litmus test that may help a service implementor in deciding + /// between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: + /// (a) Use UNAVAILABLE if the client can retry just the failing call. + /// (b) Use ABORTED if the client should retry at a higher-level + /// (e.g., restarting a read-modify-write sequence). + /// (c) Use FAILED_PRECONDITION if the client should not retry until + /// the system state has been explicitly fixed. E.g., if an "rmdir" + /// fails because the directory is non-empty, FAILED_PRECONDITION + /// should be returned since the client should not retry unless + /// they have first fixed up the directory by deleting files from it. + /// (d) Use FAILED_PRECONDITION if the client performs conditional + /// REST Get/Update/Delete on a resource and the resource on the + /// server does not match the condition. E.g., conflicting + /// read-modify-write on the same resource. + GRPC_FAILED_PRECONDITION = 9, + + /// The operation was aborted, typically due to a concurrency issue like + /// sequencer check failures, transaction aborts, etc. + /// + /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, + /// and UNAVAILABLE. + GRPC_ABORTED = 10, + + /// Operation was attempted past the valid range. E.g., seeking or reading + /// past end of file. + /// + /// Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed + /// if the system state changes. For example, a 32-bit file system will + /// generate INVALID_ARGUMENT if asked to read at an offset that is not in the + /// range [0,2^32-1], but it will generate OUT_OF_RANGE if asked to read from + /// an offset past the current file size. + /// + /// There is a fair bit of overlap between FAILED_PRECONDITION and + /// OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error) + /// when it applies so that callers who are iterating through a space can + /// easily look for an OUT_OF_RANGE error to detect when they are done. + GRPC_OUT_OF_RANGE = 11, + + /// Operation is not implemented or not supported/enabled in this service. + GRPC_UNIMPLEMENTED = 12, + + /// Internal errors. Means some invariants expected by underlying System has + /// been broken. If you see one of these errors, Something is very broken. + GRPC_INTERNAL = 13, + + /// The service is currently unavailable. This is a most likely a transient + /// condition and may be corrected by retrying with a backoff. Note that it is + /// not always safe to retry non-idempotent operations. + /// + /// \warning Although data MIGHT not have been transmitted when this + /// status occurs, there is NOT A GUARANTEE that the server has not seen + /// anything. So in general it is unsafe to retry on this status code + /// if the call is non-idempotent. + /// + /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, + /// and UNAVAILABLE. + GRPC_UNAVAILABLE = 14, + + /// Unrecoverable data loss or corruption. + GRPC_DATA_LOSS = 15, + + /// Force users to include a default branch: + GRPC_DO_NOT_USE = -1 +}; + +} /* namespace neb */ + +#endif diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index 40fc9873..6f51aa0d 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -8,21 +8,29 @@ * Modify history: ******************************************************************************/ #include "CodecHttp2.hpp" +#include #include "Http2Stream.hpp" #include "Http2Frame.hpp" #include "Http2Header.hpp" +#include "codec/CodecUtil.hpp" +#include "util/StringConverter.hpp" namespace neb { -CodecHttp2::CodecHttp2(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) - : Codec(pLogger, eCodecType) +CodecHttp2::CodecHttp2(std::shared_ptr pLogger, + E_CODEC_TYPE eCodecType, bool bChannelIsClient) + : Codec(pLogger, eCodecType), + m_bChannelIsClient(bChannelIsClient) { -#if __cplusplus >= 201401L - m_pFrame = std::make_unique(pLogger, eCodecType); -#else - m_pFrame = std::unique_ptr(new Http2Frame(pLogger, eCodecType)); -#endif + try + { + m_pFrame = new Http2Frame(pLogger, eCodecType); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + } } CodecHttp2::~CodecHttp2() @@ -35,10 +43,57 @@ CodecHttp2::~CodecHttp2() iter->second = nullptr; } m_mapStream.clear(); + if (m_pFrame != nullptr) + { + delete m_pFrame; + m_pFrame = nullptr; + } + LOG4_TRACE("codec type %d", GetCodecType()); +} + +void CodecHttp2::ConnectionSetting(CBuffer* pBuff) +{ + std::vector vecSetting; + tagSetting stSetting; + if (m_bChannelIsClient) + { + stSetting.unIdentifier = H2_SETTINGS_INITIAL_WINDOW_SIZE; + stSetting.uiValue = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + m_uiRecvWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + vecSetting.push_back(stSetting); + stSetting.unIdentifier = H2_SETTINGS_MAX_FRAME_SIZE; + stSetting.uiValue = DEFAULT_SETTINGS_MAX_FRAME_SIZE; + vecSetting.push_back(stSetting); + m_pFrame->EncodeSetting(this, vecSetting, pBuff); + } + else + { + stSetting.unIdentifier = H2_SETTINGS_INITIAL_WINDOW_SIZE; + stSetting.uiValue = 4194304; + m_uiRecvWindowSize = 4194304; + vecSetting.push_back(stSetting); + stSetting.unIdentifier = H2_SETTINGS_MAX_FRAME_SIZE; + stSetting.uiValue = 4194304; + vecSetting.push_back(stSetting); + stSetting.unIdentifier = H2_SETTINGS_MAX_HEADER_LIST_SIZE; + stSetting.uiValue = 8192; + vecSetting.push_back(stSetting); + m_pFrame->EncodeSetting(this, vecSetting, pBuff); + m_pFrame->EncodeWindowUpdate(this, 0, 4128769, pBuff); + m_pFrame->EncodePing(this, false, 0, 0, pBuff); + } } E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { + if (m_bWantMagic && m_bChannelIsClient) + { + if (pBuff->ReadableBytes() >= 24) + { + pBuff->Write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24); + m_bWantMagic = false; + } + } m_bChunkNotice = oHttpMsg.chunk_notice(); for (int i = 0; i < oHttpMsg.adding_without_index_headers_size(); ++i) { @@ -48,64 +103,191 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { m_setEncodingNeverIndexHeaders.insert(oHttpMsg.adding_never_index_headers(i)); } - return(CODEC_STATUS_OK); + if (oHttpMsg.settings_size() > 0) + { + std::vector vecSetting; + tagSetting stSetting; + for (auto it = oHttpMsg.settings().begin(); it != oHttpMsg.settings().end(); ++it) + { + stSetting.unIdentifier = it->first; + stSetting.uiValue = it->second; + } + m_pFrame->EncodeSetting(this, vecSetting, pBuff); + } + if (HTTP_REQUEST == oHttpMsg.type()) + { + if (oHttpMsg.stream_id() != 0) + { + LOG4_ERROR("request stream id must be zero."); + return(CODEC_STATUS_PART_ERR); + } + const_cast(oHttpMsg).set_stream_id(StreamIdGenerate()); + } + else + { + if (oHttpMsg.stream_id() == 0) + { + LOG4_ERROR("response stream id can not be zero."); + return(CODEC_STATUS_PART_ERR); + } + } + //const_cast(oHttpMsg).set_with_huffman(true); + if (oHttpMsg.stream_id() != m_pCodingStream->GetStreamId()) + { + auto stream_iter = m_mapStream.find(oHttpMsg.stream_id()); + if (stream_iter == m_mapStream.end()) + { + if (NewCodingStream(oHttpMsg.stream_id()) == nullptr) + { + return(CODEC_STATUS_ERR); + } + } + else + { + m_pCodingStream = stream_iter->second; + } + } + size_t uiReadIdx = pBuff->GetReadIndex(); + LOG4_TRACE("%s", oHttpMsg.DebugString().c_str()); + E_CODEC_STATUS eCodecStatus = m_pCodingStream->Encode(this, oHttpMsg, pBuff); + if (CODEC_STATUS_PAUSE == eCodecStatus + || CODEC_STATUS_ERR == eCodecStatus) + { + pBuff->SetReadIndex(uiReadIdx); + } + if (CODEC_STATUS_PART_ERR == eCodecStatus + || CODEC_STATUS_OK == eCodecStatus) + { + LOG4_TRACE("m_bChannelIsClient = %d, m_stFrameHead.uiStreamIdentifier = %u", + m_bChannelIsClient, m_stFrameHead.uiStreamIdentifier); + if (m_pCodingStream->GetStreamState() == H2_STREAM_CLOSE) + { + CloseStream(m_stFrameHead.uiStreamIdentifier); + } + } + return(eCodecStatus); +} + +E_CODEC_STATUS CodecHttp2::Encode(CBuffer* pBuff) +{ + // TODO encode not complete stream data frame } E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) { LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); - if (pBuff->ReadableBytes() <= H2_FRAME_HEAD_SIZE) + LOG4_TRACE("%s", pBuff->GetRawReadBuffer()); + if (m_bWantMagic && !m_bChannelIsClient) + { + if (pBuff->ReadableBytes() >= 24) + { + std::string strMagic; + strMagic.assign(pBuff->GetRawReadBuffer(), 24); + if (strMagic == "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") + { + m_bWantMagic = false; + pBuff->AdvanceReadIndex(24); + return(Decode(pBuff, oHttpMsg, pReactBuff)); + } + LOG4_ERROR("need upgrade magic."); + return(CODEC_STATUS_ERR); + } + return(CODEC_STATUS_PAUSE); + } + if (pBuff->ReadableBytes() < H2_FRAME_HEAD_SIZE) { + if (pBuff->ReadableBytes() == 0 && m_pHoldingHttpMsg != nullptr) + { + oHttpMsg = std::move(*m_pHoldingHttpMsg); + oHttpMsg.set_http_major(2); + oHttpMsg.set_http_minor(0); + oHttpMsg.mutable_headers()->erase( + oHttpMsg.mutable_headers()->find("Connection")); + oHttpMsg.mutable_upgrade()->set_is_upgrade(false); + oHttpMsg.mutable_upgrade()->set_protocol(""); + oHttpMsg.set_stream_id(1); + try + { + m_pCodingStream = new Http2Stream(m_pLogger, GetCodecType(), oHttpMsg.stream_id()); + m_pCodingStream->SetState(H2_STREAM_HALF_CLOSE_REMOTE); + m_mapStream.insert(std::make_pair((uint32)1, m_pCodingStream)); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + } + delete m_pHoldingHttpMsg; + m_pHoldingHttpMsg = nullptr; + return(CODEC_STATUS_OK); + } return(CODEC_STATUS_PAUSE); } - int iReadIdx = pBuff->GetReadIndex(); - pBuff->Read(&m_stFrameHead.uiLength, 3); - pBuff->Read(&m_stFrameHead.ucType, 1); - pBuff->Read(&m_stFrameHead.ucFlag, 1); - pBuff->Read(&m_stFrameHead.uiStreamIdentifier, 4); - m_stFrameHead.cR = (m_stFrameHead.uiStreamIdentifier & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; - m_stFrameHead.uiLength = ntohl(m_stFrameHead.uiLength); - m_stFrameHead.uiStreamIdentifier = ntohl(m_stFrameHead.uiStreamIdentifier & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + size_t uiReadIdx = pBuff->GetReadIndex(); + Http2Frame::DecodeFrameHeader(pBuff, m_stFrameHead); + LOG4_TRACE("m_stFrameHead.uiLength = %u, m_stFrameHead.ucType = %u, m_stFrameHead.ucFlag = %u, m_stFrameHead.uiStreamIdentifier = %u", + m_stFrameHead.uiLength, m_stFrameHead.ucType, m_stFrameHead.ucFlag, m_stFrameHead.uiStreamIdentifier); if (m_stFrameHead.uiLength > m_uiSettingsMaxFrameSize) { + LOG4_TRACE("m_uiSettingsMaxFrameSize = %u, m_stFrameHead.uiLength = %u", + m_uiSettingsMaxFrameSize, m_stFrameHead.uiLength); SetErrno(H2_ERR_FRAME_SIZE_ERROR); + pBuff->SetReadIndex(uiReadIdx); return(CODEC_STATUS_PART_ERR); } if (pBuff->ReadableBytes() < m_stFrameHead.uiLength) { - pBuff->SetReadIndex(iReadIdx); + LOG4_TRACE("pBuff->ReadableBytes() = %u, m_stFrameHead.uiLength = %u", pBuff->ReadableBytes(), m_stFrameHead.uiLength); + pBuff->SetReadIndex(uiReadIdx); return(CODEC_STATUS_PAUSE); } if (m_uiGoawayLastStreamId > 0 && m_stFrameHead.uiStreamIdentifier > m_uiGoawayLastStreamId) { + LOG4_TRACE("m_uiGoawayLastStreamId = %u, m_stFrameHead.uiStreamIdentifier = %u", m_uiGoawayLastStreamId, m_stFrameHead.uiStreamIdentifier); SetErrno(H2_ERR_CANCEL); - pBuff->SetReadIndex(iReadIdx); + pBuff->SetReadIndex(uiReadIdx); return(CODEC_STATUS_PART_ERR); } - if (m_pCodingStream != nullptr) + LOG4_TRACE("m_stFrameHead.uiStreamIdentifier = %u", m_stFrameHead.uiStreamIdentifier); + if (m_stFrameHead.uiStreamIdentifier > 0) { - if (m_stFrameHead.uiStreamIdentifier == m_pCodingStream->GetStreamId()) + if (m_pCodingStream != nullptr) { - return(m_pCodingStream->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff)); + if (m_stFrameHead.uiStreamIdentifier == m_pCodingStream->GetStreamId()) + { + E_CODEC_STATUS eCodecStatus = m_pCodingStream->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff); + LOG4_TRACE("eCodecStatus = %d", eCodecStatus); + if (CODEC_STATUS_PAUSE == eCodecStatus + || CODEC_STATUS_ERR == eCodecStatus) + { + pBuff->SetReadIndex(uiReadIdx); + } + if (CODEC_STATUS_PART_ERR == eCodecStatus + || CODEC_STATUS_OK == eCodecStatus) + { + if (m_pCodingStream->GetStreamState() == H2_STREAM_CLOSE) + { + CloseStream(m_stFrameHead.uiStreamIdentifier); + } + } + oHttpMsg.set_http_major(2); + oHttpMsg.set_http_minor(0); + return(eCodecStatus); + } } - if (!m_pCodingStream->IsEndHeaders()) + if (H2_FRAME_CONTINUATION == m_stFrameHead.ucType) { - // If the END_HEADERS bit is not set, this frame MUST be followed by another CONTINUATION frame. - // A receiver MUST treat the receipt of any other type of frame or a frame on a different - // stream as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. SetErrno(H2_ERR_PROTOCOL_ERROR); - m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "The endpoint " - "detected an unspecific protocol error. This error is for " - "use when a more specific error code is not available.", pReactBuff); + m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "A CONTINUATION " + "frame MUST be preceded by a HEADERS, PUSH_PROMISE or " + "CONTINUATION frame without the END_HEADERS flag set. ", pReactBuff); + LOG4_TRACE("The endpoint detected an unspecific protocol error. This error is for " + "use when a more specific error code is not available."); return(CODEC_STATUS_ERR); } - } - - if (m_stFrameHead.uiStreamIdentifier > 0) - { + LOG4_TRACE("m_stFrameHead.ucType = %u", m_stFrameHead.ucType); auto stream_iter = m_mapStream.find(m_stFrameHead.uiStreamIdentifier); if (stream_iter == m_mapStream.end()) { @@ -122,17 +304,13 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "The endpoint " "detected an unspecific protocol error. This error is for " "use when a more specific error code is not available.", pReactBuff); + LOG4_TRACE("The endpoint detected an unspecific protocol error. This error is for " + "use when a more specific error code is not available."); return(CODEC_STATUS_ERR); } m_uiStreamIdGenerate = m_stFrameHead.uiStreamIdentifier; - try - { - m_pCodingStream = new Http2Stream(m_pLogger, GetCodecType()); - m_mapStream.insert(std::make_pair(m_stFrameHead.uiStreamIdentifier, m_pCodingStream)); - } - catch(std::bad_alloc& e) + if (NewCodingStream(oHttpMsg.stream_id()) == nullptr) { - LOG4_ERROR("%s", e.what()); return(CODEC_STATUS_ERR); } } @@ -140,11 +318,35 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR { m_pCodingStream = stream_iter->second; } - return(m_pCodingStream->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff)); + E_CODEC_STATUS eCodecStatus = m_pCodingStream->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff); + if (CODEC_STATUS_PAUSE == eCodecStatus + || CODEC_STATUS_ERR == eCodecStatus) + { + pBuff->SetReadIndex(uiReadIdx); + } + if (CODEC_STATUS_PART_ERR == eCodecStatus + || CODEC_STATUS_OK == eCodecStatus) + { + if (m_pCodingStream->GetStreamState() == H2_STREAM_CLOSE) + { + CloseStream(m_stFrameHead.uiStreamIdentifier); + } + } + oHttpMsg.set_http_major(2); + oHttpMsg.set_http_minor(0); + return(eCodecStatus); } else { - return(m_pFrame->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff)); + E_CODEC_STATUS eCodecStatus = m_pFrame->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff); + if (CODEC_STATUS_PAUSE == eCodecStatus + || CODEC_STATUS_ERR == eCodecStatus) + { + pBuff->SetReadIndex(uiReadIdx); + } + oHttpMsg.set_http_major(2); + oHttpMsg.set_http_minor(0); + return(eCodecStatus); } } @@ -300,6 +502,10 @@ E_H2_ERR_CODE CodecHttp2::Setting(const std::vector& vecSetting) case H2_SETTINGS_INITIAL_WINDOW_SIZE: if (vecSetting[i].uiValue <= SETTINGS_MAX_INITIAL_WINDOW_SIZE) { + for (auto it = m_mapStream.begin(); it != m_mapStream.end(); ++it) + { + it->second->WindowUpdate((int32)vecSetting[i].uiValue - m_uiSettingsMaxWindowSize); + } m_uiSettingsMaxWindowSize = vecSetting[i].uiValue; } else @@ -311,6 +517,7 @@ E_H2_ERR_CODE CodecHttp2::Setting(const std::vector& vecSetting) if (vecSetting[i].uiValue <= SETTINGS_MAX_FRAME_SIZE) { m_uiSettingsMaxFrameSize = vecSetting[i].uiValue; + LOG4_TRACE("set max frame size to %u", m_uiSettingsMaxFrameSize); } else { @@ -329,7 +536,18 @@ E_H2_ERR_CODE CodecHttp2::Setting(const std::vector& vecSetting) void CodecHttp2::WindowUpdate(uint32 uiStreamId, uint32 uiIncrement) { - // TODO window update + if (uiStreamId == 0) + { + m_uiSendWindowSize += uiIncrement; + } + else + { + auto iter = m_mapStream.find(uiStreamId); + if (iter != m_mapStream.end()) + { + iter->second->WindowUpdate(uiIncrement); + } + } } E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBuff, HttpMsg& oHttpMsg) @@ -342,7 +560,6 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu int iDynamicTableIndex = -1; std::string strHeaderName; std::string strHeaderValue; - auto pHeader = oHttpMsg.mutable_headers(); while (pBuff->GetReadIndex() < uiHeaderBlockEndPos) { uiReadIndex = pBuff->GetReadIndex(); @@ -350,7 +567,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu pBuff->SetReadIndex(uiReadIndex); if (H2_HPACK_CONDITION_INDEXED_HEADER & B) { - eStatus = UnpackHeaderIndexed(pBuff, pHeader); + eStatus = UnpackHeaderIndexed(pBuff, oHttpMsg); if (eStatus != CODEC_STATUS_PART_OK) { return(eStatus); @@ -365,9 +582,8 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu { return(eStatus); } - pHeader->insert({strHeaderName, strHeaderValue}); - //UpdateDecodingDynamicTable(iDynamicTableIndex, strHeaderName, strHeaderValue); - UpdateDecodingDynamicTable(-1, strHeaderName, strHeaderValue); + ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); + UpdateDecodingDynamicTable(0, strHeaderName, strHeaderValue); } else if (H2_HPACK_CONDITION_LITERAL_HEADER_NEVER_INDEXED & B) { @@ -378,7 +594,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu { return(eStatus); } - pHeader->insert({strHeaderName, strHeaderValue}); + ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); oHttpMsg.add_adding_never_index_headers(strHeaderName); } else if (H2_HPACK_CONDITION_DYNAMIC_TABLE_SIZE_UPDATE & B) @@ -392,7 +608,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu " protocol using HPACK!"); return(CODEC_STATUS_ERR); } - pHeader->insert({strHeaderName, strHeaderValue}); + ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); UpdateDecodingDynamicTable(uiTableSize); } else // H2_HPACK_CONDITION_LITERAL_HEADER_WITHOUT_INDEXING @@ -404,7 +620,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu { return(eStatus); } - pHeader->insert({strHeaderName, strHeaderValue}); + ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); oHttpMsg.add_adding_without_index_headers(strHeaderName); } } @@ -412,10 +628,8 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu return(CODEC_STATUS_PART_OK); } -void CodecHttp2::PackHeader(const HttpMsg& oHttpMsg, CBuffer* pBuff) +void CodecHttp2::PackHeader(const HttpMsg& oHttpMsg, int iHeaderType, CBuffer* pBuff) { - size_t uiTableIndex = 0; - if (oHttpMsg.dynamic_table_update_size() > 0) { if (oHttpMsg.dynamic_table_update_size() > SETTINGS_MAX_FRAME_SIZE) @@ -429,34 +643,65 @@ void CodecHttp2::PackHeader(const HttpMsg& oHttpMsg, CBuffer* pBuff) } } - for (auto c_iter = oHttpMsg.headers().begin(); - c_iter != oHttpMsg.headers().end(); ++c_iter) + if (iHeaderType & H2_HEADER_PSEUDO) { - uiTableIndex = Http2Header::GetStaticTableIndex(c_iter->first, c_iter->second); - if (uiTableIndex == 0) + for (int i = 0; i < oHttpMsg.pseudo_header_size(); ++i) { - uiTableIndex = GetEncodingTableIndex(c_iter->first, c_iter->second); + PackHeader(oHttpMsg.pseudo_header(i).name(), oHttpMsg.pseudo_header(i).value(), oHttpMsg.with_huffman(), pBuff); } - if (uiTableIndex > 0) + } + if (iHeaderType & H2_HEADER_NORMAL) + { + std::string strHeaderName; + for (auto c_iter = oHttpMsg.headers().begin(); + c_iter != oHttpMsg.headers().end(); ++c_iter) { - PackHeaderIndexed(uiTableIndex, pBuff); + strHeaderName = c_iter->first; + std::transform(strHeaderName.begin(), strHeaderName.end(), strHeaderName.begin(), + [](unsigned char c) -> unsigned char { return std::tolower(c); }); + PackHeader(strHeaderName, c_iter->second, oHttpMsg.with_huffman(), pBuff); } - else + } + if (iHeaderType & H2_HEADER_TRAILER) + { + for (int i = 0; i < oHttpMsg.trailer_header_size(); ++i) { - auto never_index_iter = m_setEncodingNeverIndexHeaders.find(c_iter->first); - if (never_index_iter != m_setEncodingNeverIndexHeaders.end()) - { - PackHeaderNeverIndexing(c_iter->first, c_iter->second, oHttpMsg.with_huffman(), pBuff); - continue; - } - auto without_index_iter = m_setEncodingWithoutIndexHeaders.find(c_iter->first); - if (without_index_iter != m_setEncodingWithoutIndexHeaders.end()) - { - PackHeaderWithoutIndexing(c_iter->first, c_iter->second, oHttpMsg.with_huffman(), pBuff); - continue; - } - PackHeaderWithIndexing(c_iter->first, c_iter->second, oHttpMsg.with_huffman(), pBuff); + PackHeader(oHttpMsg.trailer_header(i).name(), oHttpMsg.trailer_header(i).value(), oHttpMsg.with_huffman(), pBuff); + } + } +} + +void CodecHttp2::PackHeader(const std::string& strHeaderName, const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff) +{ + size_t uiTableIndex = 0; + uiTableIndex = Http2Header::GetStaticTableIndex(strHeaderName, strHeaderValue); + if (uiTableIndex == 0) + { + uiTableIndex = GetEncodingTableIndex(strHeaderName, strHeaderValue); + } + if (uiTableIndex > 0) + { + PackHeaderIndexed(uiTableIndex, pBuff); + LOG4_TRACE("PackHeaderIndexed() %s: %s | uiTableIndex = %u", strHeaderName.c_str(), strHeaderValue.c_str(), uiTableIndex); + } + else + { + auto never_index_iter = m_setEncodingNeverIndexHeaders.find(strHeaderName); + if (never_index_iter != m_setEncodingNeverIndexHeaders.end()) + { + PackHeaderNeverIndexing(strHeaderName, strHeaderValue, bWithHuffman, pBuff); + LOG4_TRACE("PackHeaderNeverIndexing() %s: %s", strHeaderName.c_str(), strHeaderValue.c_str()); + return; + } + auto without_index_iter = m_setEncodingWithoutIndexHeaders.find(strHeaderName); + if (without_index_iter != m_setEncodingWithoutIndexHeaders.end()) + { + PackHeaderWithoutIndexing(strHeaderName, strHeaderValue, bWithHuffman, pBuff); + LOG4_TRACE("PackHeaderWithoutIndexing() %s: %s", strHeaderName.c_str(), strHeaderValue.c_str()); + return; } + PackHeaderWithIndexing(strHeaderName, strHeaderValue, bWithHuffman, pBuff); + LOG4_TRACE("PackHeaderWithIndexing() %s: %s", strHeaderName.c_str(), strHeaderValue.c_str()); } } @@ -485,7 +730,7 @@ E_CODEC_STATUS CodecHttp2::PromiseStream(uint32 uiStreamId, CBuffer* pReactBuff) Http2Stream* pPromiseStream = nullptr; try { - pPromiseStream = new Http2Stream(m_pLogger, GetCodecType()); + pPromiseStream = new Http2Stream(m_pLogger, GetCodecType(), uiStreamId); pPromiseStream->SetState(H2_STREAM_RESERVED_REMOTE); m_mapStream.insert(std::make_pair(uiStreamId, pPromiseStream)); } @@ -497,6 +742,11 @@ E_CODEC_STATUS CodecHttp2::PromiseStream(uint32 uiStreamId, CBuffer* pReactBuff) return(CODEC_STATUS_PART_OK); } +void CodecHttp2::TransferHoldingMsg(HttpMsg* pHoldingHttpMsg) +{ + m_pHoldingHttpMsg = pHoldingHttpMsg; +} + uint32 CodecHttp2::StreamIdGenerate() { if (m_bChannelIsClient) @@ -524,6 +774,25 @@ uint32 CodecHttp2::StreamIdGenerate() return(m_uiStreamIdGenerate); } +Http2Stream* CodecHttp2::NewCodingStream(uint32 uiStreamId) +{ + uint32 uiNewWindowSize = m_uiRecvWindowSize + DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + try + { + m_pCodingStream = new Http2Stream(m_pLogger, GetCodecType(), uiStreamId); + m_pCodingStream->WindowInit(m_uiSettingsMaxWindowSize); + m_uiRecvWindowSize = (uiNewWindowSize < SETTINGS_MAX_INITIAL_WINDOW_SIZE) + ? uiNewWindowSize : SETTINGS_MAX_INITIAL_WINDOW_SIZE; + m_mapStream.insert(std::make_pair(uiStreamId, m_pCodingStream)); + return(m_pCodingStream); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + return(nullptr); + } +} + TreeNode* CodecHttp2::FindStreamWeight(uint32 uiStreamId, TreeNode* pTarget) { if (pTarget == nullptr) @@ -561,8 +830,7 @@ void CodecHttp2::ReleaseStreamWeight(TreeNode* pNode) delete pNode; } -E_CODEC_STATUS CodecHttp2::UnpackHeaderIndexed(CBuffer* pBuff, - ::google::protobuf::Map< ::std::string, ::std::string >* pHeader) +E_CODEC_STATUS CodecHttp2::UnpackHeaderIndexed(CBuffer* pBuff, HttpMsg& oHttpMsg) { uint32 uiTableIndex = (uint32)Http2Header::DecodeInt(H2_HPACK_PREFIX_7_BITS, pBuff); if (uiTableIndex == 0) @@ -573,17 +841,15 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderIndexed(CBuffer* pBuff, } else if (uiTableIndex <= Http2Header::sc_uiMaxStaticTableIndex) { - pHeader->insert({ - Http2Header::sc_vecStaticTable[uiTableIndex].first, - Http2Header::sc_vecStaticTable[uiTableIndex].second}); + ClassifyHeader(Http2Header::sc_vecStaticTable[uiTableIndex].first, + Http2Header::sc_vecStaticTable[uiTableIndex].second, oHttpMsg); return(CODEC_STATUS_PART_OK); } else if (uiTableIndex <= Http2Header::sc_uiMaxStaticTableIndex + m_vecDecodingDynamicTable.size()) { uint32 uiDynamicTableIndex = uiTableIndex - Http2Header::sc_uiMaxStaticTableIndex - 1; - pHeader->insert({ - m_vecDecodingDynamicTable[uiDynamicTableIndex].Name(), - m_vecDecodingDynamicTable[uiDynamicTableIndex].Value()}); + ClassifyHeader(m_vecDecodingDynamicTable[uiDynamicTableIndex].Name(), + m_vecDecodingDynamicTable[uiDynamicTableIndex].Value(), oHttpMsg); return(CODEC_STATUS_PART_OK); } else @@ -653,6 +919,65 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF } } +void CodecHttp2::ClassifyHeader(const std::string& strHeaderName, const std::string& strHeaderValue, HttpMsg& oHttpMsg) +{ + if (oHttpMsg.stream_id() & 0x01) + { + if (oHttpMsg.body().size() > 0) + { + auto pHeader = oHttpMsg.add_trailer_header(); + pHeader->set_name(strHeaderName); + pHeader->set_value(strHeaderValue); + } + else + { + if (strHeaderName == ":method") + { + if (strHeaderValue == "POST") + { + oHttpMsg.set_method(HTTP_POST); + } + else if (strHeaderValue == "GET") + { + oHttpMsg.set_method(HTTP_GET); + } + else + { + ;// TODO other http method + } + } + else if (strHeaderName == ":path") + { + oHttpMsg.set_path(strHeaderValue); + } + else + { + oHttpMsg.mutable_headers()->insert({strHeaderName, strHeaderValue}); + } + } + } + else + { + if (oHttpMsg.body().size() > 0) + { + auto pHeader = oHttpMsg.add_trailer_header(); + pHeader->set_name(strHeaderName); + pHeader->set_value(strHeaderValue); + } + else + { + if (strHeaderName == ":status") + { + oHttpMsg.set_status_code(StringConverter::RapidAtoi(strHeaderValue.c_str())); + } + else + { + oHttpMsg.mutable_headers()->insert({strHeaderName, strHeaderValue}); + } + } + } +} + void CodecHttp2::PackHeaderIndexed(size_t uiTableIndex, CBuffer* pBuff) { Http2Header::EncodeInt(uiTableIndex, (size_t)H2_HPACK_PREFIX_7_BITS, @@ -669,7 +994,7 @@ void CodecHttp2::PackHeaderWithIndexing(const std::string& strHeaderName, uiTableIndex = GetEncodingTableIndex(strHeaderName); } - if (uiTableIndex > 0) + if (uiTableIndex > 0) // Literal Header Field with Incremental Indexing - Indexed Name. { Http2Header::EncodeInt(uiTableIndex, (size_t)H2_HPACK_PREFIX_6_BITS, (char)H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING, pBuff); @@ -682,7 +1007,7 @@ void CodecHttp2::PackHeaderWithIndexing(const std::string& strHeaderName, Http2Header::EncodeStringLiteral(strHeaderValue, pBuff); } } - else + else // Literal Header Field with Incremental Indexing - New Name. { Http2Header::EncodeInt(0, (size_t)H2_HPACK_PREFIX_6_BITS, (char)H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING, pBuff); @@ -693,9 +1018,13 @@ void CodecHttp2::PackHeaderWithIndexing(const std::string& strHeaderName, } else { + LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); Http2Header::EncodeStringLiteral(strHeaderName, pBuff); + LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); Http2Header::EncodeStringLiteral(strHeaderValue, pBuff); + LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); } + UpdateEncodingDynamicTable(0, strHeaderName, strHeaderValue); } } @@ -843,19 +1172,19 @@ size_t CodecHttp2::GetEncodingTableIndex(const std::string& strHeaderName, const // return(uiTableIndex); //} -void CodecHttp2::UpdateEncodingDynamicTable(int iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) +void CodecHttp2::UpdateEncodingDynamicTable(uint32 uiDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) { Http2Header oHeader(strHeaderName, strHeaderValue); - if (iDynamicTableIndex < 0 || iDynamicTableIndex >= (int)m_vecEncodingDynamicTable.size()) // new header + if (uiDynamicTableIndex <= 0 || uiDynamicTableIndex >= m_vecEncodingDynamicTable.size()) // new header { - while (m_uiEncodingDynamicTableSize + oHeader.HpackSize() > m_uiSettingsMaxFrameSize + while (m_uiEncodingDynamicTableSize + oHeader.HpackSize() > m_uiSettingsHeaderTableSize && m_uiEncodingDynamicTableSize > 0) { auto& rLastElement = m_vecEncodingDynamicTable.back(); m_uiEncodingDynamicTableSize -= rLastElement.HpackSize(); m_vecEncodingDynamicTable.pop_back(); } - if (oHeader.HpackSize() <= m_uiSettingsMaxFrameSize) + if (oHeader.HpackSize() <= m_uiSettingsHeaderTableSize) { m_vecEncodingDynamicTable.insert(m_vecEncodingDynamicTable.begin(), std::move(oHeader)); @@ -865,19 +1194,19 @@ void CodecHttp2::UpdateEncodingDynamicTable(int iDynamicTableIndex, const std::s else // replace header ? { while ((m_uiEncodingDynamicTableSize + oHeader.HpackSize() - - m_vecEncodingDynamicTable[iDynamicTableIndex].HpackSize()) - > m_uiSettingsMaxFrameSize + - m_vecEncodingDynamicTable[uiDynamicTableIndex].HpackSize()) + > m_uiSettingsHeaderTableSize && m_uiEncodingDynamicTableSize > 0) { auto& rLastElement = m_vecEncodingDynamicTable.back(); m_uiEncodingDynamicTableSize -= rLastElement.HpackSize(); m_vecEncodingDynamicTable.pop_back(); } - if (oHeader.HpackSize() <= m_uiSettingsMaxFrameSize) + if (oHeader.HpackSize() <= m_uiSettingsHeaderTableSize) { - if (iDynamicTableIndex < (int)m_vecEncodingDynamicTable.size()) // replace header + if (uiDynamicTableIndex < m_vecEncodingDynamicTable.size()) // replace header { - m_vecEncodingDynamicTable[iDynamicTableIndex] = std::move(oHeader); + m_vecEncodingDynamicTable[uiDynamicTableIndex] = std::move(oHeader); } else // new header { @@ -891,8 +1220,8 @@ void CodecHttp2::UpdateEncodingDynamicTable(int iDynamicTableIndex, const std::s void CodecHttp2::UpdateEncodingDynamicTable(uint32 uiTableSize) { - m_uiSettingsMaxFrameSize = uiTableSize; - while (m_uiEncodingDynamicTableSize > m_uiSettingsMaxFrameSize + m_uiSettingsHeaderTableSize = uiTableSize; + while (m_uiEncodingDynamicTableSize > m_uiSettingsHeaderTableSize && m_uiEncodingDynamicTableSize > 0) { auto& rLastElement = m_vecEncodingDynamicTable.back(); @@ -901,19 +1230,19 @@ void CodecHttp2::UpdateEncodingDynamicTable(uint32 uiTableSize) } } -void CodecHttp2::UpdateDecodingDynamicTable(int iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) +void CodecHttp2::UpdateDecodingDynamicTable(uint32 uiDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) { Http2Header oHeader(strHeaderName, strHeaderValue); - if (iDynamicTableIndex < 0 || iDynamicTableIndex >= (int)m_vecDecodingDynamicTable.size()) // new header + if (uiDynamicTableIndex <= 0 || uiDynamicTableIndex >= m_vecDecodingDynamicTable.size()) // new header { - while (m_uiDecodingDynamicTableSize + oHeader.HpackSize() > m_uiSettingsMaxFrameSize + while (m_uiDecodingDynamicTableSize + oHeader.HpackSize() > m_uiSettingsHeaderTableSize && m_uiDecodingDynamicTableSize > 0) { auto& rLastElement = m_vecDecodingDynamicTable.back(); m_uiDecodingDynamicTableSize -= rLastElement.HpackSize(); m_vecDecodingDynamicTable.pop_back(); } - if (oHeader.HpackSize() <= m_uiSettingsMaxFrameSize) + if (oHeader.HpackSize() <= m_uiSettingsHeaderTableSize) { m_vecDecodingDynamicTable.insert(m_vecDecodingDynamicTable.begin(), std::move(oHeader)); @@ -923,19 +1252,19 @@ void CodecHttp2::UpdateDecodingDynamicTable(int iDynamicTableIndex, const std::s else // replace header ? { while ((m_uiDecodingDynamicTableSize + oHeader.HpackSize() - - m_vecDecodingDynamicTable[iDynamicTableIndex].HpackSize()) - > m_uiSettingsMaxFrameSize + - m_vecDecodingDynamicTable[uiDynamicTableIndex].HpackSize()) + > m_uiSettingsHeaderTableSize && m_uiDecodingDynamicTableSize > 0) { auto& rLastElement = m_vecDecodingDynamicTable.back(); m_uiDecodingDynamicTableSize -= rLastElement.HpackSize(); m_vecDecodingDynamicTable.pop_back(); } - if (oHeader.HpackSize() <= m_uiSettingsMaxFrameSize) + if (oHeader.HpackSize() <= m_uiSettingsHeaderTableSize) { - if (iDynamicTableIndex < (int)m_vecDecodingDynamicTable.size()) // replace header + if (uiDynamicTableIndex < m_vecDecodingDynamicTable.size()) // replace header { - m_vecDecodingDynamicTable[iDynamicTableIndex] = std::move(oHeader); + m_vecDecodingDynamicTable[uiDynamicTableIndex] = std::move(oHeader); } else // new header { @@ -949,8 +1278,8 @@ void CodecHttp2::UpdateDecodingDynamicTable(int iDynamicTableIndex, const std::s void CodecHttp2::UpdateDecodingDynamicTable(uint32 uiTableSize) { - m_uiSettingsMaxFrameSize = uiTableSize; - while (m_uiDecodingDynamicTableSize > m_uiSettingsMaxFrameSize + m_uiSettingsHeaderTableSize = uiTableSize; + while (m_uiDecodingDynamicTableSize > m_uiSettingsHeaderTableSize && m_uiDecodingDynamicTableSize > 0) { auto& rLastElement = m_vecDecodingDynamicTable.back(); @@ -959,5 +1288,10 @@ void CodecHttp2::UpdateDecodingDynamicTable(uint32 uiTableSize) } } +void CodecHttp2::CloseStream(uint32 uiStreamId) +{ + RstStream(uiStreamId); +} + } /* namespace neb */ diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index aa8ee99d..e8ec3aec 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -12,6 +12,7 @@ #include #include "codec/Codec.hpp" +#include "util/http/http_parser.h" #include "pb/http.pb.h" #include "H2Comm.hpp" #include "Tree.hpp" @@ -35,7 +36,7 @@ class Http2Stream; class CodecHttp2: public Codec { public: - CodecHttp2(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + CodecHttp2(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, bool m_bChannelIsClient); virtual ~CodecHttp2(); virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) @@ -48,8 +49,14 @@ class CodecHttp2: public Codec } virtual E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff); + virtual E_CODEC_STATUS Encode(CBuffer* pBuff); virtual E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff); + /** + * @brief HTTP2 连接建立 + */ + virtual void ConnectionSetting(CBuffer* pBuff); + public: bool IsChunkNotice() const { @@ -64,7 +71,8 @@ class CodecHttp2: public Codec return(m_uiSettingsMaxFrameSize); } E_CODEC_STATUS UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBuff, HttpMsg& oHttpMsg); - void PackHeader(const HttpMsg& oHttpMsg, CBuffer* pBuff); + void PackHeader(const HttpMsg& oHttpMsg, int iHeaderType, CBuffer* pBuff); + void PackHeader(const std::string& strHeaderName, const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff); E_CODEC_STATUS PromiseStream(uint32 uiStreamId, CBuffer* pReactBuff); void SetGoaway(uint32 uiLastStreamId) { @@ -75,17 +83,20 @@ class CodecHttp2: public Codec return(m_uiStreamIdGenerate); } + void TransferHoldingMsg(HttpMsg* pHoldingHttpMsg); + protected: uint32 StreamIdGenerate(); + Http2Stream* NewCodingStream(uint32 uiStreamId); TreeNode* FindStreamWeight(uint32 uiStreamId, TreeNode* pTarget); TreeNode* FindHighestWeightStream(); void ReleaseStreamWeight(TreeNode* pNode); - E_CODEC_STATUS UnpackHeaderIndexed(CBuffer* pBuff, - ::google::protobuf::Map< ::std::string, ::std::string >* pHeader); + E_CODEC_STATUS UnpackHeaderIndexed(CBuffer* pBuff, HttpMsg& oHttpMsg); E_CODEC_STATUS UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucFirstByte, int32 iPrefixMask, int& iDynamicTableIndex, std::string& strHeaderName, std::string& strHeaderValue, bool& bWithHuffman); + void ClassifyHeader(const std::string& strHeaderName, const std::string& strHeaderValue, HttpMsg& oHttpMsg); void PackHeaderIndexed(size_t uiTableIndex, CBuffer* pBuff); void PackHeaderWithIndexing(const std::string& strHeaderName, const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff); @@ -96,13 +107,15 @@ class CodecHttp2: public Codec void PackHeaderDynamicTableSize(uint32 uiDynamicTableSize, CBuffer* pBuff); size_t GetEncodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue = ""); // size_t GetDecodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue = ""); - void UpdateEncodingDynamicTable(int iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue); + void UpdateEncodingDynamicTable(uint32 uiDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue); void UpdateEncodingDynamicTable(uint32 uiTableSize); - void UpdateDecodingDynamicTable(int iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue); + void UpdateDecodingDynamicTable(uint32 uiDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue); void UpdateDecodingDynamicTable(uint32 uiTableSize); + void CloseStream(uint32 uiStreamId); private: bool m_bChannelIsClient = false; // 当前编解码器所在channel是作为http客户端还是作为http服务端 + bool m_bWantMagic = true; bool m_bChunkNotice = false; // 是否启用分块传输通知(当包体比较大时,部分传输完毕也会通知业务层而无须等待整个http包传输完毕。) uint32 m_uiStreamIdGenerate = 0; uint32 m_uiGoawayLastStreamId = 0; @@ -112,8 +125,11 @@ class CodecHttp2: public Codec uint32 m_uiSettingsMaxWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; uint32 m_uiSettingsMaxFrameSize = DEFAULT_SETTINGS_MAX_FRAME_SIZE; uint32 m_uiSettingsMaxHeaderListSize = 50; // TODO SettingsMaxHeaderListSize + uint32 m_uiSendWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + uint32 m_uiRecvWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; tagH2FrameHead m_stFrameHead; - std::unique_ptr m_pFrame = nullptr; + HttpMsg* m_pHoldingHttpMsg = nullptr; // upgrade未完成时暂存请求 + Http2Frame* m_pFrame = nullptr; Http2Stream* m_pCodingStream = nullptr; std::unordered_map m_mapStream; TreeNode* m_pStreamWeight = nullptr; diff --git a/src/codec/http2/H2Comm.hpp b/src/codec/http2/H2Comm.hpp index 6df68cf6..47b96d3e 100644 --- a/src/codec/http2/H2Comm.hpp +++ b/src/codec/http2/H2Comm.hpp @@ -13,11 +13,11 @@ namespace neb { -const uint32 H2_FRAME_HEAD_SIZE = 72; -const uint32 SETTINGS_MAX_FRAME_SIZE = (2^24) - 1; -const uint32 DEFAULT_SETTINGS_MAX_FRAME_SIZE = (2^14); -const uint32 SETTINGS_MAX_INITIAL_WINDOW_SIZE = (2^31) - 1; -const uint32 DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE = (2^16) - 1; +const uint32 H2_FRAME_HEAD_SIZE = 9; +const uint32 SETTINGS_MAX_FRAME_SIZE = 16777215; // (2^24) - 1; +const uint32 DEFAULT_SETTINGS_MAX_FRAME_SIZE = 16384; // (2^14); +const uint32 SETTINGS_MAX_INITIAL_WINDOW_SIZE = 2147483647; // (2^31) - 1; +const uint32 DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE = 65535; // (2^16) - 1; /* * @see https://httpwg.org/specs/rfc7540.html#FRAME_SIZE_ERROR diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index 3508162b..daddcd8b 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -9,25 +9,131 @@ ******************************************************************************/ #include "Http2Frame.hpp" #include "CodecHttp2.hpp" +#include "Http2Stream.hpp" +#include "codec/CodecUtil.hpp" namespace neb { -Http2Frame::Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) - : Codec(pLogger, eCodecType) +Http2Frame::Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, Http2Stream* pStream) + : Codec(pLogger, eCodecType), m_pStream(pStream) { } Http2Frame::~Http2Frame() { + m_pStream = nullptr; + LOG4_TRACE("codec type %d", GetCodecType()); } -E_CODEC_STATUS Http2Frame::Encode(CodecHttp2* pCodecH2, - const HttpMsg& oHttpMsg, CBuffer* pBuff) +void Http2Frame::WriteMediumInt(uint32 uiValue, CBuffer* pBuff) +{ + char szData[3] = {0}; + szData[0] = (uiValue >> 16) & 0xFF; + szData[1] = (uiValue >> 8) & 0xFF; + szData[2] = uiValue & 0xFF; + pBuff->Write(szData, 3); +} + +void Http2Frame::ReadMediumInt(CBuffer* pBuff, uint32& uiValue) +{ + char szData[3] = {0}; + pBuff->ReadByte(szData[0]); + pBuff->ReadByte(szData[1]); + pBuff->ReadByte(szData[2]); + uiValue = ((uint32)szData[0] << 16) & 0xFF; + uiValue |= ((uint32)szData[1] << 8) & 0xFF; + uiValue |= (uint32)szData[2] & 0xFF; +} + +void Http2Frame::DecodeFrameHeader(CBuffer* pBuff, tagH2FrameHead& stFrameHead) +{ + ReadMediumInt(pBuff, stFrameHead.uiLength); + pBuff->Read(&stFrameHead.ucType, 1); + pBuff->Read(&stFrameHead.ucFlag, 1); + pBuff->Read(&stFrameHead.uiStreamIdentifier, 4); + stFrameHead.cR = (stFrameHead.uiStreamIdentifier & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; + stFrameHead.uiStreamIdentifier = CodecUtil::N2H(stFrameHead.uiStreamIdentifier & H2_DATA_MASK_4_BYTE_LOW_31_BIT); +} + +void Http2Frame::EncodeFrameHeader(const tagH2FrameHead& stFrameHead, CBuffer* pBuff) { + uint32 uiIdentifier = stFrameHead.cR; + uiIdentifier <<= 31; + uiIdentifier |= (CodecUtil::H2N(stFrameHead.uiStreamIdentifier)); + WriteMediumInt(stFrameHead.uiLength, pBuff); + pBuff->Write(&stFrameHead.ucType, 1); + pBuff->Write(&stFrameHead.ucFlag, 1); + pBuff->Write(&uiIdentifier, 4); +} + +E_CODEC_STATUS Http2Frame::EncodeSetting(const std::vector& vecSetting, std::string& strSettingFrame) +{ + uint16 unIdentifier = 0; + uint32 uiValue = 0; + CBuffer oBuff; + tagH2FrameHead stFrameHead; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_SETTINGS; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = 0; + stFrameHead.uiLength = 6 * vecSetting.size(); + if (stFrameHead.uiLength == 0) // H2_FRAME_FLAG_ACK + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_ACK; + } + EncodeFrameHeader(stFrameHead, &oBuff); + for (uint32 i = 0; i < vecSetting.size(); ++i) + { + unIdentifier = CodecUtil::H2N(vecSetting[i].unIdentifier); + uiValue = CodecUtil::H2N(vecSetting[i].uiValue); + oBuff.Write(&unIdentifier, 2); + oBuff.Write(&uiValue, 4); + } + strSettingFrame.assign(oBuff.GetRawReadBuffer(), oBuff.ReadableBytes()); return(CODEC_STATUS_OK); } +E_CODEC_STATUS Http2Frame::Encode(CodecHttp2* pCodecH2, + const HttpMsg& oHttpMsg, const tagPriority& stPriority, + const std::string& strPadding, CBuffer* pBuff) +{ + bool bEndStream = false; + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + if (oHttpMsg.headers_size() > 0) + { + if (oHttpMsg.body().size() == 0) + { + bEndStream = true; + } + eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, stPriority, strPadding, bEndStream, pBuff); + if (CODEC_STATUS_PART_ERR == eCodecStatus + || CODEC_STATUS_ERR == eCodecStatus) + { + return(eCodecStatus); + } + } + if (oHttpMsg.body().size() > 0) + { + if (oHttpMsg.trailer_header_size() == 0) + { + bEndStream = true; + } + eCodecStatus = EncodeData(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, bEndStream, strPadding, pBuff); + if (CODEC_STATUS_PART_ERR == eCodecStatus + || CODEC_STATUS_ERR == eCodecStatus) + { + return(eCodecStatus); + } + } + if (oHttpMsg.trailer_header_size() > 0) + { + bEndStream = true; + eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, stPriority, strPadding, bEndStream, pBuff); + } + return(eCodecStatus); +} + E_CODEC_STATUS Http2Frame::Decode(CodecHttp2* pCodecH2, const tagH2FrameHead& stFrameHead, CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) @@ -137,8 +243,10 @@ E_CODEC_STATUS Http2Frame::DecodeHeaders(CodecHttp2* pCodecH2, tagPriority stPriority; pBuff->Read(&stPriority.uiDependency, 4); stPriority.E = (stPriority.uiDependency & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; - stPriority.uiDependency = ntohl(stPriority.uiDependency & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + stPriority.uiDependency = CodecUtil::N2H(stPriority.uiDependency & H2_DATA_MASK_4_BYTE_LOW_31_BIT); pBuff->Read(&stPriority.ucWeight, 1); + LOG4_TRACE("SetPriority(identifier = %u, E = %u, ucWeight = %u, uiDependency = %u)", + stFrameHead.uiStreamIdentifier, stPriority.E, stPriority.ucWeight, stPriority.uiDependency); pCodecH2->SetPriority(stFrameHead.uiStreamIdentifier, stPriority); } E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; @@ -173,7 +281,7 @@ E_CODEC_STATUS Http2Frame::DecodePriority(CodecHttp2* pCodecH2, tagPriority stPriority; pBuff->Read(&stPriority.uiDependency, 4); stPriority.E = (stPriority.uiDependency & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; - stPriority.uiDependency = ntohl(stPriority.uiDependency & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + stPriority.uiDependency = CodecUtil::N2H(stPriority.uiDependency & H2_DATA_MASK_4_BYTE_LOW_31_BIT); pBuff->Read(&stPriority.ucWeight, 1); if (stPriority.uiDependency == 0x0) { @@ -209,7 +317,7 @@ E_CODEC_STATUS Http2Frame::DecodeRstStream(CodecHttp2* pCodecH2, } int32 iErrCode = 0; pBuff->Read(&iErrCode, 4); // the error code should be H2_ERR_CANCEL - iErrCode = ntohl(iErrCode); + iErrCode = CodecUtil::N2H((uint32)iErrCode); pCodecH2->SetErrno(iErrCode); pCodecH2->RstStream(stFrameHead.uiStreamIdentifier); return(CODEC_STATUS_PART_ERR); @@ -257,7 +365,10 @@ E_CODEC_STATUS Http2Frame::DecodeSetting(CodecHttp2* pCodecH2, { pBuff->Read(&stSetting.unIdentifier, 2); pBuff->Read(&stSetting.uiValue, 4); + stSetting.unIdentifier = CodecUtil::N2H(stSetting.unIdentifier); + stSetting.uiValue = CodecUtil::N2H(stSetting.uiValue); vecSetting.push_back(stSetting); + LOG4_TRACE("stSetting.unIdentifier = %u, stSetting.uiValue = %u", stSetting.unIdentifier, stSetting.uiValue); } E_H2_ERR_CODE eSettingResult = pCodecH2->Setting(vecSetting); if (eSettingResult == H2_ERR_NO_ERROR) @@ -307,7 +418,7 @@ E_CODEC_STATUS Http2Frame::DecodePushPromise(CodecHttp2* pCodecH2, uint32 uiStreamId = 0; pBuff->Read(&uiStreamId, 4); //R = (uiStreamId & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; - uiStreamId = ntohl(uiStreamId & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + uiStreamId = CodecUtil::N2H(uiStreamId & H2_DATA_MASK_4_BYTE_LOW_31_BIT); E_CODEC_STATUS eCodecStatus = pCodecH2->PromiseStream(uiStreamId, pReactBuff); if (eCodecStatus == CODEC_STATUS_PART_OK) { @@ -370,14 +481,13 @@ E_CODEC_STATUS Http2Frame::DecodeGoaway(CodecHttp2* pCodecH2, pBuff->Read(&uiLastStreamId, 4); pBuff->Read(&iErrCode, 4); //R = (uiLastStreamId & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; - uiLastStreamId = ntohl(uiLastStreamId & H2_DATA_MASK_4_BYTE_LOW_31_BIT); - iErrCode = ntohl(iErrCode); + uiLastStreamId = CodecUtil::N2H(uiLastStreamId & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + iErrCode = CodecUtil::N2H((uint32)iErrCode); strDebugData.assign(pBuff->GetRawReadBuffer(), stFrameHead.uiLength - 8); pBuff->AdvanceReadIndex(stFrameHead.uiLength - 8); pCodecH2->SetGoaway(uiLastStreamId); - LOG4_WARNING("errcode %d: %s", iErrCode, strDebugData.c_str()); pCodecH2->SetErrno(iErrCode); - return(CODEC_STATUS_PART_ERR); + return(CODEC_STATUS_ERR); } E_CODEC_STATUS Http2Frame::DecodeWindowUpdate(CodecHttp2* pCodecH2, @@ -390,22 +500,32 @@ E_CODEC_STATUS Http2Frame::DecodeWindowUpdate(CodecHttp2* pCodecH2, EncodeGoaway(pCodecH2, H2_ERR_FRAME_SIZE_ERROR, "TYPE_WINDOW_UPDATE length != 4", pReactBuff); return(CODEC_STATUS_ERR); } + //char cR = 0; uint32 uiIncrement = 0; pBuff->Read(&uiIncrement, 4); - uiIncrement &= H2_DATA_MASK_4_BYTE_LOW_31_BIT; + //cR = (uiIncrement & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; + uiIncrement = CodecUtil::N2H(stFrameHead.uiStreamIdentifier & H2_DATA_MASK_4_BYTE_LOW_31_BIT); if (uiIncrement == 0) { - if (stFrameHead.uiStreamIdentifier == 0) + // TODO just to confirm: curl --http2 receive MAGIC SETTING SETTING_ACK WINDOW_UPDATE(with an flow-control window increment of 0) + if (pCodecH2->GetLastStreamId() == 0) { - pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); - EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "TYPE_WINDOW_UPDATE length == 0 on connection", pReactBuff); - return(CODEC_STATUS_ERR); + LOG4_TRACE("upgrade ignore an flow-control window increment of 0."); } else { - pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); - EncodeRstStream(pCodecH2, stFrameHead.uiStreamIdentifier, H2_ERR_PROTOCOL_ERROR, pReactBuff); - return(CODEC_STATUS_PART_ERR); + if (stFrameHead.uiStreamIdentifier == 0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "TYPE_WINDOW_UPDATE length == 0 on connection", pReactBuff); + return(CODEC_STATUS_ERR); + } + else + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeRstStream(pCodecH2, stFrameHead.uiStreamIdentifier, H2_ERR_PROTOCOL_ERROR, pReactBuff); + return(CODEC_STATUS_PART_ERR); + } } } else @@ -445,32 +565,8 @@ E_CODEC_STATUS Http2Frame::DecodeContinuation(CodecHttp2* pCodecH2, } } -void Http2Frame::EncodeFrameHeader(const tagH2FrameHead& stFrameHead, CBuffer* pBuff) -{ - uint32 uiIdentifier = stFrameHead.cR; - uiIdentifier <<= 31; - uiIdentifier |= (htonl(stFrameHead.uiStreamIdentifier)); - uint32 uiNetLength = htonl(stFrameHead.uiLength); - pBuff->Write(&uiNetLength, 3); - pBuff->Write(&stFrameHead.ucType, 1); - pBuff->Write(&stFrameHead.ucFlag, 1); - pBuff->Write(&uiIdentifier, 4); -} - -void Http2Frame::EncodePriority(const tagPriority& stPriority, CBuffer* pBuff) -{ - if (stPriority.uiDependency > 0) - { - uint32 uiPriority = stPriority.E; - uiPriority <<= 31; - uiPriority |= (htonl(stPriority.uiDependency)); - pBuff->Write(&uiPriority, 4); - pBuff->Write(&stPriority.ucWeight, 1); - } -} - E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, - uint32 uiStreamId, const HttpMsg& oHttpMsg, + uint32 uiStreamId, const HttpMsg& oHttpMsg, bool bEndStream, const std::string& strPadding, CBuffer* pBuff) { if (uiStreamId == 0x0) @@ -478,46 +574,55 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); return(CODEC_STATUS_PART_ERR); } - tagH2FrameHead stFrameHead; - stFrameHead.cR = 0; - stFrameHead.ucType = H2_FRAME_DATA; - stFrameHead.ucFlag = 0; - stFrameHead.uiStreamIdentifier = uiStreamId; - if (strPadding.size() > 0) - { - stFrameHead.uiLength = 1 + oHttpMsg.body().size() + strPadding.size(); - stFrameHead.ucFlag |= H2_FRAME_FLAG_PADDED; - EncodeFrameHeader(stFrameHead, pBuff); - uint16 unNetLength = htons(strPadding.size()); - pBuff->Write(&unNetLength, 1); - pBuff->Write(oHttpMsg.body().c_str(), oHttpMsg.body().size()); - pBuff->Write(strPadding.c_str(), strPadding.size()); - } - else + E_CODEC_STATUS eEncodeStatus = CODEC_STATUS_OK; + const char* pBodyData = oHttpMsg.body().c_str(); + uint32 uiDataLen = oHttpMsg.body().size(); + uint32 uiEncodedDataLen = 0; + while (uiEncodedDataLen < uiDataLen) { - stFrameHead.uiLength = oHttpMsg.body().size(); - EncodeFrameHeader(stFrameHead, pBuff); - pBuff->Write(oHttpMsg.body().c_str(), oHttpMsg.body().size()); + eEncodeStatus = EncodeData(pCodecH2, uiStreamId, pBodyData, uiDataLen, bEndStream, strPadding, uiEncodedDataLen, pBuff); + if (eEncodeStatus != CODEC_STATUS_OK) + { + return(eEncodeStatus); + } + pBodyData += uiEncodedDataLen; + uiDataLen -= uiEncodedDataLen; } - return(CODEC_STATUS_PART_OK); + return(eEncodeStatus); } E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, uint32 uiStreamId, const HttpMsg& oHttpMsg, - const tagPriority& stPriority, const std::string& strPadding, CBuffer* pBuff) + const tagPriority& stPriority, const std::string& strPadding, + bool bEndStream, CBuffer* pBuff) { if (uiStreamId == 0x0) { pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); return(CODEC_STATUS_PART_ERR); } + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_PART_OK; tagH2FrameHead stFrameHead; CBuffer oBuffer; stFrameHead.cR = 0; stFrameHead.ucType = H2_FRAME_HEADERS; stFrameHead.ucFlag = 0; stFrameHead.uiStreamIdentifier = uiStreamId; - pCodecH2->PackHeader(oHttpMsg, &oBuffer); + if (oHttpMsg.body().size() > 0) + { + if (bEndStream) + { + pCodecH2->PackHeader(oHttpMsg, H2_HEADER_TRAILER, &oBuffer); + } + else + { + pCodecH2->PackHeader(oHttpMsg, H2_HEADER_PSEUDO | H2_HEADER_NORMAL, &oBuffer); + } + } + else + { + pCodecH2->PackHeader(oHttpMsg, H2_HEADER_PSEUDO | H2_HEADER_NORMAL | H2_HEADER_TRAILER, &oBuffer); + } if (strPadding.size() > 0) { uint32 uiAddtionLength = 0; @@ -539,24 +644,31 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, { stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); EncodeFrameHeader(stFrameHead, pBuff); - uint16 unNetLength = htons(strPadding.size()); + uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); EncodePriority(stPriority, pBuff); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); pBuff->Write(strPadding.c_str(), strPadding.size()); oBuffer.AdvanceReadIndex(stFrameHead.uiLength - uiAddtionLength); - return(EncodeContinuation(pCodecH2, uiStreamId, &oBuffer, pBuff)); + EncodeSetStreamState(stFrameHead); + return(EncodeContinuation(pCodecH2, uiStreamId, bEndStream, &oBuffer, pBuff)); } else { stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; + if (bEndStream) + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; + eCodecStatus = CODEC_STATUS_OK; + } EncodeFrameHeader(stFrameHead, pBuff); - uint16 unNetLength = htons(strPadding.size()); + uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); EncodePriority(stPriority, pBuff); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); pBuff->Write(strPadding.c_str(), strPadding.size()); - return(CODEC_STATUS_OK); + EncodeSetStreamState(stFrameHead); + return(eCodecStatus); } } else @@ -581,15 +693,24 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, EncodePriority(stPriority, pBuff); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); oBuffer.AdvanceReadIndex(stFrameHead.uiLength - uiAddtionLength); - return(EncodeContinuation(pCodecH2, uiStreamId, &oBuffer, pBuff)); + EncodeSetStreamState(stFrameHead); + return(EncodeContinuation(pCodecH2, uiStreamId, bEndStream, &oBuffer, pBuff)); } else { stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; + if (bEndStream) + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; + eCodecStatus = CODEC_STATUS_OK; + } EncodeFrameHeader(stFrameHead, pBuff); EncodePriority(stPriority, pBuff); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); - return(CODEC_STATUS_OK); + EncodeSetStreamState(stFrameHead); + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucFlag = 0x%X, pBuff->ReadableBytes() = %u", + stFrameHead.uiLength, stFrameHead.ucFlag, pBuff->ReadableBytes()); + return(eCodecStatus); } } } @@ -610,10 +731,11 @@ E_CODEC_STATUS Http2Frame::EncodePriority(CodecHttp2* pCodecH2, stFrameHead.uiLength = 5; uint32 uiPriority = stPriority.E; uiPriority <<= 31; - uiPriority |= (htonl(stPriority.uiDependency)); + uiPriority |= (CodecUtil::H2N(stPriority.uiDependency)); EncodeFrameHeader(stFrameHead, pBuff); pBuff->Write(&uiPriority, 4); pBuff->Write(&stPriority.ucWeight, 1); + EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_PART_OK); } @@ -627,17 +749,20 @@ E_CODEC_STATUS Http2Frame::EncodeRstStream(CodecHttp2* pCodecH2, stFrameHead.uiStreamIdentifier = uiStreamId; stFrameHead.uiLength = 4; EncodeFrameHeader(stFrameHead, pBuff); - uint32 uiErrCode = htonl(iErrCode); + uint32 uiErrCode = CodecUtil::H2N((uint32)iErrCode); pBuff->Write(&uiErrCode, 4); + EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); } E_CODEC_STATUS Http2Frame::EncodeSetting(CodecHttp2* pCodecH2, const std::vector& vecSetting, CBuffer* pBuff) { + uint16 unIdentifier = 0; + uint32 uiValue = 0; tagH2FrameHead stFrameHead; stFrameHead.cR = 0; - stFrameHead.ucType = H2_FRAME_RST_STREAM; + stFrameHead.ucType = H2_FRAME_SETTINGS; stFrameHead.ucFlag = 0; stFrameHead.uiStreamIdentifier = 0; stFrameHead.uiLength = 6 * vecSetting.size(); @@ -645,12 +770,19 @@ E_CODEC_STATUS Http2Frame::EncodeSetting(CodecHttp2* pCodecH2, { stFrameHead.ucFlag |= H2_FRAME_FLAG_ACK; } + else + { + pCodecH2->Setting(vecSetting); + } EncodeFrameHeader(stFrameHead, pBuff); for (uint32 i = 0; i < vecSetting.size(); ++i) { - pBuff->Write(&vecSetting[i].unIdentifier, 2); - pBuff->Write(&vecSetting[i].uiValue, 4); + unIdentifier = CodecUtil::H2N(vecSetting[i].unIdentifier); + uiValue = CodecUtil::H2N(vecSetting[i].uiValue); + pBuff->Write(&unIdentifier, 2); + pBuff->Write(&uiValue, 4); } + EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); } @@ -658,18 +790,20 @@ E_CODEC_STATUS Http2Frame::EncodeSetting(CodecHttp2* pCodecH2, CBuffer* pBuff) { tagH2FrameHead stFrameHead; stFrameHead.cR = 0; - stFrameHead.ucType = H2_FRAME_RST_STREAM; + stFrameHead.ucType = H2_FRAME_SETTINGS; stFrameHead.ucFlag = 0; stFrameHead.uiStreamIdentifier = 0; stFrameHead.uiLength = 0; stFrameHead.ucFlag |= H2_FRAME_FLAG_ACK; EncodeFrameHeader(stFrameHead, pBuff); + EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); } E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiPromiseStreamId, - const std::string& strPadding, const HttpMsg& oHttpMsg, CBuffer* pBuff) + const std::string& strPadding, const HttpMsg& oHttpMsg, + bool bEndStream, CBuffer* pBuff) { if (uiStreamId == 0x0) { @@ -683,7 +817,14 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, stFrameHead.ucType = H2_FRAME_HEADERS; stFrameHead.ucFlag = 0; stFrameHead.uiStreamIdentifier = uiStreamId; - pCodecH2->PackHeader(oHttpMsg, &oBuffer); + if (bEndStream && oHttpMsg.body().size() > 0) + { + pCodecH2->PackHeader(oHttpMsg, true, &oBuffer); + } + else + { + pCodecH2->PackHeader(oHttpMsg, false, &oBuffer); + } uint32 uiAddtionLength = 0; if (strPadding.size() > 0) { @@ -694,25 +835,31 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, { stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); EncodeFrameHeader(stFrameHead, pBuff); - uint16 unNetLength = htons(strPadding.size()); + uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); // ignore R pBuff->Write(&uiPromiseStreamId, 4); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); pBuff->Write(strPadding.c_str(), strPadding.size()); oBuffer.AdvanceReadIndex(stFrameHead.uiLength - uiAddtionLength); - return(EncodeContinuation(pCodecH2, uiStreamId, &oBuffer, pBuff)); + EncodeSetStreamState(stFrameHead); + return(EncodeContinuation(pCodecH2, uiStreamId, bEndStream, &oBuffer, pBuff)); } else { stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; + if (bEndStream) + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; + } EncodeFrameHeader(stFrameHead, pBuff); - uint16 unNetLength = htons(strPadding.size()); + uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); // ignore R pBuff->Write(&uiPromiseStreamId, 4); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); pBuff->Write(strPadding.c_str(), strPadding.size()); + EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); } } @@ -728,15 +875,21 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, pBuff->Write(&uiPromiseStreamId, 4); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); oBuffer.AdvanceReadIndex(stFrameHead.uiLength - uiAddtionLength); - return(EncodeContinuation(pCodecH2, uiStreamId, &oBuffer, pBuff)); + EncodeSetStreamState(stFrameHead); + return(EncodeContinuation(pCodecH2, uiStreamId, bEndStream, &oBuffer, pBuff)); } else { stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; + if (bEndStream) + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; + } EncodeFrameHeader(stFrameHead, pBuff); // ignore R pBuff->Write(&uiPromiseStreamId, 4); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); + EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); } } @@ -758,6 +911,7 @@ E_CODEC_STATUS Http2Frame::EncodePing(CodecHttp2* pCodecH2, EncodeFrameHeader(stFrameHead, pBuff); pBuff->Write(&iPayload1, 4); pBuff->Write(&iPayload2, 4); + EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); } @@ -772,11 +926,12 @@ E_CODEC_STATUS Http2Frame::EncodeGoaway(CodecHttp2* pCodecH2, stFrameHead.uiLength = 8 + strDebugData.size(); EncodeFrameHeader(stFrameHead, pBuff); - uint32 uiNetStreamId = htonl(pCodecH2->GetLastStreamId()); - uint32 uiNetErrCode = htonl(iErrCode); + uint32 uiNetStreamId = CodecUtil::H2N(pCodecH2->GetLastStreamId()); + uint32 uiNetErrCode = CodecUtil::H2N((uint32)iErrCode); pBuff->Write(&uiNetStreamId, 4); pBuff->Write(&uiNetErrCode, 4); pBuff->Write(strDebugData.c_str(), strDebugData.size()); + EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); } @@ -794,12 +949,14 @@ E_CODEC_STATUS Http2Frame::EncodeWindowUpdate(CodecHttp2* pCodecH2, stFrameHead.uiStreamIdentifier = uiStreamId; stFrameHead.uiLength = 4; EncodeFrameHeader(stFrameHead, pBuff); + uiIncrement = CodecUtil::H2N(uiIncrement); pBuff->Write(&uiIncrement, 4); + EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); } E_CODEC_STATUS Http2Frame::EncodeContinuation(CodecHttp2* pCodecH2, - uint32 uiStreamId, CBuffer* pHpackBuff, CBuffer* pBuff) + uint32 uiStreamId, bool bEndStream, CBuffer* pHpackBuff, CBuffer* pBuff) { if (uiStreamId == 0x0) { @@ -821,13 +978,97 @@ E_CODEC_STATUS Http2Frame::EncodeContinuation(CodecHttp2* pCodecH2, { stFrameHead.uiLength = pHpackBuff->ReadableBytes(); stFrameHead.ucFlag |= H2_FRAME_FLAG_END_HEADERS; + if (bEndStream) + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; + } } EncodeFrameHeader(stFrameHead, pBuff); pBuff->Write(pHpackBuff->GetRawBuffer(), stFrameHead.uiLength); pHpackBuff->AdvanceReadIndex(stFrameHead.uiLength); } + EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); } +void Http2Frame::EncodePriority(const tagPriority& stPriority, CBuffer* pBuff) +{ + if (stPriority.uiDependency > 0) + { + uint32 uiPriority = stPriority.E; + uiPriority <<= 31; + uiPriority |= (CodecUtil::H2N(stPriority.uiDependency)); + pBuff->Write(&uiPriority, 4); + pBuff->Write(&stPriority.ucWeight, 1); + } +} + +void Http2Frame::EncodeSetStreamState(const tagH2FrameHead& stFrameHead) +{ + if (m_pStream != nullptr) + { + m_pStream->EncodeSetState(stFrameHead); + } +} + +E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, + const char* pData, uint32 uiDataLen, bool bEndStream, const std::string& strPadding, + uint32& uiEncodedDataLen, CBuffer* pBuff) +{ + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_PART_OK; + tagH2FrameHead stFrameHead; + stFrameHead.cR = 0; + stFrameHead.ucType = H2_FRAME_DATA; + stFrameHead.ucFlag = 0; + stFrameHead.uiStreamIdentifier = uiStreamId; + if (strPadding.size() > 0) + { + stFrameHead.uiLength = 1 + uiDataLen + strPadding.size(); + if (stFrameHead.uiLength > pCodecH2->GetMaxFrameSize()) + { + stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + uiEncodedDataLen = stFrameHead.uiLength - 1 - strPadding.size(); + } + else + { + uiEncodedDataLen = uiDataLen; + if (bEndStream) + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; + } + eCodecStatus = CODEC_STATUS_OK; + } + stFrameHead.ucFlag |= H2_FRAME_FLAG_PADDED; + EncodeFrameHeader(stFrameHead, pBuff); + uint16 unNetLength = CodecUtil::H2N(strPadding.size()); + pBuff->Write(&unNetLength, 1); + pBuff->Write(pData, uiEncodedDataLen); + pBuff->Write(strPadding.c_str(), strPadding.size()); + } + else + { + if (uiDataLen > pCodecH2->GetMaxFrameSize()) + { + stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + } + else + { + stFrameHead.uiLength = uiDataLen; + if (bEndStream) + { + stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; + } + eCodecStatus = CODEC_STATUS_OK; + } + uiEncodedDataLen = stFrameHead.uiLength; + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(pData, uiEncodedDataLen); + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucFlag = 0x%X, pBuff->ReadableBytes() = %u", + stFrameHead.uiLength, stFrameHead.ucFlag, pBuff->ReadableBytes()); + } + EncodeSetStreamState(stFrameHead); + return(eCodecStatus); +} + } /* namespace neb */ diff --git a/src/codec/http2/Http2Frame.hpp b/src/codec/http2/Http2Frame.hpp index d895ec2c..318b6d1d 100644 --- a/src/codec/http2/Http2Frame.hpp +++ b/src/codec/http2/Http2Frame.hpp @@ -48,9 +48,15 @@ class Http2Stream; class Http2Frame: public Codec { public: - Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, Http2Stream* pStream = nullptr); virtual ~Http2Frame(); + static void WriteMediumInt(uint32 uiValue, CBuffer* pBuff); + static void ReadMediumInt(CBuffer* pBuff, uint32& uiValue); + static void DecodeFrameHeader(CBuffer* pBuff, tagH2FrameHead& stFrameHead); + static void EncodeFrameHeader(const tagH2FrameHead& stFrameHead, CBuffer* pBuff); + static E_CODEC_STATUS EncodeSetting(const std::vector& vecSetting, std::string& strSettingFrame); + virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) { return(CODEC_STATUS_INVALID); @@ -61,7 +67,9 @@ class Http2Frame: public Codec } virtual E_CODEC_STATUS Encode(CodecHttp2* pCodecH2, - const HttpMsg& oHttpMsg, CBuffer* pBuff); + const HttpMsg& oHttpMsg, + const tagPriority& stPriority, + const std::string& strPadding, CBuffer* pBuff); virtual E_CODEC_STATUS Decode(CodecHttp2* pCodecH2, const tagH2FrameHead& stFrameHead, CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff); @@ -99,15 +107,13 @@ class Http2Frame: public Codec HttpMsg& oHttpMsg, CBuffer* pReactBuff); - void EncodeFrameHeader(const tagH2FrameHead& stFrameHead, CBuffer* pBuff); - void EncodePriority(const tagPriority& stPriority, CBuffer* pBuff); E_CODEC_STATUS EncodeData(CodecHttp2* pCodecH2, - uint32 uiStreamId, const HttpMsg& oHttpMsg, + uint32 uiStreamId, const HttpMsg& oHttpMsg, bool bEndStream, const std::string& strPadding, CBuffer* pBuff); E_CODEC_STATUS EncodeHeaders(CodecHttp2* pCodecH2, uint32 uiStreamId, const HttpMsg& oHttpMsg, const tagPriority& stPriority, const std::string& strPadding, - CBuffer* pBuff); + bool bEndStream, CBuffer* pBuff); E_CODEC_STATUS EncodePriority(CodecHttp2* pCodecH2, uint32 uiStreamId, const tagPriority& stPriority, CBuffer* pBuff); E_CODEC_STATUS EncodeRstStream(CodecHttp2* pCodecH2, @@ -117,7 +123,8 @@ class Http2Frame: public Codec E_CODEC_STATUS EncodeSetting(CodecHttp2* pCodecH2, CBuffer* pBuff); // ACK E_CODEC_STATUS EncodePushPromise(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiPromiseStreamId, - const std::string& strPadding, const HttpMsg& oHttpMsg, CBuffer* pBuff); + const std::string& strPadding, const HttpMsg& oHttpMsg, + bool bEndStream, CBuffer* pBuff); E_CODEC_STATUS EncodePing(CodecHttp2* pCodecH2, bool bAck, int32 iPayload1, int32 iPayload2, CBuffer* pBuff); E_CODEC_STATUS EncodeGoaway(CodecHttp2* pCodecH2, @@ -125,11 +132,20 @@ class Http2Frame: public Codec E_CODEC_STATUS EncodeWindowUpdate(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiIncrement, CBuffer* pBuff); E_CODEC_STATUS EncodeContinuation(CodecHttp2* pCodecH2, - uint32 uiStreamId, CBuffer* pHpackBuff, CBuffer* pBuff); + uint32 uiStreamId, bool bEndStream, CBuffer* pHpackBuff, CBuffer* pBuff); + +protected: + void EncodePriority(const tagPriority& stPriority, CBuffer* pBuff); + void EncodeSetStreamState(const tagH2FrameHead& stFrameHead); + E_CODEC_STATUS EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, + const char* pData, uint32 uiDataLen, bool bEndStream, + const std::string& strPadding, uint32& uiEncodedDataLen, CBuffer* pBuff); private: friend class CodecHttp2; friend class Http2Stream; + + Http2Stream* m_pStream; }; } /* namespace neb */ diff --git a/src/codec/http2/Http2Header.cpp b/src/codec/http2/Http2Header.cpp index 0c264ebd..129c1fef 100644 --- a/src/codec/http2/Http2Header.cpp +++ b/src/codec/http2/Http2Header.cpp @@ -224,7 +224,20 @@ void Http2Header::EncodeStringLiteral(const std::string& strLiteral, CBuffer* pB void Http2Header::EncodeStringLiteralWithHuffman(const std::string& strLiteral, CBuffer* pBuff) { - Huffman::Instance()->Encode(strLiteral, pBuff); + CBuffer oBuff; + Huffman::Instance()->Encode(strLiteral, &oBuff); + if (oBuff.ReadableBytes() < strLiteral.size()) + { + Http2Header::EncodeInt(oBuff.ReadableBytes(), (size_t)H2_HPACK_PREFIX_7_BITS, + (char)0x80, pBuff); + pBuff->Write(oBuff.GetRawReadBuffer(), oBuff.ReadableBytes()); + } + else + { + Http2Header::EncodeInt(strLiteral.size(), (size_t)H2_HPACK_PREFIX_7_BITS, + (char)0, pBuff); + pBuff->Write(strLiteral.c_str(), strLiteral.size()); + } } bool Http2Header::DecodeStringLiteral(CBuffer* pBuff, std::string& strLiteral, bool& bWithHuffman) diff --git a/src/codec/http2/Http2Header.hpp b/src/codec/http2/Http2Header.hpp index 9f9fe5b2..c9dc1206 100644 --- a/src/codec/http2/Http2Header.hpp +++ b/src/codec/http2/Http2Header.hpp @@ -40,6 +40,13 @@ enum E_H2_HPACK_PREFIX H2_HPACK_PREFIX_8_BITS = 0xFF, }; +enum E_H2_HEADER_TYPE +{ + H2_HEADER_PSEUDO = 0x01, + H2_HEADER_NORMAL = 0x02, + H2_HEADER_TRAILER = 0x04, +}; + class Http2Header { public: diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index 5807bf31..0d30baf3 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -14,26 +14,93 @@ namespace neb { -Http2Stream::Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) +Http2Stream::Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, uint32 uiStreamId) : Codec(pLogger, eCodecType), - m_eStreamState(H2_STREAM_IDLE), m_uiStreamId(0) + m_eStreamState(H2_STREAM_IDLE), m_uiStreamId(uiStreamId), m_bEndHeaders(false), m_pFrame(nullptr) { #if __cplusplus >= 201401L - m_pFrame = std::make_unique(pLogger, eCodecType); + m_pFrame = std::make_unique(pLogger, eCodecType, this); #else - m_pFrame = std::unique_ptr(new Http2Frame(pLogger, eCodecType)); + m_pFrame = std::unique_ptr(new Http2Frame(pLogger, eCodecType, this)); #endif } Http2Stream::~Http2Stream() { + LOG4_TRACE("codec type %d", GetCodecType()); } E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, const HttpMsg& oHttpMsg, CBuffer* pBuff) { - tagH2FrameHead stFrameHead; - m_pFrame->Encode(pCodecH2, oHttpMsg, pBuff); + tagPriority stPriority; + std::string strPadding; + if (HTTP_REQUEST == oHttpMsg.type()) + { + std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(':')); + if (strSchema.size() > 0) + { + auto pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); + pHeader->set_name(":scheme"); + pHeader->set_value(strSchema); + } + else + { + auto pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); + pHeader->set_name(":scheme"); + pHeader->set_value("http"); + } + struct http_parser_url stUrl; + if(0 == http_parser_parse_url(oHttpMsg.url().c_str(), oHttpMsg.url().length(), 0, &stUrl)) + { + std::string strAuthority; + if(stUrl.field_set & (1 << UF_USERINFO) ) + { + strAuthority += oHttpMsg.url().substr(stUrl.field_data[UF_USERINFO].off, stUrl.field_data[UF_USERINFO].len); + strAuthority += "@"; + } + if(stUrl.field_set & (1 << UF_HOST) ) + { + strAuthority += oHttpMsg.url().substr(stUrl.field_data[UF_HOST].off, stUrl.field_data[UF_HOST].len); + } + if(stUrl.field_set & (1 << UF_PORT)) + { + strAuthority += ":"; + strAuthority += oHttpMsg.url().substr(stUrl.field_data[UF_PORT].off, stUrl.field_data[UF_PORT].len); + } + auto pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); + pHeader->set_name(":authority"); + pHeader->set_value(strAuthority); + std::string strPath = "/"; + if(stUrl.field_set & (1 << UF_PATH)) + { + //strPath = oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off, stUrl.field_data[UF_PATH].len); + strPath = oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off); // including: ?param1=aaa¶m2=bbb + } + pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); + pHeader->set_name(":path"); + pHeader->set_value(strPath); + } + auto pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); + pHeader->set_name(":method"); + pHeader->set_value(http_method_str((http_method)oHttpMsg.method())); + (const_cast(oHttpMsg)).mutable_headers()->insert({"user-agent", "grpc-c++-nebula"}); + (const_cast(oHttpMsg)).mutable_headers()->insert({"grpc-accept-encoding", "gzip"}); + } + else + { + auto pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); + pHeader->set_name(":status"); + pHeader->set_value(std::to_string(oHttpMsg.status_code())); + } + return(m_pFrame->Encode(pCodecH2, oHttpMsg, stPriority, strPadding, pBuff)); +} + +E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, + const tagH2FrameHead& stFrameHead, CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + LOG4_TRACE("m_eStreamState = %d, stFrameHead.ucType = %u", m_eStreamState, stFrameHead.ucType); + m_oHttpMsg.set_stream_id(m_uiStreamId); switch (m_eStreamState) { case H2_STREAM_IDLE: @@ -41,30 +108,30 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, { case H2_FRAME_HEADERS: m_eStreamState = H2_STREAM_OPEN; - if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) - { - m_bEndHeaders = true; - } break; - case H2_FRAME_PRIORITY: + case H2_FRAME_PUSH_PROMISE: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_RESERVED_REMOTE); + m_eStreamState = H2_STREAM_RESERVED_REMOTE; break; default: pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); m_pFrame->EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an unspecific protocol error." " This error is for use when a more specific error " - "code is not available.", pBuff); + "code is not available.", pReactBuff); + LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", m_eStreamState, stFrameHead.ucType); return(CODEC_STATUS_ERR); } - if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) { - m_eStreamState = H2_STREAM_HALF_CLOSE_REMOTE; + m_bEndHeaders = true; } break; case H2_STREAM_RESERVED_LOCAL: switch (stFrameHead.ucType) { case H2_FRAME_RST_STREAM: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); m_eStreamState = H2_STREAM_CLOSE; break; default: @@ -75,6 +142,7 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, switch (stFrameHead.ucType) { case H2_FRAME_HEADERS: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_HALF_CLOSE_LOCAL); m_eStreamState = H2_STREAM_HALF_CLOSE_LOCAL; if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) { @@ -82,6 +150,7 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, } break; case H2_FRAME_RST_STREAM: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); m_eStreamState = H2_STREAM_CLOSE; break; default: @@ -92,20 +161,36 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, switch (stFrameHead.ucType) { case H2_FRAME_RST_STREAM: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); m_eStreamState = H2_STREAM_CLOSE; break; + case H2_FRAME_CONTINUATION: + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + m_bEndHeaders = true; + } + break; default: break; } if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) { + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_HALF_CLOSE_REMOTE); m_eStreamState = H2_STREAM_HALF_CLOSE_REMOTE; } break; case H2_STREAM_HALF_CLOSE_LOCAL: switch (stFrameHead.ucType) { + case H2_FRAME_HEADERS: + case H2_FRAME_CONTINUATION: + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + m_bEndHeaders = true; + } + break; case H2_FRAME_RST_STREAM: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); m_eStreamState = H2_STREAM_CLOSE; break; default: @@ -113,6 +198,7 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, } if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) { + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); m_eStreamState = H2_STREAM_CLOSE; } break; @@ -125,6 +211,7 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, switch (stFrameHead.ucType) { case H2_FRAME_RST_STREAM: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); m_eStreamState = H2_STREAM_CLOSE; break; case H2_FRAME_WINDOW_UPDATE: @@ -132,52 +219,113 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, break; default: m_pFrame->EncodeRstStream(pCodecH2, - stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pBuff); + stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); + LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", m_eStreamState, stFrameHead.ucType); return(CODEC_STATUS_PART_ERR); } break; case H2_STREAM_CLOSE: + switch (stFrameHead.ucType) + { + case H2_FRAME_PRIORITY: + break; + default: + m_pFrame->EncodeRstStream(pCodecH2, + stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); + LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", m_eStreamState, stFrameHead.ucType); + return(CODEC_STATUS_PART_ERR); + } break; default: break; } - return(CODEC_STATUS_OK); + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u, m_bEndHeaders = %d", m_eStreamState, stFrameHead.ucType, m_bEndHeaders); + if (m_bEndHeaders) + { + eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); + LOG4_TRACE("eStatus = %d", eStatus); + if (CODEC_STATUS_OK == eStatus || CODEC_STATUS_PART_OK == eStatus) + { + if (oHttpMsg.stream_id() & 0x01) + { + oHttpMsg.set_type(HTTP_REQUEST); + } + else + { + oHttpMsg.set_type(HTTP_RESPONSE); + } + if (pCodecH2->IsChunkNotice()) + { + oHttpMsg = std::move(m_oHttpMsg); + } + else + { + if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) + { + oHttpMsg = std::move(m_oHttpMsg); + eStatus = CODEC_STATUS_OK; + } + } + } + } + else + { + if (stFrameHead.ucType != H2_FRAME_CONTINUATION) + { + // If the END_HEADERS bit is not set, this frame MUST be followed by another CONTINUATION frame. + // A receiver MUST treat the receipt of any other type of frame or a frame on a different + // stream as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. + SetErrno(H2_ERR_PROTOCOL_ERROR); + m_pFrame->EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint " + "detected an unspecific protocol error. This error is for " + "use when a more specific error code is not available.", pReactBuff); + LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u, m_bEndHeaders = %d", m_eStreamState, stFrameHead.ucType, m_bEndHeaders); + return(CODEC_STATUS_ERR); + } + eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); + LOG4_TRACE("eStatus = %d", eStatus); + } + return(eStatus); } -E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, - const tagH2FrameHead& stFrameHead, CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) +void Http2Stream::EncodeSetState(const tagH2FrameHead& stFrameHead) { + LOG4_TRACE("stream %u m_eStreamState = %d, stFrameHead.ucType = %u", m_uiStreamId, m_eStreamState, stFrameHead.ucType); switch (m_eStreamState) { case H2_STREAM_IDLE: switch (stFrameHead.ucType) { case H2_FRAME_HEADERS: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_OPEN); m_eStreamState = H2_STREAM_OPEN; if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) { m_bEndHeaders = true; } break; - case H2_FRAME_PRIORITY: + case H2_FRAME_PUSH_PROMISE: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_RESERVED_LOCAL); + m_eStreamState = H2_STREAM_RESERVED_LOCAL; break; default: - pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); - m_pFrame->EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, - "The endpoint detected an unspecific protocol error." - " This error is for use when a more specific error " - "code is not available.", pReactBuff); - return(CODEC_STATUS_ERR); - } - if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) - { - m_eStreamState = H2_STREAM_HALF_CLOSE_REMOTE; + LOG4_ERROR("protocol error %d", H2_ERR_PROTOCOL_ERROR); } break; case H2_STREAM_RESERVED_LOCAL: switch (stFrameHead.ucType) { + case H2_FRAME_HEADERS: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_HALF_CLOSE_REMOTE); + m_eStreamState = H2_STREAM_HALF_CLOSE_REMOTE; + if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) + { + m_bEndHeaders = true; + } + break; case H2_FRAME_RST_STREAM: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); m_eStreamState = H2_STREAM_CLOSE; break; default: @@ -187,14 +335,8 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, case H2_STREAM_RESERVED_REMOTE: switch (stFrameHead.ucType) { - case H2_FRAME_HEADERS: - m_eStreamState = H2_STREAM_HALF_CLOSE_LOCAL; - if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) - { - m_bEndHeaders = true; - } - break; case H2_FRAME_RST_STREAM: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); m_eStreamState = H2_STREAM_CLOSE; break; default: @@ -205,42 +347,28 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, switch (stFrameHead.ucType) { case H2_FRAME_RST_STREAM: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); m_eStreamState = H2_STREAM_CLOSE; break; - case H2_FRAME_CONTINUATION: - if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) - { - m_bEndHeaders = true; - } - break; default: break; } if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) { + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_HALF_CLOSE_REMOTE); m_eStreamState = H2_STREAM_HALF_CLOSE_REMOTE; } break; case H2_STREAM_HALF_CLOSE_LOCAL: switch (stFrameHead.ucType) { - case H2_FRAME_HEADERS: - case H2_FRAME_CONTINUATION: - if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) - { - m_bEndHeaders = true; - } - break; case H2_FRAME_RST_STREAM: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); m_eStreamState = H2_STREAM_CLOSE; break; default: break; } - if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) - { - m_eStreamState = H2_STREAM_CLOSE; - } break; case H2_STREAM_HALF_CLOSE_REMOTE: /** @@ -251,64 +379,33 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, switch (stFrameHead.ucType) { case H2_FRAME_RST_STREAM: + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); m_eStreamState = H2_STREAM_CLOSE; break; - case H2_FRAME_WINDOW_UPDATE: - case H2_FRAME_PRIORITY: - break; default: - m_pFrame->EncodeRstStream(pCodecH2, - stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); - return(CODEC_STATUS_PART_ERR); + break; } - break; - case H2_STREAM_CLOSE: - switch (stFrameHead.ucType) + if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) { - case H2_FRAME_PRIORITY: - break; - default: - m_pFrame->EncodeRstStream(pCodecH2, - stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); - return(CODEC_STATUS_PART_ERR); + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_CLOSE); + m_eStreamState = H2_STREAM_CLOSE; } break; + case H2_STREAM_CLOSE: + break; default: break; } - E_CODEC_STATUS eStatus = CODEC_STATUS_OK; - if (m_bEndHeaders) - { - eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); - m_oHttpMsg.set_stream_id(m_uiStreamId); - if (pCodecH2->IsChunkNotice()) - { - oHttpMsg = std::move(m_oHttpMsg); - } - else - { - if (m_eStreamState == H2_STREAM_CLOSE) - { - oHttpMsg = std::move(m_oHttpMsg); - } - } - } - else - { - if (stFrameHead.ucType != H2_FRAME_CONTINUATION) - { - // If the END_HEADERS bit is not set, this frame MUST be followed by another CONTINUATION frame. - // A receiver MUST treat the receipt of any other type of frame or a frame on a different - // stream as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. - SetErrno(H2_ERR_PROTOCOL_ERROR); - m_pFrame->EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint " - "detected an unspecific protocol error. This error is for " - "use when a more specific error code is not available.", pReactBuff); - return(CODEC_STATUS_ERR); - } - eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); - } - return(eStatus); +} + +void Http2Stream::WindowInit(uint32 uiWindowSize) +{ + m_iSendWindowSize = (int32)uiWindowSize; +} + +void Http2Stream::WindowUpdate(int32 iIncrement) +{ + m_iSendWindowSize += iIncrement; } } /* namespace neb */ diff --git a/src/codec/http2/Http2Stream.hpp b/src/codec/http2/Http2Stream.hpp index 0b821603..7b01a7e7 100644 --- a/src/codec/http2/Http2Stream.hpp +++ b/src/codec/http2/Http2Stream.hpp @@ -38,7 +38,7 @@ class CodecHttp2; class Http2Stream: public Codec { public: - Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, uint32 m_uiStreamId); virtual ~Http2Stream(); virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) @@ -66,15 +66,27 @@ class Http2Stream: public Codec return(m_bEndHeaders); } + E_H2_STREAM_STATES GetStreamState() + { + return(m_eStreamState); + } + void SetState(E_H2_STREAM_STATES eStreamState) { m_eStreamState = eStreamState; } + void EncodeSetState(const tagH2FrameHead& stFrameHead); + + void WindowInit(uint32 uiWindowSize); + void WindowUpdate(int32 iIncrement); + private: E_H2_STREAM_STATES m_eStreamState; uint32 m_uiStreamId; - bool m_bEndHeaders = false; + int32 m_iSendWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + uint32 m_uiRecvWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + bool m_bEndHeaders; std::unique_ptr m_pFrame; HttpMsg m_oHttpMsg; }; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index a8c1da61..25efcd31 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -121,6 +121,7 @@ void Dispatcher::ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_tim bool Dispatcher::OnIoRead(std::shared_ptr pChannel) { LOG4_TRACE("fd[%d]", pChannel->m_pImpl->GetFd()); + m_pLastActivityChannel = pChannel; if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) { if (pChannel->m_pImpl->GetFd() == ((Manager*)m_pLabor)->GetManagerInfo().iS2SListenFd) @@ -162,6 +163,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) switch(pChannel->GetCodecType()) { case CODEC_HTTP: + case CODEC_HTTP2: for (int i = 0; ; ++i) { HttpMsg oHttpMsg; @@ -174,9 +176,20 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); } - if (CODEC_STATUS_OK == eCodecStatus) + if (CODEC_STATUS_OK == eCodecStatus + || CODEC_STATUS_PART_OK == eCodecStatus) { - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + if (oHttpMsg.http_major() > 1) + { + if (oHttpMsg.stream_id() > 0) + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + } + } + else + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + } } else if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close { @@ -274,6 +287,8 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) switch (eCodecStatus) { case CODEC_STATUS_PAUSE: + case CODEC_STATUS_PART_OK: + case CODEC_STATUS_PART_ERR: return(true); case CODEC_STATUS_WANT_WRITE: return(SendTo(pChannel)); @@ -314,12 +329,24 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) switch(pChannel->GetCodecType()) { case CODEC_HTTP: + case CODEC_HTTP2: { HttpMsg oHttpMsg; eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); - while (CODEC_STATUS_OK == eCodecStatus) + while (CODEC_STATUS_OK == eCodecStatus + || CODEC_STATUS_PART_OK == eCodecStatus) { - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + if (oHttpMsg.http_major() > 1) + { + if (oHttpMsg.stream_id() > 0) + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + } + } + else + { + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + } eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); } if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close @@ -390,6 +417,8 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) switch (eCodecStatus) { case CODEC_STATUS_PAUSE: + case CODEC_STATUS_PART_OK: + case CODEC_STATUS_PART_ERR: return(true); case CODEC_STATUS_WANT_WRITE: return(SendTo(pChannel)); @@ -847,6 +876,7 @@ bool Dispatcher::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) } } E_CODEC_STATUS eStatus = m_iterLoaderAndWorkerChannel->second->m_pImpl->Send(iCmd, uiSeq, oMsgBody); + m_pLastActivityChannel = m_iterLoaderAndWorkerChannel->second; if (CODEC_STATUS_OK == eStatus) { RemoveIoWriteEvent(m_iterLoaderAndWorkerChannel->second); @@ -879,6 +909,11 @@ bool Dispatcher::SendTo(int iFd, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBo return(false); } +std::shared_ptr Dispatcher::GetLastActivityChannel() +{ + return(m_pLastActivityChannel); +} + bool Dispatcher::Disconnect(std::shared_ptr pChannel, bool bChannelNotice) { return(DiscardSocketChannel(pChannel, bChannelNotice)); @@ -924,9 +959,9 @@ bool Dispatcher::DiscardNamedChannel(const std::string& strIdentify) } } -bool Dispatcher::SwitchCodec(std::shared_ptr pChannel, E_CODEC_TYPE eCodecType) +Codec* Dispatcher::SwitchCodec(std::shared_ptr pChannel, E_CODEC_TYPE eCodecType, bool bIsUpgrade) { - return(pChannel->m_pImpl->SwitchCodec(eCodecType, m_pLabor->GetNodeInfo().dIoTimeout)); + return(pChannel->m_pImpl->SwitchCodec(eCodecType, m_pLabor->GetNodeInfo().dIoTimeout, bIsUpgrade)); } bool Dispatcher::AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel) diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index b15c6aa7..f21f3621 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -140,10 +140,11 @@ class Dispatcher bool SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); bool SendTo(int iFd, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + std::shared_ptr GetLastActivityChannel(); bool Disconnect(std::shared_ptr pChannel, bool bChannelNotice = true); bool Disconnect(const std::string& strIdentify, bool bChannelNotice = true); bool DiscardNamedChannel(const std::string& strIdentify); - bool SwitchCodec(std::shared_ptr pChannel, E_CODEC_TYPE eCodecType); + Codec* SwitchCodec(std::shared_ptr pChannel, E_CODEC_TYPE eCodecType, bool bIsUpgrade = false); public: void SetChannelIdentify(std::shared_ptr pChannel, const std::string& strIdentify); @@ -196,6 +197,7 @@ class Dispatcher time_t m_lLastCheckNodeTime; std::shared_ptr m_pLogger; std::unique_ptr m_pSessionNode; + std::shared_ptr m_pLastActivityChannel; // 最近一个发送或接收过数据的channel // Channel std::unordered_map > m_mapSocketChannel; @@ -223,12 +225,14 @@ template bool Dispatcher::SendTo(std::shared_ptr pChannel, Targs&&... args) { E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(std::forward(args)...); + m_pLastActivityChannel = pChannel; switch (eStatus) { case CODEC_STATUS_OK: return(true); case CODEC_STATUS_PAUSE: case CODEC_STATUS_WANT_WRITE: + case CODEC_STATUS_PART_OK: AddIoWriteEvent(pChannel); return(true); case CODEC_STATUS_WANT_READ: @@ -426,6 +430,7 @@ bool Dispatcher::AutoSend( pChannel->m_pImpl->SetRemoteAddr(strHost); pChannel->m_pImpl->SetPipeline(bPipeline); E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(std::forward(args)...); + m_pLastActivityChannel = pChannel; if (CODEC_STATUS_OK != eCodecStatus && CODEC_STATUS_PAUSE != eCodecStatus && CODEC_STATUS_WANT_WRITE != eCodecStatus diff --git a/src/pb/http.pb.cc b/src/pb/http.pb.cc index ffc0360a..15474b4a 100644 --- a/src/pb/http.pb.cc +++ b/src/pb/http.pb.cc @@ -22,6 +22,9 @@ namespace { const ::google::protobuf::Descriptor* HttpMsg_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* HttpMsg_reflection_ = NULL; +const ::google::protobuf::Descriptor* HttpMsg_Header_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + HttpMsg_Header_reflection_ = NULL; const ::google::protobuf::Descriptor* HttpMsg_Upgrade_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* HttpMsg_Upgrade_reflection_ = NULL; @@ -40,7 +43,7 @@ void protobuf_AssignDesc_http_2eproto() { "http.proto"); GOOGLE_CHECK(file != NULL); HttpMsg_descriptor_ = file->message_type(0); - static const int HttpMsg_offsets_[28] = { + static const int HttpMsg_offsets_[30] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, type_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, http_major_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, http_minor_), @@ -58,6 +61,8 @@ void protobuf_AssignDesc_http_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, is_decoding_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, chunk_notice_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, stream_id_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, pseudo_header_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, trailer_header_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, hpack_data_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, adding_without_index_headers_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, deleting_without_index_headers_), @@ -81,7 +86,23 @@ void protobuf_AssignDesc_http_2eproto() { sizeof(HttpMsg), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, _internal_metadata_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg, _is_default_instance_)); - HttpMsg_Upgrade_descriptor_ = HttpMsg_descriptor_->nested_type(0); + HttpMsg_Header_descriptor_ = HttpMsg_descriptor_->nested_type(0); + static const int HttpMsg_Header_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Header, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Header, value_), + }; + HttpMsg_Header_reflection_ = + ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( + HttpMsg_Header_descriptor_, + HttpMsg_Header::default_instance_, + HttpMsg_Header_offsets_, + -1, + -1, + -1, + sizeof(HttpMsg_Header), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Header, _internal_metadata_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Header, _is_default_instance_)); + HttpMsg_Upgrade_descriptor_ = HttpMsg_descriptor_->nested_type(1); static const int HttpMsg_Upgrade_offsets_[2] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Upgrade, is_upgrade_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Upgrade, protocol_), @@ -97,9 +118,9 @@ void protobuf_AssignDesc_http_2eproto() { sizeof(HttpMsg_Upgrade), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Upgrade, _internal_metadata_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(HttpMsg_Upgrade, _is_default_instance_)); - HttpMsg_HeadersEntry_descriptor_ = HttpMsg_descriptor_->nested_type(1); - HttpMsg_ParamsEntry_descriptor_ = HttpMsg_descriptor_->nested_type(2); - HttpMsg_SettingsEntry_descriptor_ = HttpMsg_descriptor_->nested_type(3); + HttpMsg_HeadersEntry_descriptor_ = HttpMsg_descriptor_->nested_type(2); + HttpMsg_ParamsEntry_descriptor_ = HttpMsg_descriptor_->nested_type(3); + HttpMsg_SettingsEntry_descriptor_ = HttpMsg_descriptor_->nested_type(4); } namespace { @@ -115,6 +136,8 @@ void protobuf_RegisterTypes(const ::std::string&) { protobuf_AssignDescriptorsOnce(); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( HttpMsg_descriptor_, &HttpMsg::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + HttpMsg_Header_descriptor_, &HttpMsg_Header::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( HttpMsg_Upgrade_descriptor_, &HttpMsg_Upgrade::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( @@ -151,6 +174,8 @@ void protobuf_RegisterTypes(const ::std::string&) { void protobuf_ShutdownFile_http_2eproto() { delete HttpMsg::default_instance_; delete HttpMsg_reflection_; + delete HttpMsg_Header::default_instance_; + delete HttpMsg_Header_reflection_; delete HttpMsg_Upgrade::default_instance_; delete HttpMsg_Upgrade_reflection_; } @@ -163,7 +188,7 @@ void protobuf_AddDesc_http_2eproto() { GOOGLE_PROTOBUF_VERIFY_VERSION; ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( - "\n\nhttp.proto\"\251\007\n\007HttpMsg\022\014\n\004type\030\001 \001(\005\022\022" + "\n\nhttp.proto\"\241\010\n\007HttpMsg\022\014\n\004type\030\001 \001(\005\022\022" "\n\nhttp_major\030\002 \001(\005\022\022\n\nhttp_minor\030\003 \001(\005\022\026" "\n\016content_length\030\004 \001(\005\022\016\n\006method\030\005 \001(\005\022\023" "\n\013status_code\030\006 \001(\005\022\020\n\010encoding\030\007 \001(\005\022\013\n" @@ -172,26 +197,31 @@ void protobuf_AddDesc_http_2eproto() { "2\024.HttpMsg.ParamsEntry\022!\n\007upgrade\030\014 \001(\0132" "\020.HttpMsg.Upgrade\022\022\n\nkeep_alive\030\r \001(\002\022\014\n" "\004path\030\016 \001(\t\022\023\n\013is_decoding\030\017 \001(\010\022\024\n\014chun" - "k_notice\030\023 \001(\010\022\021\n\tstream_id\030\024 \001(\r\022\022\n\nhpa" - "ck_data\030\025 \001(\t\022$\n\034adding_without_index_he" - "aders\030\026 \003(\t\022&\n\036deleting_without_index_he" - "aders\030\027 \003(\t\022\"\n\032adding_never_index_header" - "s\030\030 \003(\t\022$\n\034deleting_never_index_headers\030" - "\031 \003(\t\022!\n\031dynamic_table_update_size\030\032 \001(\r" - "\022\024\n\014with_huffman\030\033 \001(\010\022\035\n\025headers_frame_" - "padding\030\034 \001(\t\022\032\n\022data_frame_padding\030\035 \001(" - "\t\022\"\n\032push_promise_frame_padding\030\036 \001(\t\022(\n" - "\010settings\030\037 \003(\0132\026.HttpMsg.SettingsEntry\032" + "k_notice\030\023 \001(\010\022\021\n\tstream_id\030\024 \001(\r\022&\n\rpse" + "udo_header\030\025 \003(\0132\017.HttpMsg.Header\022\'\n\016tra" + "iler_header\030\026 \003(\0132\017.HttpMsg.Header\022\022\n\nhp" + "ack_data\030\027 \001(\t\022$\n\034adding_without_index_h" + "eaders\030\030 \003(\t\022&\n\036deleting_without_index_h" + "eaders\030\031 \003(\t\022\"\n\032adding_never_index_heade" + "rs\030\032 \003(\t\022$\n\034deleting_never_index_headers" + "\030\033 \003(\t\022!\n\031dynamic_table_update_size\030\034 \001(" + "\r\022\024\n\014with_huffman\030\035 \001(\010\022\035\n\025headers_frame" + "_padding\030\036 \001(\t\022\032\n\022data_frame_padding\030\037 \001" + "(\t\022\"\n\032push_promise_frame_padding\030 \001(\t\022(" + "\n\010settings\030! \003(\0132\026.HttpMsg.SettingsEntry" + "\032%\n\006Header\022\014\n\004name\030\001 \001(\t\022\r\n\005value\030\002 \001(\t\032" "/\n\007Upgrade\022\022\n\nis_upgrade\030\001 \001(\010\022\020\n\010protoc" "ol\030\002 \001(\t\032.\n\014HeadersEntry\022\013\n\003key\030\001 \001(\t\022\r\n" "\005value\030\002 \001(\t:\0028\001\032-\n\013ParamsEntry\022\013\n\003key\030\001" " \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\032/\n\rSettingsEntry" - "\022\013\n\003key\030\001 \001(\r\022\r\n\005value\030\002 \001(\r:\0028\001b\006proto3", 960); + "\022\013\n\003key\030\001 \001(\r\022\r\n\005value\030\002 \001(\r:\0028\001b\006proto3", 1080); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "http.proto", &protobuf_RegisterTypes); HttpMsg::default_instance_ = new HttpMsg(); + HttpMsg_Header::default_instance_ = new HttpMsg_Header(); HttpMsg_Upgrade::default_instance_ = new HttpMsg_Upgrade(); HttpMsg::default_instance_->InitAsDefaultInstance(); + HttpMsg_Header::default_instance_->InitAsDefaultInstance(); HttpMsg_Upgrade::default_instance_->InitAsDefaultInstance(); ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_http_2eproto); } @@ -205,6 +235,296 @@ struct StaticDescriptorInitializer_http_2eproto { // =================================================================== +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +const int HttpMsg_Header::kNameFieldNumber; +const int HttpMsg_Header::kValueFieldNumber; +#endif // !defined(_MSC_VER) || _MSC_VER >= 1900 + +HttpMsg_Header::HttpMsg_Header() + : ::google::protobuf::Message(), _internal_metadata_(NULL) { + SharedCtor(); + // @@protoc_insertion_point(constructor:HttpMsg.Header) +} + +void HttpMsg_Header::InitAsDefaultInstance() { + _is_default_instance_ = true; +} + +HttpMsg_Header::HttpMsg_Header(const HttpMsg_Header& from) + : ::google::protobuf::Message(), + _internal_metadata_(NULL) { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:HttpMsg.Header) +} + +void HttpMsg_Header::SharedCtor() { + _is_default_instance_ = false; + ::google::protobuf::internal::GetEmptyString(); + _cached_size_ = 0; + name_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + +HttpMsg_Header::~HttpMsg_Header() { + // @@protoc_insertion_point(destructor:HttpMsg.Header) + SharedDtor(); +} + +void HttpMsg_Header::SharedDtor() { + name_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + if (this != default_instance_) { + } +} + +void HttpMsg_Header::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* HttpMsg_Header::descriptor() { + protobuf_AssignDescriptorsOnce(); + return HttpMsg_Header_descriptor_; +} + +const HttpMsg_Header& HttpMsg_Header::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_http_2eproto(); + return *default_instance_; +} + +HttpMsg_Header* HttpMsg_Header::default_instance_ = NULL; + +HttpMsg_Header* HttpMsg_Header::New(::google::protobuf::Arena* arena) const { + HttpMsg_Header* n = new HttpMsg_Header; + if (arena != NULL) { + arena->Own(n); + } + return n; +} + +void HttpMsg_Header::Clear() { +// @@protoc_insertion_point(message_clear_start:HttpMsg.Header) + name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + +bool HttpMsg_Header::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:HttpMsg.Header) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (tag == 10) { + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_name())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->name().data(), this->name().length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "HttpMsg.Header.name")); + } else { + goto handle_unusual; + } + if (input->ExpectTag(18)) goto parse_value; + break; + } + + // optional string value = 2; + case 2: { + if (tag == 18) { + parse_value: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_value())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->value().data(), this->value().length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "HttpMsg.Header.value")); + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag)); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:HttpMsg.Header) + return true; +failure: + // @@protoc_insertion_point(parse_failure:HttpMsg.Header) + return false; +#undef DO_ +} + +void HttpMsg_Header::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:HttpMsg.Header) + // optional string name = 1; + if (this->name().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->name().data(), this->name().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.Header.name"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 1, this->name(), output); + } + + // optional string value = 2; + if (this->value().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->value().data(), this->value().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.Header.value"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 2, this->value(), output); + } + + // @@protoc_insertion_point(serialize_end:HttpMsg.Header) +} + +::google::protobuf::uint8* HttpMsg_Header::InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:HttpMsg.Header) + // optional string name = 1; + if (this->name().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->name().data(), this->name().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.Header.name"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 1, this->name(), target); + } + + // optional string value = 2; + if (this->value().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->value().data(), this->value().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "HttpMsg.Header.value"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 2, this->value(), target); + } + + // @@protoc_insertion_point(serialize_to_array_end:HttpMsg.Header) + return target; +} + +int HttpMsg_Header::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:HttpMsg.Header) + int total_size = 0; + + // optional string name = 1; + if (this->name().size() > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->name()); + } + + // optional string value = 2; + if (this->value().size() > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->value()); + } + + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void HttpMsg_Header::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:HttpMsg.Header) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + const HttpMsg_Header* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); + if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:HttpMsg.Header) + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:HttpMsg.Header) + MergeFrom(*source); + } +} + +void HttpMsg_Header::MergeFrom(const HttpMsg_Header& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:HttpMsg.Header) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + if (from.name().size() > 0) { + + name_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.name_); + } + if (from.value().size() > 0) { + + value_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.value_); + } +} + +void HttpMsg_Header::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:HttpMsg.Header) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void HttpMsg_Header::CopyFrom(const HttpMsg_Header& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:HttpMsg.Header) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool HttpMsg_Header::IsInitialized() const { + + return true; +} + +void HttpMsg_Header::Swap(HttpMsg_Header* other) { + if (other == this) return; + InternalSwap(other); +} +void HttpMsg_Header::InternalSwap(HttpMsg_Header* other) { + name_.Swap(&other->name_); + value_.Swap(&other->value_); + _internal_metadata_.Swap(&other->_internal_metadata_); + std::swap(_cached_size_, other->_cached_size_); +} + +::google::protobuf::Metadata HttpMsg_Header::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = HttpMsg_Header_descriptor_; + metadata.reflection = HttpMsg_Header_reflection_; + return metadata; +} + + +// ------------------------------------------------------------------- + #if !defined(_MSC_VER) || _MSC_VER >= 1900 const int HttpMsg_Upgrade::kIsUpgradeFieldNumber; const int HttpMsg_Upgrade::kProtocolFieldNumber; @@ -496,6 +816,8 @@ const int HttpMsg::kPathFieldNumber; const int HttpMsg::kIsDecodingFieldNumber; const int HttpMsg::kChunkNoticeFieldNumber; const int HttpMsg::kStreamIdFieldNumber; +const int HttpMsg::kPseudoHeaderFieldNumber; +const int HttpMsg::kTrailerHeaderFieldNumber; const int HttpMsg::kHpackDataFieldNumber; const int HttpMsg::kAddingWithoutIndexHeadersFieldNumber; const int HttpMsg::kDeletingWithoutIndexHeadersFieldNumber; @@ -650,6 +972,8 @@ void HttpMsg::Clear() { headers_.Clear(); params_.Clear(); + pseudo_header_.Clear(); + trailer_header_.Clear(); adding_without_index_headers_.Clear(); deleting_without_index_headers_.Clear(); adding_never_index_headers_.Clear(); @@ -949,13 +1273,46 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(170)) goto parse_hpack_data; + if (input->ExpectTag(170)) goto parse_pseudo_header; break; } - // optional string hpack_data = 21; + // repeated .HttpMsg.Header pseudo_header = 21; case 21: { if (tag == 170) { + parse_pseudo_header: + DO_(input->IncrementRecursionDepth()); + parse_loop_pseudo_header: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( + input, add_pseudo_header())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(170)) goto parse_loop_pseudo_header; + if (input->ExpectTag(178)) goto parse_loop_trailer_header; + input->UnsafeDecrementRecursionDepth(); + break; + } + + // repeated .HttpMsg.Header trailer_header = 22; + case 22: { + if (tag == 178) { + DO_(input->IncrementRecursionDepth()); + parse_loop_trailer_header: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( + input, add_trailer_header())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(178)) goto parse_loop_trailer_header; + input->UnsafeDecrementRecursionDepth(); + if (input->ExpectTag(186)) goto parse_hpack_data; + break; + } + + // optional string hpack_data = 23; + case 23: { + if (tag == 186) { parse_hpack_data: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->mutable_hpack_data())); @@ -966,13 +1323,13 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(178)) goto parse_adding_without_index_headers; + if (input->ExpectTag(194)) goto parse_adding_without_index_headers; break; } - // repeated string adding_without_index_headers = 22; - case 22: { - if (tag == 178) { + // repeated string adding_without_index_headers = 24; + case 24: { + if (tag == 194) { parse_adding_without_index_headers: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->add_adding_without_index_headers())); @@ -984,14 +1341,14 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(178)) goto parse_adding_without_index_headers; - if (input->ExpectTag(186)) goto parse_deleting_without_index_headers; + if (input->ExpectTag(194)) goto parse_adding_without_index_headers; + if (input->ExpectTag(202)) goto parse_deleting_without_index_headers; break; } - // repeated string deleting_without_index_headers = 23; - case 23: { - if (tag == 186) { + // repeated string deleting_without_index_headers = 25; + case 25: { + if (tag == 202) { parse_deleting_without_index_headers: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->add_deleting_without_index_headers())); @@ -1003,14 +1360,14 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(186)) goto parse_deleting_without_index_headers; - if (input->ExpectTag(194)) goto parse_adding_never_index_headers; + if (input->ExpectTag(202)) goto parse_deleting_without_index_headers; + if (input->ExpectTag(210)) goto parse_adding_never_index_headers; break; } - // repeated string adding_never_index_headers = 24; - case 24: { - if (tag == 194) { + // repeated string adding_never_index_headers = 26; + case 26: { + if (tag == 210) { parse_adding_never_index_headers: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->add_adding_never_index_headers())); @@ -1022,14 +1379,14 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(194)) goto parse_adding_never_index_headers; - if (input->ExpectTag(202)) goto parse_deleting_never_index_headers; + if (input->ExpectTag(210)) goto parse_adding_never_index_headers; + if (input->ExpectTag(218)) goto parse_deleting_never_index_headers; break; } - // repeated string deleting_never_index_headers = 25; - case 25: { - if (tag == 202) { + // repeated string deleting_never_index_headers = 27; + case 27: { + if (tag == 218) { parse_deleting_never_index_headers: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->add_deleting_never_index_headers())); @@ -1041,14 +1398,14 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(202)) goto parse_deleting_never_index_headers; - if (input->ExpectTag(208)) goto parse_dynamic_table_update_size; + if (input->ExpectTag(218)) goto parse_deleting_never_index_headers; + if (input->ExpectTag(224)) goto parse_dynamic_table_update_size; break; } - // optional uint32 dynamic_table_update_size = 26; - case 26: { - if (tag == 208) { + // optional uint32 dynamic_table_update_size = 28; + case 28: { + if (tag == 224) { parse_dynamic_table_update_size: DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( @@ -1057,13 +1414,13 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(216)) goto parse_with_huffman; + if (input->ExpectTag(232)) goto parse_with_huffman; break; } - // optional bool with_huffman = 27; - case 27: { - if (tag == 216) { + // optional bool with_huffman = 29; + case 29: { + if (tag == 232) { parse_with_huffman: DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( @@ -1072,13 +1429,13 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(226)) goto parse_headers_frame_padding; + if (input->ExpectTag(242)) goto parse_headers_frame_padding; break; } - // optional string headers_frame_padding = 28; - case 28: { - if (tag == 226) { + // optional string headers_frame_padding = 30; + case 30: { + if (tag == 242) { parse_headers_frame_padding: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->mutable_headers_frame_padding())); @@ -1089,13 +1446,13 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(234)) goto parse_data_frame_padding; + if (input->ExpectTag(250)) goto parse_data_frame_padding; break; } - // optional string data_frame_padding = 29; - case 29: { - if (tag == 234) { + // optional string data_frame_padding = 31; + case 31: { + if (tag == 250) { parse_data_frame_padding: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->mutable_data_frame_padding())); @@ -1106,13 +1463,13 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(242)) goto parse_push_promise_frame_padding; + if (input->ExpectTag(258)) goto parse_push_promise_frame_padding; break; } - // optional string push_promise_frame_padding = 30; - case 30: { - if (tag == 242) { + // optional string push_promise_frame_padding = 32; + case 32: { + if (tag == 258) { parse_push_promise_frame_padding: DO_(::google::protobuf::internal::WireFormatLite::ReadString( input, this->mutable_push_promise_frame_padding())); @@ -1123,13 +1480,13 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(250)) goto parse_settings; + if (input->ExpectTag(266)) goto parse_settings; break; } - // map settings = 31; - case 31: { - if (tag == 250) { + // map settings = 33; + case 33: { + if (tag == 266) { parse_settings: DO_(input->IncrementRecursionDepth()); parse_loop_settings: @@ -1144,7 +1501,7 @@ bool HttpMsg::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(250)) goto parse_loop_settings; + if (input->ExpectTag(266)) goto parse_loop_settings; input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; @@ -1367,97 +1724,109 @@ void HttpMsg::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteUInt32(20, this->stream_id(), output); } - // optional string hpack_data = 21; + // repeated .HttpMsg.Header pseudo_header = 21; + for (unsigned int i = 0, n = this->pseudo_header_size(); i < n; i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 21, this->pseudo_header(i), output); + } + + // repeated .HttpMsg.Header trailer_header = 22; + for (unsigned int i = 0, n = this->trailer_header_size(); i < n; i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 22, this->trailer_header(i), output); + } + + // optional string hpack_data = 23; if (this->hpack_data().size() > 0) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->hpack_data().data(), this->hpack_data().length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.hpack_data"); ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( - 21, this->hpack_data(), output); + 23, this->hpack_data(), output); } - // repeated string adding_without_index_headers = 22; + // repeated string adding_without_index_headers = 24; for (int i = 0; i < this->adding_without_index_headers_size(); i++) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->adding_without_index_headers(i).data(), this->adding_without_index_headers(i).length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.adding_without_index_headers"); ::google::protobuf::internal::WireFormatLite::WriteString( - 22, this->adding_without_index_headers(i), output); + 24, this->adding_without_index_headers(i), output); } - // repeated string deleting_without_index_headers = 23; + // repeated string deleting_without_index_headers = 25; for (int i = 0; i < this->deleting_without_index_headers_size(); i++) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->deleting_without_index_headers(i).data(), this->deleting_without_index_headers(i).length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.deleting_without_index_headers"); ::google::protobuf::internal::WireFormatLite::WriteString( - 23, this->deleting_without_index_headers(i), output); + 25, this->deleting_without_index_headers(i), output); } - // repeated string adding_never_index_headers = 24; + // repeated string adding_never_index_headers = 26; for (int i = 0; i < this->adding_never_index_headers_size(); i++) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->adding_never_index_headers(i).data(), this->adding_never_index_headers(i).length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.adding_never_index_headers"); ::google::protobuf::internal::WireFormatLite::WriteString( - 24, this->adding_never_index_headers(i), output); + 26, this->adding_never_index_headers(i), output); } - // repeated string deleting_never_index_headers = 25; + // repeated string deleting_never_index_headers = 27; for (int i = 0; i < this->deleting_never_index_headers_size(); i++) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->deleting_never_index_headers(i).data(), this->deleting_never_index_headers(i).length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.deleting_never_index_headers"); ::google::protobuf::internal::WireFormatLite::WriteString( - 25, this->deleting_never_index_headers(i), output); + 27, this->deleting_never_index_headers(i), output); } - // optional uint32 dynamic_table_update_size = 26; + // optional uint32 dynamic_table_update_size = 28; if (this->dynamic_table_update_size() != 0) { - ::google::protobuf::internal::WireFormatLite::WriteUInt32(26, this->dynamic_table_update_size(), output); + ::google::protobuf::internal::WireFormatLite::WriteUInt32(28, this->dynamic_table_update_size(), output); } - // optional bool with_huffman = 27; + // optional bool with_huffman = 29; if (this->with_huffman() != 0) { - ::google::protobuf::internal::WireFormatLite::WriteBool(27, this->with_huffman(), output); + ::google::protobuf::internal::WireFormatLite::WriteBool(29, this->with_huffman(), output); } - // optional string headers_frame_padding = 28; + // optional string headers_frame_padding = 30; if (this->headers_frame_padding().size() > 0) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->headers_frame_padding().data(), this->headers_frame_padding().length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.headers_frame_padding"); ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( - 28, this->headers_frame_padding(), output); + 30, this->headers_frame_padding(), output); } - // optional string data_frame_padding = 29; + // optional string data_frame_padding = 31; if (this->data_frame_padding().size() > 0) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->data_frame_padding().data(), this->data_frame_padding().length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.data_frame_padding"); ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( - 29, this->data_frame_padding(), output); + 31, this->data_frame_padding(), output); } - // optional string push_promise_frame_padding = 30; + // optional string push_promise_frame_padding = 32; if (this->push_promise_frame_padding().size() > 0) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->push_promise_frame_padding().data(), this->push_promise_frame_padding().length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.push_promise_frame_padding"); ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( - 30, this->push_promise_frame_padding(), output); + 32, this->push_promise_frame_padding(), output); } - // map settings = 31; + // map settings = 33; if (!this->settings().empty()) { typedef ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >::const_pointer ConstPtr; @@ -1481,7 +1850,7 @@ void HttpMsg::SerializeWithCachedSizes( entry.reset(settings_.NewEntryWrapper( items[i].second->first, items[i].second->second)); ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( - 31, *entry, output); + 33, *entry, output); } } else { ::google::protobuf::scoped_ptr entry; @@ -1491,7 +1860,7 @@ void HttpMsg::SerializeWithCachedSizes( entry.reset(settings_.NewEntryWrapper( it->first, it->second)); ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( - 31, *entry, output); + 33, *entry, output); } } } @@ -1707,7 +2076,21 @@ ::google::protobuf::uint8* HttpMsg::InternalSerializeWithCachedSizesToArray( target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(20, this->stream_id(), target); } - // optional string hpack_data = 21; + // repeated .HttpMsg.Header pseudo_header = 21; + for (unsigned int i = 0, n = this->pseudo_header_size(); i < n; i++) { + target = ::google::protobuf::internal::WireFormatLite:: + InternalWriteMessageNoVirtualToArray( + 21, this->pseudo_header(i), false, target); + } + + // repeated .HttpMsg.Header trailer_header = 22; + for (unsigned int i = 0, n = this->trailer_header_size(); i < n; i++) { + target = ::google::protobuf::internal::WireFormatLite:: + InternalWriteMessageNoVirtualToArray( + 22, this->trailer_header(i), false, target); + } + + // optional string hpack_data = 23; if (this->hpack_data().size() > 0) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->hpack_data().data(), this->hpack_data().length(), @@ -1715,60 +2098,60 @@ ::google::protobuf::uint8* HttpMsg::InternalSerializeWithCachedSizesToArray( "HttpMsg.hpack_data"); target = ::google::protobuf::internal::WireFormatLite::WriteStringToArray( - 21, this->hpack_data(), target); + 23, this->hpack_data(), target); } - // repeated string adding_without_index_headers = 22; + // repeated string adding_without_index_headers = 24; for (int i = 0; i < this->adding_without_index_headers_size(); i++) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->adding_without_index_headers(i).data(), this->adding_without_index_headers(i).length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.adding_without_index_headers"); target = ::google::protobuf::internal::WireFormatLite:: - WriteStringToArray(22, this->adding_without_index_headers(i), target); + WriteStringToArray(24, this->adding_without_index_headers(i), target); } - // repeated string deleting_without_index_headers = 23; + // repeated string deleting_without_index_headers = 25; for (int i = 0; i < this->deleting_without_index_headers_size(); i++) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->deleting_without_index_headers(i).data(), this->deleting_without_index_headers(i).length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.deleting_without_index_headers"); target = ::google::protobuf::internal::WireFormatLite:: - WriteStringToArray(23, this->deleting_without_index_headers(i), target); + WriteStringToArray(25, this->deleting_without_index_headers(i), target); } - // repeated string adding_never_index_headers = 24; + // repeated string adding_never_index_headers = 26; for (int i = 0; i < this->adding_never_index_headers_size(); i++) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->adding_never_index_headers(i).data(), this->adding_never_index_headers(i).length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.adding_never_index_headers"); target = ::google::protobuf::internal::WireFormatLite:: - WriteStringToArray(24, this->adding_never_index_headers(i), target); + WriteStringToArray(26, this->adding_never_index_headers(i), target); } - // repeated string deleting_never_index_headers = 25; + // repeated string deleting_never_index_headers = 27; for (int i = 0; i < this->deleting_never_index_headers_size(); i++) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->deleting_never_index_headers(i).data(), this->deleting_never_index_headers(i).length(), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "HttpMsg.deleting_never_index_headers"); target = ::google::protobuf::internal::WireFormatLite:: - WriteStringToArray(25, this->deleting_never_index_headers(i), target); + WriteStringToArray(27, this->deleting_never_index_headers(i), target); } - // optional uint32 dynamic_table_update_size = 26; + // optional uint32 dynamic_table_update_size = 28; if (this->dynamic_table_update_size() != 0) { - target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(26, this->dynamic_table_update_size(), target); + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(28, this->dynamic_table_update_size(), target); } - // optional bool with_huffman = 27; + // optional bool with_huffman = 29; if (this->with_huffman() != 0) { - target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(27, this->with_huffman(), target); + target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(29, this->with_huffman(), target); } - // optional string headers_frame_padding = 28; + // optional string headers_frame_padding = 30; if (this->headers_frame_padding().size() > 0) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->headers_frame_padding().data(), this->headers_frame_padding().length(), @@ -1776,10 +2159,10 @@ ::google::protobuf::uint8* HttpMsg::InternalSerializeWithCachedSizesToArray( "HttpMsg.headers_frame_padding"); target = ::google::protobuf::internal::WireFormatLite::WriteStringToArray( - 28, this->headers_frame_padding(), target); + 30, this->headers_frame_padding(), target); } - // optional string data_frame_padding = 29; + // optional string data_frame_padding = 31; if (this->data_frame_padding().size() > 0) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->data_frame_padding().data(), this->data_frame_padding().length(), @@ -1787,10 +2170,10 @@ ::google::protobuf::uint8* HttpMsg::InternalSerializeWithCachedSizesToArray( "HttpMsg.data_frame_padding"); target = ::google::protobuf::internal::WireFormatLite::WriteStringToArray( - 29, this->data_frame_padding(), target); + 31, this->data_frame_padding(), target); } - // optional string push_promise_frame_padding = 30; + // optional string push_promise_frame_padding = 32; if (this->push_promise_frame_padding().size() > 0) { ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( this->push_promise_frame_padding().data(), this->push_promise_frame_padding().length(), @@ -1798,10 +2181,10 @@ ::google::protobuf::uint8* HttpMsg::InternalSerializeWithCachedSizesToArray( "HttpMsg.push_promise_frame_padding"); target = ::google::protobuf::internal::WireFormatLite::WriteStringToArray( - 30, this->push_promise_frame_padding(), target); + 32, this->push_promise_frame_padding(), target); } - // map settings = 31; + // map settings = 33; if (!this->settings().empty()) { typedef ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >::const_pointer ConstPtr; @@ -1826,7 +2209,7 @@ ::google::protobuf::uint8* HttpMsg::InternalSerializeWithCachedSizesToArray( items[i].second->first, items[i].second->second)); target = ::google::protobuf::internal::WireFormatLite:: InternalWriteMessageNoVirtualToArray( - 31, *entry, deterministic, target); + 33, *entry, deterministic, target); ; } } else { @@ -1838,7 +2221,7 @@ ::google::protobuf::uint8* HttpMsg::InternalSerializeWithCachedSizesToArray( it->first, it->second)); target = ::google::protobuf::internal::WireFormatLite:: InternalWriteMessageNoVirtualToArray( - 31, *entry, deterministic, target); + 33, *entry, deterministic, target); ; } } @@ -1951,40 +2334,40 @@ int HttpMsg::ByteSize() const { this->stream_id()); } - // optional string hpack_data = 21; + // optional string hpack_data = 23; if (this->hpack_data().size() > 0) { total_size += 2 + ::google::protobuf::internal::WireFormatLite::StringSize( this->hpack_data()); } - // optional uint32 dynamic_table_update_size = 26; + // optional uint32 dynamic_table_update_size = 28; if (this->dynamic_table_update_size() != 0) { total_size += 2 + ::google::protobuf::internal::WireFormatLite::UInt32Size( this->dynamic_table_update_size()); } - // optional bool with_huffman = 27; + // optional bool with_huffman = 29; if (this->with_huffman() != 0) { total_size += 2 + 1; } - // optional string headers_frame_padding = 28; + // optional string headers_frame_padding = 30; if (this->headers_frame_padding().size() > 0) { total_size += 2 + ::google::protobuf::internal::WireFormatLite::StringSize( this->headers_frame_padding()); } - // optional string data_frame_padding = 29; + // optional string data_frame_padding = 31; if (this->data_frame_padding().size() > 0) { total_size += 2 + ::google::protobuf::internal::WireFormatLite::StringSize( this->data_frame_padding()); } - // optional string push_promise_frame_padding = 30; + // optional string push_promise_frame_padding = 32; if (this->push_promise_frame_padding().size() > 0) { total_size += 2 + ::google::protobuf::internal::WireFormatLite::StringSize( @@ -2017,35 +2400,51 @@ int HttpMsg::ByteSize() const { } } - // repeated string adding_without_index_headers = 22; + // repeated .HttpMsg.Header pseudo_header = 21; + total_size += 2 * this->pseudo_header_size(); + for (int i = 0; i < this->pseudo_header_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->pseudo_header(i)); + } + + // repeated .HttpMsg.Header trailer_header = 22; + total_size += 2 * this->trailer_header_size(); + for (int i = 0; i < this->trailer_header_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->trailer_header(i)); + } + + // repeated string adding_without_index_headers = 24; total_size += 2 * this->adding_without_index_headers_size(); for (int i = 0; i < this->adding_without_index_headers_size(); i++) { total_size += ::google::protobuf::internal::WireFormatLite::StringSize( this->adding_without_index_headers(i)); } - // repeated string deleting_without_index_headers = 23; + // repeated string deleting_without_index_headers = 25; total_size += 2 * this->deleting_without_index_headers_size(); for (int i = 0; i < this->deleting_without_index_headers_size(); i++) { total_size += ::google::protobuf::internal::WireFormatLite::StringSize( this->deleting_without_index_headers(i)); } - // repeated string adding_never_index_headers = 24; + // repeated string adding_never_index_headers = 26; total_size += 2 * this->adding_never_index_headers_size(); for (int i = 0; i < this->adding_never_index_headers_size(); i++) { total_size += ::google::protobuf::internal::WireFormatLite::StringSize( this->adding_never_index_headers(i)); } - // repeated string deleting_never_index_headers = 25; + // repeated string deleting_never_index_headers = 27; total_size += 2 * this->deleting_never_index_headers_size(); for (int i = 0; i < this->deleting_never_index_headers_size(); i++) { total_size += ::google::protobuf::internal::WireFormatLite::StringSize( this->deleting_never_index_headers(i)); } - // map settings = 31; + // map settings = 33; total_size += 2 * this->settings_size(); { ::google::protobuf::scoped_ptr entry; @@ -2088,6 +2487,8 @@ void HttpMsg::MergeFrom(const HttpMsg& from) { } headers_.MergeFrom(from.headers_); params_.MergeFrom(from.params_); + pseudo_header_.MergeFrom(from.pseudo_header_); + trailer_header_.MergeFrom(from.trailer_header_); adding_without_index_headers_.MergeFrom(from.adding_without_index_headers_); deleting_without_index_headers_.MergeFrom(from.deleting_without_index_headers_); adding_never_index_headers_.MergeFrom(from.adding_never_index_headers_); @@ -2206,6 +2607,8 @@ void HttpMsg::InternalSwap(HttpMsg* other) { std::swap(is_decoding_, other->is_decoding_); std::swap(chunk_notice_, other->chunk_notice_); std::swap(stream_id_, other->stream_id_); + pseudo_header_.UnsafeArenaSwap(&other->pseudo_header_); + trailer_header_.UnsafeArenaSwap(&other->trailer_header_); hpack_data_.Swap(&other->hpack_data_); adding_without_index_headers_.UnsafeArenaSwap(&other->adding_without_index_headers_); deleting_without_index_headers_.UnsafeArenaSwap(&other->deleting_without_index_headers_); @@ -2230,6 +2633,98 @@ ::google::protobuf::Metadata HttpMsg::GetMetadata() const { } #if PROTOBUF_INLINE_NOT_IN_HEADERS +// HttpMsg_Header + +// optional string name = 1; +void HttpMsg_Header::clear_name() { + name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& HttpMsg_Header::name() const { + // @@protoc_insertion_point(field_get:HttpMsg.Header.name) + return name_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg_Header::set_name(const ::std::string& value) { + + name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.Header.name) +} + void HttpMsg_Header::set_name(const char* value) { + + name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.Header.name) +} + void HttpMsg_Header::set_name(const char* value, size_t size) { + + name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.Header.name) +} + ::std::string* HttpMsg_Header::mutable_name() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.Header.name) + return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* HttpMsg_Header::release_name() { + // @@protoc_insertion_point(field_release:HttpMsg.Header.name) + + return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg_Header::set_allocated_name(::std::string* name) { + if (name != NULL) { + + } else { + + } + name_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), name); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.Header.name) +} + +// optional string value = 2; +void HttpMsg_Header::clear_value() { + value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& HttpMsg_Header::value() const { + // @@protoc_insertion_point(field_get:HttpMsg.Header.value) + return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg_Header::set_value(const ::std::string& value) { + + value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.Header.value) +} + void HttpMsg_Header::set_value(const char* value) { + + value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.Header.value) +} + void HttpMsg_Header::set_value(const char* value, size_t size) { + + value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.Header.value) +} + ::std::string* HttpMsg_Header::mutable_value() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.Header.value) + return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* HttpMsg_Header::release_value() { + // @@protoc_insertion_point(field_release:HttpMsg.Header.value) + + return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void HttpMsg_Header::set_allocated_value(::std::string* value) { + if (value != NULL) { + + } else { + + } + value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.Header.value) +} + +// ------------------------------------------------------------------- + // HttpMsg_Upgrade // optional bool is_upgrade = 1; @@ -2654,7 +3149,67 @@ void HttpMsg::clear_stream_id() { // @@protoc_insertion_point(field_set:HttpMsg.stream_id) } -// optional string hpack_data = 21; +// repeated .HttpMsg.Header pseudo_header = 21; +int HttpMsg::pseudo_header_size() const { + return pseudo_header_.size(); +} +void HttpMsg::clear_pseudo_header() { + pseudo_header_.Clear(); +} +const ::HttpMsg_Header& HttpMsg::pseudo_header(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.pseudo_header) + return pseudo_header_.Get(index); +} +::HttpMsg_Header* HttpMsg::mutable_pseudo_header(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.pseudo_header) + return pseudo_header_.Mutable(index); +} +::HttpMsg_Header* HttpMsg::add_pseudo_header() { + // @@protoc_insertion_point(field_add:HttpMsg.pseudo_header) + return pseudo_header_.Add(); +} +::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >* +HttpMsg::mutable_pseudo_header() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.pseudo_header) + return &pseudo_header_; +} +const ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >& +HttpMsg::pseudo_header() const { + // @@protoc_insertion_point(field_list:HttpMsg.pseudo_header) + return pseudo_header_; +} + +// repeated .HttpMsg.Header trailer_header = 22; +int HttpMsg::trailer_header_size() const { + return trailer_header_.size(); +} +void HttpMsg::clear_trailer_header() { + trailer_header_.Clear(); +} +const ::HttpMsg_Header& HttpMsg::trailer_header(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.trailer_header) + return trailer_header_.Get(index); +} +::HttpMsg_Header* HttpMsg::mutable_trailer_header(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.trailer_header) + return trailer_header_.Mutable(index); +} +::HttpMsg_Header* HttpMsg::add_trailer_header() { + // @@protoc_insertion_point(field_add:HttpMsg.trailer_header) + return trailer_header_.Add(); +} +::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >* +HttpMsg::mutable_trailer_header() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.trailer_header) + return &trailer_header_; +} +const ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >& +HttpMsg::trailer_header() const { + // @@protoc_insertion_point(field_list:HttpMsg.trailer_header) + return trailer_header_; +} + +// optional string hpack_data = 23; void HttpMsg::clear_hpack_data() { hpack_data_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -2698,7 +3253,7 @@ void HttpMsg::clear_hpack_data() { // @@protoc_insertion_point(field_set_allocated:HttpMsg.hpack_data) } -// repeated string adding_without_index_headers = 22; +// repeated string adding_without_index_headers = 24; int HttpMsg::adding_without_index_headers_size() const { return adding_without_index_headers_.size(); } @@ -2753,7 +3308,7 @@ HttpMsg::mutable_adding_without_index_headers() { return &adding_without_index_headers_; } -// repeated string deleting_without_index_headers = 23; +// repeated string deleting_without_index_headers = 25; int HttpMsg::deleting_without_index_headers_size() const { return deleting_without_index_headers_.size(); } @@ -2808,7 +3363,7 @@ HttpMsg::mutable_deleting_without_index_headers() { return &deleting_without_index_headers_; } -// repeated string adding_never_index_headers = 24; +// repeated string adding_never_index_headers = 26; int HttpMsg::adding_never_index_headers_size() const { return adding_never_index_headers_.size(); } @@ -2863,7 +3418,7 @@ HttpMsg::mutable_adding_never_index_headers() { return &adding_never_index_headers_; } -// repeated string deleting_never_index_headers = 25; +// repeated string deleting_never_index_headers = 27; int HttpMsg::deleting_never_index_headers_size() const { return deleting_never_index_headers_.size(); } @@ -2918,7 +3473,7 @@ HttpMsg::mutable_deleting_never_index_headers() { return &deleting_never_index_headers_; } -// optional uint32 dynamic_table_update_size = 26; +// optional uint32 dynamic_table_update_size = 28; void HttpMsg::clear_dynamic_table_update_size() { dynamic_table_update_size_ = 0u; } @@ -2932,7 +3487,7 @@ void HttpMsg::clear_dynamic_table_update_size() { // @@protoc_insertion_point(field_set:HttpMsg.dynamic_table_update_size) } -// optional bool with_huffman = 27; +// optional bool with_huffman = 29; void HttpMsg::clear_with_huffman() { with_huffman_ = false; } @@ -2946,7 +3501,7 @@ void HttpMsg::clear_with_huffman() { // @@protoc_insertion_point(field_set:HttpMsg.with_huffman) } -// optional string headers_frame_padding = 28; +// optional string headers_frame_padding = 30; void HttpMsg::clear_headers_frame_padding() { headers_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -2990,7 +3545,7 @@ void HttpMsg::clear_headers_frame_padding() { // @@protoc_insertion_point(field_set_allocated:HttpMsg.headers_frame_padding) } -// optional string data_frame_padding = 29; +// optional string data_frame_padding = 31; void HttpMsg::clear_data_frame_padding() { data_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -3034,7 +3589,7 @@ void HttpMsg::clear_data_frame_padding() { // @@protoc_insertion_point(field_set_allocated:HttpMsg.data_frame_padding) } -// optional string push_promise_frame_padding = 30; +// optional string push_promise_frame_padding = 32; void HttpMsg::clear_push_promise_frame_padding() { push_promise_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -3078,7 +3633,7 @@ void HttpMsg::clear_push_promise_frame_padding() { // @@protoc_insertion_point(field_set_allocated:HttpMsg.push_promise_frame_padding) } -// map settings = 31; +// map settings = 33; int HttpMsg::settings_size() const { return settings_.size(); } diff --git a/src/pb/http.pb.h b/src/pb/http.pb.h index 02c64375..4c0be508 100644 --- a/src/pb/http.pb.h +++ b/src/pb/http.pb.h @@ -37,10 +37,110 @@ void protobuf_AssignDesc_http_2eproto(); void protobuf_ShutdownFile_http_2eproto(); class HttpMsg; +class HttpMsg_Header; class HttpMsg_Upgrade; // =================================================================== +class HttpMsg_Header : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:HttpMsg.Header) */ { + public: + HttpMsg_Header(); + virtual ~HttpMsg_Header(); + + HttpMsg_Header(const HttpMsg_Header& from); + + inline HttpMsg_Header& operator=(const HttpMsg_Header& from) { + CopyFrom(from); + return *this; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const HttpMsg_Header& default_instance(); + + void Swap(HttpMsg_Header* other); + + // implements Message ---------------------------------------------- + + inline HttpMsg_Header* New() const { return New(NULL); } + + HttpMsg_Header* New(::google::protobuf::Arena* arena) const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const HttpMsg_Header& from); + void MergeFrom(const HttpMsg_Header& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const { + return InternalSerializeWithCachedSizesToArray(false, output); + } + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(HttpMsg_Header* other); + private: + inline ::google::protobuf::Arena* GetArenaNoVirtual() const { + return _internal_metadata_.arena(); + } + inline void* MaybeArenaPtr() const { + return _internal_metadata_.raw_arena_ptr(); + } + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string name = 1; + void clear_name(); + static const int kNameFieldNumber = 1; + const ::std::string& name() const; + void set_name(const ::std::string& value); + void set_name(const char* value); + void set_name(const char* value, size_t size); + ::std::string* mutable_name(); + ::std::string* release_name(); + void set_allocated_name(::std::string* name); + + // optional string value = 2; + void clear_value(); + static const int kValueFieldNumber = 2; + const ::std::string& value() const; + void set_value(const ::std::string& value); + void set_value(const char* value); + void set_value(const char* value, size_t size); + ::std::string* mutable_value(); + ::std::string* release_value(); + void set_allocated_value(::std::string* value); + + // @@protoc_insertion_point(class_scope:HttpMsg.Header) + private: + + ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + bool _is_default_instance_; + ::google::protobuf::internal::ArenaStringPtr name_; + ::google::protobuf::internal::ArenaStringPtr value_; + mutable int _cached_size_; + friend void protobuf_AddDesc_http_2eproto(); + friend void protobuf_AssignDesc_http_2eproto(); + friend void protobuf_ShutdownFile_http_2eproto(); + + void InitAsDefaultInstance(); + static HttpMsg_Header* default_instance_; +}; +// ------------------------------------------------------------------- + class HttpMsg_Upgrade : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:HttpMsg.Upgrade) */ { public: HttpMsg_Upgrade(); @@ -193,6 +293,7 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c // nested types ---------------------------------------------------- + typedef HttpMsg_Header Header; typedef HttpMsg_Upgrade Upgrade; // accessors ------------------------------------------------------- @@ -323,9 +424,33 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c ::google::protobuf::uint32 stream_id() const; void set_stream_id(::google::protobuf::uint32 value); - // optional string hpack_data = 21; + // repeated .HttpMsg.Header pseudo_header = 21; + int pseudo_header_size() const; + void clear_pseudo_header(); + static const int kPseudoHeaderFieldNumber = 21; + const ::HttpMsg_Header& pseudo_header(int index) const; + ::HttpMsg_Header* mutable_pseudo_header(int index); + ::HttpMsg_Header* add_pseudo_header(); + ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >* + mutable_pseudo_header(); + const ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >& + pseudo_header() const; + + // repeated .HttpMsg.Header trailer_header = 22; + int trailer_header_size() const; + void clear_trailer_header(); + static const int kTrailerHeaderFieldNumber = 22; + const ::HttpMsg_Header& trailer_header(int index) const; + ::HttpMsg_Header* mutable_trailer_header(int index); + ::HttpMsg_Header* add_trailer_header(); + ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >* + mutable_trailer_header(); + const ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >& + trailer_header() const; + + // optional string hpack_data = 23; void clear_hpack_data(); - static const int kHpackDataFieldNumber = 21; + static const int kHpackDataFieldNumber = 23; const ::std::string& hpack_data() const; void set_hpack_data(const ::std::string& value); void set_hpack_data(const char* value); @@ -334,10 +459,10 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c ::std::string* release_hpack_data(); void set_allocated_hpack_data(::std::string* hpack_data); - // repeated string adding_without_index_headers = 22; + // repeated string adding_without_index_headers = 24; int adding_without_index_headers_size() const; void clear_adding_without_index_headers(); - static const int kAddingWithoutIndexHeadersFieldNumber = 22; + static const int kAddingWithoutIndexHeadersFieldNumber = 24; const ::std::string& adding_without_index_headers(int index) const; ::std::string* mutable_adding_without_index_headers(int index); void set_adding_without_index_headers(int index, const ::std::string& value); @@ -350,10 +475,10 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c const ::google::protobuf::RepeatedPtrField< ::std::string>& adding_without_index_headers() const; ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_adding_without_index_headers(); - // repeated string deleting_without_index_headers = 23; + // repeated string deleting_without_index_headers = 25; int deleting_without_index_headers_size() const; void clear_deleting_without_index_headers(); - static const int kDeletingWithoutIndexHeadersFieldNumber = 23; + static const int kDeletingWithoutIndexHeadersFieldNumber = 25; const ::std::string& deleting_without_index_headers(int index) const; ::std::string* mutable_deleting_without_index_headers(int index); void set_deleting_without_index_headers(int index, const ::std::string& value); @@ -366,10 +491,10 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c const ::google::protobuf::RepeatedPtrField< ::std::string>& deleting_without_index_headers() const; ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_deleting_without_index_headers(); - // repeated string adding_never_index_headers = 24; + // repeated string adding_never_index_headers = 26; int adding_never_index_headers_size() const; void clear_adding_never_index_headers(); - static const int kAddingNeverIndexHeadersFieldNumber = 24; + static const int kAddingNeverIndexHeadersFieldNumber = 26; const ::std::string& adding_never_index_headers(int index) const; ::std::string* mutable_adding_never_index_headers(int index); void set_adding_never_index_headers(int index, const ::std::string& value); @@ -382,10 +507,10 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c const ::google::protobuf::RepeatedPtrField< ::std::string>& adding_never_index_headers() const; ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_adding_never_index_headers(); - // repeated string deleting_never_index_headers = 25; + // repeated string deleting_never_index_headers = 27; int deleting_never_index_headers_size() const; void clear_deleting_never_index_headers(); - static const int kDeletingNeverIndexHeadersFieldNumber = 25; + static const int kDeletingNeverIndexHeadersFieldNumber = 27; const ::std::string& deleting_never_index_headers(int index) const; ::std::string* mutable_deleting_never_index_headers(int index); void set_deleting_never_index_headers(int index, const ::std::string& value); @@ -398,21 +523,21 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c const ::google::protobuf::RepeatedPtrField< ::std::string>& deleting_never_index_headers() const; ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_deleting_never_index_headers(); - // optional uint32 dynamic_table_update_size = 26; + // optional uint32 dynamic_table_update_size = 28; void clear_dynamic_table_update_size(); - static const int kDynamicTableUpdateSizeFieldNumber = 26; + static const int kDynamicTableUpdateSizeFieldNumber = 28; ::google::protobuf::uint32 dynamic_table_update_size() const; void set_dynamic_table_update_size(::google::protobuf::uint32 value); - // optional bool with_huffman = 27; + // optional bool with_huffman = 29; void clear_with_huffman(); - static const int kWithHuffmanFieldNumber = 27; + static const int kWithHuffmanFieldNumber = 29; bool with_huffman() const; void set_with_huffman(bool value); - // optional string headers_frame_padding = 28; + // optional string headers_frame_padding = 30; void clear_headers_frame_padding(); - static const int kHeadersFramePaddingFieldNumber = 28; + static const int kHeadersFramePaddingFieldNumber = 30; const ::std::string& headers_frame_padding() const; void set_headers_frame_padding(const ::std::string& value); void set_headers_frame_padding(const char* value); @@ -421,9 +546,9 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c ::std::string* release_headers_frame_padding(); void set_allocated_headers_frame_padding(::std::string* headers_frame_padding); - // optional string data_frame_padding = 29; + // optional string data_frame_padding = 31; void clear_data_frame_padding(); - static const int kDataFramePaddingFieldNumber = 29; + static const int kDataFramePaddingFieldNumber = 31; const ::std::string& data_frame_padding() const; void set_data_frame_padding(const ::std::string& value); void set_data_frame_padding(const char* value); @@ -432,9 +557,9 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c ::std::string* release_data_frame_padding(); void set_allocated_data_frame_padding(::std::string* data_frame_padding); - // optional string push_promise_frame_padding = 30; + // optional string push_promise_frame_padding = 32; void clear_push_promise_frame_padding(); - static const int kPushPromiseFramePaddingFieldNumber = 30; + static const int kPushPromiseFramePaddingFieldNumber = 32; const ::std::string& push_promise_frame_padding() const; void set_push_promise_frame_padding(const ::std::string& value); void set_push_promise_frame_padding(const char* value); @@ -443,10 +568,10 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c ::std::string* release_push_promise_frame_padding(); void set_allocated_push_promise_frame_padding(::std::string* push_promise_frame_padding); - // map settings = 31; + // map settings = 33; int settings_size() const; void clear_settings(); - static const int kSettingsFieldNumber = 31; + static const int kSettingsFieldNumber = 33; const ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >& settings() const; ::google::protobuf::Map< ::google::protobuf::uint32, ::google::protobuf::uint32 >* @@ -491,11 +616,13 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c 0 > params_; ::HttpMsg_Upgrade* upgrade_; ::google::protobuf::internal::ArenaStringPtr path_; - ::google::protobuf::internal::ArenaStringPtr hpack_data_; + ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header > pseudo_header_; ::google::protobuf::uint32 stream_id_; bool is_decoding_; bool chunk_notice_; bool with_huffman_; + ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header > trailer_header_; + ::google::protobuf::internal::ArenaStringPtr hpack_data_; ::google::protobuf::RepeatedPtrField< ::std::string> adding_without_index_headers_; ::google::protobuf::RepeatedPtrField< ::std::string> deleting_without_index_headers_; ::google::protobuf::RepeatedPtrField< ::std::string> adding_never_index_headers_; @@ -529,6 +656,98 @@ class HttpMsg : public ::google::protobuf::Message /* @@protoc_insertion_point(c // =================================================================== #if !PROTOBUF_INLINE_NOT_IN_HEADERS +// HttpMsg_Header + +// optional string name = 1; +inline void HttpMsg_Header::clear_name() { + name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& HttpMsg_Header::name() const { + // @@protoc_insertion_point(field_get:HttpMsg.Header.name) + return name_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg_Header::set_name(const ::std::string& value) { + + name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.Header.name) +} +inline void HttpMsg_Header::set_name(const char* value) { + + name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.Header.name) +} +inline void HttpMsg_Header::set_name(const char* value, size_t size) { + + name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.Header.name) +} +inline ::std::string* HttpMsg_Header::mutable_name() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.Header.name) + return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* HttpMsg_Header::release_name() { + // @@protoc_insertion_point(field_release:HttpMsg.Header.name) + + return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg_Header::set_allocated_name(::std::string* name) { + if (name != NULL) { + + } else { + + } + name_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), name); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.Header.name) +} + +// optional string value = 2; +inline void HttpMsg_Header::clear_value() { + value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& HttpMsg_Header::value() const { + // @@protoc_insertion_point(field_get:HttpMsg.Header.value) + return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg_Header::set_value(const ::std::string& value) { + + value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:HttpMsg.Header.value) +} +inline void HttpMsg_Header::set_value(const char* value) { + + value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:HttpMsg.Header.value) +} +inline void HttpMsg_Header::set_value(const char* value, size_t size) { + + value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:HttpMsg.Header.value) +} +inline ::std::string* HttpMsg_Header::mutable_value() { + + // @@protoc_insertion_point(field_mutable:HttpMsg.Header.value) + return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* HttpMsg_Header::release_value() { + // @@protoc_insertion_point(field_release:HttpMsg.Header.value) + + return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void HttpMsg_Header::set_allocated_value(::std::string* value) { + if (value != NULL) { + + } else { + + } + value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set_allocated:HttpMsg.Header.value) +} + +// ------------------------------------------------------------------- + // HttpMsg_Upgrade // optional bool is_upgrade = 1; @@ -953,7 +1172,67 @@ inline void HttpMsg::set_stream_id(::google::protobuf::uint32 value) { // @@protoc_insertion_point(field_set:HttpMsg.stream_id) } -// optional string hpack_data = 21; +// repeated .HttpMsg.Header pseudo_header = 21; +inline int HttpMsg::pseudo_header_size() const { + return pseudo_header_.size(); +} +inline void HttpMsg::clear_pseudo_header() { + pseudo_header_.Clear(); +} +inline const ::HttpMsg_Header& HttpMsg::pseudo_header(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.pseudo_header) + return pseudo_header_.Get(index); +} +inline ::HttpMsg_Header* HttpMsg::mutable_pseudo_header(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.pseudo_header) + return pseudo_header_.Mutable(index); +} +inline ::HttpMsg_Header* HttpMsg::add_pseudo_header() { + // @@protoc_insertion_point(field_add:HttpMsg.pseudo_header) + return pseudo_header_.Add(); +} +inline ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >* +HttpMsg::mutable_pseudo_header() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.pseudo_header) + return &pseudo_header_; +} +inline const ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >& +HttpMsg::pseudo_header() const { + // @@protoc_insertion_point(field_list:HttpMsg.pseudo_header) + return pseudo_header_; +} + +// repeated .HttpMsg.Header trailer_header = 22; +inline int HttpMsg::trailer_header_size() const { + return trailer_header_.size(); +} +inline void HttpMsg::clear_trailer_header() { + trailer_header_.Clear(); +} +inline const ::HttpMsg_Header& HttpMsg::trailer_header(int index) const { + // @@protoc_insertion_point(field_get:HttpMsg.trailer_header) + return trailer_header_.Get(index); +} +inline ::HttpMsg_Header* HttpMsg::mutable_trailer_header(int index) { + // @@protoc_insertion_point(field_mutable:HttpMsg.trailer_header) + return trailer_header_.Mutable(index); +} +inline ::HttpMsg_Header* HttpMsg::add_trailer_header() { + // @@protoc_insertion_point(field_add:HttpMsg.trailer_header) + return trailer_header_.Add(); +} +inline ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >* +HttpMsg::mutable_trailer_header() { + // @@protoc_insertion_point(field_mutable_list:HttpMsg.trailer_header) + return &trailer_header_; +} +inline const ::google::protobuf::RepeatedPtrField< ::HttpMsg_Header >& +HttpMsg::trailer_header() const { + // @@protoc_insertion_point(field_list:HttpMsg.trailer_header) + return trailer_header_; +} + +// optional string hpack_data = 23; inline void HttpMsg::clear_hpack_data() { hpack_data_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -997,7 +1276,7 @@ inline void HttpMsg::set_allocated_hpack_data(::std::string* hpack_data) { // @@protoc_insertion_point(field_set_allocated:HttpMsg.hpack_data) } -// repeated string adding_without_index_headers = 22; +// repeated string adding_without_index_headers = 24; inline int HttpMsg::adding_without_index_headers_size() const { return adding_without_index_headers_.size(); } @@ -1052,7 +1331,7 @@ HttpMsg::mutable_adding_without_index_headers() { return &adding_without_index_headers_; } -// repeated string deleting_without_index_headers = 23; +// repeated string deleting_without_index_headers = 25; inline int HttpMsg::deleting_without_index_headers_size() const { return deleting_without_index_headers_.size(); } @@ -1107,7 +1386,7 @@ HttpMsg::mutable_deleting_without_index_headers() { return &deleting_without_index_headers_; } -// repeated string adding_never_index_headers = 24; +// repeated string adding_never_index_headers = 26; inline int HttpMsg::adding_never_index_headers_size() const { return adding_never_index_headers_.size(); } @@ -1162,7 +1441,7 @@ HttpMsg::mutable_adding_never_index_headers() { return &adding_never_index_headers_; } -// repeated string deleting_never_index_headers = 25; +// repeated string deleting_never_index_headers = 27; inline int HttpMsg::deleting_never_index_headers_size() const { return deleting_never_index_headers_.size(); } @@ -1217,7 +1496,7 @@ HttpMsg::mutable_deleting_never_index_headers() { return &deleting_never_index_headers_; } -// optional uint32 dynamic_table_update_size = 26; +// optional uint32 dynamic_table_update_size = 28; inline void HttpMsg::clear_dynamic_table_update_size() { dynamic_table_update_size_ = 0u; } @@ -1231,7 +1510,7 @@ inline void HttpMsg::set_dynamic_table_update_size(::google::protobuf::uint32 va // @@protoc_insertion_point(field_set:HttpMsg.dynamic_table_update_size) } -// optional bool with_huffman = 27; +// optional bool with_huffman = 29; inline void HttpMsg::clear_with_huffman() { with_huffman_ = false; } @@ -1245,7 +1524,7 @@ inline void HttpMsg::set_with_huffman(bool value) { // @@protoc_insertion_point(field_set:HttpMsg.with_huffman) } -// optional string headers_frame_padding = 28; +// optional string headers_frame_padding = 30; inline void HttpMsg::clear_headers_frame_padding() { headers_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1289,7 +1568,7 @@ inline void HttpMsg::set_allocated_headers_frame_padding(::std::string* headers_ // @@protoc_insertion_point(field_set_allocated:HttpMsg.headers_frame_padding) } -// optional string data_frame_padding = 29; +// optional string data_frame_padding = 31; inline void HttpMsg::clear_data_frame_padding() { data_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1333,7 +1612,7 @@ inline void HttpMsg::set_allocated_data_frame_padding(::std::string* data_frame_ // @@protoc_insertion_point(field_set_allocated:HttpMsg.data_frame_padding) } -// optional string push_promise_frame_padding = 30; +// optional string push_promise_frame_padding = 32; inline void HttpMsg::clear_push_promise_frame_padding() { push_promise_frame_padding_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1377,7 +1656,7 @@ inline void HttpMsg::set_allocated_push_promise_frame_padding(::std::string* pus // @@protoc_insertion_point(field_set_allocated:HttpMsg.push_promise_frame_padding) } -// map settings = 31; +// map settings = 33; inline int HttpMsg::settings_size() const { return settings_.size(); } @@ -1398,6 +1677,8 @@ HttpMsg::mutable_settings() { #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS // ------------------------------------------------------------------- +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) diff --git a/src/util/json/CJsonObject.cpp b/src/util/json/CJsonObject.cpp index b45d22f0..32217508 100644 --- a/src/util/json/CJsonObject.cpp +++ b/src/util/json/CJsonObject.cpp @@ -21,11 +21,15 @@ CJsonObject::CJsonObject() : m_pJsonData(NULL), m_pExternJsonDataRef(NULL), m_pKeyTravers(NULL) { // m_pJsonData = cJSON_CreateObject(); + m_array_iter = m_mapJsonArrayRef.end(); + m_object_iter = m_mapJsonObjectRef.end(); } CJsonObject::CJsonObject(const std::string& strJson) : m_pJsonData(NULL), m_pExternJsonDataRef(NULL), m_pKeyTravers(NULL) { + m_array_iter = m_mapJsonArrayRef.end(); + m_object_iter = m_mapJsonObjectRef.end(); Parse(strJson); } @@ -34,6 +38,8 @@ CJsonObject::CJsonObject(const CJsonObject* pJsonObject) { if (pJsonObject) { + m_array_iter = m_mapJsonArrayRef.end(); + m_object_iter = m_mapJsonObjectRef.end(); Parse(pJsonObject->ToString()); } } @@ -41,9 +47,30 @@ CJsonObject::CJsonObject(const CJsonObject* pJsonObject) CJsonObject::CJsonObject(const CJsonObject& oJsonObject) : m_pJsonData(NULL), m_pExternJsonDataRef(NULL), m_pKeyTravers(NULL) { + m_array_iter = m_mapJsonArrayRef.end(); + m_object_iter = m_mapJsonObjectRef.end(); Parse(oJsonObject.ToString()); } +#if __cplusplus >= 201101L +CJsonObject::CJsonObject(CJsonObject&& oJsonObject) + : m_pJsonData(oJsonObject.m_pJsonData), + m_pExternJsonDataRef(oJsonObject.m_pExternJsonDataRef), + m_pKeyTravers(oJsonObject.m_pKeyTravers), + mc_pError(oJsonObject.mc_pError) +{ + oJsonObject.m_pJsonData = NULL; + oJsonObject.m_pExternJsonDataRef = NULL; + oJsonObject.m_pKeyTravers = NULL; + oJsonObject.mc_pError = NULL; + m_strErrMsg = std::move(oJsonObject.m_strErrMsg); + m_mapJsonArrayRef = std::move(oJsonObject.m_mapJsonArrayRef); + m_mapJsonObjectRef = std::move(oJsonObject.m_mapJsonObjectRef); + m_array_iter = m_mapJsonArrayRef.end(); + m_object_iter = m_mapJsonObjectRef.end(); +} +#endif + CJsonObject::~CJsonObject() { Clear(); @@ -55,6 +82,26 @@ CJsonObject& CJsonObject::operator=(const CJsonObject& oJsonObject) return(*this); } +#if __cplusplus >= 201101L +CJsonObject& CJsonObject::operator=(CJsonObject&& oJsonObject) +{ + m_pJsonData = oJsonObject.m_pJsonData; + oJsonObject.m_pJsonData = NULL; + m_pExternJsonDataRef = oJsonObject.m_pExternJsonDataRef; + oJsonObject.m_pExternJsonDataRef = NULL; + m_pKeyTravers = oJsonObject.m_pKeyTravers; + oJsonObject.m_pKeyTravers = NULL; + mc_pError = oJsonObject.mc_pError; + oJsonObject.mc_pError = NULL; + m_strErrMsg = std::move(oJsonObject.m_strErrMsg); + m_mapJsonArrayRef = std::move(oJsonObject.m_mapJsonArrayRef); + m_mapJsonObjectRef = std::move(oJsonObject.m_mapJsonObjectRef); + m_array_iter = m_mapJsonArrayRef.end(); + m_object_iter = m_mapJsonObjectRef.end(); + return(*this); +} +#endif + bool CJsonObject::operator==(const CJsonObject& oJsonObject) const { return(this->ToString() == oJsonObject.ToString()); @@ -101,6 +148,8 @@ bool CJsonObject::AddEmptySubObject(const std::string& strKey) } cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); m_pKeyTravers = pFocusData; + m_strLastObjectKey = ""; + m_object_iter = m_mapJsonObjectRef.end(); return(true); } @@ -145,6 +194,8 @@ bool CJsonObject::AddEmptySubArray(const std::string& strKey) } cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); m_pKeyTravers = pFocusData; + m_uiLastArrayIndex = 0; + m_array_iter = m_mapJsonArrayRef.end(); return(true); } @@ -202,8 +253,15 @@ void CJsonObject::ResetTraversing() CJsonObject& CJsonObject::operator[](const std::string& strKey) { - std::map::iterator iter; - iter = m_mapJsonObjectRef.find(strKey); + if (strKey == m_strLastObjectKey && m_object_iter != m_mapJsonObjectRef.end()) + { + return(*(m_object_iter->second)); + } +#if __cplusplus < 201101L + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif if (iter == m_mapJsonObjectRef.end()) { cJSON* pJsonStruct = NULL; @@ -236,14 +294,23 @@ CJsonObject& CJsonObject::operator[](const std::string& strKey) } else { + m_object_iter = iter; + m_strLastObjectKey = strKey; return(*(iter->second)); } } CJsonObject& CJsonObject::operator[](unsigned int uiWhich) { - std::map::iterator iter; - iter = m_mapJsonArrayRef.find(uiWhich); + if (uiWhich == m_uiLastArrayIndex && m_array_iter != m_mapJsonArrayRef.end()) + { + return(*(m_array_iter->second)); + } +#if __cplusplus < 201101L + std::map::iterator iter = m_mapJsonArrayRef.find(uiWhich); +#else + auto iter = m_mapJsonArrayRef.find(uiWhich); +#endif if (iter == m_mapJsonArrayRef.end()) { cJSON* pJsonStruct = NULL; @@ -276,6 +343,8 @@ CJsonObject& CJsonObject::operator[](unsigned int uiWhich) } else { + m_uiLastArrayIndex = uiWhich; + m_array_iter = iter; return(*(iter->second)); } } @@ -316,7 +385,11 @@ std::string CJsonObject::operator()(const std::string& strKey) const } else { +#if LLONG_MAX==LLONG_MAX snprintf(szNumber, sizeof(szNumber), "%ld", (int64)pJsonStruct->valueint); +#else + snprintf(szNumber, sizeof(szNumber), "%lld", (int64)pJsonStruct->valueint); +#endif } } else @@ -327,7 +400,11 @@ std::string CJsonObject::operator()(const std::string& strKey) const } else { +#if LLONG_MAX==LLONG_MAX snprintf(szNumber, sizeof(szNumber), "%lu", pJsonStruct->valueint); +#else + snprintf(szNumber, sizeof(szNumber), "%llu", pJsonStruct->valueint); +#endif } } return(std::string(szNumber)); @@ -392,7 +469,11 @@ std::string CJsonObject::operator()(unsigned int uiWhich) const } else { +#if LLONG_MAX==LLONG_MAX snprintf(szNumber, sizeof(szNumber), "%ld", (int64)pJsonStruct->valueint); +#else + snprintf(szNumber, sizeof(szNumber), "%lld", (int64)pJsonStruct->valueint); +#endif } } else @@ -403,7 +484,11 @@ std::string CJsonObject::operator()(unsigned int uiWhich) const } else { +#if LLONG_MAX==LLONG_MAX snprintf(szNumber, sizeof(szNumber), "%lu", pJsonStruct->valueint); +#else + snprintf(szNumber, sizeof(szNumber), "%llu", pJsonStruct->valueint); +#endif } } return(std::string(szNumber)); @@ -435,11 +520,11 @@ std::string CJsonObject::operator()(unsigned int uiWhich) const bool CJsonObject::Parse(const std::string& strJson) { Clear(); - m_pJsonData = cJSON_Parse(strJson.c_str()); + m_pJsonData = cJSON_Parse(strJson.c_str(), &mc_pError); m_pKeyTravers = m_pJsonData; if (m_pJsonData == NULL) { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + m_strErrMsg = std::string("prase json string error at ") + mc_pError; return(false); } return(true); @@ -454,8 +539,12 @@ void CJsonObject::Clear() cJSON_Delete(m_pJsonData); m_pJsonData = NULL; } +#if __cplusplus < 201101L for (std::map::iterator iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ++iter) +#else + for (auto iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ++iter) +#endif { if (iter->second != NULL) { @@ -464,8 +553,13 @@ void CJsonObject::Clear() } } m_mapJsonArrayRef.clear(); + m_array_iter = m_mapJsonArrayRef.end(); +#if __cplusplus < 201101L for (std::map::iterator iter = m_mapJsonObjectRef.begin(); iter != m_mapJsonObjectRef.end(); ++iter) +#else + for (auto iter = m_mapJsonObjectRef.begin(); iter != m_mapJsonObjectRef.end(); ++iter) +#endif { if (iter->second != NULL) { @@ -474,6 +568,7 @@ void CJsonObject::Clear() } } m_mapJsonObjectRef.clear(); + m_object_iter = m_mapJsonObjectRef.end(); } bool CJsonObject::IsEmpty() const @@ -927,10 +1022,76 @@ bool CJsonObject::Add(const std::string& strKey, const CJsonObject& oJsonObject) m_strErrMsg = "key exists!"; return(false); } - cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str(), &mc_pError) ; + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + mc_pError; + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } +#if __cplusplus < 201101L + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + m_pKeyTravers = pFocusData; + m_strLastObjectKey = ""; + m_object_iter = m_mapJsonObjectRef.end(); + return(true); +} + +#if __cplusplus < 201101L +bool CJsonObject::AddWithMove(const std::string& strKey, CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = oJsonObject.m_pJsonData; + oJsonObject.m_pJsonData = NULL; if (pJsonStruct == NULL) { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + m_strErrMsg = "can not move a non-independent(internal) CJsonObject from one to another."; return(false); } cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); @@ -949,8 +1110,72 @@ bool CJsonObject::Add(const std::string& strKey, const CJsonObject& oJsonObject) m_mapJsonObjectRef.erase(iter); } m_pKeyTravers = pFocusData; + m_strLastObjectKey = ""; + m_object_iter = m_mapJsonObjectRef.end(); + return(true); +} +#else +bool CJsonObject::Add(const std::string& strKey, CJsonObject&& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = oJsonObject.m_pJsonData; + oJsonObject.m_pJsonData = NULL; + if (pJsonStruct == NULL) + { + m_strErrMsg = "can not move a non-independent(internal) CJsonObject from one to another."; + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + auto iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + m_pKeyTravers = pFocusData; + m_strLastObjectKey = ""; + m_object_iter = m_mapJsonObjectRef.end(); return(true); } +#endif bool CJsonObject::Add(const std::string& strKey, const std::string& strValue) { @@ -1328,19 +1553,25 @@ bool CJsonObject::Add(const std::string& strKey, double dValue) return(true); } -bool CJsonObject::ReplaceAdd(const std::string& strKey,const CJsonObject& oJsonObject) +#if __cplusplus < 201101L +bool CJsonObject::ReplaceAdd(const std::string& strKey, const CJsonObject& oJsonObject) { - if(Replace(strKey,oJsonObject) == false) - return Add(strKey,oJsonObject); - return true; + if (KeyExist(strKey)) + { + return(Replace(strKey, oJsonObject)); + } + return(Add(strKey, oJsonObject)); } -bool CJsonObject::ReplaceAdd(const std::string& strKey,const std::string& strValue) +bool CJsonObject::ReplaceAdd(const std::string& strKey, const std::string& strValue) { - if(Replace(strKey,strValue) == false) - return Add(strKey,strValue); - return true; + if (KeyExist(strKey)) + { + return(Replace(strKey, strValue)); + } + return(Add(strKey, strValue)); } +#endif bool CJsonObject::AddNull(const std::string& strKey) { @@ -1411,7 +1642,11 @@ bool CJsonObject::Delete(const std::string& strKey) return(false); } cJSON_DeleteItemFromObject(pFocusData, strKey.c_str()); +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif if (iter != m_mapJsonObjectRef.end()) { if (iter->second != NULL) @@ -1422,6 +1657,8 @@ bool CJsonObject::Delete(const std::string& strKey) m_mapJsonObjectRef.erase(iter); } m_pKeyTravers = pFocusData; + m_strLastObjectKey = ""; + m_object_iter = m_mapJsonObjectRef.end(); return(true); } @@ -1446,10 +1683,63 @@ bool CJsonObject::Replace(const std::string& strKey, const CJsonObject& oJsonObj m_strErrMsg = "not a json object! json array?"; return(false); } - cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str(), &mc_pError); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + mc_pError; + return(false); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } +#if __cplusplus < 201101L + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + m_strLastObjectKey = ""; + m_object_iter = m_mapJsonObjectRef.end(); + return(true); +} + +#if __cplusplus < 201101L +bool CJsonObject::ReplaceWithMove(const std::string& strKey, CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = oJsonObject.m_pJsonData; + oJsonObject.m_pJsonData = NULL; if (pJsonStruct == NULL) { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + m_strErrMsg = "can not move a non-independent(internal) CJsonObject from one to another."; return(false); } cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); @@ -1467,8 +1757,59 @@ bool CJsonObject::Replace(const std::string& strKey, const CJsonObject& oJsonObj } m_mapJsonObjectRef.erase(iter); } + m_strLastObjectKey = ""; + m_object_iter = m_mapJsonObjectRef.end(); + return(true); +} +#else +bool CJsonObject::Replace(const std::string& strKey, CJsonObject&& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = oJsonObject.m_pJsonData; + oJsonObject.m_pJsonData = NULL; + if (pJsonStruct == NULL) + { + m_strErrMsg = "can not move a non-independent(internal) CJsonObject from one to another."; + return(false); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + auto iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + m_strLastObjectKey = ""; + m_object_iter = m_mapJsonObjectRef.end(); return(true); } +#endif bool CJsonObject::Replace(const std::string& strKey, const std::string& strValue) { @@ -1496,7 +1837,11 @@ bool CJsonObject::Replace(const std::string& strKey, const std::string& strValue { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif if (iter != m_mapJsonObjectRef.end()) { if (iter->second != NULL) @@ -1540,7 +1885,11 @@ bool CJsonObject::Replace(const std::string& strKey, int32 iValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif if (iter != m_mapJsonObjectRef.end()) { if (iter->second != NULL) @@ -1584,7 +1933,11 @@ bool CJsonObject::Replace(const std::string& strKey, uint32 uiValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif if (iter != m_mapJsonObjectRef.end()) { if (iter->second != NULL) @@ -1628,7 +1981,11 @@ bool CJsonObject::Replace(const std::string& strKey, int64 llValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif if (iter != m_mapJsonObjectRef.end()) { if (iter->second != NULL) @@ -1672,7 +2029,11 @@ bool CJsonObject::Replace(const std::string& strKey, uint64 ullValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif if (iter != m_mapJsonObjectRef.end()) { if (iter->second != NULL) @@ -1716,7 +2077,11 @@ bool CJsonObject::Replace(const std::string& strKey, bool bValue, bool bValueAga { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif if (iter != m_mapJsonObjectRef.end()) { if (iter->second != NULL) @@ -1760,7 +2125,11 @@ bool CJsonObject::Replace(const std::string& strKey, float fValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif if (iter != m_mapJsonObjectRef.end()) { if (iter->second != NULL) @@ -1804,7 +2173,11 @@ bool CJsonObject::Replace(const std::string& strKey, double dValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif if (iter != m_mapJsonObjectRef.end()) { if (iter->second != NULL) @@ -1848,7 +2221,11 @@ bool CJsonObject::ReplaceWithNull(const std::string& strKey) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonObjectRef.find(strKey); +#else + auto iter = m_mapJsonObjectRef.find(strKey); +#endif if (iter != m_mapJsonObjectRef.end()) { if (iter->second != NULL) @@ -2226,10 +2603,10 @@ bool CJsonObject::Add(const CJsonObject& oJsonObject) m_strErrMsg = "not a json array! json object?"; return(false); } - cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str(), &mc_pError); if (pJsonStruct == NULL) { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + m_strErrMsg = std::string("prase json string error at ") + mc_pError; return(false); } int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); @@ -2240,8 +2617,12 @@ bool CJsonObject::Add(const CJsonObject& oJsonObject) return(false); } unsigned int uiLastIndex = (unsigned int)cJSON_GetArraySize(pFocusData) - 1; +#if __cplusplus < 201101L for (std::map::iterator iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ) +#else + for (auto iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ) +#endif { if (iter->first >= uiLastIndex) { @@ -2257,10 +2638,13 @@ bool CJsonObject::Add(const CJsonObject& oJsonObject) iter++; } } + m_uiLastArrayIndex = 0; + m_array_iter = m_mapJsonArrayRef.end(); return(true); } -bool CJsonObject::Add(const std::string& strValue) +#if __cplusplus < 201101L +bool CJsonObject::AddWithMove(CJsonObject& oJsonObject) { cJSON* pFocusData = NULL; if (m_pJsonData != NULL) @@ -2287,9 +2671,11 @@ bool CJsonObject::Add(const std::string& strValue) m_strErrMsg = "not a json array! json object?"; return(false); } - cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + cJSON* pJsonStruct = oJsonObject.m_pJsonData; + oJsonObject.m_pJsonData = NULL; if (pJsonStruct == NULL) { + m_strErrMsg = "can not move a non-independent(internal) CJsonObject from one to another."; return(false); } int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); @@ -2299,10 +2685,29 @@ bool CJsonObject::Add(const std::string& strValue) { return(false); } + unsigned int uiLastIndex = (unsigned int)cJSON_GetArraySize(pFocusData) - 1; + for (std::map::iterator iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ) + { + if (iter->first >= uiLastIndex) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter++); + } + else + { + iter++; + } + } + m_uiLastArrayIndex = 0; + m_array_iter = m_mapJsonArrayRef.end(); return(true); } - -bool CJsonObject::Add(int32 iValue) +#else +bool CJsonObject::Add(CJsonObject&& oJsonObject) { cJSON* pFocusData = NULL; if (m_pJsonData != NULL) @@ -2329,9 +2734,11 @@ bool CJsonObject::Add(int32 iValue) m_strErrMsg = "not a json array! json object?"; return(false); } - cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + cJSON* pJsonStruct = oJsonObject.m_pJsonData; + oJsonObject.m_pJsonData = NULL; if (pJsonStruct == NULL) { + m_strErrMsg = "can not move a non-independent(internal) CJsonObject from one to another."; return(false); } int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); @@ -2341,9 +2748,113 @@ bool CJsonObject::Add(int32 iValue) { return(false); } - return(true); -} - + unsigned int uiLastIndex = (unsigned int)cJSON_GetArraySize(pFocusData) - 1; + for (auto iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ) + { + if (iter->first >= uiLastIndex) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter++); + } + else + { + iter++; + } + } + m_uiLastArrayIndex = 0; + m_array_iter = m_mapJsonArrayRef.end(); + return(true); +} +#endif + +bool CJsonObject::Add(const std::string& strValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(int32 iValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + bool CJsonObject::Add(uint32 uiValue) { cJSON* pFocusData = NULL; @@ -2665,10 +3176,10 @@ bool CJsonObject::AddAsFirst(const CJsonObject& oJsonObject) m_strErrMsg = "not a json array! json object?"; return(false); } - cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str(), &mc_pError); if (pJsonStruct == NULL) { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + m_strErrMsg = std::string("prase json string error at ") + mc_pError; return(false); } int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); @@ -2678,8 +3189,12 @@ bool CJsonObject::AddAsFirst(const CJsonObject& oJsonObject) { return(false); } +#if __cplusplus < 201101L for (std::map::iterator iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ) +#else + for (auto iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ) +#endif { if (iter->second != NULL) { @@ -2688,9 +3203,123 @@ bool CJsonObject::AddAsFirst(const CJsonObject& oJsonObject) } m_mapJsonArrayRef.erase(iter++); } + m_uiLastArrayIndex = 0; + m_array_iter = m_mapJsonArrayRef.end(); return(true); } +#if __cplusplus < 201101L +bool CJsonObject::AddAsFirstWithMove(CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = oJsonObject.m_pJsonData; + oJsonObject.m_pJsonData = NULL; + if (pJsonStruct == NULL) + { + m_strErrMsg = "can not move a non-independent(internal) CJsonObject from one to another."; + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + for (std::map::iterator iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter++); + } + m_uiLastArrayIndex = 0; + m_array_iter = m_mapJsonArrayRef.end(); + return(true); +} +#else +bool CJsonObject::AddAsFirst(CJsonObject&& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = oJsonObject.m_pJsonData; + oJsonObject.m_pJsonData = NULL; + if (pJsonStruct == NULL) + { + m_strErrMsg = "can not move a non-independent(internal) CJsonObject from one to another."; + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + for (auto iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter++); + } + m_uiLastArrayIndex = 0; + m_array_iter = m_mapJsonArrayRef.end(); + return(true); +} +#endif + bool CJsonObject::AddAsFirst(const std::string& strValue) { cJSON* pFocusData = NULL; @@ -3091,8 +3720,12 @@ bool CJsonObject::Delete(int iWhich) return(false); } cJSON_DeleteItemFromArray(pFocusData, iWhich); +#if __cplusplus < 201101L for (std::map::iterator iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ) +#else + for (auto iter = m_mapJsonArrayRef.begin(); iter != m_mapJsonArrayRef.end(); ) +#endif { if (iter->first >= (unsigned int)iWhich) { @@ -3108,6 +3741,8 @@ bool CJsonObject::Delete(int iWhich) iter++; } } + m_uiLastArrayIndex = 0; + m_array_iter = m_mapJsonArrayRef.end(); return(true); } @@ -3132,10 +3767,10 @@ bool CJsonObject::Replace(int iWhich, const CJsonObject& oJsonObject) m_strErrMsg = "not a json array! json object?"; return(false); } - cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str(), &mc_pError); if (pJsonStruct == NULL) { - m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + m_strErrMsg = std::string("prase json string error at ") + mc_pError; return(false); } cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); @@ -3143,7 +3778,11 @@ bool CJsonObject::Replace(int iWhich, const CJsonObject& oJsonObject) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); +#else + auto iter = m_mapJsonArrayRef.find(iWhich); +#endif if (iter != m_mapJsonArrayRef.end()) { if (iter->second != NULL) @@ -3153,9 +3792,109 @@ bool CJsonObject::Replace(int iWhich, const CJsonObject& oJsonObject) } m_mapJsonArrayRef.erase(iter); } + m_uiLastArrayIndex = 0; + m_array_iter = m_mapJsonArrayRef.end(); return(true); } +#if __cplusplus < 201101L +bool CJsonObject::ReplaceWithMove(int iWhich, CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = oJsonObject.m_pJsonData; + oJsonObject.m_pJsonData = NULL; + if (pJsonStruct == NULL) + { + m_strErrMsg = "can not move a non-independent(internal) CJsonObject from one to another."; + return(false); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + m_uiLastArrayIndex = 0; + m_array_iter = m_mapJsonArrayRef.end(); + return(true); +} +#else +bool CJsonObject::Replace(int iWhich, CJsonObject&& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = oJsonObject.m_pJsonData; + oJsonObject.m_pJsonData = NULL; + if (pJsonStruct == NULL) + { + m_strErrMsg = "can not move a non-independent(internal) CJsonObject from one to another."; + return(false); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + auto iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + m_uiLastArrayIndex = 0; + m_array_iter = m_mapJsonArrayRef.end(); + return(true); +} +#endif + bool CJsonObject::Replace(int iWhich, const std::string& strValue) { cJSON* pFocusData = NULL; @@ -3182,7 +3921,11 @@ bool CJsonObject::Replace(int iWhich, const std::string& strValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); +#else + auto iter = m_mapJsonArrayRef.find(iWhich); +#endif if (iter != m_mapJsonArrayRef.end()) { if (iter->second != NULL) @@ -3226,7 +3969,11 @@ bool CJsonObject::Replace(int iWhich, int32 iValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); +#else + auto iter = m_mapJsonArrayRef.find(iWhich); +#endif if (iter != m_mapJsonArrayRef.end()) { if (iter->second != NULL) @@ -3270,7 +4017,11 @@ bool CJsonObject::Replace(int iWhich, uint32 uiValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); +#else + auto iter = m_mapJsonArrayRef.find(iWhich); +#endif if (iter != m_mapJsonArrayRef.end()) { if (iter->second != NULL) @@ -3314,7 +4065,11 @@ bool CJsonObject::Replace(int iWhich, int64 llValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); +#else + auto iter = m_mapJsonArrayRef.find(iWhich); +#endif if (iter != m_mapJsonArrayRef.end()) { if (iter->second != NULL) @@ -3358,7 +4113,11 @@ bool CJsonObject::Replace(int iWhich, uint64 ullValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); +#else + auto iter = m_mapJsonArrayRef.find(iWhich); +#endif if (iter != m_mapJsonArrayRef.end()) { if (iter->second != NULL) @@ -3402,7 +4161,11 @@ bool CJsonObject::Replace(int iWhich, bool bValue, bool bValueAgain) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); +#else + auto iter = m_mapJsonArrayRef.find(iWhich); +#endif if (iter != m_mapJsonArrayRef.end()) { if (iter->second != NULL) @@ -3446,7 +4209,11 @@ bool CJsonObject::Replace(int iWhich, float fValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); +#else + auto iter = m_mapJsonArrayRef.find(iWhich); +#endif if (iter != m_mapJsonArrayRef.end()) { if (iter->second != NULL) @@ -3490,7 +4257,11 @@ bool CJsonObject::Replace(int iWhich, double dValue) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); +#else + auto iter = m_mapJsonArrayRef.find(iWhich); +#endif if (iter != m_mapJsonArrayRef.end()) { if (iter->second != NULL) @@ -3534,7 +4305,11 @@ bool CJsonObject::ReplaceWithNull(int iWhich) { return(false); } +#if __cplusplus < 201101L std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); +#else + auto iter = m_mapJsonArrayRef.find(iWhich); +#endif if (iter != m_mapJsonArrayRef.end()) { if (iter->second != NULL) diff --git a/src/util/json/CJsonObject.hpp b/src/util/json/CJsonObject.hpp index ef3da13d..9c97aafb 100644 --- a/src/util/json/CJsonObject.hpp +++ b/src/util/json/CJsonObject.hpp @@ -13,13 +13,17 @@ #include #include -#include #include +#include #include #include #include -#include #include +#if __cplusplus < 201101L +#include +#else +#include +#endif #ifdef __cplusplus extern "C" { #endif @@ -39,9 +43,15 @@ class CJsonObject CJsonObject(const std::string& strJson); CJsonObject(const CJsonObject* pJsonObject); CJsonObject(const CJsonObject& oJsonObject); +#if __cplusplus >= 201101L + CJsonObject(CJsonObject&& oJsonObject); +#endif virtual ~CJsonObject(); CJsonObject& operator=(const CJsonObject& oJsonObject); +#if __cplusplus >= 201101L + CJsonObject& operator=(CJsonObject&& oJsonObject); +#endif bool operator==(const CJsonObject& oJsonObject) const; bool Parse(const std::string& strJson); void Clear(); @@ -73,6 +83,11 @@ class CJsonObject bool Get(const std::string& strKey, double& dValue) const; bool IsNull(const std::string& strKey) const; bool Add(const std::string& strKey, const CJsonObject& oJsonObject); +#if __cplusplus < 201101L + bool AddWithMove(const std::string& strKey, CJsonObject& oJsonObject); +#else + bool Add(const std::string& strKey, CJsonObject&& oJsonObject); +#endif bool Add(const std::string& strKey, const std::string& strValue); bool Add(const std::string& strKey, int32 iValue); bool Add(const std::string& strKey, uint32 uiValue); @@ -84,6 +99,11 @@ class CJsonObject bool AddNull(const std::string& strKey); // add null like this: "key":null bool Delete(const std::string& strKey); bool Replace(const std::string& strKey, const CJsonObject& oJsonObject); +#if __cplusplus < 201101L + bool ReplaceWithMove(const std::string& strKey, CJsonObject& oJsonObject); +#else + bool Replace(const std::string& strKey, CJsonObject&& oJsonObject); +#endif bool Replace(const std::string& strKey, const std::string& strValue); bool Replace(const std::string& strKey, int32 iValue); bool Replace(const std::string& strKey, uint32 uiValue); @@ -93,14 +113,29 @@ class CJsonObject bool Replace(const std::string& strKey, float fValue); bool Replace(const std::string& strKey, double dValue); bool ReplaceWithNull(const std::string& strKey); // replace value with null - bool ReplaceAdd(const std::string& strKey,const CJsonObject& oJsonObject); - bool ReplaceAdd(const std::string& strKey,const std::string& strValue); - template bool ReplaceAdd(const std::string& strKey,T value) +#if __cplusplus < 201101L + bool ReplaceAdd(const std::string& strKey, const CJsonObject& oJsonObject); + bool ReplaceAdd(const std::string& strKey, const std::string& strValue); + template + bool ReplaceAdd(const std::string& strKey, T value) + { + if (KeyExist(strKey)) + { + return(Replace(strKey, value)); + } + return(Add(strKey, value)); + } +#else + template + bool ReplaceAdd(const std::string& strKey, T&& value) { - if(Replace(strKey,value) == false) - return Add(strKey,value); - return true; + if (KeyExist(strKey)) + { + return(Replace(strKey, std::forward(value))); + } + return(Add(strKey, std::forward(value))); } +#endif public: // method of json array int GetArraySize(); @@ -117,6 +152,11 @@ class CJsonObject bool Get(int iWhich, double& dValue) const; bool IsNull(int iWhich) const; bool Add(const CJsonObject& oJsonObject); +#if __cplusplus < 201101L + bool AddWithMove(CJsonObject& oJsonObject); +#else + bool Add(CJsonObject&& oJsonObject); +#endif bool Add(const std::string& strValue); bool Add(int32 iValue); bool Add(uint32 uiValue); @@ -127,6 +167,11 @@ class CJsonObject bool Add(double dValue); bool AddNull(); // add a null value bool AddAsFirst(const CJsonObject& oJsonObject); +#if __cplusplus < 201101L + bool AddAsFirstWithMove(CJsonObject& oJsonObject); +#else + bool AddAsFirst(CJsonObject&& oJsonObject); +#endif bool AddAsFirst(const std::string& strValue); bool AddAsFirst(int32 iValue); bool AddAsFirst(uint32 uiValue); @@ -138,6 +183,11 @@ class CJsonObject bool AddNullAsFirst(); // add a null value bool Delete(int iWhich); bool Replace(int iWhich, const CJsonObject& oJsonObject); +#if __cplusplus < 201101L + bool ReplaceWithMove(int iWhich, CJsonObject& oJsonObject); +#else + bool Replace(int iWhich, CJsonObject&& oJsonObject); +#endif bool Replace(int iWhich, const std::string& strValue); bool Replace(int iWhich, int32 iValue); bool Replace(int iWhich, uint32 uiValue); @@ -155,9 +205,21 @@ class CJsonObject cJSON* m_pJsonData; cJSON* m_pExternJsonDataRef; cJSON* m_pKeyTravers; + const char* mc_pError; std::string m_strErrMsg; + uint32 m_uiLastArrayIndex; // corresponds with m_array_iter + std::string m_strLastObjectKey; // corresponds with m_object_iter +#if __cplusplus < 201101L std::map m_mapJsonArrayRef; + std::map::iterator m_array_iter; std::map m_mapJsonObjectRef; + std::map::iterator m_object_iter; +#else + std::unordered_map m_mapJsonArrayRef; + std::unordered_map::iterator m_object_iter; + std::unordered_map m_mapJsonObjectRef; + std::unordered_map::iterator m_array_iter; +#endif }; } diff --git a/src/util/json/cJSON.c b/src/util/json/cJSON.c index a1f4ba8d..9e1b22cf 100644 --- a/src/util/json/cJSON.c +++ b/src/util/json/cJSON.c @@ -38,12 +38,14 @@ #define UINT_MAX 4294967295U #endif +/* remove global variable for thread safe. --by Bwar on 2020-11-15 static const char *ep; const char *cJSON_GetErrorPtr() { return ep; } +*/ static int cJSON_strcasecmp(const char *s1, const char *s2) { @@ -214,7 +216,7 @@ static char *print_int(cJSON *item) /* Parse the input text into an unescaped cstring, and populate item. */ static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; -static const char *parse_string(cJSON *item, const char *str) +static const char *parse_string(cJSON *item, const char *str, const char **ep) { const char *ptr = str + 1; char *ptr2; @@ -223,7 +225,7 @@ static const char *parse_string(cJSON *item, const char *str) unsigned uc, uc2; if (*str != '\"') { - ep = str; + *ep = str; return 0; } /* not a string! */ @@ -394,11 +396,11 @@ static char *print_string(cJSON *item) } /* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item, const char *value); +static const char *parse_value(cJSON *item, const char *value, const char **ep); static char *print_value(cJSON *item, int depth, int fmt); -static const char *parse_array(cJSON *item, const char *value); +static const char *parse_array(cJSON *item, const char *value, const char **ep); static char *print_array(cJSON *item, int depth, int fmt); -static const char *parse_object(cJSON *item, const char *value); +static const char *parse_object(cJSON *item, const char *value, const char **ep); static char *print_object(cJSON *item, int depth, int fmt); /* Utility to jump whitespace and cr/lf */ @@ -410,14 +412,14 @@ static const char *skip(const char *in) } /* Parse an object - create a new root, and populate. */ -cJSON *cJSON_Parse(const char *value) +cJSON *cJSON_Parse(const char *value, const char **ep) { cJSON *c = cJSON_New_Item(); - ep = 0; + *ep = 0; if (!c) return 0; /* memory fail */ - if (!parse_value(c, skip(value))) + if (!parse_value(c, skip(value), ep)) { cJSON_Delete(c); return 0; @@ -436,7 +438,7 @@ char *cJSON_PrintUnformatted(cJSON *item) } /* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item, const char *value) +static const char *parse_value(cJSON *item, const char *value, const char **ep) { if (!value) return 0; /* Fail on null. */ @@ -458,7 +460,7 @@ static const char *parse_value(cJSON *item, const char *value) } if (*value == '\"') { - return parse_string(item, value); + return parse_string(item, value, ep); } if (*value == '-' || (*value >= '0' && *value <= '9')) { @@ -466,14 +468,14 @@ static const char *parse_value(cJSON *item, const char *value) } if (*value == '[') { - return parse_array(item, value); + return parse_array(item, value, ep); } if (*value == '{') { - return parse_object(item, value); + return parse_object(item, value, ep); } - ep = value; + *ep = value; return 0; /* failure. */ } @@ -514,12 +516,12 @@ static char *print_value(cJSON *item, int depth, int fmt) } /* Build an array from input text. */ -static const char *parse_array(cJSON *item, const char *value) +static const char *parse_array(cJSON *item, const char *value, const char **ep) { cJSON *child; if (*value != '[') { - ep = value; + *ep = value; return 0; } /* not an array! */ @@ -531,7 +533,7 @@ static const char *parse_array(cJSON *item, const char *value) item->child = child = cJSON_New_Item(); if (!item->child) return 0; /* memory fail */ - value = skip(parse_value(child, skip(value))); /* skip any spacing, get the value. */ + value = skip(parse_value(child, skip(value), ep)); /* skip any spacing, get the value. */ if (!value) return 0; @@ -543,14 +545,14 @@ static const char *parse_array(cJSON *item, const char *value) child->next = new_item; new_item->prev = child; child = new_item; - value = skip(parse_value(child, skip(value + 1))); + value = skip(parse_value(child, skip(value + 1), ep)); if (!value) return 0; /* memory fail */ } if (*value == ']') return value + 1; /* end of array */ - ep = value; + *ep = value; return 0; /* malformed. */ } @@ -625,12 +627,12 @@ static char *print_array(cJSON *item, int depth, int fmt) } /* Build an object from the text. */ -static const char *parse_object(cJSON *item, const char *value) +static const char *parse_object(cJSON *item, const char *value, const char **ep) { cJSON *child; if (*value != '{') { - ep = value; + *ep = value; return 0; } /* not an object! */ @@ -642,17 +644,17 @@ static const char *parse_object(cJSON *item, const char *value) item->child = child = cJSON_New_Item(); if (!item->child) return 0; - value = skip(parse_string(child, skip(value))); + value = skip(parse_string(child, skip(value), ep)); if (!value) return 0; child->string = child->valuestring; child->valuestring = 0; if (*value != ':') { - ep = value; + *ep = value; return 0; } /* fail! */ - value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ + value = skip(parse_value(child, skip(value + 1), ep)); /* skip any spacing, get the value. */ if (!value) return 0; @@ -664,24 +666,24 @@ static const char *parse_object(cJSON *item, const char *value) child->next = new_item; new_item->prev = child; child = new_item; - value = skip(parse_string(child, skip(value + 1))); + value = skip(parse_string(child, skip(value + 1), ep)); if (!value) return 0; child->string = child->valuestring; child->valuestring = 0; if (*value != ':') { - ep = value; + *ep = value; return 0; } /* fail! */ - value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ + value = skip(parse_value(child, skip(value + 1), ep)); /* skip any spacing, get the value. */ if (!value) return 0; } if (*value == '}') return value + 1; /* end of array */ - ep = value; + *ep = value; return 0; /* malformed. */ } diff --git a/src/util/json/cJSON.h b/src/util/json/cJSON.h index 506d7f3f..717a748e 100644 --- a/src/util/json/cJSON.h +++ b/src/util/json/cJSON.h @@ -74,7 +74,7 @@ typedef struct cJSON_Hooks extern void cJSON_InitHooks(cJSON_Hooks* hooks); /* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ -extern cJSON *cJSON_Parse(const char *value); +extern cJSON *cJSON_Parse(const char *value, const char **ep); /* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ extern char *cJSON_Print(cJSON *item); /* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ @@ -89,8 +89,9 @@ extern cJSON *cJSON_GetArrayItem(cJSON *array, int item); /* Get item "string" from object. Case insensitive. */ extern cJSON *cJSON_GetObjectItem(cJSON *object, const char *string); +/* remove gloal variable for thread safe. --by Bwar on 2020-11-15 */ /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ -extern const char *cJSON_GetErrorPtr(); +/* extern const char *cJSON_GetErrorPtr(); */ /* These calls create a cJSON item of the appropriate type. */ extern cJSON *cJSON_CreateNull(); From f6fb65bc032a5384317615c45765694b004f4216 Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 1 Mar 2021 00:21:33 +0800 Subject: [PATCH 124/176] add http2 flow control; rename model to operator --- src/Makefile | 4 +- src/actor/Actor.cpp | 4 +- src/actor/Actor.hpp | 8 +- src/actor/ActorBuilder.cpp | 30 ++--- src/actor/ActorBuilder.hpp | 18 +-- src/actor/chain/Chain.cpp | 8 +- src/actor/cmd/GrpcModule.cpp | 10 +- src/actor/model/Model.hpp | 58 --------- src/actor/operator/Operator.hpp | 62 +++++++++ src/actor/step/sys_step/StepRedisCluster.cpp | 16 +-- src/channel/SocketChannelImpl.cpp | 2 +- src/codec/http2/CodecHttp2.cpp | 126 ++++++++++++------- src/codec/http2/CodecHttp2.hpp | 10 +- src/codec/http2/H2Comm.hpp | 9 ++ src/codec/http2/Http2Frame.hpp | 4 + src/ios/Dispatcher.cpp | 1 + 16 files changed, 217 insertions(+), 153 deletions(-) delete mode 100644 src/actor/model/Model.hpp create mode 100644 src/actor/operator/Operator.hpp diff --git a/src/Makefile b/src/Makefile index a1c3111f..316adc3b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -38,8 +38,8 @@ LDFLAGS := $(LDFLAGS) -D_LINUX_OS_ \ -L$(LIB3RD_PATH)/lib -lprotobuf \ -L$(SYSTEM_LIB_PATH) -lc -lrt -ldl -SUB_INCLUDE = channel ios labor codec pb mydis logger -DEEP_SUB_INCLUDE = actor util +SUB_INCLUDE = channel ios labor pb mydis logger +DEEP_SUB_INCLUDE = actor util codec CPP_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cpp)) CC_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cc)) C_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c)) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index aac50748..ec353150 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -128,9 +128,9 @@ bool Actor::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& strErrMsg, return(m_pLabor->GetActorBuilder()->ExecStep(uiStepSeq, iErrno, strErrMsg, data)); } -std::shared_ptr Actor::GetModel(const std::string& strModelName) +std::shared_ptr Actor::GetOperator(const std::string& strOperatorName) { - return(m_pLabor->GetActorBuilder()->GetModel(strModelName)); + return(m_pLabor->GetActorBuilder()->GetOperator(strOperatorName)); } std::shared_ptr Actor::GetContext() diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 9eddec59..f7645c49 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -53,7 +53,7 @@ class Session; class Timer; class Context; class Step; -class Model; +class Operator; class Chain; class Actor: public std::enable_shared_from_this @@ -71,8 +71,8 @@ class Actor: public std::enable_shared_from_this ACT_HTTP_STEP = 7, ///< Step步骤对象,处理http请求或响应 ACT_REDIS_STEP = 8, ///< Step步骤对象,处理redis请求或响应 ACT_RAW_STEP = 9, ///< Step步骤对象,处理未经编解码的裸数据 - ACT_MODEL = 10, ///< Model模型对象,Model(IO无关)与Step(异步IO相关)共同构成功能链 - ACT_CHAIN = 11, ///< Chain链对象,用于将Model和Step组合成功能链 + ACT_OPERATOR = 10, ///< Operator算子对象,Operator(IO无关)与Step(异步IO相关)共同构成功能链 + ACT_CHAIN = 11, ///< Chain链对象,用于将Operator和Step组合成功能链 }; public: @@ -125,7 +125,7 @@ class Actor: public std::enable_shared_from_this std::shared_ptr GetSession(uint32 uiSessionId); std::shared_ptr GetSession(const std::string& strSessionId); bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); - std::shared_ptr GetModel(const std::string& strModelName); + std::shared_ptr GetOperator(const std::string& strOperatorName); std::shared_ptr GetContext(); void SetContext(std::shared_ptr pContext); void AddAssemblyLine(std::shared_ptr pSession); diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 74e4aeb5..a8f6ee37 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -22,7 +22,7 @@ #include "step/RawStep.hpp" #include "cmd/RedisCmd.hpp" #include "cmd/RawCmd.hpp" -#include "model/Model.hpp" +#include "operator/Operator.hpp" #include "chain/Chain.hpp" #include "actor/session/sys_session/SessionLogger.hpp" #include "ios/Dispatcher.hpp" @@ -875,8 +875,8 @@ std::shared_ptr ActorBuilder::InitializeSharedActor(Actor* pCreator, std: return(pSharedActor); } break; - case Actor::ACT_MODEL: - if (TransformToSharedModel(pCreator, pSharedActor)) + case Actor::ACT_OPERATOR: + if (TransformToSharedOperator(pCreator, pSharedActor)) { return(pSharedActor); } @@ -888,7 +888,7 @@ std::shared_ptr ActorBuilder::InitializeSharedActor(Actor* pCreator, std: } break; default: - LOG4_ERROR("\"%s\" must be a Step, a Session, a Model, a Cmd or a Module.", + LOG4_ERROR("\"%s\" must be a Step, a Session, a Operator, a Cmd or a Module.", strActorName.c_str()); return(nullptr); } @@ -1038,20 +1038,20 @@ bool ActorBuilder::TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor) +bool ActorBuilder::TransformToSharedOperator(Actor* pCreator, std::shared_ptr pSharedActor) { - std::shared_ptr pSharedModel = std::dynamic_pointer_cast(pSharedActor); - auto ret = m_mapModel.insert(std::make_pair(pSharedModel->GetActorName(), pSharedModel)); + std::shared_ptr pSharedOperator = std::dynamic_pointer_cast(pSharedActor); + auto ret = m_mapOperator.insert(std::make_pair(pSharedOperator->GetActorName(), pSharedOperator)); if (ret.second) { - if (pSharedModel->Init()) + if (pSharedOperator->Init()) { return(true); } } else { - LOG4_ERROR("operator \"%s\" exist.", pSharedModel->GetActorName().c_str()); + LOG4_ERROR("operator \"%s\" exist.", pSharedOperator->GetActorName().c_str()); } return(false); } @@ -1176,10 +1176,10 @@ bool ActorBuilder::ExecStep(uint32 uiStepSeq, int iErrno, const std::string& str } } -std::shared_ptr ActorBuilder::GetModel(const std::string& strModelName) +std::shared_ptr ActorBuilder::GetOperator(const std::string& strOperatorName) { - auto iter = m_mapModel.find(strModelName); - if (iter == m_mapModel.end()) + auto iter = m_mapOperator.find(strOperatorName); + if (iter == m_mapOperator.end()) { return(nullptr); } @@ -1489,10 +1489,10 @@ void ActorBuilder::UnloadDynamicSymbol(CJsonObject& oOneSoConf) } for (int k = 0; k < oOneSoConf["model"].GetArraySize(); ++k) { - auto class_iter = m_mapModel.find(oOneSoConf["model"](k)); - if (class_iter != m_mapModel.end()) + auto class_iter = m_mapOperator.find(oOneSoConf["model"](k)); + if (class_iter != m_mapOperator.end()) { - m_mapModel.erase(class_iter); + m_mapOperator.erase(class_iter); } } } diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 47aa833a..857b4da9 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -58,7 +58,7 @@ class Context; class Step; class RedisStep; class HttpStep; -class Model; +class Operator; class Chain; class SessionLogger; @@ -124,7 +124,7 @@ class ActorBuilder std::shared_ptr MakeSharedContext(Actor* pCreator, const std::string& strContextName, Targs&&... args); template - std::shared_ptr MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs&&... args); + std::shared_ptr MakeSharedOperator(Actor* pCreator, const std::string& strOperatorName, Targs&&... args); template std::shared_ptr MakeSharedChain(Actor* pCreator, const std::string& strChainName, Targs&&... args); @@ -135,7 +135,7 @@ class ActorBuilder bool TransformToSharedModule(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor); - bool TransformToSharedModel(Actor* pCreator, std::shared_ptr pSharedActor); + bool TransformToSharedOperator(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); public: @@ -143,7 +143,7 @@ class ActorBuilder virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); - virtual std::shared_ptr GetModel(const std::string& strModelName); + virtual std::shared_ptr GetOperator(const std::string& strOperatorName); virtual bool ResetTimeout(std::shared_ptr pSharedActor); int32 GetStepNum(); bool ReloadCmdConf(); @@ -183,10 +183,10 @@ class ActorBuilder std::unordered_map > m_mapCmd; std::unordered_map > m_mapModule; - // Chain and Model - std::unordered_map > > m_mapChainConf; //key为Chain的配置名(ChainFlag),value为由Model类名和Step类名构成的ChainBlock链 + // Chain and Operator + std::unordered_map > > m_mapChainConf; //key为Chain的配置名(ChainFlag),value为由Operator类名和Step类名构成的ChainBlock链 std::unordered_map > m_mapChain; //key为Chain的Sequence,称为ChainId - std::unordered_map > m_mapModel; //key为Model类名 + std::unordered_map > m_mapOperator; //key为Operator类名 // Step and Session std::unordered_map > m_mapCallbackStep; @@ -270,9 +270,9 @@ std::shared_ptr ActorBuilder::MakeSharedContext(Actor* pCreator, const } template -std::shared_ptr ActorBuilder::MakeSharedModel(Actor* pCreator, const std::string& strModelName, Targs&&... args) +std::shared_ptr ActorBuilder::MakeSharedOperator(Actor* pCreator, const std::string& strOperatorName, Targs&&... args) { - return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strModelName, std::forward(args)...))); + return(std::dynamic_pointer_cast(MakeSharedActor(pCreator, strOperatorName, std::forward(args)...))); } template diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 74b45674..91e465f8 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -12,7 +12,7 @@ #include "util/json/CJsonObject.hpp" #include "actor/context/Context.hpp" #include "actor/step/Step.hpp" -#include "actor/model/Model.hpp" +#include "actor/operator/Operator.hpp" namespace neb { @@ -83,7 +83,7 @@ E_CMD_STATUS Chain::Next() for (auto iter = vecTurnBlocks.begin(); iter != vecTurnBlocks.end(); ++iter) { LOG4_TRACE("(%s)", (*iter).c_str()); - std::shared_ptr pSharedModel = GetModel(*iter); + std::shared_ptr pSharedModel = GetModel(*iter); if (pSharedModel == nullptr) { std::shared_ptr pSharedActor = MakeSharedActor(*iter); @@ -94,9 +94,9 @@ E_CMD_STATUS Chain::Next() break; } // pSharedModel->SetContext(GetContext()); it had been set in MakeSharedActor(). - if (Actor::ACT_MODEL == pSharedActor->GetActorType()) + if (Actor::ACT_OPERATOR == pSharedActor->GetActorType()) { - pSharedModel = std::dynamic_pointer_cast(pSharedActor); + pSharedModel = std::dynamic_pointer_cast(pSharedActor); eResult = pSharedModel->Submit(); pSharedModel->SetContext(nullptr); pSharedModel->SetTraceId(""); diff --git a/src/actor/cmd/GrpcModule.cpp b/src/actor/cmd/GrpcModule.cpp index b3c4980e..4a364e72 100644 --- a/src/actor/cmd/GrpcModule.cpp +++ b/src/actor/cmd/GrpcModule.cpp @@ -25,16 +25,14 @@ GrpcModule::~GrpcModule() bool GrpcModule::AnyMessage( std::shared_ptr pChannel, const HttpMsg& oHttpMsg) { - char szLength[5] = {0}; uint8 ucCompressedFlag = 0; uint32 uiMessageLength = 0; std::string strMessage; ucCompressedFlag = oHttpMsg.body()[0]; - szLength[0] = oHttpMsg.body()[4]; - szLength[1] = oHttpMsg.body()[3]; - szLength[2] = oHttpMsg.body()[2]; - szLength[3] = oHttpMsg.body()[1]; - uiMessageLength = StringConverter::RapidAtoi(szLength); + uiMessageLength |= (oHttpMsg.body()[4] & 0xFF); + uiMessageLength |= ((oHttpMsg.body()[3] << 8) & 0xFF); + uiMessageLength |= ((oHttpMsg.body()[2] << 16) & 0xFF); + uiMessageLength |= ((oHttpMsg.body()[1] << 24) & 0xFF); if (ucCompressedFlag) { auto iter = oHttpMsg.headers().find("grpc-encoding"); diff --git a/src/actor/model/Model.hpp b/src/actor/model/Model.hpp deleted file mode 100644 index 3c2da09c..00000000 --- a/src/actor/model/Model.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file Model.hpp - * @brief - * @author Bwar - * @date: 2019年6月7日 - * @note - * Modify history: - ******************************************************************************/ -#ifndef SRC_ACTOR_MODEL_HPP_ -#define SRC_ACTOR_MODEL_HPP_ - -#include "actor/Actor.hpp" -#include "actor/DynamicCreator.hpp" - -namespace neb -{ - -class ActorBuilder; - -class Model: public Actor -{ -public: - Model() - : Actor(Actor::ACT_MODEL, gc_dNoTimeout) - { - } - Model(const Model&) = delete; - Model& operator=(const Model&) = delete; - virtual ~Model(){} - - /** - * @brief 初始化Model;重新加载配置 - * @note Model类实例初始化函数,大部分Model不需要初始化,需要初始化的Model可派生后实现此函数, - * 在此函数里可以读取配置文件(配置文件必须为json格式)。配置文件由Model的设计者自行定义, - * 存放于conf/目录,配置文件名最好与Model名字保持一致,加上.json后缀。配置文件的更新同步 - * 会由框架自动完成。 - * Init()需设计成可重入方法,因各服务框架在收到Beacon的重新加载配置指令后会执行每个 - * Model的Init()方法,所以必须保证Init()方法执行后没有副作用。 - * @return 是否初始化成功 - */ - virtual bool Init() - { - return(true); - } - - /** - * @brief 提交 - */ - virtual E_CMD_STATUS Submit() = 0; - -private: - friend class ActorBuilder; -}; - -} /* namespace neb */ - -#endif /* SRC_ACTOR_MODEL_HPP_ */ diff --git a/src/actor/operator/Operator.hpp b/src/actor/operator/Operator.hpp new file mode 100644 index 00000000..c45de68d --- /dev/null +++ b/src/actor/operator/Operator.hpp @@ -0,0 +1,62 @@ +/******************************************************************************* + * Project: Nebula + * @file Operator.hpp + * @brief + * @author Bwar + * @date: 2019年6月7日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_OPERATOR_HPP_ +#define SRC_ACTOR_OPERATOR_HPP_ + +#include "actor/Actor.hpp" +#include "actor/DynamicCreator.hpp" + +namespace neb +{ + +class ActorBuilder; + +/** + * @brief 算子 + * @note 用于IO无关的计算操作,比如推荐系统中的特征算子和模型计算 + */ +class Operator: public Actor +{ +public: + Operator() + : Actor(Actor::ACT_OPERATOR, gc_dNoTimeout) + { + } + Operator(const Operator&) = delete; + Operator& operator=(const Operator&) = delete; + virtual ~Operator(){} + + /** + * @brief 初始化Operator;重新加载配置 + * @note Operator类实例初始化函数,大部分Operator不需要初始化,需要初始化的Operator可派生后实现此函数, + * 在此函数里可以读取配置文件(配置文件必须为json格式)。配置文件由Operator的设计者自行定义, + * 存放于conf/目录,配置文件名最好与Operator名字保持一致,加上.json后缀。配置文件的更新同步 + * 会由框架自动完成。 + * Init()需设计成可重入方法,因各服务框架在收到Beacon的重新加载配置指令后会执行每个 + * Operator的Init()方法,所以必须保证Init()方法执行后没有副作用。 + * @return 是否初始化成功 + */ + virtual bool Init() + { + return(true); + } + + /** + * @brief 提交 + */ + virtual E_CMD_STATUS Submit() = 0; + +private: + friend class ActorBuilder; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_MODEL_HPP_ */ diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index 37345766..fe8da40c 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -22,6 +22,7 @@ const uint16 StepRedisCluster::sc_unClusterSlots = 16384; const std::unordered_set StepRedisCluster::s_setSupportExtractCmd = { + "PING", // strings "APPEND","BITCOUNT","BITFIELD","BITPOS","DECR","DECRBY","GET", "GETBIT","GETRANGE","GETSET","INCR","INCRBY","INCRBYFLOAT","MGET", @@ -566,16 +567,17 @@ bool StepRedisCluster::ExtractCmd(const RedisMsg& oRedisMsg, { if (oRedisMsg.element_size() < 2) { - LOG4_ERROR("param num fault of %s, invalid redis cmd: %s", - strCmd.c_str(), oRedisMsg.DebugString().c_str()); - return(false); + strTag = ""; } - if (REDIS_REPLY_STRING != oRedisMsg.element(1).type() || oRedisMsg.element(1).str().size() == 0) + else { - LOG4_ERROR("cmd element be a REDIS_REPLY_STRING, invalid redis cmd: %s", oRedisMsg.DebugString().c_str()); - return(false); + if (REDIS_REPLY_STRING != oRedisMsg.element(1).type() || oRedisMsg.element(1).str().size() == 0) + { + LOG4_ERROR("cmd element be a REDIS_REPLY_STRING, invalid redis cmd: %s", oRedisMsg.DebugString().c_str()); + return(false); + } + GetTag(oRedisMsg.element(1).str(), strTag); } - GetTag(oRedisMsg.element(1).str(), strTag); vecHashKeys.emplace_back(std::move(strTag)); if (s_setWriteCmd.find(strCmd) == s_setWriteCmd.end()) { diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 2af4c568..f9453ed7 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -373,7 +373,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq return(CODEC_STATUS_OK); } - if (CODEC_STATUS_OK != eCodecStatus || CODEC_STATUS_PART_OK != eCodecStatus) + if (CODEC_STATUS_OK != eCodecStatus && CODEC_STATUS_PART_OK != eCodecStatus) { return(eCodecStatus); } diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index 6f51aa0d..a627cd56 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -26,6 +26,9 @@ CodecHttp2::CodecHttp2(std::shared_ptr pLogger, try { m_pFrame = new Http2Frame(pLogger, eCodecType); + m_pStreamWeightRoot = new TreeNodepData = new tagStreamWeight(); + m_pStreamWeightRoot->pData->uiStreamId = 0; } catch(std::bad_alloc& e) { @@ -35,8 +38,8 @@ CodecHttp2::CodecHttp2(std::shared_ptr pLogger, CodecHttp2::~CodecHttp2() { - ReleaseStreamWeight(m_pStreamWeight); - m_pStreamWeight = nullptr; + ReleaseStreamWeight(m_pStreamWeightRoot); + m_pStreamWeightRoot = nullptr; for (auto iter = m_mapStream.begin(); iter != m_mapStream.end(); ++iter) { delete iter->second; @@ -168,11 +171,6 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) return(eCodecStatus); } -E_CODEC_STATUS CodecHttp2::Encode(CBuffer* pBuff) -{ - // TODO encode not complete stream data frame -} - E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) { LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); @@ -301,15 +299,14 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR if (m_stFrameHead.uiStreamIdentifier <= m_uiStreamIdGenerate) { SetErrno(H2_ERR_PROTOCOL_ERROR); - m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "The endpoint " - "detected an unspecific protocol error. This error is for " - "use when a more specific error code is not available.", pReactBuff); - LOG4_TRACE("The endpoint detected an unspecific protocol error. This error is for " - "use when a more specific error code is not available."); + m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "The " + "identifier of a newly established stream MUST be " + "numerically greater than all streams that the " + "initiating endpoint has opened or reserved.", pReactBuff); return(CODEC_STATUS_ERR); } m_uiStreamIdGenerate = m_stFrameHead.uiStreamIdentifier; - if (NewCodingStream(oHttpMsg.stream_id()) == nullptr) + if (NewCodingStream(m_stFrameHead.uiStreamIdentifier) == nullptr) { return(CODEC_STATUS_ERR); } @@ -352,27 +349,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR void CodecHttp2::SetPriority(uint32 uiStreamId, const tagPriority& stPriority) { - if (m_pStreamWeight == nullptr) - { - try - { - m_pStreamWeight = new TreeNode(); - m_pStreamWeight->pData = new tagStreamWeight(); - m_pStreamWeight->pData->uiStreamId = stPriority.uiDependency; - m_pStreamWeight->pFirstChild = new TreeNode(); - m_pStreamWeight->pFirstChild->pData = new tagStreamWeight(); - m_pStreamWeight->pFirstChild->pData->uiStreamId = uiStreamId; - m_pStreamWeight->pFirstChild->pData->ucWeight = stPriority.ucWeight; - m_pStreamWeight->pFirstChild->pParent = m_pStreamWeight; - } - catch(std::bad_alloc& e) - { - LOG4_ERROR("%s", e.what()); - return; - } - } - - auto pCurrentStreamWeight = FindStreamWeight(uiStreamId, m_pStreamWeight); + auto pCurrentStreamWeight = FindStreamWeight(uiStreamId, m_pStreamWeightRoot); if (pCurrentStreamWeight == nullptr) { try @@ -404,11 +381,11 @@ void CodecHttp2::SetPriority(uint32 uiStreamId, const tagPriority& stPriority) pLast->pRightBrother = pCurrentStreamWeight->pRightBrother; } } - auto pDependencyStreamWeight = FindStreamWeight(stPriority.uiDependency, m_pStreamWeight); + auto pDependencyStreamWeight = FindStreamWeight(stPriority.uiDependency, m_pStreamWeightRoot); if (pDependencyStreamWeight == nullptr) { - pCurrentStreamWeight->pRightBrother = m_pStreamWeight->pRightBrother; - m_pStreamWeight->pRightBrother = pCurrentStreamWeight; + pCurrentStreamWeight->pRightBrother = m_pStreamWeightRoot->pRightBrother; + m_pStreamWeightRoot->pRightBrother = pCurrentStreamWeight; } else { @@ -430,7 +407,7 @@ void CodecHttp2::RstStream(uint32 uiStreamId) auto iter = m_mapStream.find(uiStreamId); if (iter != m_mapStream.end()) { - auto pStreamWeight = FindStreamWeight(uiStreamId, m_pStreamWeight); + auto pStreamWeight = FindStreamWeight(uiStreamId, m_pStreamWeightRoot); if (pStreamWeight != nullptr) { auto pParent = pStreamWeight->pParent; @@ -504,7 +481,7 @@ E_H2_ERR_CODE CodecHttp2::Setting(const std::vector& vecSetting) { for (auto it = m_mapStream.begin(); it != m_mapStream.end(); ++it) { - it->second->WindowUpdate((int32)vecSetting[i].uiValue - m_uiSettingsMaxWindowSize); + it->second->WindowInit(vecSetting[i].uiValue - m_uiSettingsMaxWindowSize); } m_uiSettingsMaxWindowSize = vecSetting[i].uiValue; } @@ -536,16 +513,45 @@ E_H2_ERR_CODE CodecHttp2::Setting(const std::vector& vecSetting) void CodecHttp2::WindowUpdate(uint32 uiStreamId, uint32 uiIncrement) { - if (uiStreamId == 0) + m_uiSendWindowSize += uiIncrement; + if (uiStreamId > 0) { - m_uiSendWindowSize += uiIncrement; + auto iter = m_mapStream.find(uiStreamId); + if (iter != m_mapStream.end()) + { + iter->second->WindowUpdate((int32)uiIncrement); + } } - else +} + +void CodecHttp2::ShrinkSendWindow(uint32 uiStreamId, uint32 uiSendLength) +{ + m_uiSendWindowSize -= uiSendLength; + if (uiStreamId > 0) + { + auto iter = m_mapStream.find(uiStreamId); + if (iter != m_mapStream.end()) + { + iter->second->WindowUpdate(-uiSendLength); + } + } +} + +void CodecHttp2::ShrinkRecvWindow(uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) +{ + m_uiRecvWindowSize -= uiRecvLength; + if (m_uiRecvWindowSize < DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE / 4) + { + m_pFrame->EncodeWindowUpdate(this, 0, + SETTINGS_MAX_INITIAL_WINDOW_SIZE - m_uiRecvWindowSize, pBuff); + } + m_uiRecvWindowSize = SETTINGS_MAX_INITIAL_WINDOW_SIZE; + if (uiStreamId > 0) { auto iter = m_mapStream.find(uiStreamId); if (iter != m_mapStream.end()) { - iter->second->WindowUpdate(uiIncrement); + iter->second->ShrinkRecvWindow(this, uiStreamId, uiRecvLength, pBuff); } } } @@ -742,6 +748,40 @@ E_CODEC_STATUS CodecHttp2::PromiseStream(uint32 uiStreamId, CBuffer* pReactBuff) return(CODEC_STATUS_PART_OK); } +E_CODEC_STATUS CodecHttp2::SendWaittingFrameData(CBuffer* pBuff) +{ + if (m_pStreamWeightRoot != nullptr) + { + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + uint32 uiStreamId = 0; + std::vector vecCompleteStream; + auto pCurrent = m_pStreamWeightRoot->pFirstChild; + while (pCurrent) + { + uiStreamId = pCurrent->pData->uiStreamId; + auto iter = m_mapStream.find(uiStreamId); + if (iter != m_mapStream.end()) + { + eStatus = iter->second->SendWaittingFrameData(this, pBuff); + if (eStatus == CODEC_STATUS_OK) + { + vecCompleteStream.push_back(uiStreamId); + } + else + { + break; + } + } + } + for (auto id : vecCompleteStream) + { + CloseStream(id); + } + return(eStatus); + } + return(CODEC_STATUS_OK); +} + void CodecHttp2::TransferHoldingMsg(HttpMsg* pHoldingHttpMsg) { m_pHoldingHttpMsg = pHoldingHttpMsg; diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index e8ec3aec..13d6d79c 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -49,7 +49,6 @@ class CodecHttp2: public Codec } virtual E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff); - virtual E_CODEC_STATUS Encode(CBuffer* pBuff); virtual E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff); /** @@ -66,6 +65,12 @@ class CodecHttp2: public Codec void RstStream(uint32 uiStreamId); E_H2_ERR_CODE Setting(const std::vector& vecSetting); void WindowUpdate(uint32 uiStreamId, uint32 uiIncrement); + void ShrinkSendWindow(uint32 uiStreamId, uint32 uiSendLength); + void ShrinkSendWindow(uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff); + uint32 GetSendWindowSize() + { + return(m_uiSendWindowSize); + } uint32 GetMaxFrameSize() { return(m_uiSettingsMaxFrameSize); @@ -83,6 +88,7 @@ class CodecHttp2: public Codec return(m_uiStreamIdGenerate); } + E_CODEC_STATUS SendWaittingFrameData(CBuffer* pBuff); void TransferHoldingMsg(HttpMsg* pHoldingHttpMsg); protected: @@ -132,7 +138,7 @@ class CodecHttp2: public Codec Http2Frame* m_pFrame = nullptr; Http2Stream* m_pCodingStream = nullptr; std::unordered_map m_mapStream; - TreeNode* m_pStreamWeight = nullptr; + TreeNode* m_pStreamWeightRoot = nullptr; uint32 m_uiEncodingDynamicTableSize = 0; uint32 m_uiDecodingDynamicTableSize = 0; diff --git a/src/codec/http2/H2Comm.hpp b/src/codec/http2/H2Comm.hpp index 47b96d3e..0139a199 100644 --- a/src/codec/http2/H2Comm.hpp +++ b/src/codec/http2/H2Comm.hpp @@ -65,6 +65,15 @@ struct tagH2FrameHead uint8 ucFlag = 0; ///< 为帧类型专用的布尔标志保留的8位字段。标志被分配特定于指定帧类型的语义。没有为特定帧类型定义语义的标志务必被忽略,并且在发送时务必保持未设置(0x0)。 ///< 保留的1位字段。该位的语义是未定义的,并且该位必须在发送时保持未设置(0x0),并且在接收时必须忽略。 uint32 uiLength = 0; ///< 帧有效载荷的长度,表示为无符号的24位整数。除非接收方为SETTINGS_MAX_FRAME_SIZE设置了较大的值,否则不得发送大于2 ^ 14(16,384)的值。帧头的9个八位字节不包含在该值中。 uint32 uiStreamIdentifier = 0; ///< 流标识符,表示为一个无符号的31位整数。值0x0保留给与整个连接相关联的帧,而不是单个流。 + tagH2FrameHead& operator=(const tagH2FrameHead stFrameHead) + { + cR = stFrameHead.cR; + ucType = stFrameHead.ucType; + ucFlag = stFrameHead.ucFlag; + uiLength = stFrameHead.uiLength; + uiStreamIdentifier = stFrameHead.uiStreamIdentifier; + return(*this); + } }; struct tagPriority diff --git a/src/codec/http2/Http2Frame.hpp b/src/codec/http2/Http2Frame.hpp index 318b6d1d..4abdedee 100644 --- a/src/codec/http2/Http2Frame.hpp +++ b/src/codec/http2/Http2Frame.hpp @@ -11,6 +11,7 @@ #define SRC_CODEC_HTTP2_HTTP2FRAME_HPP_ #include +#include #include "Definition.hpp" #include "codec/Codec.hpp" #include "pb/http.pb.h" @@ -73,6 +74,7 @@ class Http2Frame: public Codec virtual E_CODEC_STATUS Decode(CodecHttp2* pCodecH2, const tagH2FrameHead& stFrameHead, CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff); + E_CODEC_STATUS SendWaittingFrameData(CodecHttp2* pCodecH2, CBuffer* pBuff); protected: E_CODEC_STATUS DecodeData(CodecHttp2* pCodecH2, @@ -145,6 +147,8 @@ class Http2Frame: public Codec friend class CodecHttp2; friend class Http2Stream; + std::list m_listWaittingFrameData; + tagH2FrameHead m_stLastDataFrameHead; Http2Stream* m_pStream; }; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 25efcd31..9207710b 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -567,6 +567,7 @@ bool Dispatcher::FdTransfer(int iFd) else { pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + pChannel->m_pImpl->Send(); AddIoTimeout(pChannel, 1.0); // 为了防止大量连接攻击,初始化连接只有一秒即超时,在正常发送第一个数据包之后才采用正常配置的网络IO超时检查 } return(true); From 4b23d72a2a63f4dfed430cc2e2f02dd5425670ec Mon Sep 17 00:00:00 2001 From: yuanboshe Date: Tue, 2 Mar 2021 17:04:07 +0800 Subject: [PATCH 125/176] Refine ActorBuilder --- src/actor/ActorBuilder.cpp | 168 +++++++++++++++---------------------- 1 file changed, 69 insertions(+), 99 deletions(-) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index a8f6ee37..fbb3498e 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -182,13 +182,12 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH LOG4_DEBUG("cmd %u, seq %u", oMsgHead.cmd(), oMsgHead.seq()); if (gc_uiCmdReq & oMsgHead.cmd()) // 新请求 { - MsgHead oOutMsgHead; - MsgBody oOutMsgBody; + // 从conf/json配置表里面找到了cmd,则执行对应的actor auto cmd_iter = m_mapCmd.find(gc_uiCmdBit & oMsgHead.cmd()); if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) { if (oMsgBody.trace_id().length() > 10) - { + { cmd_iter->second->SetTraceId(oMsgBody.trace_id()); } else @@ -199,103 +198,74 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH } cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); } - else // 没有对应的cmd,是需由接入层转发的请求 - { - if (CMD_REQ_SET_LOG_LEVEL == oMsgHead.cmd()) - { - LogLevel oLogLevel; - oLogLevel.ParseFromString(oMsgBody.data()); - LOG4_INFO("log level set to %d, net_log_level set to %d", - oLogLevel.log_level(), oLogLevel.net_log_level()); - m_pLogger->SetLogLevel(oLogLevel.log_level()); - m_pLogger->SetNetLogLevel(oLogLevel.net_log_level()); - } - else if (CMD_REQ_RELOAD_SO == oMsgHead.cmd()) - { - CJsonObject oSoConfJson; - if (oSoConfJson.Parse(oMsgBody.data())) - { - DynamicLoad(oSoConfJson); - } - else - { - LOG4_ERROR("json parse string error: \"%s\""); - } - } - else - { - if (CODEC_NEBULA == pChannel->GetCodecType()) // 内部服务往客户端发送 if (std::string("0.0.0.0") == strFromIp) - { - cmd_iter = m_mapCmd.find(CMD_REQ_TO_CLIENT); - if (cmd_iter != m_mapCmd.end()) - { - if (oMsgBody.trace_id().length() > 10) - { - cmd_iter->second->SetTraceId(oMsgBody.trace_id()); - } - else - { - std::ostringstream oss; - oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); - cmd_iter->second->SetTraceId(oss.str()); - } - cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); - } - else - { - snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); - LOG4_ERROR(m_pErrBuff); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); - oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); - oOutMsgHead.set_cmd(CMD_RSP_SYS_ERROR); - oOutMsgHead.set_seq(oMsgHead.seq()); - oOutMsgHead.set_len(oOutMsgBody.ByteSize()); - } - } - else - { - cmd_iter = m_mapCmd.find(CMD_REQ_FROM_CLIENT); - if (cmd_iter != m_mapCmd.end()) - { - if (oMsgBody.trace_id().length() > 10) - { - cmd_iter->second->SetTraceId(oMsgBody.trace_id()); - } - else - { - std::ostringstream oss; - oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); - cmd_iter->second->SetTraceId(oss.str()); - } - cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); - } - else - { - snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); - LOG4_ERROR(m_pErrBuff); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); - oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); - oOutMsgHead.set_cmd(CMD_RSP_SYS_ERROR); - oOutMsgHead.set_seq(oMsgHead.seq()); - oOutMsgHead.set_len(oOutMsgBody.ByteSize()); - } - } -//#else - snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); - LOG4_ERROR(m_pErrBuff); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); - oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); - oOutMsgHead.set_cmd(CMD_RSP_SYS_ERROR); - oOutMsgHead.set_seq(oMsgHead.seq()); - oOutMsgHead.set_len(oOutMsgBody.ByteSize()); -//#endif - } - } - if (CMD_RSP_SYS_ERROR == oOutMsgHead.cmd()) + // 如果请求的cmd=9,则设置本server的log level + else if (CMD_REQ_SET_LOG_LEVEL == oMsgHead.cmd()) + { + LogLevel oLogLevel; + oLogLevel.ParseFromString(oMsgBody.data()); + LOG4_INFO("log level set to %d, net_log_level set to %d", + oLogLevel.log_level(), oLogLevel.net_log_level()); + m_pLogger->SetLogLevel(oLogLevel.log_level()); + m_pLogger->SetNetLogLevel(oLogLevel.net_log_level()); + } + // 如果请求的cmd=11,则在本server端重新加载动态库 + else if (CMD_REQ_RELOAD_SO == oMsgHead.cmd()) + { + CJsonObject oSoConfJson; + if (oSoConfJson.Parse(oMsgBody.data())) + { + DynamicLoad(oSoConfJson); + } + else + { + LOG4_ERROR("json parse string error: \"%s\""); + } + } + // 如果是内部消息,且从conf/json配置表里面找到cmd=503,则503对应的actor截获该请求并进行处理 + // 内部服务往客户端发送 if (std::string("0.0.0.0") == strFromIp) + else if (CODEC_NEBULA == pChannel->GetCodecType() && (cmd_iter = m_mapCmd.find(CMD_REQ_TO_CLIENT), cmd_iter != m_mapCmd.end())) + { + if (oMsgBody.trace_id().length() > 10) + { + cmd_iter->second->SetTraceId(oMsgBody.trace_id()); + } + else + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + cmd_iter->second->SetTraceId(oss.str()); + } + cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); + } + // 如果不是内部消息,且从conf/json配置表里面找到了cmd=501,则501对应的actor截获该请求并进行处理 + else if (cmd_iter = m_mapCmd.find(CMD_REQ_FROM_CLIENT), cmd_iter != m_mapCmd.end()) + { + if (oMsgBody.trace_id().length() > 10) + { + cmd_iter->second->SetTraceId(oMsgBody.trace_id()); + } + else + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + cmd_iter->second->SetTraceId(oss.str()); + } + cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); + } + // 如果前面的所有条件均不满足,则向请求方相应ERR_UNKNOWN_CMD的结果 + else { - LOG4_TRACE("no cmd handler."); - m_pLabor->GetDispatcher()->SendTo(pChannel, oOutMsgHead.cmd(), oOutMsgHead.seq(), oOutMsgBody); - return(false); + MsgHead oOutMsgHead; + MsgBody oOutMsgBody; + snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); + LOG4_ERROR(m_pErrBuff); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); + oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); + oOutMsgHead.set_cmd(oMsgHead.cmd() + 1); + oOutMsgHead.set_seq(oMsgHead.seq()); + oOutMsgHead.set_len(oOutMsgBody.ByteSize()); + m_pLabor->GetDispatcher()->SendTo(pChannel, oOutMsgHead.cmd(), oOutMsgHead.seq(), oOutMsgBody); + return(false); } } else // 回调 From 164a444c3f18829419b535120345d015579a9177 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 7 Mar 2021 23:31:08 +0800 Subject: [PATCH 126/176] h2 test passing --- src/actor/Actor.cpp | 4 +- src/actor/ActorBuilder.cpp | 155 ++++++++++++++++-------------- src/actor/chain/Chain.cpp | 2 +- src/channel/SocketChannelImpl.cpp | 4 +- src/codec/http2/CodecHttp2.cpp | 2 +- src/codec/http2/CodecHttp2.hpp | 2 +- src/codec/http2/Http2Frame.cpp | 122 ++++++++++++++++++++--- src/codec/http2/Http2Stream.cpp | 16 +++ src/codec/http2/Http2Stream.hpp | 7 ++ src/ios/Dispatcher.cpp | 23 +++-- src/ios/Dispatcher.hpp | 2 +- src/labor/Manager.cpp | 40 +++++--- src/labor/Worker.cpp | 24 ++++- 13 files changed, 291 insertions(+), 112 deletions(-) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index ec353150..9bc50f90 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -260,8 +260,8 @@ bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSe } else { - LOG4_ERROR("MsgBody is not a request message."); - return(false); + LOG4_ERROR("the request message has no req_target."); + return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); } } diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index fbb3498e..03df54fd 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -182,12 +182,13 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH LOG4_DEBUG("cmd %u, seq %u", oMsgHead.cmd(), oMsgHead.seq()); if (gc_uiCmdReq & oMsgHead.cmd()) // 新请求 { - // 从conf/json配置表里面找到了cmd,则执行对应的actor + MsgHead oOutMsgHead; + MsgBody oOutMsgBody; auto cmd_iter = m_mapCmd.find(gc_uiCmdBit & oMsgHead.cmd()); if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) { if (oMsgBody.trace_id().length() > 10) - { + { cmd_iter->second->SetTraceId(oMsgBody.trace_id()); } else @@ -198,74 +199,86 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH } cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); } - // 如果请求的cmd=9,则设置本server的log level - else if (CMD_REQ_SET_LOG_LEVEL == oMsgHead.cmd()) - { - LogLevel oLogLevel; - oLogLevel.ParseFromString(oMsgBody.data()); - LOG4_INFO("log level set to %d, net_log_level set to %d", - oLogLevel.log_level(), oLogLevel.net_log_level()); - m_pLogger->SetLogLevel(oLogLevel.log_level()); - m_pLogger->SetNetLogLevel(oLogLevel.net_log_level()); - } - // 如果请求的cmd=11,则在本server端重新加载动态库 - else if (CMD_REQ_RELOAD_SO == oMsgHead.cmd()) - { - CJsonObject oSoConfJson; - if (oSoConfJson.Parse(oMsgBody.data())) - { - DynamicLoad(oSoConfJson); - } - else - { - LOG4_ERROR("json parse string error: \"%s\""); - } - } - // 如果是内部消息,且从conf/json配置表里面找到cmd=503,则503对应的actor截获该请求并进行处理 - // 内部服务往客户端发送 if (std::string("0.0.0.0") == strFromIp) - else if (CODEC_NEBULA == pChannel->GetCodecType() && (cmd_iter = m_mapCmd.find(CMD_REQ_TO_CLIENT), cmd_iter != m_mapCmd.end())) - { - if (oMsgBody.trace_id().length() > 10) - { - cmd_iter->second->SetTraceId(oMsgBody.trace_id()); - } - else - { - std::ostringstream oss; - oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); - cmd_iter->second->SetTraceId(oss.str()); - } - cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); - } - // 如果不是内部消息,且从conf/json配置表里面找到了cmd=501,则501对应的actor截获该请求并进行处理 - else if (cmd_iter = m_mapCmd.find(CMD_REQ_FROM_CLIENT), cmd_iter != m_mapCmd.end()) - { - if (oMsgBody.trace_id().length() > 10) - { - cmd_iter->second->SetTraceId(oMsgBody.trace_id()); - } - else - { - std::ostringstream oss; - oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); - cmd_iter->second->SetTraceId(oss.str()); - } - cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); - } - // 如果前面的所有条件均不满足,则向请求方相应ERR_UNKNOWN_CMD的结果 - else + else // 没有对应的cmd,是需由接入层转发的请求 { - MsgHead oOutMsgHead; - MsgBody oOutMsgBody; - snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); - LOG4_ERROR(m_pErrBuff); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); - oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); - oOutMsgHead.set_cmd(oMsgHead.cmd() + 1); - oOutMsgHead.set_seq(oMsgHead.seq()); - oOutMsgHead.set_len(oOutMsgBody.ByteSize()); - m_pLabor->GetDispatcher()->SendTo(pChannel, oOutMsgHead.cmd(), oOutMsgHead.seq(), oOutMsgBody); - return(false); + if (CMD_REQ_SET_LOG_LEVEL == oMsgHead.cmd()) + { + LogLevel oLogLevel; + oLogLevel.ParseFromString(oMsgBody.data()); + LOG4_INFO("log level set to %d, net_log_level set to %d", + oLogLevel.log_level(), oLogLevel.net_log_level()); + m_pLogger->SetLogLevel(oLogLevel.log_level()); + m_pLogger->SetNetLogLevel(oLogLevel.net_log_level()); + } + else if (CMD_REQ_RELOAD_SO == oMsgHead.cmd()) + { + CJsonObject oSoConfJson; + if (oSoConfJson.Parse(oMsgBody.data())) + { + DynamicLoad(oSoConfJson); + } + else + { + LOG4_ERROR("json parse string error: \"%s\""); + } + } + else + { + if (CODEC_NEBULA == pChannel->GetCodecType()) // 内部服务往客户端发送 if (std::string("0.0.0.0") == strFromIp) + { + cmd_iter = m_mapCmd.find(CMD_REQ_TO_CLIENT); + if (cmd_iter != m_mapCmd.end()) + { + if (oMsgBody.trace_id().length() > 10) + { + cmd_iter->second->SetTraceId(oMsgBody.trace_id()); + } + else + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + cmd_iter->second->SetTraceId(oss.str()); + } + cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); + } + else + { + snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); + LOG4_ERROR(m_pErrBuff); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); + oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); + m_pLabor->GetDispatcher()->SendTo(pChannel, oMsgHead.cmd() + 1, oMsgHead.seq(), oOutMsgBody); + return(false); + } + } + else + { + cmd_iter = m_mapCmd.find(CMD_REQ_FROM_CLIENT); + if (cmd_iter != m_mapCmd.end()) + { + if (oMsgBody.trace_id().length() > 10) + { + cmd_iter->second->SetTraceId(oMsgBody.trace_id()); + } + else + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + cmd_iter->second->SetTraceId(oss.str()); + } + cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); + } + else + { + snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); + LOG4_ERROR(m_pErrBuff); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); + oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); + m_pLabor->GetDispatcher()->SendTo(pChannel, oMsgHead.cmd() + 1, oMsgHead.seq(), oOutMsgBody); + return(false); + } + } + } } } else // 回调 @@ -1200,7 +1213,7 @@ void ActorBuilder::BootLoadCmd(CJsonObject& oCmdConf) for (int i = 0; i < oCmdConf["cmd"].GetArraySize(); ++i) { oCmdConf["cmd"][i].Get("cmd", iCmd); - auto pCmd = MakeSharedCmd(nullptr, oCmdConf["cmd"][i]("class"), iCmd); + auto pCmd = MakeSharedCmd(nullptr, oCmdConf["cmd"][i]("class"), (int32)iCmd); if (pCmd != nullptr) { LOG4_INFO("succeed in loading %s with cmd %d", oCmdConf["cmd"][i]("class").c_str(), iCmd); @@ -1363,7 +1376,7 @@ void ActorBuilder::LoadDynamicSymbol(CJsonObject& oOneSoConf) std::unordered_set setCmd; m_mapLoadedCmd.insert(std::make_pair(oOneSoConf["cmd"][i]("class"), setCmd)); oOneSoConf["cmd"][i].Get("cmd", iCmd); - MakeSharedCmd(nullptr, oOneSoConf["cmd"][i]("class"), iCmd); + MakeSharedCmd(nullptr, oOneSoConf["cmd"][i]("class"), (int32)iCmd); } for (int j = 0; j < oOneSoConf["module"].GetArraySize(); ++j) { diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 91e465f8..80061410 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -83,7 +83,7 @@ E_CMD_STATUS Chain::Next() for (auto iter = vecTurnBlocks.begin(); iter != vecTurnBlocks.end(); ++iter) { LOG4_TRACE("(%s)", (*iter).c_str()); - std::shared_ptr pSharedModel = GetModel(*iter); + std::shared_ptr pSharedModel = GetOperator(*iter); if (pSharedModel == nullptr) { std::shared_ptr pSharedActor = MakeSharedActor(*iter); diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index f9453ed7..bb15b70d 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -143,7 +143,9 @@ ev_tstamp SocketChannelImpl::GetKeepAlive() bool SocketChannelImpl::NeedAliveCheck() const { - if (CODEC_HTTP == m_pCodec->GetCodecType() || CODEC_NEBULA == m_pCodec->GetCodecType()) + if (CODEC_HTTP == m_pCodec->GetCodecType() + || CODEC_NEBULA == m_pCodec->GetCodecType() + || CODEC_NEBULA_IN_NODE == m_pCodec->GetCodecType()) { return(false); } diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index a627cd56..1417c9ee 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -26,7 +26,7 @@ CodecHttp2::CodecHttp2(std::shared_ptr pLogger, try { m_pFrame = new Http2Frame(pLogger, eCodecType); - m_pStreamWeightRoot = new TreeNode(); m_pStreamWeightRoot->pData = new tagStreamWeight(); m_pStreamWeightRoot->pData->uiStreamId = 0; } diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index 13d6d79c..4629560c 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -66,7 +66,7 @@ class CodecHttp2: public Codec E_H2_ERR_CODE Setting(const std::vector& vecSetting); void WindowUpdate(uint32 uiStreamId, uint32 uiIncrement); void ShrinkSendWindow(uint32 uiStreamId, uint32 uiSendLength); - void ShrinkSendWindow(uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff); + void ShrinkRecvWindow(uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff); uint32 GetSendWindowSize() { return(m_uiSendWindowSize); diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index daddcd8b..7d201e98 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -23,6 +23,12 @@ Http2Frame::Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecTy Http2Frame::~Http2Frame() { m_pStream = nullptr; + for (auto iter = m_listWaittingFrameData.begin(); + iter != m_listWaittingFrameData.end(); ++iter) + { + DELETE(*iter); + } + m_listWaittingFrameData.clear(); LOG4_TRACE("codec type %d", GetCodecType()); } @@ -205,6 +211,7 @@ E_CODEC_STATUS Http2Frame::DecodeData(CodecHttp2* pCodecH2, oHttpMsg.mutable_body()->append(pBuff->GetRawReadBuffer(), stFrameHead.uiLength); pBuff->AdvanceReadIndex(stFrameHead.uiLength); } + pCodecH2->ShrinkRecvWindow(stFrameHead.uiStreamIdentifier, stFrameHead.uiLength, pReactBuff); return(CODEC_STATUS_PART_OK); } @@ -531,6 +538,14 @@ E_CODEC_STATUS Http2Frame::DecodeWindowUpdate(CodecHttp2* pCodecH2, else { pCodecH2->WindowUpdate(stFrameHead.uiStreamIdentifier, uiIncrement); + if (stFrameHead.uiStreamIdentifier > 0) + { + SendWaittingFrameData(pCodecH2, pReactBuff); + } + else + { + pCodecH2->SendWaittingFrameData(pReactBuff); + } } return(CODEC_STATUS_OK); } @@ -1039,11 +1054,38 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, eCodecStatus = CODEC_STATUS_OK; } stFrameHead.ucFlag |= H2_FRAME_FLAG_PADDED; - EncodeFrameHeader(stFrameHead, pBuff); - uint16 unNetLength = CodecUtil::H2N(strPadding.size()); - pBuff->Write(&unNetLength, 1); - pBuff->Write(pData, uiEncodedDataLen); - pBuff->Write(strPadding.c_str(), strPadding.size()); + if (uiEncodedDataLen < pCodecH2->GetSendWindowSize() + && (int32)uiEncodedDataLen < m_pStream->GetSendWindowSize()) + { + EncodeFrameHeader(stFrameHead, pBuff); + uint16 unNetLength = CodecUtil::H2N(strPadding.size()); + pBuff->Write(&unNetLength, 1); + pBuff->Write(pData, uiEncodedDataLen); + pBuff->Write(strPadding.c_str(), strPadding.size()); + pCodecH2->ShrinkSendWindow(uiStreamId, uiEncodedDataLen); + EncodeSetStreamState(stFrameHead); + } + else + { + CBuffer* pWaittingBuff = nullptr; + try + { + pWaittingBuff = new CBuffer(); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + return(CODEC_STATUS_PART_ERR); + } + m_listWaittingFrameData.push_back(pWaittingBuff); + EncodeFrameHeader(stFrameHead, pWaittingBuff); + uint16 unNetLength = CodecUtil::H2N(strPadding.size()); + pWaittingBuff->Write(&unNetLength, 1); + pWaittingBuff->Write(pData, uiEncodedDataLen); + pWaittingBuff->Write(strPadding.c_str(), strPadding.size()); + m_stLastDataFrameHead = stFrameHead; + eCodecStatus = CODEC_STATUS_PART_OK; + } } else { @@ -1060,15 +1102,73 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, } eCodecStatus = CODEC_STATUS_OK; } - uiEncodedDataLen = stFrameHead.uiLength; - EncodeFrameHeader(stFrameHead, pBuff); - pBuff->Write(pData, uiEncodedDataLen); - LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucFlag = 0x%X, pBuff->ReadableBytes() = %u", - stFrameHead.uiLength, stFrameHead.ucFlag, pBuff->ReadableBytes()); + if (uiEncodedDataLen < pCodecH2->GetSendWindowSize() + && (int32)uiEncodedDataLen < m_pStream->GetSendWindowSize()) + { + uiEncodedDataLen = stFrameHead.uiLength; + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(pData, uiEncodedDataLen); + pCodecH2->ShrinkSendWindow(uiStreamId, uiEncodedDataLen); + EncodeSetStreamState(stFrameHead); + } + else + { + CBuffer* pWaittingBuff = nullptr; + try + { + pWaittingBuff = new CBuffer(); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + return(CODEC_STATUS_PART_ERR); + } + m_listWaittingFrameData.push_back(pWaittingBuff); + uiEncodedDataLen = stFrameHead.uiLength; + EncodeFrameHeader(stFrameHead, pWaittingBuff); + pWaittingBuff->Write(pData, uiEncodedDataLen); + m_stLastDataFrameHead = stFrameHead; + eCodecStatus = CODEC_STATUS_PART_OK; + } } - EncodeSetStreamState(stFrameHead); return(eCodecStatus); } +E_CODEC_STATUS Http2Frame::SendWaittingFrameData(CodecHttp2* pCodecH2, CBuffer* pBuff) +{ + LOG4_TRACE("m_listWaittingFrameData.size() = %u", m_listWaittingFrameData.size()); + if (m_listWaittingFrameData.empty()) + { + return(CODEC_STATUS_OK); + } + uint32 uiDataLen = 0; + for (auto iter = m_listWaittingFrameData.begin(); + iter != m_listWaittingFrameData.end(); ++iter) + { + uiDataLen = (*iter)->ReadableBytes(); + if (uiDataLen < pCodecH2->GetSendWindowSize() + && (int32)uiDataLen < m_pStream->GetSendWindowSize()) + { + pBuff->Write(*iter, uiDataLen); + DELETE(*iter); + m_listWaittingFrameData.erase(iter); + iter = m_listWaittingFrameData.begin(); + } + else + { + break; + } + } + if (m_listWaittingFrameData.empty()) + { + EncodeSetStreamState(m_stLastDataFrameHead); + return(CODEC_STATUS_OK); + } + else + { + return(CODEC_STATUS_PART_OK); + } +} + } /* namespace neb */ diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index 0d30baf3..2c8f0683 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -408,5 +408,21 @@ void Http2Stream::WindowUpdate(int32 iIncrement) m_iSendWindowSize += iIncrement; } +void Http2Stream::ShrinkRecvWindow(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) +{ + m_uiRecvWindowSize -= uiRecvLength; + if (m_uiRecvWindowSize < DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE / 4) + { + m_pFrame->EncodeWindowUpdate(pCodecH2, uiStreamId, + SETTINGS_MAX_INITIAL_WINDOW_SIZE - m_uiRecvWindowSize, pBuff); + } + m_uiRecvWindowSize = SETTINGS_MAX_INITIAL_WINDOW_SIZE; +} + +E_CODEC_STATUS Http2Stream::SendWaittingFrameData(CodecHttp2* pCodecH2, CBuffer* pBuff) +{ + return(m_pFrame->SendWaittingFrameData(pCodecH2, pBuff)); +} + } /* namespace neb */ diff --git a/src/codec/http2/Http2Stream.hpp b/src/codec/http2/Http2Stream.hpp index 7b01a7e7..24bda484 100644 --- a/src/codec/http2/Http2Stream.hpp +++ b/src/codec/http2/Http2Stream.hpp @@ -71,6 +71,11 @@ class Http2Stream: public Codec return(m_eStreamState); } + int32 GetSendWindowSize() + { + return(m_iSendWindowSize); + } + void SetState(E_H2_STREAM_STATES eStreamState) { m_eStreamState = eStreamState; @@ -80,6 +85,8 @@ class Http2Stream: public Codec void WindowInit(uint32 uiWindowSize); void WindowUpdate(int32 iIncrement); + void ShrinkRecvWindow(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff); + E_CODEC_STATUS SendWaittingFrameData(CodecHttp2* pCodecH2, CBuffer* pBuff); private: E_H2_STREAM_STATES m_eStreamState; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 9207710b..6a22a5ad 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -268,7 +268,9 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) { if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) { - if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 + if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() + && CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType() + && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 { LOG4_DEBUG("invalid request, please login first!"); DiscardSocketChannel(pChannel); @@ -398,7 +400,9 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) { if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) { - if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 + if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() + && CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType() + && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 { LOG4_DEBUG("invalid request, please login first!"); DiscardSocketChannel(pChannel); @@ -500,7 +504,7 @@ bool Dispatcher::FdTransfer(int iFd) } std::shared_ptr pChannel = nullptr; LOG4_TRACE("fd[%d] transfer successfully.", iAcceptFd); - if (CODEC_NEBULA != iCodec && m_pLabor->WithSsl()) + if ((CODEC_NEBULA != iCodec) && (CODEC_NEBULA_IN_NODE != iCodec) && m_pLabor->WithSsl()) { pChannel = CreateSocketChannel(iAcceptFd, E_CODEC_TYPE(iCodec), false, true); } @@ -670,7 +674,8 @@ bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) } else // 关闭文件描述符并清理相关资源 { - if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType()) // 非内部服务器间的连接才会在超时中关闭 + if ((CODEC_NEBULA != pChannel->m_pImpl->GetCodecType()) + && (CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType())) // 非内部服务器间的连接才会在超时中关闭 { LOG4_TRACE("io timeout!"); DiscardSocketChannel(pChannel); @@ -1259,7 +1264,7 @@ std::shared_ptr Dispatcher::CreateSocketChannel(int iFd, E_CODEC_ if (bInitResult) { m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); - if (CODEC_NEBULA != eCodecType) + if ((CODEC_NEBULA != eCodecType) && (CODEC_NEBULA_IN_NODE != eCodecType)) { ++m_iClientNum; } @@ -1324,7 +1329,10 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b if (CODEC_NEBULA_IN_NODE == pChannel->m_pImpl->GetCodecType()) { auto inner_channel_iter = m_mapLoaderAndWorkerChannel.find(pChannel->GetFd()); - m_mapLoaderAndWorkerChannel.erase(inner_channel_iter); + if (inner_channel_iter != m_mapLoaderAndWorkerChannel.end()) + { + m_mapLoaderAndWorkerChannel.erase(inner_channel_iter); + } m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); } @@ -1332,7 +1340,8 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b if (channel_iter != m_mapSocketChannel.end()) { m_mapSocketChannel.erase(channel_iter); - if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType()) + if ((CODEC_NEBULA != pChannel->m_pImpl->GetCodecType()) + && (CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType())) { --m_iClientNum; } diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index f21f3621..5a87ba07 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -536,7 +536,7 @@ bool Dispatcher::Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecTy { if ("BEACON" == strNodeType) { - LOG4_WARNING("no beacon config."); + LOG4_TRACE("no beacon config."); } else { diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 41e14e67..1ec6394d 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -136,8 +136,24 @@ bool Manager::InitLogger(const CJsonObject& oJsonConf) int32 iMaxLogLineLen = 1024; bool bAlwaysFlushLog = true; std::string strLoggingHost; - std::string strLogname = m_stNodeInfo.strWorkPath + std::string("/") - + oJsonConf("log_path") + std::string("/") + getproctitle() + std::string(".log"); + std::string strLogPath; + std::string strLogname; + if (oJsonConf.Get("log_path", strLogPath)) + { + if (strLogPath[0] == '/') + { + strLogname = strLogPath + std::string("/") + getproctitle() + std::string(".log"); + } + else + { + strLogname = m_stNodeInfo.strWorkPath + std::string("/") + + strLogPath + std::string("/") + getproctitle() + std::string(".log"); + } + } + else + { + strLogname = m_stNodeInfo.strWorkPath + std::string("/logs/") + getproctitle() + std::string(".log"); + } std::string strParttern = "[%D,%d{%q}][%p] [%l] %m%n"; std::ostringstream ssServerName; ssServerName << getproctitle() << " " << m_stNodeInfo.strHostForServer << ":" << m_stNodeInfo.iPortForServer; @@ -447,8 +463,8 @@ void Manager::CreateLoader() x_sock_set_block(iDataFds[0], 0); m_stNodeInfo.uiLoaderNum = 1; m_pSessionManager->AddLoaderInfo(0, iPid, iControlFds[0], iDataFds[0]); - std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); - std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); + std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); + std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->AddIoReadEvent(pChannelData); @@ -500,8 +516,8 @@ void Manager::CreateLoaderThread() t.detach(); m_stNodeInfo.uiLoaderNum = 1; m_pSessionManager->AddLoaderInfo(0, getpid(), iControlFds[0], iDataFds[0]); - std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); - std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); + std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); + std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->AddIoReadEvent(pChannelData); @@ -556,8 +572,8 @@ void Manager::CreateWorker() x_sock_set_block(iControlFds[0], 0); x_sock_set_block(iDataFds[0], 0); m_pSessionManager->AddWorkerInfo(i, iPid, iControlFds[0], iDataFds[0]); - std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); - std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); + std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); + std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->AddIoReadEvent(pChannelData); @@ -608,8 +624,8 @@ void Manager::CreateWorkerThread() ossThreadId << t.get_id(); m_pSessionManager->AddWorkerThreadId(strtoull(ossThreadId.str().c_str(), NULL, 10)); m_pSessionManager->AddWorkerInfo(i, getpid(), iControlFds[0], iDataFds[0]); - std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); - std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); + std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); + std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->AddIoReadEvent(pChannelData); @@ -678,8 +694,8 @@ bool Manager::RestartWorker(int iDeathPid) x_sock_set_block(iControlFds[0], 0); x_sock_set_block(iDataFds[0], 0); m_pSessionManager->AddWorkerInfo(iWorkerIndex, iNewPid, iControlFds[0], iDataFds[0]); - std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA); - std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA); + std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); + std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->AddIoReadEvent(pChannelData); diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 899c86e6..d776030b 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -245,8 +245,24 @@ bool Worker::InitLogger(const CJsonObject& oJsonConf, const std::string& strLogN int32 iLogLevel = 0; int32 iNetLogLevel = 0; bool bAlwaysFlushLog = true; - std::string strLogname = m_stNodeInfo.strWorkPath + std::string("/") + oJsonConf("log_path") - + std::string("/") + strLogNameBase + std::string(".log"); + std::string strLogname; + std::string strLogPath; + if (oJsonConf.Get("log_path", strLogPath)) + { + if (strLogPath[0] == '/') + { + strLogname = strLogPath + std::string("/") + strLogNameBase + std::string(".log"); + } + else + { + strLogname = m_stNodeInfo.strWorkPath + std::string("/") + + strLogPath + std::string("/") + strLogNameBase + std::string(".log"); + } + } + else + { + strLogname = m_stNodeInfo.strWorkPath + std::string("/logs/") + strLogNameBase + std::string(".log"); + } std::string strParttern = "[%D,%d{%q}][%p] [%l] %m%n"; oJsonConf.Get("max_log_file_size", iMaxLogFileSize); oJsonConf.Get("max_log_file_num", iMaxLogFileNum); @@ -322,9 +338,9 @@ bool Worker::CreateEvents() AddPeriodicTaskEvent(); // 注册网络IO事件 - m_pManagerDataChannel = m_pDispatcher->CreateSocketChannel(m_stWorkerInfo.iDataFd, CODEC_NEBULA); + m_pManagerDataChannel = m_pDispatcher->CreateSocketChannel(m_stWorkerInfo.iDataFd, CODEC_NEBULA_IN_NODE); m_pDispatcher->SetChannelStatus(m_pManagerDataChannel, CHANNEL_STATUS_ESTABLISHED); - m_pManagerControlChannel = m_pDispatcher->CreateSocketChannel(m_stWorkerInfo.iControlFd, CODEC_NEBULA); + m_pManagerControlChannel = m_pDispatcher->CreateSocketChannel(m_stWorkerInfo.iControlFd, CODEC_NEBULA_IN_NODE); m_pDispatcher->SetChannelStatus(m_pManagerControlChannel, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->AddIoReadEvent(m_pManagerDataChannel); m_pDispatcher->AddIoReadEvent(m_pManagerControlChannel); From 992d03122f725b86c34b38b41058eda9890c0bdf Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 14 Mar 2021 16:43:50 +0800 Subject: [PATCH 127/176] http2 huffman decode bug fixed --- src/actor/ActorBuilder.cpp | 8 ++++++++ src/actor/step/sys_step/StepRedisCluster.cpp | 2 ++ src/codec/http2/Http2Frame.cpp | 4 ++-- src/codec/http2/Http2Header.cpp | 1 + src/codec/http2/Http2Stream.cpp | 2 +- src/codec/http2/Huffman.cpp | 7 +++++-- src/ios/Dispatcher.cpp | 4 ++++ 7 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 03df54fd..6f398a06 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -795,6 +795,14 @@ void ActorBuilder::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdOnGetCustomConf", (int)CMD_REQ_GET_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdOnStartService", (int)CMD_REQ_START_SERVICE); MakeSharedCmd(nullptr, "neb::CmdDataReport", (int)CMD_REQ_DATA_REPORT); + std::string strModulePath = "/healthy"; + MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); + strModulePath = "/health"; + MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); + strModulePath = "/status"; + MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); + strModulePath = "http_upgrade"; + MakeSharedModule(nullptr, "neb::ModuleHttpUpgrade", strModulePath); } else { diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index fe8da40c..e886d9b8 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -10,6 +10,7 @@ #include "StepRedisCluster.hpp" #include +#include #include "util/StringCoder.hpp" #include "ios/Dispatcher.hpp" #include "util/StringCoder.hpp" @@ -493,6 +494,7 @@ bool StepRedisCluster::ExtractCmd(const RedisMsg& oRedisMsg, return(false); } strCmd = oRedisMsg.element(0).str(); + std::transform(strCmd.begin(), strCmd.end(), strCmd.begin(), [](unsigned char c)->unsigned char{return std::toupper(c);}); if (s_setSupportExtractCmd.find(strCmd) == s_setSupportExtractCmd.end()) { LOG4_ERROR("cmd %s not supported by StepRedisCluster", strCmd.c_str()); diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index 7d201e98..d8edfc57 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -47,8 +47,8 @@ void Http2Frame::ReadMediumInt(CBuffer* pBuff, uint32& uiValue) pBuff->ReadByte(szData[0]); pBuff->ReadByte(szData[1]); pBuff->ReadByte(szData[2]); - uiValue = ((uint32)szData[0] << 16) & 0xFF; - uiValue |= ((uint32)szData[1] << 8) & 0xFF; + uiValue = (((uint32)szData[0] & 0xFF) << 16); + uiValue |= (((uint32)szData[1] & 0xFF) << 8); uiValue |= (uint32)szData[2] & 0xFF; } diff --git a/src/codec/http2/Http2Header.cpp b/src/codec/http2/Http2Header.cpp index 129c1fef..4eb90544 100644 --- a/src/codec/http2/Http2Header.cpp +++ b/src/codec/http2/Http2Header.cpp @@ -252,6 +252,7 @@ bool Http2Header::DecodeStringLiteral(CBuffer* pBuff, std::string& strLiteral, b return(false); } + strLiteral.clear(); if (B & 0x80) // HUFFMAN { bWithHuffman = true; diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index 2c8f0683..d3ddae06 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -240,7 +240,7 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, break; } E_CODEC_STATUS eStatus = CODEC_STATUS_OK; - LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u, m_bEndHeaders = %d", m_eStreamState, stFrameHead.ucType, m_bEndHeaders); + LOG4_TRACE("m_eStreamState = %d, stFrameHead.ucType = %u, m_bEndHeaders = %d", m_eStreamState, stFrameHead.ucType, m_bEndHeaders); if (m_bEndHeaders) { eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); diff --git a/src/codec/http2/Huffman.cpp b/src/codec/http2/Huffman.cpp index 253dac1b..401e4e07 100644 --- a/src/codec/http2/Huffman.cpp +++ b/src/codec/http2/Huffman.cpp @@ -141,7 +141,7 @@ bool Huffman::Decode(CBuffer* pBuff, int iBuffLength, std::string& strData) for (int i = 0; i < iBuffLength; ++i) { pBuff->ReadByte(c); - uiCurrent = (uiCurrent << 8) | c; + uiCurrent = (uiCurrent << 8) | (c & 0xFF); ucBits += 8; while (ucBits >= 8) { @@ -173,7 +173,7 @@ bool Huffman::Decode(CBuffer* pBuff, int iBuffLength, std::string& strData) pNode = pNode->vecChildren[uiChild]; if (pNode->vecChildren.size() > 0 || pNode->ucTerminalBits > ucBits) { - return(false); + break; } strData.append(1, pNode->ucSymbol); ucBits -= pNode->ucTerminalBits; @@ -189,6 +189,9 @@ void Huffman::BuildTree() uint8_t ucLen = 0; for (int i = 0; i < 256; i++) { + ucSym = i; + uiCode = CODES[i]; + ucLen = CODE_LENGTHS[i]; Node* pTerminal = new Node(ucSym, ucLen); Node* pCurrent = m_pRoot; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 6a22a5ad..12486871 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -266,6 +266,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) if (CODEC_STATUS_OK == eCodecStatus) { + /* if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) { if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() @@ -277,6 +278,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) return(false); } } + */ m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); } else @@ -398,6 +400,7 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); if (CODEC_STATUS_OK == eCodecStatus) { + /* if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) { if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() @@ -409,6 +412,7 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) return(false); } } + */ m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); } else From d697db63768f5d67fe8eccb4e8856d02a51972b1 Mon Sep 17 00:00:00 2001 From: wxd Date: Fri, 19 Mar 2021 16:06:09 +0800 Subject: [PATCH 128/176] Correct spelling mistakes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92939393..8123d1c8 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Nebula can be used as a single high-performance TCP server, but building a clust build completed, you can start the server: ``` ./configure.sh -./startup.s +./startup.sh ``` The server should have started successfully now, startup.sh will print the server that had been started, If not, check logs for reason. Notice that the default configuration file of Nebula limits the number of connections per IP in a period. If you have a large amount of testing, you should check the configuration limit. If the server has been successfully started, testing with postman or curl. From cf9e9f8346831fd8946f3f559cf7b726b02588e6 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 4 Apr 2021 17:34:54 +0800 Subject: [PATCH 129/176] grpc; channel read and write optimization; absolute log path --- conf/nebula.json | 2 + src/actor/Actor.cpp | 9 +- src/actor/ActorBuilder.cpp | 2 +- src/actor/ActorSender.cpp | 16 +- src/actor/chain/Chain.cpp | 28 +- src/actor/cmd/GrpcModule.cpp | 6 +- src/actor/step/GrpcStep.cpp | 14 +- src/actor/step/GrpcStep.hpp | 7 +- src/channel/SocketChannelImpl.cpp | 330 ++++++++++++--------- src/channel/SocketChannelImpl.hpp | 1 + src/codec/CodecProto.cpp | 2 +- src/codec/http2/CodecHttp2.cpp | 457 ++++++++++++++++-------------- src/codec/http2/CodecHttp2.hpp | 35 ++- src/codec/http2/Http2Frame.cpp | 21 +- src/codec/http2/Http2Header.cpp | 59 ++-- src/codec/http2/Http2Header.hpp | 18 +- src/codec/http2/Http2Stream.cpp | 22 +- src/ios/Dispatcher.cpp | 58 +++- src/ios/Nodes.cpp | 2 +- src/labor/NodeInfo.hpp | 1 + src/labor/Worker.cpp | 1 + 21 files changed, 659 insertions(+), 432 deletions(-) diff --git a/conf/nebula.json b/conf/nebula.json index fd2e328c..6efdbd8c 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -7,6 +7,8 @@ "access_port": 9988, "//access_codec": "接入端编解码器,目前支持CODEC_PRIVATE(4),CODEC_HTTP(3),CODEC_PROTOBUF(2)", "access_codec": 4, + "//need_channel_verify":"是否需要连接验证,如设置为true,第一个消息必须是验证消息,未经验证的连接收到第二个消息会被立即关闭", + "need_channel_verify":false, "gateway": "113.102.157.188", "gateway_port": 9988, "//host": "系统内各Server之间通信绑定的IP(Server to Server)", diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 9bc50f90..b52f53cb 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -199,7 +199,14 @@ bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMs { bWithSsl = true; } - return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, GetSequence())); + if (oHttpMsg.http_major() == 2) + { + return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP2, bWithSsl, bPipeline, oHttpMsg, GetSequence())); + } + else + { + return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, GetSequence())); + } } bool Actor::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 6f398a06..597f6f57 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -405,7 +405,7 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http { E_CMD_STATUS eResult; http_step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = (std::dynamic_pointer_cast(http_step_iter->second))->Callback(pChannel, oHttpMsg); + eResult = http_step_iter->second->Callback(pChannel, oHttpMsg); if (CMD_STATUS_RUNNING != eResult) { uint32 uiChainId = http_step_iter->second->GetChainId(); diff --git a/src/actor/ActorSender.cpp b/src/actor/ActorSender.cpp index 516e4aac..1a8091c4 100644 --- a/src/actor/ActorSender.cpp +++ b/src/actor/ActorSender.cpp @@ -78,7 +78,14 @@ bool ActorSender::SendTo(Actor* pActor, const std::string& strHost, int iPort, c { bWithSsl = true; } - return(pActor->m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, pActor->GetSequence())); + if (oHttpMsg.http_major() == 2) + { + return(pActor->m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP2, bWithSsl, bPipeline, oHttpMsg, pActor->GetSequence())); + } + else + { + return(pActor->m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, pActor->GetSequence())); + } } bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) @@ -155,8 +162,9 @@ bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, oHttpMsg.set_status_code(200); oHttpMsg.set_stream_id(uiStreamId); oHttpMsg.mutable_headers()->insert({"content-type", "application/grpc"}); - oHttpMsg.add_adding_never_index_headers(":status"); - oHttpMsg.add_adding_never_index_headers("content-type"); + oHttpMsg.add_adding_never_index_headers("grpc-status"); + oHttpMsg.add_adding_never_index_headers("grpc-message"); + oHttpMsg.add_adding_never_index_headers("x-trace-id"); if (eStatus == GRPC_OK) { uint8 ucCompressedFlag = 0; @@ -220,7 +228,7 @@ bool ActorSender::SendTo(Actor* pActor, const std::string& strUrl, const std::st oHttpMsg.set_method(HTTP_POST); oHttpMsg.set_url(strUrl); oHttpMsg.mutable_headers()->insert({"content-type", "application/grpc"}); - oHttpMsg.add_adding_never_index_headers("content-type"); + oHttpMsg.add_adding_never_index_headers("x-trace-id"); if(0 == http_parser_parse_url(strUrl.c_str(), strUrl.length(), 0, &stUrl)) { if(stUrl.field_set & (1 << UF_PORT)) diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 80061410..9ca5b466 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -83,8 +83,8 @@ E_CMD_STATUS Chain::Next() for (auto iter = vecTurnBlocks.begin(); iter != vecTurnBlocks.end(); ++iter) { LOG4_TRACE("(%s)", (*iter).c_str()); - std::shared_ptr pSharedModel = GetOperator(*iter); - if (pSharedModel == nullptr) + std::shared_ptr pSharedOperator = GetOperator(*iter); + if (pSharedOperator == nullptr) { std::shared_ptr pSharedActor = MakeSharedActor(*iter); if (pSharedActor == nullptr) @@ -93,13 +93,13 @@ E_CMD_STATUS Chain::Next() iter->c_str(), m_strChainFlag.c_str()); break; } - // pSharedModel->SetContext(GetContext()); it had been set in MakeSharedActor(). + // pSharedOperator->SetContext(GetContext()); it had been set in MakeSharedActor(). if (Actor::ACT_OPERATOR == pSharedActor->GetActorType()) { - pSharedModel = std::dynamic_pointer_cast(pSharedActor); - eResult = pSharedModel->Submit(); - pSharedModel->SetContext(nullptr); - pSharedModel->SetTraceId(""); + pSharedOperator = std::dynamic_pointer_cast(pSharedActor); + eResult = pSharedOperator->Submit(); + pSharedOperator->SetContext(nullptr); + pSharedOperator->SetTraceId(""); if (CMD_STATUS_FAULT == eResult) { return(CMD_STATUS_FAULT); @@ -124,18 +124,18 @@ E_CMD_STATUS Chain::Next() } else { - LOG4_ERROR("\"%s\" is not a Model or Step, only Model and Step can be a Chain block.", + LOG4_ERROR("\"%s\" is not a Operator or Step, only Operator and Step can be a Chain block.", pSharedActor->GetActorName().c_str()); return(CMD_STATUS_FAULT); } } else { - pSharedModel->SetContext(GetContext()); - pSharedModel->SetTraceId(GetTraceId()); - eResult = pSharedModel->Submit(); - pSharedModel->SetContext(nullptr); - pSharedModel->SetTraceId(""); + pSharedOperator->SetContext(GetContext()); + pSharedOperator->SetTraceId(GetTraceId()); + eResult = pSharedOperator->Submit(); + pSharedOperator->SetContext(nullptr); + pSharedOperator->SetTraceId(""); if (CMD_STATUS_FAULT == eResult) { return(CMD_STATUS_FAULT); @@ -148,7 +148,7 @@ E_CMD_STATUS Chain::Next() { return(CMD_STATUS_RUNNING); } - else // 只有Model的链块(无IO回调),执行完当前链块后立即执行下一个链块 + else // 只有Operator的链块(无IO回调),执行完当前链块后立即执行下一个链块 { return(Next()); } diff --git a/src/actor/cmd/GrpcModule.cpp b/src/actor/cmd/GrpcModule.cpp index 4a364e72..3c125c98 100644 --- a/src/actor/cmd/GrpcModule.cpp +++ b/src/actor/cmd/GrpcModule.cpp @@ -30,9 +30,9 @@ bool GrpcModule::AnyMessage( std::string strMessage; ucCompressedFlag = oHttpMsg.body()[0]; uiMessageLength |= (oHttpMsg.body()[4] & 0xFF); - uiMessageLength |= ((oHttpMsg.body()[3] << 8) & 0xFF); - uiMessageLength |= ((oHttpMsg.body()[2] << 16) & 0xFF); - uiMessageLength |= ((oHttpMsg.body()[1] << 24) & 0xFF); + uiMessageLength |= ((oHttpMsg.body()[3] & 0xFF) << 8); + uiMessageLength |= ((oHttpMsg.body()[2] & 0xFF) << 16); + uiMessageLength |= ((oHttpMsg.body()[1] & 0xFF) << 24); if (ucCompressedFlag) { auto iter = oHttpMsg.headers().find("grpc-encoding"); diff --git a/src/actor/step/GrpcStep.cpp b/src/actor/step/GrpcStep.cpp index 6a753da6..8bcddce0 100644 --- a/src/actor/step/GrpcStep.cpp +++ b/src/actor/step/GrpcStep.cpp @@ -14,7 +14,7 @@ namespace neb { GrpcStep::GrpcStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) - : Step(ACT_HTTP_STEP, pNextStep, dTimeout) + : HttpStep(pNextStep, dTimeout) { } @@ -23,18 +23,16 @@ GrpcStep::~GrpcStep() } E_CMD_STATUS GrpcStep::Callback( - std::shared_ptr pChannel, const HttpMsg& oHttpMsg) + std::shared_ptr pChannel, const HttpMsg& oHttpMsg, void* data) { - char szLength[5] = {0}; uint8 ucCompressedFlag = 0; uint32 uiMessageLength = 0; std::string strResponseData; ucCompressedFlag = oHttpMsg.body()[0]; - szLength[0] = oHttpMsg.body()[4]; - szLength[1] = oHttpMsg.body()[3]; - szLength[2] = oHttpMsg.body()[2]; - szLength[3] = oHttpMsg.body()[1]; - uiMessageLength = StringConverter::RapidAtoi(szLength); + uiMessageLength |= (oHttpMsg.body()[4] & 0xFF); + uiMessageLength |= ((oHttpMsg.body()[3] & 0xFF) << 8); + uiMessageLength |= ((oHttpMsg.body()[2] & 0xFF) << 16); + uiMessageLength |= ((oHttpMsg.body()[1] & 0xFF) << 24); if (ucCompressedFlag) { auto iter = oHttpMsg.headers().find("grpc-encoding"); diff --git a/src/actor/step/GrpcStep.hpp b/src/actor/step/GrpcStep.hpp index b5e1c25e..0f106bdc 100644 --- a/src/actor/step/GrpcStep.hpp +++ b/src/actor/step/GrpcStep.hpp @@ -10,12 +10,12 @@ #ifndef SRC_ACTOR_STEP_GRPCSTEP_HPP_ #define SRC_ACTOR_STEP_GRPCSTEP_HPP_ -#include "Step.hpp" +#include "HttpStep.hpp" namespace neb { -class GrpcStep: public Step +class GrpcStep: public HttpStep { public: GrpcStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); @@ -25,7 +25,8 @@ class GrpcStep: public Step virtual E_CMD_STATUS Callback( std::shared_ptr pChannel, - const HttpMsg& oHttpMsg); + const HttpMsg& oHttpMsg, + void* data = NULL); virtual E_CMD_STATUS Callback( std::shared_ptr pChannel, diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index bb15b70d..0079a1ae 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -22,7 +22,7 @@ namespace neb { SocketChannelImpl::SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) - : m_ucChannelStatus(CHANNEL_STATUS_INIT), m_bIsClientConnection(false), + : m_ucChannelStatus(CHANNEL_STATUS_INIT),m_eLastCodecStatus(CODEC_STATUS_OK), m_bIsClientConnection(false), m_unRemoteWorkerIdx(0), m_iFd(iFd), m_uiSeq(ulSeq), m_uiForeignSeq(0), m_bPipeline(true), m_uiUnitTimeMsgNum(0), m_uiMsgNum(0), m_dActiveTime(0.0), m_dKeepAlive(dKeepAlive), @@ -35,7 +35,7 @@ SocketChannelImpl::SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ SocketChannelImpl::~SocketChannelImpl() { - LOG4_DEBUG("SocketChannelImpl::~SocketChannelImpl() fd %d, seq %u", m_iFd, m_uiSeq); + LOG4_TRACE("SocketChannelImpl::~SocketChannelImpl() fd %d, seq %u", m_iFd, m_uiSeq); m_listPipelineStepSeq.clear(); if (CHANNEL_STATUS_CLOSED != m_ucChannelStatus) { @@ -170,7 +170,6 @@ E_CODEC_STATUS SocketChannelImpl::Send() return(CODEC_STATUS_ERR); } int iNeedWriteLen = 0; - int iWrittenLen = 0; iNeedWriteLen = m_pSendBuff->ReadableBytes(); if (0 == iNeedWriteLen) { @@ -190,9 +189,16 @@ E_CODEC_STATUS SocketChannelImpl::Send() } m_dActiveTime = m_pLabor->GetNowTime(); - iWrittenLen = Write(m_pSendBuff, m_iErrno); - LOG4_TRACE("iNeedWriteLen = %d, iWrittenLen = %d", iNeedWriteLen, iWrittenLen); - if (iWrittenLen >= 0) + int iHadWrittenLen = 0; + int iWrittenLen = 0; + do + { + iWrittenLen = Write(m_pSendBuff, m_iErrno); + iHadWrittenLen += iWrittenLen; + } + while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); + LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); + if (iHadWrittenLen >= 0) { if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) @@ -200,7 +206,7 @@ E_CODEC_STATUS SocketChannelImpl::Send() m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); } m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iWrittenLen && 0 == m_pWaitForSendBuff->ReadableBytes()) + if (iNeedWriteLen == iHadWrittenLen && 0 == m_pWaitForSendBuff->ReadableBytes()) { return(CODEC_STATUS_OK); } @@ -300,9 +306,16 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& } errno = 0; - int iWrittenLen = Write(m_pSendBuff, m_iErrno); - LOG4_TRACE("iNeedWriteLen = %d, iWrittenLen = %d", iNeedWriteLen, iWrittenLen); - if (iWrittenLen >= 0) + int iHadWrittenLen = 0; + int iWrittenLen = 0; + do + { + iWrittenLen = Write(m_pSendBuff, m_iErrno); + iHadWrittenLen += iWrittenLen; + } + while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); + LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); + if (iHadWrittenLen >= 0) { if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) @@ -310,7 +323,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); } m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iWrittenLen) + if (iNeedWriteLen == iHadWrittenLen) { if (CMD_RSP_TELL_WORKER == iCmd) { @@ -386,10 +399,16 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq return(eCodecStatus); } - int iWrittenLen = Write(m_pSendBuff, m_iErrno); - LOG4_TRACE("fd[%d], channel_seq[%u] iWrittenLen = %d, m_iErrno = %d", - GetFd(), GetSequence(), iWrittenLen, m_iErrno); - if (iWrittenLen >= 0) + int iHadWrittenLen = 0; + int iWrittenLen = 0; + do + { + iWrittenLen = Write(m_pSendBuff, m_iErrno); + iHadWrittenLen += iWrittenLen; + } + while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); + LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); + if (iHadWrittenLen >= 0) { if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) @@ -401,7 +420,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq m_listPipelineStepSeq.push_back(uiStepSeq); } m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iWrittenLen) + if (iNeedWriteLen == iHadWrittenLen) { return(eCodecStatus); } @@ -470,10 +489,16 @@ E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepS return(eCodecStatus); } - int iWrittenLen = Write(m_pSendBuff, m_iErrno); - LOG4_TRACE("fd[%d], channel_seq[%u] iWrittenLen = %d, m_iErrno = %d", - GetFd(), GetSequence(), iWrittenLen, m_iErrno); - if (iWrittenLen >= 0) + int iHadWrittenLen = 0; + int iWrittenLen = 0; + do + { + iWrittenLen = Write(m_pSendBuff, m_iErrno); + iHadWrittenLen += iWrittenLen; + } + while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); + LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); + if (iHadWrittenLen >= 0) { if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) @@ -485,7 +510,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepS m_listPipelineStepSeq.push_back(uiStepSeq); } m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iWrittenLen) + if (iNeedWriteLen == iHadWrittenLen) { return(CODEC_STATUS_OK); } @@ -545,10 +570,16 @@ E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint3 return(eCodecStatus); } - int iWrittenLen = Write(m_pSendBuff, m_iErrno); - LOG4_TRACE("fd[%d], channel_seq[%u] iWrittenLen = %d, m_iErrno = %d", - GetFd(), GetSequence(), iWrittenLen, m_iErrno); - if (iWrittenLen >= 0) + int iHadWrittenLen = 0; + int iWrittenLen = 0; + do + { + iWrittenLen = Write(m_pSendBuff, m_iErrno); + iHadWrittenLen += iWrittenLen; + } + while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); + LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); + if (iHadWrittenLen >= 0) { if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) @@ -560,7 +591,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint3 m_listPipelineStepSeq.push_back(uiStepSeq); } m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iWrittenLen) + if (iNeedWriteLen == iHadWrittenLen) { return(CODEC_STATUS_OK); } @@ -592,9 +623,38 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) return(CODEC_STATUS_ERR); } int iReadLen = 0; - iReadLen = Read(m_pRecvBuff, m_iErrno); - LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - if (iReadLen > 0) + int iHadReadLen = 0; + do + { + iReadLen = Read(m_pRecvBuff, m_iErrno); + iHadReadLen += iReadLen; + LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", + m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); + } + while(iReadLen > 0); + if (iReadLen == 0) + { + m_eLastCodecStatus = CODEC_STATUS_EOF; + } + else if (iReadLen < 0) + { + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + { + m_dActiveTime = m_pLabor->GetNowTime(); + m_eLastCodecStatus = CODEC_STATUS_PAUSE; + //return(CODEC_STATUS_PAUSE); + } + else + { + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_eLastCodecStatus = CODEC_STATUS_INT; + //return(CODEC_STATUS_INT); + } + } + + if (iHadReadLen > 0) { if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) @@ -668,25 +728,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_uiSeq, oMsgHead.cmd(), oMsgHead.seq()); return(eCodecStatus); } - else if (iReadLen == 0) - { - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_DEBUG("fd %d closed by peer, error %d %s!", - m_iFd, m_iErrno, m_strErrMsg.c_str()); - return(CODEC_STATUS_EOF); - } - else - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - return(CODEC_STATUS_PAUSE); - } - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); - return(CODEC_STATUS_INT); - } + return(m_eLastCodecStatus); } E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) @@ -703,10 +745,39 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) return(CODEC_STATUS_ERR); } int iReadLen = 0; - iReadLen = Read(m_pRecvBuff, m_iErrno); - LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", - m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - if (iReadLen > 0) + int iHadReadLen = 0; + do + { + iReadLen = Read(m_pRecvBuff, m_iErrno); + LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", + m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); + iHadReadLen += iReadLen; + } + while (iReadLen > 0); + + if (iReadLen == 0) + { + m_eLastCodecStatus = CODEC_STATUS_EOF; + } + else if (iReadLen < 0) + { + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + { + m_dActiveTime = m_pLabor->GetNowTime(); + m_eLastCodecStatus = CODEC_STATUS_PAUSE; + //return(CODEC_STATUS_PAUSE); + } + else + { + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_eLastCodecStatus = CODEC_STATUS_INT; + return(CODEC_STATUS_INT); + } + } + + if (iHadReadLen > 0) { if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) @@ -771,33 +842,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) } return(eCodecStatus); } - else if (iReadLen == 0) - { - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_TRACE("fd %d closed by peer, error %d %s!", - m_iFd, m_iErrno, m_strErrMsg.c_str()); - if (m_pRecvBuff->ReadableBytes() > 0) - { - E_CODEC_STATUS eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); - if (CODEC_STATUS_PAUSE == eCodecStatus || CODEC_STATUS_OK == eCodecStatus) - { - oHttpMsg.set_is_decoding(false); - } - } - return(CODEC_STATUS_EOF); - } - else - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - return(CODEC_STATUS_PAUSE); - } - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); - return(CODEC_STATUS_INT); - } + return(m_eLastCodecStatus); } E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) @@ -814,10 +859,34 @@ E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) return(CODEC_STATUS_ERR); } int iReadLen = 0; - iReadLen = Read(m_pRecvBuff, m_iErrno); - LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", - m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - if (iReadLen > 0) + int iHadReadLen = 0; + do + { + iReadLen = Read(m_pRecvBuff, m_iErrno); + LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", + m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); + iHadReadLen += iReadLen; + } + while (iReadLen > 0); + + if (iReadLen == 0) + { + m_eLastCodecStatus = CODEC_STATUS_EOF; + } + else if (iReadLen < 0) + { + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + { + m_dActiveTime = m_pLabor->GetNowTime(); + return(CODEC_STATUS_PAUSE); + } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + return(CODEC_STATUS_INT); + } + + if (iHadReadLen > 0) { if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) @@ -845,18 +914,33 @@ E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) } return(eCodecStatus); } - else if (iReadLen == 0) + return(m_eLastCodecStatus); +} + +E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_TRACE("fd %d closed by peer, error %d %s!", - m_iFd, m_iErrno, m_strErrMsg.c_str()); - if (m_pRecvBuff->ReadableBytes() > 0) - { - ((CodecResp*)m_pCodec)->Decode(m_pRecvBuff, oRedisReply); - } + LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); return(CODEC_STATUS_EOF); } - else + int iReadLen = 0; + int iHadReadLen = 0; + do + { + iReadLen = Read(m_pRecvBuff, m_iErrno); + LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", + m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); + iHadReadLen += iReadLen; + } + while (iReadLen > 0); + + if (iReadLen == 0) + { + m_eLastCodecStatus = CODEC_STATUS_EOF; + } + else if (iReadLen < 0) { if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { @@ -868,21 +952,8 @@ E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) m_iFd, m_iErrno, m_strErrMsg.c_str()); return(CODEC_STATUS_INT); } -} -E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) - { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); - return(CODEC_STATUS_EOF); - } - int iReadLen = 0; - iReadLen = Read(m_pRecvBuff, m_iErrno); - LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", - m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - if (iReadLen > 0) + if (iHadReadLen > 0) { if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) @@ -903,29 +974,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) } return(CODEC_STATUS_PAUSE); } - else if (iReadLen == 0) - { - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_TRACE("fd %d closed by peer, error %d %s!", - m_iFd, m_iErrno, m_strErrMsg.c_str()); - if (m_pRecvBuff->ReadableBytes() > 0) - { - oRawBuff.Write(m_pRecvBuff, m_pRecvBuff->ReadableBytes()); - } - return(CODEC_STATUS_EOF); - } - else - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - return(CODEC_STATUS_PAUSE); - } - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); - return(CODEC_STATUS_INT); - } + return(m_eLastCodecStatus); } E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) @@ -951,6 +1000,12 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) } LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_uiSeq, oMsgHead.cmd(), oMsgHead.seq()); } + if ((CODEC_STATUS_PAUSE == eCodecStatus) + && (CODEC_STATUS_EOF == m_eLastCodecStatus + || CODEC_STATUS_INT == m_eLastCodecStatus)) + { + return(m_eLastCodecStatus); + } return(eCodecStatus); } @@ -1007,6 +1062,12 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) } } } + if ((CODEC_STATUS_PAUSE == eCodecStatus) + && (CODEC_STATUS_EOF == m_eLastCodecStatus + || CODEC_STATUS_INT == m_eLastCodecStatus)) + { + return(m_eLastCodecStatus); + } return(eCodecStatus); } @@ -1029,6 +1090,12 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(RedisReply& oRedisReply) m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; } } + if ((CODEC_STATUS_PAUSE == eCodecStatus) + && (CODEC_STATUS_EOF == m_eLastCodecStatus + || CODEC_STATUS_INT == m_eLastCodecStatus)) + { + return(m_eLastCodecStatus); + } return(eCodecStatus); } @@ -1051,6 +1118,11 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(CBuffer& oRawBuff) } return(CODEC_STATUS_OK); } + if (CODEC_STATUS_EOF == m_eLastCodecStatus + || CODEC_STATUS_INT == m_eLastCodecStatus) + { + return(m_eLastCodecStatus); + } return(CODEC_STATUS_PAUSE); } diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 4aca3a06..c0fce2ab 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -221,6 +221,7 @@ class SocketChannelImpl: public Channel private: uint8 m_ucChannelStatus; + E_CODEC_STATUS m_eLastCodecStatus; ///< 连接关闭前的最后一个编解码状态(当且仅当连接的应用层读缓冲区有数据未处理完而对端关闭连接时使用) char m_szErrBuff[256]; bool m_bIsClientConnection; uint16 m_unRemoteWorkerIdx; ///< 对端Worker进程ID,若不涉及则无需关心 diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp index 25db9749..0e5f9126 100644 --- a/src/codec/CodecProto.cpp +++ b/src/codec/CodecProto.cpp @@ -97,7 +97,7 @@ E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oM } else { - LOG4_WARNING("oMsgHead.ParseFromArray() error!"); // maybe port scan from operation and maintenance system. + LOG4_TRACE("oMsgHead.ParseFromArray() error!"); // maybe port scan from operation and maintenance system. return(CODEC_STATUS_ERR); } } diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index 1417c9ee..eb19e9b3 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -29,6 +29,12 @@ CodecHttp2::CodecHttp2(std::shared_ptr pLogger, m_pStreamWeightRoot = new TreeNode(); m_pStreamWeightRoot->pData = new tagStreamWeight(); m_pStreamWeightRoot->pData->uiStreamId = 0; + + Http2Header oHeader("", ""); + m_vecEncodingDynamicTable.assign(8, oHeader); + m_iNextEncodingDynamicTableHeaderIndex = m_vecEncodingDynamicTable.size() - 1; + m_vecDecodingDynamicTable.assign(8, oHeader); + m_iNextDecodingDynamicTableHeaderIndex = m_vecDecodingDynamicTable.size() - 1; } catch(std::bad_alloc& e) { @@ -60,6 +66,8 @@ void CodecHttp2::ConnectionSetting(CBuffer* pBuff) tagSetting stSetting; if (m_bChannelIsClient) { + pBuff->Write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24); + m_bWantMagic = false; stSetting.unIdentifier = H2_SETTINGS_INITIAL_WINDOW_SIZE; stSetting.uiValue = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; m_uiRecvWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; @@ -91,11 +99,8 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { if (m_bWantMagic && m_bChannelIsClient) { - if (pBuff->ReadableBytes() >= 24) - { - pBuff->Write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24); - m_bWantMagic = false; - } + pBuff->Write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24); + m_bWantMagic = false; } m_bChunkNotice = oHttpMsg.chunk_notice(); for (int i = 0; i < oHttpMsg.adding_without_index_headers_size(); ++i) @@ -135,7 +140,7 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) } } //const_cast(oHttpMsg).set_with_huffman(true); - if (oHttpMsg.stream_id() != m_pCodingStream->GetStreamId()) + if (m_pCodingStream == nullptr || oHttpMsg.stream_id() != m_pCodingStream->GetStreamId()) { auto stream_iter = m_mapStream.find(oHttpMsg.stream_id()); if (stream_iter == m_mapStream.end()) @@ -563,7 +568,6 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu E_CODEC_STATUS eStatus; bool bWithHuffman = false; - int iDynamicTableIndex = -1; std::string strHeaderName; std::string strHeaderValue; while (pBuff->GetReadIndex() < uiHeaderBlockEndPos) @@ -582,19 +586,18 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu else if (H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING & B) { eStatus = UnpackHeaderLiteralIndexing(pBuff, B, - H2_HPACK_PREFIX_6_BITS, iDynamicTableIndex, + H2_HPACK_PREFIX_6_BITS, strHeaderName, strHeaderValue, bWithHuffman); if (eStatus != CODEC_STATUS_PART_OK) { return(eStatus); } ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); - UpdateDecodingDynamicTable(0, strHeaderName, strHeaderValue); } else if (H2_HPACK_CONDITION_LITERAL_HEADER_NEVER_INDEXED & B) { eStatus = UnpackHeaderLiteralIndexing(pBuff, B, - H2_HPACK_PREFIX_4_BITS, iDynamicTableIndex, + H2_HPACK_PREFIX_4_BITS, strHeaderName, strHeaderValue, bWithHuffman); if (eStatus != CODEC_STATUS_PART_OK) { @@ -620,7 +623,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu else // H2_HPACK_CONDITION_LITERAL_HEADER_WITHOUT_INDEXING { eStatus = UnpackHeaderLiteralIndexing(pBuff, B, - H2_HPACK_PREFIX_4_BITS, iDynamicTableIndex, + H2_HPACK_PREFIX_4_BITS, strHeaderName, strHeaderValue, bWithHuffman); if (eStatus != CODEC_STATUS_PART_OK) { @@ -679,35 +682,28 @@ void CodecHttp2::PackHeader(const HttpMsg& oHttpMsg, int iHeaderType, CBuffer* p void CodecHttp2::PackHeader(const std::string& strHeaderName, const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff) { - size_t uiTableIndex = 0; - uiTableIndex = Http2Header::GetStaticTableIndex(strHeaderName, strHeaderValue); - if (uiTableIndex == 0) - { - uiTableIndex = GetEncodingTableIndex(strHeaderName, strHeaderValue); - } - if (uiTableIndex > 0) + uint32 uiHeaderIndex = 0; + uint32 uiHeaderNameIndex = 0; + uiHeaderIndex = GetEncodingTableIndex(strHeaderName, strHeaderValue, uiHeaderNameIndex); + if (uiHeaderIndex > 0) { - PackHeaderIndexed(uiTableIndex, pBuff); - LOG4_TRACE("PackHeaderIndexed() %s: %s | uiTableIndex = %u", strHeaderName.c_str(), strHeaderValue.c_str(), uiTableIndex); + PackHeaderIndexed(uiHeaderIndex, pBuff); } else { auto never_index_iter = m_setEncodingNeverIndexHeaders.find(strHeaderName); if (never_index_iter != m_setEncodingNeverIndexHeaders.end()) { - PackHeaderNeverIndexing(strHeaderName, strHeaderValue, bWithHuffman, pBuff); - LOG4_TRACE("PackHeaderNeverIndexing() %s: %s", strHeaderName.c_str(), strHeaderValue.c_str()); + PackHeaderNeverIndexing(strHeaderName, strHeaderValue, uiHeaderNameIndex, bWithHuffman, pBuff); return; } auto without_index_iter = m_setEncodingWithoutIndexHeaders.find(strHeaderName); if (without_index_iter != m_setEncodingWithoutIndexHeaders.end()) { - PackHeaderWithoutIndexing(strHeaderName, strHeaderValue, bWithHuffman, pBuff); - LOG4_TRACE("PackHeaderWithoutIndexing() %s: %s", strHeaderName.c_str(), strHeaderValue.c_str()); + PackHeaderWithoutIndexing(strHeaderName, strHeaderValue, uiHeaderNameIndex, bWithHuffman, pBuff); return; } - PackHeaderWithIndexing(strHeaderName, strHeaderValue, bWithHuffman, pBuff); - LOG4_TRACE("PackHeaderWithIndexing() %s: %s", strHeaderName.c_str(), strHeaderValue.c_str()); + PackHeaderWithIndexing(strHeaderName, strHeaderValue, uiHeaderNameIndex, bWithHuffman, pBuff); } } @@ -879,31 +875,37 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderIndexed(CBuffer* pBuff, HttpMsg& oHttpMsg LOG4_ERROR("hpack index value of 0 is not used!"); return(CODEC_STATUS_ERR); } - else if (uiTableIndex <= Http2Header::sc_uiMaxStaticTableIndex) + else if (uiTableIndex < Http2Header::sc_uiMaxStaticTableLength) { ClassifyHeader(Http2Header::sc_vecStaticTable[uiTableIndex].first, Http2Header::sc_vecStaticTable[uiTableIndex].second, oHttpMsg); return(CODEC_STATUS_PART_OK); } - else if (uiTableIndex <= Http2Header::sc_uiMaxStaticTableIndex + m_vecDecodingDynamicTable.size()) - { - uint32 uiDynamicTableIndex = uiTableIndex - Http2Header::sc_uiMaxStaticTableIndex - 1; - ClassifyHeader(m_vecDecodingDynamicTable[uiDynamicTableIndex].Name(), - m_vecDecodingDynamicTable[uiDynamicTableIndex].Value(), oHttpMsg); - return(CODEC_STATUS_PART_OK); - } else { - SetErrno(H2_ERR_COMPRESSION_ERROR); - LOG4_ERROR("hpack index value of %u was greater than the sum of " - "the lengths of both static table and dynamic tables!", uiTableIndex); - return(CODEC_STATUS_ERR); + uint32 uiDynamicTableIndex = DecodingDynamicTableIndex2VectorIndex(uiTableIndex - Http2Header::sc_uiMaxStaticTableLength); + if (uiTableIndex <= Http2Header::sc_uiMaxStaticTableIndex + m_vecDecodingDynamicTable.size()) + { + ClassifyHeader(m_vecDecodingDynamicTable[uiDynamicTableIndex].Name(), + m_vecDecodingDynamicTable[uiDynamicTableIndex].Value(), oHttpMsg); + return(CODEC_STATUS_PART_OK); + } + else + { + SetErrno(H2_ERR_COMPRESSION_ERROR); + LOG4_ERROR("hpack index value of %u was greater than the sum of " + "the lengths of both static table and dynamic tables!", uiTableIndex); + return(CODEC_STATUS_ERR); + } } } E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucFirstByte, int32 iPrefixMask, - int& iDynamicTableIndex, std::string& strHeaderName, std::string& strHeaderValue, bool& bWithHuffman) + std::string& strHeaderName, std::string& strHeaderValue, bool& bWithHuffman) { + strHeaderName.clear(); + strHeaderValue.clear(); + bWithHuffman = false; // Literal Header Field with Incremental Indexing — Indexed Name if (iPrefixMask & ucFirstByte) { @@ -914,7 +916,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF LOG4_ERROR("hpack index value of 0 is not used!"); return(CODEC_STATUS_ERR); } - else if (uiTableIndex <= Http2Header::sc_uiMaxStaticTableIndex) + else if (uiTableIndex < Http2Header::sc_uiMaxStaticTableLength) { if (!Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) { @@ -923,26 +925,37 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF return(CODEC_STATUS_ERR); } strHeaderName = Http2Header::sc_vecStaticTable[uiTableIndex].first; + if (H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING & ucFirstByte) + { + UpdateDecodingDynamicTable(-1, strHeaderName, strHeaderValue); + } return(CODEC_STATUS_PART_OK); } - else if (uiTableIndex < Http2Header::sc_uiMaxStaticTableIndex + m_vecDecodingDynamicTable.size()) + else { - uint32 uiDynamicTableIndex = uiTableIndex - Http2Header::sc_uiMaxStaticTableIndex - 1; - if (!Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) + uint32 uiDynamicTableIndex = DecodingDynamicTableIndex2VectorIndex(uiTableIndex - Http2Header::sc_uiMaxStaticTableLength); + if (uiDynamicTableIndex < m_vecDecodingDynamicTable.size()) + { + if (!Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) + { + SetErrno(H2_ERR_COMPRESSION_ERROR); + LOG4_ERROR("DecodeStringLiteral failed!"); + return(CODEC_STATUS_ERR); + } + strHeaderName = m_vecDecodingDynamicTable[uiDynamicTableIndex].Name(); + if (H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING & ucFirstByte) + { + UpdateDecodingDynamicTable(uiDynamicTableIndex, strHeaderName, strHeaderValue); + } + return(CODEC_STATUS_PART_OK); + } + else { SetErrno(H2_ERR_COMPRESSION_ERROR); - LOG4_ERROR("DecodeStringLiteral failed!"); + LOG4_ERROR("hpack index value of %u was greater than the sum of " + "the lengths of both static table and dynamic tables!", uiTableIndex); return(CODEC_STATUS_ERR); } - strHeaderName = m_vecDecodingDynamicTable[uiDynamicTableIndex].Name(); - return(CODEC_STATUS_PART_OK); - } - else - { - SetErrno(H2_ERR_COMPRESSION_ERROR); - LOG4_ERROR("hpack index value of %d was greater than the sum of " - "the lengths of both static table and dynamic tables!", uiTableIndex); - return(CODEC_STATUS_ERR); } } else // Literal Header Field with Incremental Indexing — New Name @@ -951,6 +964,10 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF if (Http2Header::DecodeStringLiteral(pBuff, strHeaderName, bWithHuffman) && Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) { + if (H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING & ucFirstByte) + { + UpdateDecodingDynamicTable(-1, strHeaderName, strHeaderValue); + } return(CODEC_STATUS_PART_OK); } SetErrno(H2_ERR_COMPRESSION_ERROR); @@ -1025,18 +1042,12 @@ void CodecHttp2::PackHeaderIndexed(size_t uiTableIndex, CBuffer* pBuff) } void CodecHttp2::PackHeaderWithIndexing(const std::string& strHeaderName, - const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff) + const std::string& strHeaderValue, uint32 uiHeaderNameIndex, + bool bWithHuffman, CBuffer* pBuff) { - size_t uiTableIndex = 0; - uiTableIndex = Http2Header::GetStaticTableIndex(strHeaderName); - if (uiTableIndex == 0) + if (uiHeaderNameIndex > 0) // Literal Header Field with Incremental Indexing - Indexed Name. { - uiTableIndex = GetEncodingTableIndex(strHeaderName); - } - - if (uiTableIndex > 0) // Literal Header Field with Incremental Indexing - Indexed Name. - { - Http2Header::EncodeInt(uiTableIndex, (size_t)H2_HPACK_PREFIX_6_BITS, + Http2Header::EncodeInt(uiHeaderNameIndex, (size_t)H2_HPACK_PREFIX_6_BITS, (char)H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING, pBuff); if (bWithHuffman) { @@ -1046,6 +1057,7 @@ void CodecHttp2::PackHeaderWithIndexing(const std::string& strHeaderName, { Http2Header::EncodeStringLiteral(strHeaderValue, pBuff); } + UpdateEncodingDynamicTable(strHeaderName, strHeaderValue); } else // Literal Header Field with Incremental Indexing - New Name. { @@ -1058,29 +1070,20 @@ void CodecHttp2::PackHeaderWithIndexing(const std::string& strHeaderName, } else { - LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); Http2Header::EncodeStringLiteral(strHeaderName, pBuff); - LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); Http2Header::EncodeStringLiteral(strHeaderValue, pBuff); - LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); } - UpdateEncodingDynamicTable(0, strHeaderName, strHeaderValue); + UpdateEncodingDynamicTable(strHeaderName, strHeaderValue); } } void CodecHttp2::PackHeaderWithoutIndexing(const std::string& strHeaderName, - const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff) + const std::string& strHeaderValue,uint32 uiHeaderNameIndex, + bool bWithHuffman, CBuffer* pBuff) { - size_t uiTableIndex = 0; - uiTableIndex = Http2Header::GetStaticTableIndex(strHeaderName); - if (uiTableIndex == 0) - { - uiTableIndex = GetEncodingTableIndex(strHeaderName); - } - - if (uiTableIndex > 0) + if (uiHeaderNameIndex > 0) { - Http2Header::EncodeInt(uiTableIndex, (size_t)H2_HPACK_PREFIX_4_BITS, + Http2Header::EncodeInt(uiHeaderNameIndex, (size_t)H2_HPACK_PREFIX_4_BITS, (char)H2_HPACK_CONDITION_LITERAL_HEADER_WITHOUT_INDEXING, pBuff); if (bWithHuffman) { @@ -1109,18 +1112,12 @@ void CodecHttp2::PackHeaderWithoutIndexing(const std::string& strHeaderName, } void CodecHttp2::PackHeaderNeverIndexing(const std::string& strHeaderName, - const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff) + const std::string& strHeaderValue, uint32 uiHeaderNameIndex, + bool bWithHuffman, CBuffer* pBuff) { - size_t uiTableIndex = 0; - uiTableIndex = Http2Header::GetStaticTableIndex(strHeaderName); - if (uiTableIndex == 0) - { - uiTableIndex = GetEncodingTableIndex(strHeaderName); - } - - if (uiTableIndex > 0) + if (uiHeaderNameIndex > 0) { - Http2Header::EncodeInt(uiTableIndex, (size_t)H2_HPACK_PREFIX_4_BITS, + Http2Header::EncodeInt(uiHeaderNameIndex, (size_t)H2_HPACK_PREFIX_4_BITS, (char)H2_HPACK_CONDITION_LITERAL_HEADER_NEVER_INDEXED, pBuff); if (bWithHuffman) { @@ -1154,178 +1151,228 @@ void CodecHttp2::PackHeaderDynamicTableSize(uint32 uiDynamicTableSize, CBuffer* (char)H2_HPACK_CONDITION_DYNAMIC_TABLE_SIZE_UPDATE, pBuff); } -size_t CodecHttp2::GetEncodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue) +uint32 CodecHttp2::GetEncodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue, uint32& uiNameIndex) { - size_t uiTableIndex = 0; - if (strHeaderValue.size() == 0) + uiNameIndex = 0; + uint32 uiTableIndex = 0; + uiTableIndex = Http2Header::GetStaticTableIndex(strHeaderName, strHeaderValue, uiNameIndex); + if (uiTableIndex > 0) + { + return(uiTableIndex); + } + int iEncodingDynamicTableLength = (int)m_vecEncodingDynamicTable.size(); + for (int i = m_iNextEncodingDynamicTableHeaderIndex + 1; i < iEncodingDynamicTableLength; ++i) { - for (size_t i = 0; i < m_vecEncodingDynamicTable.size(); ++i) + if (m_vecEncodingDynamicTable[i].Name() == strHeaderName) { - if (m_vecEncodingDynamicTable[i].Name() == strHeaderName) + if (m_vecEncodingDynamicTable[i].Value() == strHeaderValue) { - uiTableIndex = Http2Header::sc_uiMaxStaticTableIndex + i + 1; + uiTableIndex = 1 - m_iNextEncodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; break; } + else if (uiNameIndex == 0) + { + uiNameIndex = 1 - m_iNextEncodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; + } } } - else + return(uiTableIndex); +} + +uint32 CodecHttp2::GetDecodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue, uint32& uiNameIndex) +{ + uiNameIndex = 0; + uint32 uiTableIndex = 0; + uiTableIndex = Http2Header::GetStaticTableIndex(strHeaderName, strHeaderValue, uiNameIndex); + if (uiTableIndex > 0) + { + return(uiTableIndex); + } + int iDecodingDynamicTableLength = (int)m_vecDecodingDynamicTable.size(); + for (int i = m_iNextDecodingDynamicTableHeaderIndex + 1; i < iDecodingDynamicTableLength; ++i) { - for (size_t i = 0; i < m_vecEncodingDynamicTable.size(); ++i) + if (m_vecDecodingDynamicTable[i].Name() == strHeaderName) { - if (m_vecEncodingDynamicTable[i].Name() == strHeaderName - && m_vecEncodingDynamicTable[i].Value() == strHeaderValue) + if (m_vecDecodingDynamicTable[i].Value() == strHeaderValue) { - uiTableIndex = Http2Header::sc_uiMaxStaticTableIndex + i + 1; + uiTableIndex = 1 - m_iNextDecodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; break; } + else if (uiNameIndex == 0) + { + uiNameIndex = 1 - m_iNextDecodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; + } } } return(uiTableIndex); } -//size_t CodecHttp2::GetDecodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue) -//{ -// size_t uiTableIndex = 0; -// if (strHeaderValue.size() == 0) -// { -// for (size_t i = 0; i < m_vecDecodingDynamicTable.size(); ++i) -// { -// if (m_vecDecodingDynamicTable[i].Name() == strHeaderName) -// { -// uiTableIndex = Http2Header::sc_uiMaxStaticTableIndex + i + 1; -// break; -// } -// } -// } -// else -// { -// for (size_t i = 0; i < m_vecDecodingDynamicTable.size(); ++i) -// { -// if (m_vecDecodingDynamicTable[i].Name() == strHeaderName -// && m_vecDecodingDynamicTable[i].Value() == strHeaderValue) -// { -// uiTableIndex = Http2Header::sc_uiMaxStaticTableIndex + i + 1; -// break; -// } -// } -// } -// return(uiTableIndex); -//} - -void CodecHttp2::UpdateEncodingDynamicTable(uint32 uiDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) +void CodecHttp2::UpdateEncodingDynamicTable(const std::string& strHeaderName, const std::string& strHeaderValue) { Http2Header oHeader(strHeaderName, strHeaderValue); - if (uiDynamicTableIndex <= 0 || uiDynamicTableIndex >= m_vecEncodingDynamicTable.size()) // new header + uint32 uiEntrySize = oHeader.HpackSize(); + // if the new header is too big, drop all entries + if (uiEntrySize > m_uiSettingsHeaderTableSize) { - while (m_uiEncodingDynamicTableSize + oHeader.HpackSize() > m_uiSettingsHeaderTableSize - && m_uiEncodingDynamicTableSize > 0) - { - auto& rLastElement = m_vecEncodingDynamicTable.back(); - m_uiEncodingDynamicTableSize -= rLastElement.HpackSize(); - m_vecEncodingDynamicTable.pop_back(); - } - if (oHeader.HpackSize() <= m_uiSettingsHeaderTableSize) - { - m_vecEncodingDynamicTable.insert(m_vecEncodingDynamicTable.begin(), - std::move(oHeader)); - m_uiEncodingDynamicTableSize += oHeader.HpackSize(); - } + m_uiEncodingDynamicTableSize = 0; + m_uiEncodingDynamicTableHeaderCount = 0; + m_iNextEncodingDynamicTableHeaderIndex = m_vecEncodingDynamicTable.size() - 1; + return; } - else // replace header ? + + // evict headers to required length + int32 iNeedRecoverSize = (m_uiEncodingDynamicTableSize + uiEntrySize) - m_uiSettingsHeaderTableSize; + UpdateEncodingDynamicTable(iNeedRecoverSize); + + if (m_uiEncodingDynamicTableHeaderCount + 1 > m_vecEncodingDynamicTable.size()) // need to grow the dynamic table. { - while ((m_uiEncodingDynamicTableSize + oHeader.HpackSize() - - m_vecEncodingDynamicTable[uiDynamicTableIndex].HpackSize()) - > m_uiSettingsHeaderTableSize - && m_uiEncodingDynamicTableSize > 0) + Http2Header oHeader("", ""); + std::vector vecDynamicTable; + vecDynamicTable.assign(m_vecEncodingDynamicTable.size() * 2, oHeader); + for (uint32 i = 0; i < m_vecEncodingDynamicTable.size(); ++i) { - auto& rLastElement = m_vecEncodingDynamicTable.back(); - m_uiEncodingDynamicTableSize -= rLastElement.HpackSize(); - m_vecEncodingDynamicTable.pop_back(); + vecDynamicTable[m_vecEncodingDynamicTable.size() + i] = std::move(m_vecEncodingDynamicTable[i]); } - if (oHeader.HpackSize() <= m_uiSettingsHeaderTableSize) + m_iNextEncodingDynamicTableHeaderIndex = m_vecEncodingDynamicTable.size() - 1; + m_vecEncodingDynamicTable = std::move(vecDynamicTable); + } + int iCurrent = m_iNextEncodingDynamicTableHeaderIndex--; + m_vecEncodingDynamicTable[iCurrent] = std::move(oHeader); + ++m_uiEncodingDynamicTableHeaderCount; + m_uiEncodingDynamicTableSize += uiEntrySize; +} + +uint32 CodecHttp2::UpdateEncodingDynamicTable(int32 iRecoverSize) +{ + uint32 uiEvictHeaderNum = 0; + if (iRecoverSize > 0) + { + int32 iEvictHeaderSize = 0; + // determine how many headers need to be evicted. + for (int32 i = (int32)m_vecEncodingDynamicTable.size() - 1; + iEvictHeaderSize < iRecoverSize; --i) { - if (uiDynamicTableIndex < m_vecEncodingDynamicTable.size()) // replace header - { - m_vecEncodingDynamicTable[uiDynamicTableIndex] = std::move(oHeader); - } - else // new header + if (i < 0 || i < m_iNextEncodingDynamicTableHeaderIndex) { - m_vecEncodingDynamicTable.insert(m_vecEncodingDynamicTable.begin(), - std::move(oHeader)); + break; } - m_uiEncodingDynamicTableSize += oHeader.HpackSize(); + iEvictHeaderSize += m_vecEncodingDynamicTable[i].HpackSize(); + m_uiSettingsHeaderTableSize -= m_vecEncodingDynamicTable[i].HpackSize(); + --m_uiEncodingDynamicTableHeaderCount; + ++uiEvictHeaderNum; + } + + // move headers in dynamic table + uint32 uiFrom = 0; + uint32 uiTo = 0; + for (uint32 j = 0; j < uiEvictHeaderNum; ++j) + { + uiTo = m_vecEncodingDynamicTable.size() - 1 - j; + uiFrom = m_vecEncodingDynamicTable.size() - 1 - j - uiEvictHeaderNum; + m_vecEncodingDynamicTable[uiTo] = std::move(m_vecEncodingDynamicTable[uiFrom]); } + m_iNextEncodingDynamicTableHeaderIndex += uiEvictHeaderNum; } + return(uiEvictHeaderNum); } -void CodecHttp2::UpdateEncodingDynamicTable(uint32 uiTableSize) +uint32 CodecHttp2::EncodingDynamicTableIndex2VectorIndex(uint32 uiIndex) { - m_uiSettingsHeaderTableSize = uiTableSize; - while (m_uiEncodingDynamicTableSize > m_uiSettingsHeaderTableSize - && m_uiEncodingDynamicTableSize > 0) - { - auto& rLastElement = m_vecEncodingDynamicTable.back(); - m_uiEncodingDynamicTableSize -= rLastElement.HpackSize(); - m_vecEncodingDynamicTable.pop_back(); - } + return(m_iNextEncodingDynamicTableHeaderIndex + 1 + uiIndex); } -void CodecHttp2::UpdateDecodingDynamicTable(uint32 uiDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) +void CodecHttp2::UpdateDecodingDynamicTable(int32 iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) { Http2Header oHeader(strHeaderName, strHeaderValue); - if (uiDynamicTableIndex <= 0 || uiDynamicTableIndex >= m_vecDecodingDynamicTable.size()) // new header + uint32 uiEntrySize = oHeader.HpackSize(); + + if (iDynamicTableIndex < 0 || iDynamicTableIndex >= (int32)m_vecDecodingDynamicTable.size()) // new header { - while (m_uiDecodingDynamicTableSize + oHeader.HpackSize() > m_uiSettingsHeaderTableSize - && m_uiDecodingDynamicTableSize > 0) + // if the new header is too big, drop all entries. + if (uiEntrySize > m_uiSettingsHeaderTableSize) { - auto& rLastElement = m_vecDecodingDynamicTable.back(); - m_uiDecodingDynamicTableSize -= rLastElement.HpackSize(); - m_vecDecodingDynamicTable.pop_back(); + m_uiDecodingDynamicTableSize = 0; + m_uiDecodingDynamicTableHeaderCount = 0; + m_iNextDecodingDynamicTableHeaderIndex = m_vecDecodingDynamicTable.size() - 1; + return; } - if (oHeader.HpackSize() <= m_uiSettingsHeaderTableSize) + // evict headers to required length. + int32 uiNeedRecoverSize = (m_uiDecodingDynamicTableSize + uiEntrySize) - m_uiSettingsHeaderTableSize; + UpdateDecodingDynamicTable(uiNeedRecoverSize); + if (m_uiDecodingDynamicTableHeaderCount + 1 > m_vecDecodingDynamicTable.size()) // need to grow the dynamic table. { - m_vecDecodingDynamicTable.insert(m_vecDecodingDynamicTable.begin(), - std::move(oHeader)); - m_uiDecodingDynamicTableSize += oHeader.HpackSize(); + Http2Header oHeader("", ""); + std::vector vecDynamicTable; + vecDynamicTable.assign(m_vecDecodingDynamicTable.size() * 2, oHeader); + for (uint32 i = 0; i < m_vecDecodingDynamicTable.size(); ++i) + { + vecDynamicTable[m_vecDecodingDynamicTable.size() + i] = std::move(m_vecDecodingDynamicTable[i]); + } + m_iNextDecodingDynamicTableHeaderIndex = m_vecDecodingDynamicTable.size() - 1; + m_vecDecodingDynamicTable = std::move(vecDynamicTable); } + int iCurrent = m_iNextDecodingDynamicTableHeaderIndex--; + m_vecDecodingDynamicTable[iCurrent] = std::move(oHeader); + ++m_uiDecodingDynamicTableHeaderCount; + m_uiDecodingDynamicTableSize += uiEntrySize; } - else // replace header ? + else { - while ((m_uiDecodingDynamicTableSize + oHeader.HpackSize() - - m_vecDecodingDynamicTable[uiDynamicTableIndex].HpackSize()) - > m_uiSettingsHeaderTableSize - && m_uiDecodingDynamicTableSize > 0) + int32 iAddSize = uiEntrySize - m_vecDecodingDynamicTable[DecodingDynamicTableIndex2VectorIndex(iDynamicTableIndex)].HpackSize(); + // if the replacement header is too big, drop all entries. + if (iAddSize > (int32)m_uiSettingsHeaderTableSize) { - auto& rLastElement = m_vecDecodingDynamicTable.back(); - m_uiDecodingDynamicTableSize -= rLastElement.HpackSize(); - m_vecDecodingDynamicTable.pop_back(); + m_uiDecodingDynamicTableSize = 0; + m_uiDecodingDynamicTableHeaderCount = 0; + m_iNextDecodingDynamicTableHeaderIndex = m_vecDecodingDynamicTable.size() - 1; + return; } - if (oHeader.HpackSize() <= m_uiSettingsHeaderTableSize) + // evict headers to the required length. + int32 iNeedRecoverSize = (m_uiDecodingDynamicTableSize + iAddSize) - m_uiSettingsHeaderTableSize; + uint32 uiEvictedNum = UpdateDecodingDynamicTable(iNeedRecoverSize); + iDynamicTableIndex += DecodingDynamicTableIndex2VectorIndex(iDynamicTableIndex) + uiEvictedNum; + m_vecDecodingDynamicTable[iDynamicTableIndex] = std::move(oHeader); + m_uiDecodingDynamicTableSize += iAddSize; + } +} + +uint32 CodecHttp2::UpdateDecodingDynamicTable(int32 iRecoverSize) +{ + uint32 uiEvictHeaderNum = 0; + if (iRecoverSize > 0) + { + int32 iEvictHeaderSize = 0; + // determine how many headers need to be evicted. + for (int32 i = (int32)m_vecDecodingDynamicTable.size() - 1; + iEvictHeaderSize < iRecoverSize; --i) { - if (uiDynamicTableIndex < m_vecDecodingDynamicTable.size()) // replace header - { - m_vecDecodingDynamicTable[uiDynamicTableIndex] = std::move(oHeader); - } - else // new header + if (i < 0 || i < m_iNextDecodingDynamicTableHeaderIndex) { - m_vecDecodingDynamicTable.insert(m_vecDecodingDynamicTable.begin(), - std::move(oHeader)); + break; } - m_uiDecodingDynamicTableSize += oHeader.HpackSize(); + iEvictHeaderSize += m_vecDecodingDynamicTable[i].HpackSize(); + m_uiDecodingDynamicTableSize -= m_vecDecodingDynamicTable[i].HpackSize(); + --m_uiDecodingDynamicTableHeaderCount; + ++uiEvictHeaderNum; } + + // move headers in dynamic table + uint32 uiFrom = 0; + uint32 uiTo = 0; + for (uint32 j = 0; j < uiEvictHeaderNum; ++j) + { + uiTo = m_vecDecodingDynamicTable.size() - 1 - j; + uiFrom = m_vecDecodingDynamicTable.size() - 1 - j - uiEvictHeaderNum; + m_vecDecodingDynamicTable[uiTo] = std::move(m_vecDecodingDynamicTable[uiFrom]); + } + m_iNextDecodingDynamicTableHeaderIndex += uiEvictHeaderNum; } + return(uiEvictHeaderNum); } -void CodecHttp2::UpdateDecodingDynamicTable(uint32 uiTableSize) +uint32 CodecHttp2::DecodingDynamicTableIndex2VectorIndex(uint32 uiIndex) { - m_uiSettingsHeaderTableSize = uiTableSize; - while (m_uiDecodingDynamicTableSize > m_uiSettingsHeaderTableSize - && m_uiDecodingDynamicTableSize > 0) - { - auto& rLastElement = m_vecDecodingDynamicTable.back(); - m_uiDecodingDynamicTableSize -= rLastElement.HpackSize(); - m_vecDecodingDynamicTable.pop_back(); - } + return(m_iNextDecodingDynamicTableHeaderIndex + 1 + uiIndex); } void CodecHttp2::CloseStream(uint32 uiStreamId) diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index 4629560c..0bf460de 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -57,6 +57,10 @@ class CodecHttp2: public Codec virtual void ConnectionSetting(CBuffer* pBuff); public: + bool IsClient() const + { + return(m_bChannelIsClient); + } bool IsChunkNotice() const { return(m_bChunkNotice); @@ -100,23 +104,24 @@ class CodecHttp2: public Codec E_CODEC_STATUS UnpackHeaderIndexed(CBuffer* pBuff, HttpMsg& oHttpMsg); E_CODEC_STATUS UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucFirstByte, int32 iPrefixMask, - int& iDynamicTableIndex, std::string& strHeaderName, std::string& strHeaderValue, - bool& bWithHuffman); + std::string& strHeaderName, std::string& strHeaderValue, bool& bWithHuffman); void ClassifyHeader(const std::string& strHeaderName, const std::string& strHeaderValue, HttpMsg& oHttpMsg); void PackHeaderIndexed(size_t uiTableIndex, CBuffer* pBuff); void PackHeaderWithIndexing(const std::string& strHeaderName, - const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff); + const std::string& strHeaderValue, uint32 uiHeaderNameIndex, bool bWithHuffman, CBuffer* pBuff); void PackHeaderWithoutIndexing(const std::string& strHeaderName, - const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff); + const std::string& strHeaderValue, uint32 uiHeaderNameIndex, bool bWithHuffman, CBuffer* pBuff); void PackHeaderNeverIndexing(const std::string& strHeaderName, - const std::string& strHeaderValue, bool bWithHuffman, CBuffer* pBuff); + const std::string& strHeaderValue, uint32 uiHeaderNameIndex, bool bWithHuffman, CBuffer* pBuff); void PackHeaderDynamicTableSize(uint32 uiDynamicTableSize, CBuffer* pBuff); - size_t GetEncodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue = ""); -// size_t GetDecodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue = ""); - void UpdateEncodingDynamicTable(uint32 uiDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue); - void UpdateEncodingDynamicTable(uint32 uiTableSize); - void UpdateDecodingDynamicTable(uint32 uiDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue); - void UpdateDecodingDynamicTable(uint32 uiTableSize); + uint32 GetEncodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue, uint32& uiNameIndex); + uint32 GetDecodingTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue, uint32& uiNameIndex); + void UpdateEncodingDynamicTable(const std::string& strHeaderName, const std::string& strHeaderValue); + uint32 UpdateEncodingDynamicTable(int32 iRecoverSize); + uint32 EncodingDynamicTableIndex2VectorIndex(uint32 uiIndex); + void UpdateDecodingDynamicTable(int32 iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue); + uint32 UpdateDecodingDynamicTable(int32 iRecoverSize); + uint32 DecodingDynamicTableIndex2VectorIndex(uint32 uiIndex); void CloseStream(uint32 uiStreamId); private: @@ -140,10 +145,14 @@ class CodecHttp2: public Codec std::unordered_map m_mapStream; TreeNode* m_pStreamWeightRoot = nullptr; - uint32 m_uiEncodingDynamicTableSize = 0; - uint32 m_uiDecodingDynamicTableSize = 0; std::vector m_vecEncodingDynamicTable; + uint32 m_uiEncodingDynamicTableSize = 0; + uint32 m_uiEncodingDynamicTableHeaderCount = 0; + int32 m_iNextEncodingDynamicTableHeaderIndex = 0; std::vector m_vecDecodingDynamicTable; + uint32 m_uiDecodingDynamicTableSize = 0; + uint32 m_uiDecodingDynamicTableHeaderCount = 0; + int32 m_iNextDecodingDynamicTableHeaderIndex = 0; std::unordered_set m_setEncodingWithoutIndexHeaders; std::unordered_set m_setEncodingNeverIndexHeaders; }; diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index d8edfc57..42e74970 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -514,13 +514,9 @@ E_CODEC_STATUS Http2Frame::DecodeWindowUpdate(CodecHttp2* pCodecH2, uiIncrement = CodecUtil::N2H(stFrameHead.uiStreamIdentifier & H2_DATA_MASK_4_BYTE_LOW_31_BIT); if (uiIncrement == 0) { + LOG4_TRACE("ignore an flow-control window increment of 0."); // TODO just to confirm: curl --http2 receive MAGIC SETTING SETTING_ACK WINDOW_UPDATE(with an flow-control window increment of 0) - if (pCodecH2->GetLastStreamId() == 0) - { - LOG4_TRACE("upgrade ignore an flow-control window increment of 0."); - } - else - { + /* if (stFrameHead.uiStreamIdentifier == 0) { pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); @@ -533,11 +529,12 @@ E_CODEC_STATUS Http2Frame::DecodeWindowUpdate(CodecHttp2* pCodecH2, EncodeRstStream(pCodecH2, stFrameHead.uiStreamIdentifier, H2_ERR_PROTOCOL_ERROR, pReactBuff); return(CODEC_STATUS_PART_ERR); } - } + */ } else { pCodecH2->WindowUpdate(stFrameHead.uiStreamIdentifier, uiIncrement); + EncodeWindowUpdate(pCodecH2, stFrameHead.uiStreamIdentifier, 0, pReactBuff); if (stFrameHead.uiStreamIdentifier > 0) { SendWaittingFrameData(pCodecH2, pReactBuff); @@ -592,16 +589,18 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, E_CODEC_STATUS eEncodeStatus = CODEC_STATUS_OK; const char* pBodyData = oHttpMsg.body().c_str(); uint32 uiDataLen = oHttpMsg.body().size(); + uint32 uiHadEncodeDataLen = 0; uint32 uiEncodedDataLen = 0; - while (uiEncodedDataLen < uiDataLen) + while (uiHadEncodeDataLen < uiDataLen) { - eEncodeStatus = EncodeData(pCodecH2, uiStreamId, pBodyData, uiDataLen, bEndStream, strPadding, uiEncodedDataLen, pBuff); - if (eEncodeStatus != CODEC_STATUS_OK) + eEncodeStatus = EncodeData(pCodecH2, uiStreamId, pBodyData, + uiDataLen - uiHadEncodeDataLen, bEndStream, strPadding, uiEncodedDataLen, pBuff); + if (eEncodeStatus != CODEC_STATUS_PART_OK && eEncodeStatus != CODEC_STATUS_OK) { return(eEncodeStatus); } pBodyData += uiEncodedDataLen; - uiDataLen -= uiEncodedDataLen; + uiHadEncodeDataLen += uiEncodedDataLen; } return(eEncodeStatus); } diff --git a/src/codec/http2/Http2Header.cpp b/src/codec/http2/Http2Header.cpp index 4eb90544..d246e26f 100644 --- a/src/codec/http2/Http2Header.cpp +++ b/src/codec/http2/Http2Header.cpp @@ -13,7 +13,8 @@ namespace neb { -const size_t Http2Header::sc_uiMaxStaticTableIndex = 61; +const uint32 Http2Header::sc_uiMaxStaticTableLength = 62; +const uint32 Http2Header::sc_uiMaxStaticTableIndex = 61; const std::vector> Http2Header::sc_vecStaticTable = { {"unknow", "undefine"}, {":authority", ""}, ///< 1 @@ -79,7 +80,7 @@ const std::vector> Http2Header::sc_vecStatic {"www-authenticate", ""} ///< 61 }; -const std::unordered_map Http2Header::sc_mapStaticTable = { +const std::unordered_map Http2Header::sc_mapStaticTable = { {":authority", 1}, {":method GET", 2}, {":method POST", 3}, @@ -149,6 +150,13 @@ Http2Header::Http2Header(const std::string& strName, const std::string& strValue m_uiHpackSize = m_strName.size() + m_strValue.size() + 32; } +Http2Header::Http2Header(const Http2Header& rHeader) + : m_strName(rHeader.m_strName), + m_strValue(rHeader.m_strValue), + m_uiHpackSize(rHeader.m_uiHpackSize) +{ +} + Http2Header::Http2Header(Http2Header&& rHeader) : m_strName(std::move(rHeader.m_strName)), m_strValue(std::move(rHeader.m_strValue)), @@ -160,6 +168,14 @@ Http2Header::~Http2Header() { } +Http2Header& Http2Header::operator=(const Http2Header& rHeader) +{ + m_strName = rHeader.m_strName; + m_strValue = rHeader.m_strValue; + m_uiHpackSize = rHeader.m_uiHpackSize; + return(*this); +} + Http2Header& Http2Header::operator=(Http2Header&& rHeader) { m_strName = std::move(rHeader.m_strName); @@ -168,15 +184,15 @@ Http2Header& Http2Header::operator=(Http2Header&& rHeader) return(*this); } -void Http2Header::EncodeInt(size_t uiValue, size_t uiPrefix, char cBits, CBuffer* pBuff) +void Http2Header::EncodeInt(uint32 uiValue, uint32 uiPrefix, char cBits, CBuffer* pBuff) { if (uiValue < uiPrefix) { - pBuff->WriteByte(cBits | uiValue); + pBuff->WriteByte((cBits & 0xFF) | uiValue); } else { - pBuff->WriteByte(cBits | uiPrefix); + pBuff->WriteByte((cBits & 0xFF) | uiPrefix); int I = uiValue - uiPrefix; int B = 0; while (I >= 128) @@ -217,7 +233,7 @@ int Http2Header::DecodeInt(int iPrefixMask, CBuffer* pBuff) void Http2Header::EncodeStringLiteral(const std::string& strLiteral, CBuffer* pBuff) { - Http2Header::EncodeInt(strLiteral.size(), (size_t)H2_HPACK_PREFIX_7_BITS, + Http2Header::EncodeInt(strLiteral.size(), (uint32)H2_HPACK_PREFIX_7_BITS, (char)0, pBuff); pBuff->Write(strLiteral.c_str(), strLiteral.size()); } @@ -228,13 +244,13 @@ void Http2Header::EncodeStringLiteralWithHuffman(const std::string& strLiteral, Huffman::Instance()->Encode(strLiteral, &oBuff); if (oBuff.ReadableBytes() < strLiteral.size()) { - Http2Header::EncodeInt(oBuff.ReadableBytes(), (size_t)H2_HPACK_PREFIX_7_BITS, + Http2Header::EncodeInt(oBuff.ReadableBytes(), (uint32)H2_HPACK_PREFIX_7_BITS, (char)0x80, pBuff); pBuff->Write(oBuff.GetRawReadBuffer(), oBuff.ReadableBytes()); } else { - Http2Header::EncodeInt(strLiteral.size(), (size_t)H2_HPACK_PREFIX_7_BITS, + Http2Header::EncodeInt(strLiteral.size(), (uint32)H2_HPACK_PREFIX_7_BITS, (char)0, pBuff); pBuff->Write(strLiteral.c_str(), strLiteral.size()); } @@ -243,11 +259,11 @@ void Http2Header::EncodeStringLiteralWithHuffman(const std::string& strLiteral, bool Http2Header::DecodeStringLiteral(CBuffer* pBuff, std::string& strLiteral, bool& bWithHuffman) { char B = 0; - size_t uiReadIndex = pBuff->GetReadIndex(); + uint32 uiReadIndex = pBuff->GetReadIndex(); pBuff->ReadByte(B); pBuff->SetReadIndex(uiReadIndex); int iStringLength = DecodeInt(H2_HPACK_PREFIX_7_BITS, pBuff); - if (pBuff->ReadableBytes() < (size_t)iStringLength) + if (pBuff->ReadableBytes() < (uint32)iStringLength) { return(false); } @@ -266,26 +282,21 @@ bool Http2Header::DecodeStringLiteral(CBuffer* pBuff, std::string& strLiteral, b } } -size_t Http2Header::GetStaticTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue) +uint32 Http2Header::GetStaticTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue, uint32& uiNameIndex) { - if (strHeaderValue.size() == 0) + uiNameIndex = 0; + auto c_iter = sc_mapStaticTable.find(strHeaderName + " " + strHeaderValue); + if (c_iter == sc_mapStaticTable.end()) { auto c_iter = sc_mapStaticTable.find(strHeaderName); - if (c_iter == sc_mapStaticTable.end()) - { - return(0); - } - return(c_iter->second); - } - else - { - auto c_iter = sc_mapStaticTable.find(strHeaderName + " " + strHeaderValue); - if (c_iter == sc_mapStaticTable.end()) + if (c_iter != sc_mapStaticTable.end()) { - return(0); + uiNameIndex = c_iter->second; } - return(c_iter->second); + return(0); } + uiNameIndex = c_iter->second; + return(c_iter->second); } } /* namespace neb */ diff --git a/src/codec/http2/Http2Header.hpp b/src/codec/http2/Http2Header.hpp index c9dc1206..692e633a 100644 --- a/src/codec/http2/Http2Header.hpp +++ b/src/codec/http2/Http2Header.hpp @@ -13,6 +13,7 @@ #include #include #include +#include "Definition.hpp" #include "util/CBuffer.hpp" namespace neb @@ -51,11 +52,11 @@ class Http2Header { public: Http2Header(const std::string& strName, const std::string& strValue); - Http2Header(const Http2Header& rHeader) = delete; + Http2Header(const Http2Header& rHeader); Http2Header(Http2Header&& rHeader); virtual ~Http2Header(); - Http2Header& operator=(const Http2Header& rHeader) = delete; + Http2Header& operator=(const Http2Header& rHeader); Http2Header& operator=(Http2Header&& rHeader); inline const std::string& Name() const @@ -68,7 +69,7 @@ class Http2Header return(m_strValue); } - inline size_t HpackSize() const + inline uint32 HpackSize() const { return(m_uiHpackSize); } @@ -76,7 +77,7 @@ class Http2Header private: std::string m_strName; std::string m_strValue; - size_t m_uiHpackSize; + uint32 m_uiHpackSize; public: /** @@ -86,7 +87,7 @@ class Http2Header * @param[in] cBits bits of the prefix byte. * @param[in,out] pBuff encode buffer. */ - static void EncodeInt(size_t uiValue, size_t uiPrefix, char cBits, CBuffer* pBuff); + static void EncodeInt(uint32 uiValue, uint32 uiPrefix, char cBits, CBuffer* pBuff); /** * @brief Pseudocode to decode an integer I @@ -117,12 +118,13 @@ class Http2Header */ static bool DecodeStringLiteral(CBuffer* pBuff, std::string& strLiteral, bool& bWithHuffman); - static size_t GetStaticTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue = ""); + static uint32 GetStaticTableIndex(const std::string& strHeaderName, const std::string& strHeaderValue, uint32& uiNameIndex); public: - static const size_t sc_uiMaxStaticTableIndex; + static const uint32 sc_uiMaxStaticTableLength; + static const uint32 sc_uiMaxStaticTableIndex; static const std::vector> sc_vecStaticTable; - static const std::unordered_map sc_mapStaticTable; + static const std::unordered_map sc_mapStaticTable; }; } /* namespace neb */ diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index d3ddae06..e68e59e1 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -247,13 +247,27 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, LOG4_TRACE("eStatus = %d", eStatus); if (CODEC_STATUS_OK == eStatus || CODEC_STATUS_PART_OK == eStatus) { - if (oHttpMsg.stream_id() & 0x01) + if (pCodecH2->IsClient()) { - oHttpMsg.set_type(HTTP_REQUEST); + if (oHttpMsg.stream_id() & 0x01) + { + oHttpMsg.set_type(HTTP_RESPONSE); + } + else + { + oHttpMsg.set_type(HTTP_REQUEST); + } } else { - oHttpMsg.set_type(HTTP_RESPONSE); + if (oHttpMsg.stream_id() & 0x01) + { + oHttpMsg.set_type(HTTP_REQUEST); + } + else + { + oHttpMsg.set_type(HTTP_RESPONSE); + } } if (pCodecH2->IsChunkNotice()) { @@ -356,7 +370,7 @@ void Http2Stream::EncodeSetState(const tagH2FrameHead& stFrameHead) if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) { LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_HALF_CLOSE_REMOTE); - m_eStreamState = H2_STREAM_HALF_CLOSE_REMOTE; + m_eStreamState = H2_STREAM_HALF_CLOSE_LOCAL; } break; case H2_STREAM_HALF_CLOSE_LOCAL: diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 12486871..f107d1e1 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -267,7 +267,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) if (CODEC_STATUS_OK == eCodecStatus) { /* - if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) + if (m_pLabor->GetNodeInfo().bChannelVerify && !pChannel->m_pImpl->IsChannelVerify()) { if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() && CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType() @@ -401,7 +401,7 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) if (CODEC_STATUS_OK == eCodecStatus) { /* - if (m_pLabor->GetNodeInfo().bIsAccess && !pChannel->m_pImpl->IsChannelVerify()) + if (m_pLabor->GetNodeInfo().bChannelVerify && !pChannel->m_pImpl->IsChannelVerify()) { if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() && CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType() @@ -506,6 +506,7 @@ bool Dispatcher::FdTransfer(int iFd) LOG4_WARNING("fail to set TCP_NODELAY"); } } + x_sock_set_block(iAcceptFd, 0); std::shared_ptr pChannel = nullptr; LOG4_TRACE("fd[%d] transfer successfully.", iAcceptFd); if ((CODEC_NEBULA != iCodec) && (CODEC_NEBULA_IN_NODE != iCodec) && m_pLabor->WithSsl()) @@ -1225,6 +1226,7 @@ bool Dispatcher::Init() Codec::AddAutoSwitchCodecType(CODEC_HTTP); Codec::AddAutoSwitchCodecType(CODEC_PROTO); Codec::AddAutoSwitchCodecType(CODEC_RESP); + Codec::AddAutoSwitchCodecType(CODEC_HTTP2); Codec::AddAutoSwitchCodecType(CODEC_PRIVATE); return(true); } @@ -1469,6 +1471,32 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); return(false); } + int iKeepAlive = 1; + int iKeepIdle = 60; + int iKeepInterval = 5; + int iKeepCount = 3; + int iTcpNoDelay = 1; + if (setsockopt(iAcceptFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPALIVE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPIDLE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPINTVL"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPALIVE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)) < 0) + { + LOG4_WARNING("fail to set TCP_NODELAY"); + } + x_sock_set_block(iAcceptFd, 0); inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientAddr, sizeof(szClientAddr)); LOG4_TRACE("accept connect from \"%s\"", szClientAddr); } @@ -1482,6 +1510,32 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); return(false); } + int iKeepAlive = 1; + int iKeepIdle = 60; + int iKeepInterval = 5; + int iKeepCount = 3; + int iTcpNoDelay = 1; + if (setsockopt(iAcceptFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPALIVE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPIDLE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPINTVL"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)) < 0) + { + LOG4_WARNING("fail to set SO_KEEPALIVE"); + } + if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)) < 0) + { + LOG4_WARNING("fail to set TCP_NODELAY"); + } + x_sock_set_block(iAcceptFd, 0); inet_ntop(AF_INET6, &stClientAddr.sin6_addr, szClientAddr, sizeof(szClientAddr)); LOG4_TRACE("accept connect from \"%s\"", szClientAddr); } diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index 0c11bdfc..6ef323ee 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -343,7 +343,7 @@ bool Nodes::SplitAddAndGetNode(const std::string& strNodeType, std::string& strN { std::vector vecAddress; Split(strNodeType, ",", vecAddress); - if (vecAddress.size() <= 1) + if (vecAddress.size() < 1) { return(false); } diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index 951f6c52..9068af5b 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -33,6 +33,7 @@ struct NodeInfo int32 iGatewayPort = 0; ///< 对Client服务的真实端口 bool bThreadMode = 0; ///< 是否线程模型 bool bIsAccess = false; ///< 是否接入Server + bool bChannelVerify = false; ///< 是否需要连接验证 ev_tstamp dIoTimeout = 10.0; ///< IO(连接)超时配置 ev_tstamp dDataReportInterval = 60.0; ///< 统计数据上报时间间隔 ev_tstamp dMsgStatInterval = 60.0; ///< 客户端连接发送数据包统计时间间隔 diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index d776030b..7993c458 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -136,6 +136,7 @@ bool Worker::Init(CJsonObject& oJsonConf) oJsonConf.Get("port", m_stNodeInfo.iPortForServer); oJsonConf.Get("access_host", m_stNodeInfo.strHostForClient); oJsonConf.Get("access_port", m_stNodeInfo.iPortForClient); + oJsonConf.Get("need_channel_verify", m_stNodeInfo.bChannelVerify); oJsonConf.Get("gateway", m_stNodeInfo.strGateway); oJsonConf.Get("gateway_port", m_stNodeInfo.iGatewayPort); m_oNodeConf = oJsonConf; From 75529e4087ee09da772151b1a1f474295ebe610e Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 10 Apr 2021 19:20:57 +0800 Subject: [PATCH 130/176] grpc test passed --- README_cn.md | 11 ++++--- src/actor/step/sys_step/StepRedisCluster.cpp | 7 +++-- src/channel/SocketChannel.cpp | 2 +- src/channel/SocketChannelImpl.cpp | 32 +++++++++++--------- src/channel/SocketChannelImpl.hpp | 2 +- 5 files changed, 32 insertions(+), 22 deletions(-) diff --git a/README_cn.md b/README_cn.md index 946bc92c..c15be8a3 100644 --- a/README_cn.md +++ b/README_cn.md @@ -25,13 +25,13 @@ ## 概述 -  Nebula是一个灵活,高性能的面向业务的IoC分布式网络框架,专为生产环境而设计。Nebula以C\+\+语言开发基于事件驱动型的TCP协议,支持包括proto3、http、https、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建高性能的分布式服务。Nebula自身核心代码只有2万行左右(不计算proto文件生成的代码)。 +  Nebula是一个灵活,高性能的面向业务的IoC分布式网络框架,专为生产环境而设计。Nebula以C\+\+语言开发基于事件驱动型的TCP协议,支持包括proto3、http、https、http2、grpc、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建高性能的分布式服务。Nebula自身核心代码只有2万行左右(不计算proto文件生成的代码)。   Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建分布式服务才能真正体现其价值。为了能快速搭建分布式服务,开发了包括各种类型服务的NebulaBootstrap解决方案。   Nebula是一个产线级的框架和分布式解决方案项目,适用于即时通讯、数据采集、实时计算、消息推送等应用场景,也适用于web后台服务。Nebula已有即时通讯、埋点数据采集及实时分析的生产应用案例,很快将有一个面向庞大用户群的推荐引擎产线应用案例。 -  把Nebula用于学习交流也不错,Bwar欢迎更多有兴趣的开发者加入到Nebula这个项目中来。Nebula是个proactor模式开发框架,不错,是proactor不是reactor(框架层实现的proactor而不是操作系统支持),应用于IO密集型的项目可以达到非常好的性能。对用惯了RPC框架的人而言,Nebula跟RPC很不一样,不过使用起来并不会比RPC复杂多少,但比RPC性能要高很多;对了解异步回调编程方式的开发者,Nebula是个非常简单的框架,比写常见的异步回调写法要简单多了。Nebula网络框架的技术分享和交流见[C++网络框架Nebula](https://zhuanlan.zhihu.com/c_216558336) +  Nebula是个proactor模式开发框架,是proactor不是reactor(框架层实现的proactor而不是操作系统支持),应用于IO密集型的项目可以达到非常好的性能。对用惯了RPC框架的人而言,Nebula跟RPC很不一样,不过使用起来并不会比RPC复杂多少,但比RPC性能要高很多;对了解异步回调编程方式的开发者,Nebula是个非常简单的框架,比写常见的异步回调写法要简单多了。Nebula网络框架的技术分享和交流见[C++网络框架Nebula](https://zhuanlan.zhihu.com/c_216558336)   Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(也是Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++14)是Starship(C++03)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的应用Nebio(埋点数据采集和实时分析项目)在2018年7月底上线并稳定运行。 @@ -73,7 +73,7 @@ * [Step组件](docs/cn/step.md) * [Session组件](docs/cn/session.md) * Context组件 - * Model组件 + * Operator组件 * Chain组件 * [Actor类](docs/cn/actor.md) @@ -108,11 +108,14 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 开发任务 - 完成开发指南 - - 支持http2 - 原生支持dubbo、grpc等协议 ## 版本历史 +#### v1.5 + - 增加原生http2服务端和客户端支持 + - 增加原生grpc服务端和客户端支持 + - 增加绝对路径程序日志 #### v1.4 - 以原生的CodecResp替代hiredis客户端 - 增加redis cluster支持 diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index e886d9b8..e3fd4143 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -23,7 +23,7 @@ const uint16 StepRedisCluster::sc_unClusterSlots = 16384; const std::unordered_set StepRedisCluster::s_setSupportExtractCmd = { - "PING", + "PING","ECHO","QUIT","SELECT", // strings "APPEND","BITCOUNT","BITFIELD","BITPOS","DECR","DECRBY","GET", "GETBIT","GETRANGE","GETSET","INCR","INCRBY","INCRBYFLOAT","MGET", @@ -46,7 +46,10 @@ const std::unordered_set StepRedisCluster::s_setSupportExtractCmd = // keys "DEL","DUMP","EXISTS","EXPIRE","EXPIREAT","MOVE","PERSIST","PEXPIRE", "PEXPIREAT","PTTL","RANDOMKEY","RESTORE","SORT","TOUCH","TTL","TYPE", - "UNLINK" + "UNLINK", + // servers + "ACL","COMMAND","CONFIG","DBSIZE","DEBUG","FLUSHALL","FLUSHDB","INFO", + "LOLWUT","LASTSAVE","MEMORY" }; const std::unordered_set StepRedisCluster::s_setReadCmd = diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index c554cc79..0d91d5f2 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -57,7 +57,7 @@ int SocketChannel::GetFd() const bool SocketChannel::IsClient() const { - return(m_pImpl->GetFd()); + return(m_pImpl->IsClient()); } bool SocketChannel::IsPipeline() const diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 0079a1ae..1ec4017f 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -765,7 +765,6 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) { m_dActiveTime = m_pLabor->GetNowTime(); m_eLastCodecStatus = CODEC_STATUS_PAUSE; - //return(CODEC_STATUS_PAUSE); } else { @@ -773,7 +772,6 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, m_strErrMsg.c_str()); m_eLastCodecStatus = CODEC_STATUS_INT; - return(CODEC_STATUS_INT); } } @@ -878,12 +876,15 @@ E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { m_dActiveTime = m_pLabor->GetNowTime(); - return(CODEC_STATUS_PAUSE); + m_eLastCodecStatus = CODEC_STATUS_PAUSE; + } + else + { + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_eLastCodecStatus = CODEC_STATUS_INT; } - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); - return(CODEC_STATUS_INT); } if (iHadReadLen > 0) @@ -945,12 +946,15 @@ E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { m_dActiveTime = m_pLabor->GetNowTime(); - return(CODEC_STATUS_PAUSE); + m_eLastCodecStatus = CODEC_STATUS_PAUSE; + } + else + { + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_eLastCodecStatus = CODEC_STATUS_INT; } - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); - return(CODEC_STATUS_INT); } if (iHadReadLen > 0) @@ -1266,13 +1270,13 @@ bool SocketChannelImpl::Close() } else { - LOG4_WARNING("failed to close channel(fd %d), errno %d, it will be close later.", m_iFd, errno); + LOG4_TRACE("failed to close channel(fd %d), errno %d, it will be close later.", m_iFd, errno); return(false); } } else { - LOG4_WARNING("channel(fd %d, seq %u) had been closed before.", m_iFd, GetSequence()); + LOG4_TRACE("channel(fd %d, seq %u) had been closed before.", m_iFd, GetSequence()); return(false); } } diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index c0fce2ab..abbe8f11 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -90,7 +90,7 @@ class SocketChannelImpl: public Channel return(m_bPipeline); } - bool bIsClient() const + bool IsClient() const { return(m_bIsClientConnection); } From 8ce6be9660f18e2a09c2844cb03084598d513dd2 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 10 Apr 2021 19:26:12 +0800 Subject: [PATCH 131/176] v1.5 release --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8123d1c8..c353e87e 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ English | [中文](/README_cn.md)         ## Overview -Nebula is a flexible, high-performance business-oriented IoC distributed network framework developed in C++ language, designed for production environments. It supports multiple application layer communication protocols including proto3, http, https, and websocket. Nebula makes it easy to deploy a fast and high-performance distributed service with C++, while keeping the same server architecture and APIs. NebulaBootstrap provides out-of-the-box integration with Nebula service, but can be easily extended to serve other types of application. +Nebula is a flexible, high-performance business-oriented IoC distributed network framework developed in C++ language, designed for production environments. It supports multiple application layer communication protocols including proto3, http, https, http2, grpc, and websocket. Nebula makes it easy to deploy a fast and high-performance distributed service with C++, while keeping the same server architecture and APIs. NebulaBootstrap provides out-of-the-box integration with Nebula service, but can be easily extended to serve other types of application. Nebula is a production level framework and distributed solution project for instant messaging, data collection, real-time computing, message push and other applications, as well as web api services. There were production applications for instant messaging, data acquisition and real-time analysis on line now, and a recommendation engine application for a large user base will be born soon. By the way, using Nebula for toy-level projects is also good for learning network communication. Bwar welcomes more developers to join the Nebula project. Nebula is a proactor development framework(a framework-implemented proactor, not an operating system support). The IO-intensive application on nebula with be good performance. @@ -125,10 +125,13 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Todo list - Complete writing user guide - - http2 support ## Change log +#### v1.5 + - add native http2 + - add native grpc + - add absolute log path #### v1.4 - replace hiredis client with native CodecResp - add redis cluster client From 91c994dbb76487d083c17b31ddd5c8bc4dca2dce Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 18 Apr 2021 00:21:11 +0800 Subject: [PATCH 132/176] grpc client test passed --- README.md | 7 +- README_cn.md | 3 +- src/actor/ActorBuilder.cpp | 4 +- src/actor/ActorBuilder.hpp | 3 +- src/actor/ActorSender.cpp | 1 + src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp | 2 +- .../sys_session/manager/SessionManager.cpp | 2 +- src/actor/step/GrpcStep.cpp | 14 +- src/channel/SocketChannelImpl.cpp | 66 +++++++-- src/channel/SocketChannelImpl.hpp | 8 +- src/codec/CodecResp.cpp | 3 +- src/codec/http2/CodecHttp2.cpp | 134 +++++++++++------- src/codec/http2/CodecHttp2.hpp | 17 +-- src/codec/http2/H2Comm.hpp | 10 +- src/codec/http2/Http2Frame.cpp | 107 +++++++------- src/codec/http2/Http2Stream.cpp | 32 ++--- src/codec/http2/Http2Stream.hpp | 4 +- src/codec/http2/Huffman.cpp | 2 +- src/ios/Dispatcher.cpp | 18 ++- src/labor/Manager.cpp | 5 +- 20 files changed, 273 insertions(+), 169 deletions(-) diff --git a/README.md b/README.md index c353e87e..62b6cb14 100644 --- a/README.md +++ b/README.md @@ -128,9 +128,10 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log -#### v1.5 - - add native http2 - - add native grpc +#### v1.5.0 + - add native http2 server and client + - add native grpc server and client + - channel read and write optimization - add absolute log path #### v1.4 - replace hiredis client with native CodecResp diff --git a/README_cn.md b/README_cn.md index c15be8a3..90bb9fe6 100644 --- a/README_cn.md +++ b/README_cn.md @@ -112,9 +112,10 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 -#### v1.5 +#### v1.5.0 - 增加原生http2服务端和客户端支持 - 增加原生grpc服务端和客户端支持 + - 优化channel读写 - 增加绝对路径程序日志 #### v1.4 - 以原生的CodecResp替代hiredis客户端 diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 597f6f57..648d86bf 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -328,7 +328,7 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH return(true); } -bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, E_CODEC_STATUS eCodecStatus) { if (HTTP_REQUEST == oHttpMsg.type()) // 新请求 { @@ -392,7 +392,7 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const Http } else { - auto http_step_iter = m_mapCallbackStep.find(pChannel->m_pImpl->PopStepSeq()); + auto http_step_iter = m_mapCallbackStep.find(pChannel->m_pImpl->PopStepSeq(oHttpMsg.stream_id(), eCodecStatus)); if (!pChannel->IsPipeline() && pChannel->m_pImpl->GetPipelineStepSeq().empty()) { m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 857b4da9..89c3e66f 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -40,6 +40,7 @@ #include "util/CBuffer.hpp" #include "ActorFactory.hpp" #include "logger/NetLogger.hpp" +#include "codec/Codec.hpp" namespace neb { @@ -94,7 +95,7 @@ class ActorBuilder bool OnSessionTimeout(std::shared_ptr pSession); bool OnChainTimeout(std::shared_ptr pChain); bool OnMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); - bool OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + bool OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK); bool OnMessage(std::shared_ptr pChannel, const RedisMsg& oRedisMsg, uint32 uiFinalStepSeq = 0); bool OnMessage(std::shared_ptr pChannel, const CBuffer& oBuffer); bool OnError(std::shared_ptr pChannel, uint32 uiStepSeq, int iErrno, const std::string& strErrMsg); diff --git a/src/actor/ActorSender.cpp b/src/actor/ActorSender.cpp index 1a8091c4..2908fb3c 100644 --- a/src/actor/ActorSender.cpp +++ b/src/actor/ActorSender.cpp @@ -228,6 +228,7 @@ bool ActorSender::SendTo(Actor* pActor, const std::string& strUrl, const std::st oHttpMsg.set_method(HTTP_POST); oHttpMsg.set_url(strUrl); oHttpMsg.mutable_headers()->insert({"content-type", "application/grpc"}); + oHttpMsg.mutable_headers()->insert({"te", "trailers"}); oHttpMsg.add_adding_never_index_headers("x-trace-id"); if(0 == http_parser_parse_url(strUrl.c_str(), strUrl.length(), 0, &stUrl)) { diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp index ba59a049..8be415b6 100644 --- a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp +++ b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp @@ -195,7 +195,7 @@ bool ModuleHttpUpgrade::UpgradeToHttp2(std::shared_ptr pChannel, { return(false); } - ((CodecHttp2*)pCodec)->Setting(vecSetting); + ((CodecHttp2*)pCodec)->Setting(vecSetting, false); return(true); } diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index dfbf69e2..4e100f8d 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -272,7 +272,7 @@ const std::vector& SessionManager::GetWorkerThreadId() const void SessionManager::AddWorkerThreadId(uint64 ullThreadId) { if (std::find(m_vecWorkerThreadId.begin(), m_vecWorkerThreadId.end(), ullThreadId) - != m_vecWorkerThreadId.end()) + == m_vecWorkerThreadId.end()) { m_vecWorkerThreadId.push_back(ullThreadId); } diff --git a/src/actor/step/GrpcStep.cpp b/src/actor/step/GrpcStep.cpp index 8bcddce0..94b875b6 100644 --- a/src/actor/step/GrpcStep.cpp +++ b/src/actor/step/GrpcStep.cpp @@ -46,10 +46,13 @@ E_CMD_STATUS GrpcStep::Callback( { if (iter->second == "gzip") { - if (!CodecUtil::Gunzip(oHttpMsg.body().substr(5, uiMessageLength), strResponseData)) + if (uiMessageLength > 0) { - LOG4_ERROR("failed to gunzip message."); - return(Callback(pChannel, strResponseData, GRPC_INVALID_ARGUMENT, "failed to gunzip message.")); + if (!CodecUtil::Gunzip(oHttpMsg.body().substr(5, uiMessageLength), strResponseData)) + { + LOG4_ERROR("failed to gunzip message."); + return(Callback(pChannel, strResponseData, GRPC_INVALID_ARGUMENT, "failed to gunzip message.")); + } } } else @@ -61,7 +64,10 @@ E_CMD_STATUS GrpcStep::Callback( } else { - strResponseData.assign(oHttpMsg.body(), 5, uiMessageLength); + if (uiMessageLength > 0) + { + strResponseData.assign(oHttpMsg.body(), 5, uiMessageLength); + } } int iStatus = 0; diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 1ec4017f..309ca0ec 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -118,15 +118,31 @@ E_CODEC_TYPE SocketChannelImpl::GetCodecType() const return(m_pCodec->GetCodecType()); } -uint32 SocketChannelImpl::PopStepSeq() +uint32 SocketChannelImpl::PopStepSeq(uint32 uiStreamId, E_CODEC_STATUS eCodecStatus) { if (m_listPipelineStepSeq.empty()) { + auto iter = m_mapStreamStepSeq.find(uiStreamId); + if (iter != m_mapStreamStepSeq.end()) + { + uint32 uiStepSeq = iter->second; + if (CODEC_STATUS_OK == eCodecStatus) + { + m_mapStreamStepSeq.erase(iter); + } + return(uiStepSeq); + } return(0); } - uint32 uiStepSeq = m_listPipelineStepSeq.front(); - m_listPipelineStepSeq.pop_front(); - return(uiStepSeq); + else + { + uint32 uiStepSeq = m_listPipelineStepSeq.front(); + if (CODEC_STATUS_OK == eCodecStatus) + { + m_listPipelineStepSeq.pop_front(); + } + return(uiStepSeq); + } } ev_tstamp SocketChannelImpl::GetKeepAlive() @@ -365,7 +381,25 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq switch (m_ucChannelStatus) { case CHANNEL_STATUS_ESTABLISHED: - eCodecStatus = ((CodecHttp*)m_pCodec)->Encode(oHttpMsg, m_pSendBuff); + if (CODEC_HTTP == m_pCodec->GetCodecType()) + { + eCodecStatus = ((CodecHttp*)m_pCodec)->Encode(oHttpMsg, m_pSendBuff); + } + else + { + eCodecStatus = ((CodecHttp2*)m_pCodec)->Encode(oHttpMsg, m_pSendBuff); + } + if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) + { + if (CODEC_HTTP == m_pCodec->GetCodecType()) + { + m_listPipelineStepSeq.push_back(uiStepSeq); + } + else + { + m_mapStreamStepSeq.insert(std::make_pair(((CodecHttp2*)m_pCodec)->GetLastStreamId(), uiStepSeq)); + } + } break; case CHANNEL_STATUS_CLOSED: LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); @@ -376,11 +410,25 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq case CHANNEL_STATUS_CONNECTED: case CHANNEL_STATUS_TRY_CONNECT: case CHANNEL_STATUS_INIT: - eCodecStatus = ((CodecHttp*)m_pCodec)->Encode(oHttpMsg, m_pWaitForSendBuff); + if (CODEC_HTTP == m_pCodec->GetCodecType()) + { + eCodecStatus = ((CodecHttp*)m_pCodec)->Encode(oHttpMsg, m_pWaitForSendBuff); + } + else + { + eCodecStatus = ((CodecHttp2*)m_pCodec)->Encode(oHttpMsg, m_pWaitForSendBuff); + } if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) { eCodecStatus = CODEC_STATUS_PAUSE; - m_listPipelineStepSeq.push_back(uiStepSeq); + if (CODEC_HTTP == m_pCodec->GetCodecType()) + { + m_listPipelineStepSeq.push_back(uiStepSeq); + } + else + { + m_mapStreamStepSeq.insert(std::make_pair(((CodecHttp2*)m_pCodec)->GetLastStreamId(), uiStepSeq)); + } } break; default: @@ -415,10 +463,6 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq { m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); } - if (uiStepSeq > 0) - { - m_listPipelineStepSeq.push_back(uiStepSeq); - } m_dActiveTime = m_pLabor->GetNowTime(); if (iNeedWriteLen == iHadWrittenLen) { diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index abbe8f11..11730285 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -78,7 +78,7 @@ class SocketChannelImpl: public Channel return(m_uiSeq); } - uint32 PopStepSeq(); + uint32 PopStepSeq(uint32 uiStreamId = 0, E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK); ev_tstamp GetActiveTime() const { @@ -144,6 +144,11 @@ class SocketChannelImpl: public Channel return(m_listPipelineStepSeq); } + const std::unordered_map& GetStreamStepSeq() const + { + return(m_mapStreamStepSeq); + } + Labor* GetLabor() { return(m_pLabor); @@ -247,6 +252,7 @@ class SocketChannelImpl: public Channel std::string m_strIdentify; ///< 连接标识(可以为空,不为空时用于标识业务层与连接的关系) std::string m_strRemoteAddr; ///< 对端IP地址(不是客户端地址,但可能跟客户端地址相同) std::list m_listPipelineStepSeq; ///< 等待回调的Step seq + std::unordered_map m_mapStreamStepSeq; ///< 等待回调的http2 step seq std::set m_setSkipCodecType; ///< Codec转换需跳过的CodecType Labor* m_pLabor; SocketChannel* m_pSocketChannel; diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index add63f9c..053e6dfc 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -103,9 +103,10 @@ E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply) default: oReply.set_type(REDIS_REPLY_ERROR); oReply.set_integer(REDIS_ERR_PROTOCOL); + pBuff->SetReadIndex(uiReadIndex); return(CODEC_STATUS_ERR); } - if (CODEC_STATUS_PAUSE == eStatus) + if (CODEC_STATUS_OK != eStatus) { pBuff->SetReadIndex(uiReadIndex); } diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index eb19e9b3..5b1afebd 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -69,29 +69,38 @@ void CodecHttp2::ConnectionSetting(CBuffer* pBuff) pBuff->Write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24); m_bWantMagic = false; stSetting.unIdentifier = H2_SETTINGS_INITIAL_WINDOW_SIZE; - stSetting.uiValue = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; - m_uiRecvWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + stSetting.uiValue = 1048576; + m_uiRecvWindowSize = 1048576; + m_uiSendWindowSize = 1048576; vecSetting.push_back(stSetting); stSetting.unIdentifier = H2_SETTINGS_MAX_FRAME_SIZE; - stSetting.uiValue = DEFAULT_SETTINGS_MAX_FRAME_SIZE; + stSetting.uiValue = 4194304; + m_uiSettingsMaxDecodeFrameSize = 4194304; vecSetting.push_back(stSetting); m_pFrame->EncodeSetting(this, vecSetting, pBuff); + m_pFrame->EncodeWindowUpdate(this, 0, 4128769, pBuff); + // In HTTP/2, 1 on client is reserved for Upgrade. + m_uiStreamIdGenerate = 1; } else { stSetting.unIdentifier = H2_SETTINGS_INITIAL_WINDOW_SIZE; stSetting.uiValue = 4194304; m_uiRecvWindowSize = 4194304; + m_uiSendWindowSize = 1048576; vecSetting.push_back(stSetting); stSetting.unIdentifier = H2_SETTINGS_MAX_FRAME_SIZE; stSetting.uiValue = 4194304; + m_uiSettingsMaxDecodeFrameSize = 4194304; vecSetting.push_back(stSetting); stSetting.unIdentifier = H2_SETTINGS_MAX_HEADER_LIST_SIZE; stSetting.uiValue = 8192; + m_uiSettingsMaxHeaderListSize = 8192; vecSetting.push_back(stSetting); m_pFrame->EncodeSetting(this, vecSetting, pBuff); m_pFrame->EncodeWindowUpdate(this, 0, 4128769, pBuff); m_pFrame->EncodePing(this, false, 0, 0, pBuff); + m_uiStreamIdGenerate = 2; } } @@ -166,11 +175,11 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) if (CODEC_STATUS_PART_ERR == eCodecStatus || CODEC_STATUS_OK == eCodecStatus) { - LOG4_TRACE("m_bChannelIsClient = %d, m_stFrameHead.uiStreamIdentifier = %u", - m_bChannelIsClient, m_stFrameHead.uiStreamIdentifier); + LOG4_TRACE("m_bChannelIsClient = %d, m_pCodingStream->GetStreamId() = %u", + m_bChannelIsClient, m_pCodingStream->GetStreamId()); if (m_pCodingStream->GetStreamState() == H2_STREAM_CLOSE) { - CloseStream(m_stFrameHead.uiStreamIdentifier); + CloseStream(m_stDecodeFrameHead.uiStreamIdentifier); } } return(eCodecStatus); @@ -227,40 +236,40 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR } size_t uiReadIdx = pBuff->GetReadIndex(); - Http2Frame::DecodeFrameHeader(pBuff, m_stFrameHead); - LOG4_TRACE("m_stFrameHead.uiLength = %u, m_stFrameHead.ucType = %u, m_stFrameHead.ucFlag = %u, m_stFrameHead.uiStreamIdentifier = %u", - m_stFrameHead.uiLength, m_stFrameHead.ucType, m_stFrameHead.ucFlag, m_stFrameHead.uiStreamIdentifier); - if (m_stFrameHead.uiLength > m_uiSettingsMaxFrameSize) + Http2Frame::DecodeFrameHeader(pBuff, m_stDecodeFrameHead); + LOG4_TRACE("m_stDecodeFrameHead.uiLength = %u, m_stDecodeFrameHead.ucType = %u, m_stDecodeFrameHead.ucFlag = %u, m_stDecodeFrameHead.uiStreamIdentifier = %u", + m_stDecodeFrameHead.uiLength, m_stDecodeFrameHead.ucType, m_stDecodeFrameHead.ucFlag, m_stDecodeFrameHead.uiStreamIdentifier); + if (m_stDecodeFrameHead.uiLength > m_uiSettingsMaxDecodeFrameSize) { - LOG4_TRACE("m_uiSettingsMaxFrameSize = %u, m_stFrameHead.uiLength = %u", - m_uiSettingsMaxFrameSize, m_stFrameHead.uiLength); + LOG4_TRACE("m_uiSettingsMaxDecodeFrameSize = %u, m_stDecodeFrameHead.uiLength = %u", + m_uiSettingsMaxDecodeFrameSize, m_stDecodeFrameHead.uiLength); SetErrno(H2_ERR_FRAME_SIZE_ERROR); - pBuff->SetReadIndex(uiReadIdx); + m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "frame size is larger than max frame size.", pReactBuff); return(CODEC_STATUS_PART_ERR); } - if (pBuff->ReadableBytes() < m_stFrameHead.uiLength) + if (pBuff->ReadableBytes() < m_stDecodeFrameHead.uiLength) { - LOG4_TRACE("pBuff->ReadableBytes() = %u, m_stFrameHead.uiLength = %u", pBuff->ReadableBytes(), m_stFrameHead.uiLength); + LOG4_TRACE("pBuff->ReadableBytes() = %u, m_stDecodeFrameHead.uiLength = %u", pBuff->ReadableBytes(), m_stDecodeFrameHead.uiLength); pBuff->SetReadIndex(uiReadIdx); return(CODEC_STATUS_PAUSE); } - if (m_uiGoawayLastStreamId > 0 && m_stFrameHead.uiStreamIdentifier > m_uiGoawayLastStreamId) + if (m_uiGoawayLastStreamId > 0 && m_stDecodeFrameHead.uiStreamIdentifier > m_uiGoawayLastStreamId) { - LOG4_TRACE("m_uiGoawayLastStreamId = %u, m_stFrameHead.uiStreamIdentifier = %u", m_uiGoawayLastStreamId, m_stFrameHead.uiStreamIdentifier); + LOG4_TRACE("m_uiGoawayLastStreamId = %u, m_stDecodeFrameHead.uiStreamIdentifier = %u", m_uiGoawayLastStreamId, m_stDecodeFrameHead.uiStreamIdentifier); SetErrno(H2_ERR_CANCEL); pBuff->SetReadIndex(uiReadIdx); return(CODEC_STATUS_PART_ERR); } - LOG4_TRACE("m_stFrameHead.uiStreamIdentifier = %u", m_stFrameHead.uiStreamIdentifier); - if (m_stFrameHead.uiStreamIdentifier > 0) + LOG4_TRACE("m_stDecodeFrameHead.uiStreamIdentifier = %u", m_stDecodeFrameHead.uiStreamIdentifier); + if (m_stDecodeFrameHead.uiStreamIdentifier > 0) { if (m_pCodingStream != nullptr) { - if (m_stFrameHead.uiStreamIdentifier == m_pCodingStream->GetStreamId()) + if (m_stDecodeFrameHead.uiStreamIdentifier == m_pCodingStream->GetStreamId()) { - E_CODEC_STATUS eCodecStatus = m_pCodingStream->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff); + E_CODEC_STATUS eCodecStatus = m_pCodingStream->Decode(this, m_stDecodeFrameHead, pBuff, oHttpMsg, pReactBuff); LOG4_TRACE("eCodecStatus = %d", eCodecStatus); if (CODEC_STATUS_PAUSE == eCodecStatus || CODEC_STATUS_ERR == eCodecStatus) @@ -272,7 +281,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR { if (m_pCodingStream->GetStreamState() == H2_STREAM_CLOSE) { - CloseStream(m_stFrameHead.uiStreamIdentifier); + CloseStream(m_stDecodeFrameHead.uiStreamIdentifier); } } oHttpMsg.set_http_major(2); @@ -280,7 +289,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR return(eCodecStatus); } } - if (H2_FRAME_CONTINUATION == m_stFrameHead.ucType) + if (H2_FRAME_CONTINUATION == m_stDecodeFrameHead.ucType) { SetErrno(H2_ERR_PROTOCOL_ERROR); m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "A CONTINUATION " @@ -290,8 +299,8 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR "use when a more specific error code is not available."); return(CODEC_STATUS_ERR); } - LOG4_TRACE("m_stFrameHead.ucType = %u", m_stFrameHead.ucType); - auto stream_iter = m_mapStream.find(m_stFrameHead.uiStreamIdentifier); + LOG4_TRACE("m_stDecodeFrameHead.ucType = %u", m_stDecodeFrameHead.ucType); + auto stream_iter = m_mapStream.find(m_stDecodeFrameHead.uiStreamIdentifier); if (stream_iter == m_mapStream.end()) { /** The identifier of a newly established stream MUST be numerically @@ -301,17 +310,32 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR * that receives an unexpected stream identifier MUST respond with * a connection error (Section 5.4.1) of type PROTOCOL_ERROR. */ - if (m_stFrameHead.uiStreamIdentifier <= m_uiStreamIdGenerate) + if (m_stDecodeFrameHead.uiStreamIdentifier <= m_uiStreamIdGenerate) { - SetErrno(H2_ERR_PROTOCOL_ERROR); - m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "The " - "identifier of a newly established stream MUST be " - "numerically greater than all streams that the " - "initiating endpoint has opened or reserved.", pReactBuff); - return(CODEC_STATUS_ERR); + if (H2_FRAME_WINDOW_UPDATE == m_stDecodeFrameHead.ucType) + { + E_CODEC_STATUS eCodecStatus = m_pFrame->Decode(this, m_stDecodeFrameHead, pBuff, oHttpMsg, pReactBuff); + if (CODEC_STATUS_PAUSE == eCodecStatus + || CODEC_STATUS_ERR == eCodecStatus) + { + pBuff->SetReadIndex(uiReadIdx); + } + oHttpMsg.set_http_major(2); + oHttpMsg.set_http_minor(0); + return(eCodecStatus); + } + else + { + SetErrno(H2_ERR_PROTOCOL_ERROR); + m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "The " + "identifier of a newly established stream MUST be " + "numerically greater than all streams that the " + "initiating endpoint has opened or reserved.", pReactBuff); + return(CODEC_STATUS_ERR); + } } - m_uiStreamIdGenerate = m_stFrameHead.uiStreamIdentifier; - if (NewCodingStream(m_stFrameHead.uiStreamIdentifier) == nullptr) + m_uiStreamIdGenerate = m_stDecodeFrameHead.uiStreamIdentifier; + if (NewCodingStream(m_stDecodeFrameHead.uiStreamIdentifier) == nullptr) { return(CODEC_STATUS_ERR); } @@ -320,7 +344,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR { m_pCodingStream = stream_iter->second; } - E_CODEC_STATUS eCodecStatus = m_pCodingStream->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff); + E_CODEC_STATUS eCodecStatus = m_pCodingStream->Decode(this, m_stDecodeFrameHead, pBuff, oHttpMsg, pReactBuff); if (CODEC_STATUS_PAUSE == eCodecStatus || CODEC_STATUS_ERR == eCodecStatus) { @@ -331,7 +355,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR { if (m_pCodingStream->GetStreamState() == H2_STREAM_CLOSE) { - CloseStream(m_stFrameHead.uiStreamIdentifier); + CloseStream(m_stDecodeFrameHead.uiStreamIdentifier); } } oHttpMsg.set_http_major(2); @@ -340,7 +364,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR } else { - E_CODEC_STATUS eCodecStatus = m_pFrame->Decode(this, m_stFrameHead, pBuff, oHttpMsg, pReactBuff); + E_CODEC_STATUS eCodecStatus = m_pFrame->Decode(this, m_stDecodeFrameHead, pBuff, oHttpMsg, pReactBuff); if (CODEC_STATUS_PAUSE == eCodecStatus || CODEC_STATUS_ERR == eCodecStatus) { @@ -456,7 +480,7 @@ void CodecHttp2::RstStream(uint32 uiStreamId) } } -E_H2_ERR_CODE CodecHttp2::Setting(const std::vector& vecSetting) +E_H2_ERR_CODE CodecHttp2::Setting(const std::vector& vecSetting, bool bRecvSetting) { for (size_t i = 0; i < vecSetting.size(); ++i) { @@ -498,8 +522,15 @@ E_H2_ERR_CODE CodecHttp2::Setting(const std::vector& vecSetting) case H2_SETTINGS_MAX_FRAME_SIZE: if (vecSetting[i].uiValue <= SETTINGS_MAX_FRAME_SIZE) { - m_uiSettingsMaxFrameSize = vecSetting[i].uiValue; - LOG4_TRACE("set max frame size to %u", m_uiSettingsMaxFrameSize); + if (bRecvSetting) + { + m_uiSettingsMaxEncodeFrameSize = vecSetting[i].uiValue; + LOG4_TRACE("set max frame size to %u", m_uiSettingsMaxEncodeFrameSize); + } + else + { + m_uiSettingsMaxDecodeFrameSize = vecSetting[i].uiValue; + } } else { @@ -529,7 +560,7 @@ void CodecHttp2::WindowUpdate(uint32 uiStreamId, uint32 uiIncrement) } } -void CodecHttp2::ShrinkSendWindow(uint32 uiStreamId, uint32 uiSendLength) +void CodecHttp2::UpdateSendWindow(uint32 uiStreamId, uint32 uiSendLength) { m_uiSendWindowSize -= uiSendLength; if (uiStreamId > 0) @@ -542,21 +573,16 @@ void CodecHttp2::ShrinkSendWindow(uint32 uiStreamId, uint32 uiSendLength) } } -void CodecHttp2::ShrinkRecvWindow(uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) +void CodecHttp2::UpdateRecvWindow(uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) { - m_uiRecvWindowSize -= uiRecvLength; - if (m_uiRecvWindowSize < DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE / 4) - { - m_pFrame->EncodeWindowUpdate(this, 0, - SETTINGS_MAX_INITIAL_WINDOW_SIZE - m_uiRecvWindowSize, pBuff); - } + m_pFrame->EncodeWindowUpdate(this, 0, uiRecvLength, pBuff); m_uiRecvWindowSize = SETTINGS_MAX_INITIAL_WINDOW_SIZE; if (uiStreamId > 0) { auto iter = m_mapStream.find(uiStreamId); if (iter != m_mapStream.end()) { - iter->second->ShrinkRecvWindow(this, uiStreamId, uiRecvLength, pBuff); + iter->second->UpdateRecvWindow(this, uiStreamId, uiRecvLength, pBuff); } } } @@ -884,7 +910,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderIndexed(CBuffer* pBuff, HttpMsg& oHttpMsg else { uint32 uiDynamicTableIndex = DecodingDynamicTableIndex2VectorIndex(uiTableIndex - Http2Header::sc_uiMaxStaticTableLength); - if (uiTableIndex <= Http2Header::sc_uiMaxStaticTableIndex + m_vecDecodingDynamicTable.size()) + if (uiDynamicTableIndex < m_vecDecodingDynamicTable.size()) { ClassifyHeader(m_vecDecodingDynamicTable[uiDynamicTableIndex].Name(), m_vecDecodingDynamicTable[uiDynamicTableIndex].Value(), oHttpMsg); @@ -1167,12 +1193,12 @@ uint32 CodecHttp2::GetEncodingTableIndex(const std::string& strHeaderName, const { if (m_vecEncodingDynamicTable[i].Value() == strHeaderValue) { - uiTableIndex = 1 - m_iNextEncodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; + uiTableIndex = i - m_iNextEncodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; break; } else if (uiNameIndex == 0) { - uiNameIndex = 1 - m_iNextEncodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; + uiNameIndex = i - m_iNextEncodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; } } } @@ -1195,12 +1221,12 @@ uint32 CodecHttp2::GetDecodingTableIndex(const std::string& strHeaderName, const { if (m_vecDecodingDynamicTable[i].Value() == strHeaderValue) { - uiTableIndex = 1 - m_iNextDecodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; + uiTableIndex = i - m_iNextDecodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; break; } else if (uiNameIndex == 0) { - uiNameIndex = 1 - m_iNextDecodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; + uiNameIndex = i - m_iNextDecodingDynamicTableHeaderIndex + Http2Header::sc_uiMaxStaticTableIndex; } } } diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index 0bf460de..258e4032 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -67,17 +67,17 @@ class CodecHttp2: public Codec } void SetPriority(uint32 uiStreamId, const tagPriority& stPriority); void RstStream(uint32 uiStreamId); - E_H2_ERR_CODE Setting(const std::vector& vecSetting); + E_H2_ERR_CODE Setting(const std::vector& vecSetting, bool bRecvSetting = false); void WindowUpdate(uint32 uiStreamId, uint32 uiIncrement); - void ShrinkSendWindow(uint32 uiStreamId, uint32 uiSendLength); - void ShrinkRecvWindow(uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff); + void UpdateSendWindow(uint32 uiStreamId, uint32 uiSendLength); + void UpdateRecvWindow(uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff); uint32 GetSendWindowSize() { return(m_uiSendWindowSize); } - uint32 GetMaxFrameSize() + uint32 GetMaxEncodeFrameSize() { - return(m_uiSettingsMaxFrameSize); + return(m_uiSettingsMaxEncodeFrameSize); } E_CODEC_STATUS UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBuff, HttpMsg& oHttpMsg); void PackHeader(const HttpMsg& oHttpMsg, int iHeaderType, CBuffer* pBuff); @@ -134,11 +134,12 @@ class CodecHttp2: public Codec uint32 m_uiSettingsHeaderTableSize = 4096; uint32 m_uiSettingsMaxConcurrentStreams = 100; uint32 m_uiSettingsMaxWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; - uint32 m_uiSettingsMaxFrameSize = DEFAULT_SETTINGS_MAX_FRAME_SIZE; - uint32 m_uiSettingsMaxHeaderListSize = 50; // TODO SettingsMaxHeaderListSize + uint32 m_uiSettingsMaxEncodeFrameSize = DEFAULT_SETTINGS_MAX_FRAME_SIZE; + uint32 m_uiSettingsMaxDecodeFrameSize = DEFAULT_SETTINGS_MAX_FRAME_SIZE; + uint32 m_uiSettingsMaxHeaderListSize = 8192; // TODO SettingsMaxHeaderListSize uint32 m_uiSendWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; uint32 m_uiRecvWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; - tagH2FrameHead m_stFrameHead; + tagH2FrameHead m_stDecodeFrameHead; HttpMsg* m_pHoldingHttpMsg = nullptr; // upgrade未完成时暂存请求 Http2Frame* m_pFrame = nullptr; Http2Stream* m_pCodingStream = nullptr; diff --git a/src/codec/http2/H2Comm.hpp b/src/codec/http2/H2Comm.hpp index 0139a199..f3e417b8 100644 --- a/src/codec/http2/H2Comm.hpp +++ b/src/codec/http2/H2Comm.hpp @@ -14,10 +14,10 @@ namespace neb { const uint32 H2_FRAME_HEAD_SIZE = 9; -const uint32 SETTINGS_MAX_FRAME_SIZE = 16777215; // (2^24) - 1; -const uint32 DEFAULT_SETTINGS_MAX_FRAME_SIZE = 16384; // (2^14); -const uint32 SETTINGS_MAX_INITIAL_WINDOW_SIZE = 2147483647; // (2^31) - 1; -const uint32 DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE = 65535; // (2^16) - 1; +const uint32 SETTINGS_MAX_FRAME_SIZE = 16777215; // (2^24) - 1 +const uint32 DEFAULT_SETTINGS_MAX_FRAME_SIZE = 16384; // (2^14) +const uint32 SETTINGS_MAX_INITIAL_WINDOW_SIZE = 2147483647; // (2^31) - 1 +const uint32 DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE = 65535; // (2^16) - 1 /* * @see https://httpwg.org/specs/rfc7540.html#FRAME_SIZE_ERROR @@ -65,7 +65,7 @@ struct tagH2FrameHead uint8 ucFlag = 0; ///< 为帧类型专用的布尔标志保留的8位字段。标志被分配特定于指定帧类型的语义。没有为特定帧类型定义语义的标志务必被忽略,并且在发送时务必保持未设置(0x0)。 ///< 保留的1位字段。该位的语义是未定义的,并且该位必须在发送时保持未设置(0x0),并且在接收时必须忽略。 uint32 uiLength = 0; ///< 帧有效载荷的长度,表示为无符号的24位整数。除非接收方为SETTINGS_MAX_FRAME_SIZE设置了较大的值,否则不得发送大于2 ^ 14(16,384)的值。帧头的9个八位字节不包含在该值中。 uint32 uiStreamIdentifier = 0; ///< 流标识符,表示为一个无符号的31位整数。值0x0保留给与整个连接相关联的帧,而不是单个流。 - tagH2FrameHead& operator=(const tagH2FrameHead stFrameHead) + tagH2FrameHead& operator=(const tagH2FrameHead& stFrameHead) { cR = stFrameHead.cR; ucType = stFrameHead.ucType; diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index 42e74970..7cbc9949 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -58,15 +58,17 @@ void Http2Frame::DecodeFrameHeader(CBuffer* pBuff, tagH2FrameHead& stFrameHead) pBuff->Read(&stFrameHead.ucType, 1); pBuff->Read(&stFrameHead.ucFlag, 1); pBuff->Read(&stFrameHead.uiStreamIdentifier, 4); + stFrameHead.uiStreamIdentifier = CodecUtil::N2H(stFrameHead.uiStreamIdentifier); stFrameHead.cR = (stFrameHead.uiStreamIdentifier & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; - stFrameHead.uiStreamIdentifier = CodecUtil::N2H(stFrameHead.uiStreamIdentifier & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + stFrameHead.uiStreamIdentifier &= H2_DATA_MASK_4_BYTE_LOW_31_BIT; } void Http2Frame::EncodeFrameHeader(const tagH2FrameHead& stFrameHead, CBuffer* pBuff) { uint32 uiIdentifier = stFrameHead.cR; uiIdentifier <<= 31; - uiIdentifier |= (CodecUtil::H2N(stFrameHead.uiStreamIdentifier)); + uiIdentifier |= stFrameHead.uiStreamIdentifier; + uiIdentifier = CodecUtil::H2N(uiIdentifier); WriteMediumInt(stFrameHead.uiLength, pBuff); pBuff->Write(&stFrameHead.ucType, 1); pBuff->Write(&stFrameHead.ucFlag, 1); @@ -106,7 +108,7 @@ E_CODEC_STATUS Http2Frame::Encode(CodecHttp2* pCodecH2, { bool bEndStream = false; E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; - if (oHttpMsg.headers_size() > 0) + if (oHttpMsg.headers_size() > 0 || oHttpMsg.pseudo_header_size() > 0) { if (oHttpMsg.body().size() == 0) { @@ -211,7 +213,7 @@ E_CODEC_STATUS Http2Frame::DecodeData(CodecHttp2* pCodecH2, oHttpMsg.mutable_body()->append(pBuff->GetRawReadBuffer(), stFrameHead.uiLength); pBuff->AdvanceReadIndex(stFrameHead.uiLength); } - pCodecH2->ShrinkRecvWindow(stFrameHead.uiStreamIdentifier, stFrameHead.uiLength, pReactBuff); + pCodecH2->UpdateRecvWindow(stFrameHead.uiStreamIdentifier, stFrameHead.uiLength, pReactBuff); return(CODEC_STATUS_PART_OK); } @@ -249,8 +251,9 @@ E_CODEC_STATUS Http2Frame::DecodeHeaders(CodecHttp2* pCodecH2, { tagPriority stPriority; pBuff->Read(&stPriority.uiDependency, 4); + stPriority.uiDependency = CodecUtil::N2H(stPriority.uiDependency); stPriority.E = (stPriority.uiDependency & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; - stPriority.uiDependency = CodecUtil::N2H(stPriority.uiDependency & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + stPriority.uiDependency &= H2_DATA_MASK_4_BYTE_LOW_31_BIT; pBuff->Read(&stPriority.ucWeight, 1); LOG4_TRACE("SetPriority(identifier = %u, E = %u, ucWeight = %u, uiDependency = %u)", stFrameHead.uiStreamIdentifier, stPriority.E, stPriority.ucWeight, stPriority.uiDependency); @@ -287,8 +290,9 @@ E_CODEC_STATUS Http2Frame::DecodePriority(CodecHttp2* pCodecH2, } tagPriority stPriority; pBuff->Read(&stPriority.uiDependency, 4); + stPriority.uiDependency = CodecUtil::N2H(stPriority.uiDependency); stPriority.E = (stPriority.uiDependency & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; - stPriority.uiDependency = CodecUtil::N2H(stPriority.uiDependency & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + stPriority.uiDependency &= H2_DATA_MASK_4_BYTE_LOW_31_BIT; pBuff->Read(&stPriority.ucWeight, 1); if (stPriority.uiDependency == 0x0) { @@ -326,7 +330,10 @@ E_CODEC_STATUS Http2Frame::DecodeRstStream(CodecHttp2* pCodecH2, pBuff->Read(&iErrCode, 4); // the error code should be H2_ERR_CANCEL iErrCode = CodecUtil::N2H((uint32)iErrCode); pCodecH2->SetErrno(iErrCode); - pCodecH2->RstStream(stFrameHead.uiStreamIdentifier); + if (m_pStream != nullptr) + { + m_pStream->SetState(H2_STREAM_CLOSE); + } return(CODEC_STATUS_PART_ERR); } @@ -377,7 +384,7 @@ E_CODEC_STATUS Http2Frame::DecodeSetting(CodecHttp2* pCodecH2, vecSetting.push_back(stSetting); LOG4_TRACE("stSetting.unIdentifier = %u, stSetting.uiValue = %u", stSetting.unIdentifier, stSetting.uiValue); } - E_H2_ERR_CODE eSettingResult = pCodecH2->Setting(vecSetting); + E_H2_ERR_CODE eSettingResult = pCodecH2->Setting(vecSetting, true); if (eSettingResult == H2_ERR_NO_ERROR) { EncodeSetting(pCodecH2, pReactBuff); @@ -424,8 +431,9 @@ E_CODEC_STATUS Http2Frame::DecodePushPromise(CodecHttp2* pCodecH2, //unsigned char R; uint32 uiStreamId = 0; pBuff->Read(&uiStreamId, 4); + uiStreamId = CodecUtil::N2H(uiStreamId); //R = (uiStreamId & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; - uiStreamId = CodecUtil::N2H(uiStreamId & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + uiStreamId &= H2_DATA_MASK_4_BYTE_LOW_31_BIT; E_CODEC_STATUS eCodecStatus = pCodecH2->PromiseStream(uiStreamId, pReactBuff); if (eCodecStatus == CODEC_STATUS_PART_OK) { @@ -487,8 +495,9 @@ E_CODEC_STATUS Http2Frame::DecodeGoaway(CodecHttp2* pCodecH2, std::string strDebugData; pBuff->Read(&uiLastStreamId, 4); pBuff->Read(&iErrCode, 4); + uiLastStreamId = CodecUtil::N2H(uiLastStreamId); //R = (uiLastStreamId & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; - uiLastStreamId = CodecUtil::N2H(uiLastStreamId & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + uiLastStreamId &= H2_DATA_MASK_4_BYTE_LOW_31_BIT; iErrCode = CodecUtil::N2H((uint32)iErrCode); strDebugData.assign(pBuff->GetRawReadBuffer(), stFrameHead.uiLength - 8); pBuff->AdvanceReadIndex(stFrameHead.uiLength - 8); @@ -510,31 +519,27 @@ E_CODEC_STATUS Http2Frame::DecodeWindowUpdate(CodecHttp2* pCodecH2, //char cR = 0; uint32 uiIncrement = 0; pBuff->Read(&uiIncrement, 4); + uiIncrement = CodecUtil::N2H(uiIncrement); //cR = (uiIncrement & H2_DATA_MASK_4_BYTE_HIGHEST_BIT) >> 31; - uiIncrement = CodecUtil::N2H(stFrameHead.uiStreamIdentifier & H2_DATA_MASK_4_BYTE_LOW_31_BIT); + uiIncrement &= H2_DATA_MASK_4_BYTE_LOW_31_BIT; if (uiIncrement == 0) { - LOG4_TRACE("ignore an flow-control window increment of 0."); - // TODO just to confirm: curl --http2 receive MAGIC SETTING SETTING_ACK WINDOW_UPDATE(with an flow-control window increment of 0) - /* - if (stFrameHead.uiStreamIdentifier == 0) - { - pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); - EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "TYPE_WINDOW_UPDATE length == 0 on connection", pReactBuff); - return(CODEC_STATUS_ERR); - } - else - { - pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); - EncodeRstStream(pCodecH2, stFrameHead.uiStreamIdentifier, H2_ERR_PROTOCOL_ERROR, pReactBuff); - return(CODEC_STATUS_PART_ERR); - } - */ + if (stFrameHead.uiStreamIdentifier == 0) + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "TYPE_WINDOW_UPDATE length == 0 on connection", pReactBuff); + return(CODEC_STATUS_ERR); + } + else + { + pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + EncodeRstStream(pCodecH2, stFrameHead.uiStreamIdentifier, H2_ERR_PROTOCOL_ERROR, pReactBuff); + return(CODEC_STATUS_PART_ERR); + } } else { pCodecH2->WindowUpdate(stFrameHead.uiStreamIdentifier, uiIncrement); - EncodeWindowUpdate(pCodecH2, stFrameHead.uiStreamIdentifier, 0, pReactBuff); if (stFrameHead.uiStreamIdentifier > 0) { SendWaittingFrameData(pCodecH2, pReactBuff); @@ -641,7 +646,7 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, { uint32 uiAddtionLength = 0; stFrameHead.ucFlag |= H2_FRAME_FLAG_PADDED; - if (stPriority.uiDependency > 0) + if (stPriority.ucWeight > 0) { stFrameHead.ucFlag |= H2_FRAME_FLAG_PRIORITY; // 6bytes = 48bits = 8 + 1 + 31 + 8 @@ -654,9 +659,9 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, uiAddtionLength = 1 + strPadding.size(); } - if (stFrameHead.uiLength > pCodecH2->GetMaxFrameSize()) + if (stFrameHead.uiLength > pCodecH2->GetMaxEncodeFrameSize()) { - stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); EncodeFrameHeader(stFrameHead, pBuff); uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); @@ -688,7 +693,7 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, else { uint32 uiAddtionLength = 0; - if (stPriority.uiDependency > 0) + if (stPriority.ucWeight > 0) { stFrameHead.ucFlag |= H2_FRAME_FLAG_PRIORITY; // 5bytes = 40bits = 1 + 31 + 8 @@ -700,9 +705,9 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, stFrameHead.uiLength = oBuffer.ReadableBytes(); } - if (stFrameHead.uiLength > pCodecH2->GetMaxFrameSize()) + if (stFrameHead.uiLength > pCodecH2->GetMaxEncodeFrameSize()) { - stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); EncodeFrameHeader(stFrameHead, pBuff); EncodePriority(stPriority, pBuff); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); @@ -745,7 +750,8 @@ E_CODEC_STATUS Http2Frame::EncodePriority(CodecHttp2* pCodecH2, stFrameHead.uiLength = 5; uint32 uiPriority = stPriority.E; uiPriority <<= 31; - uiPriority |= (CodecUtil::H2N(stPriority.uiDependency)); + uiPriority |= stPriority.uiDependency; + uiPriority = CodecUtil::H2N(uiPriority); EncodeFrameHeader(stFrameHead, pBuff); pBuff->Write(&uiPriority, 4); pBuff->Write(&stPriority.ucWeight, 1); @@ -786,7 +792,7 @@ E_CODEC_STATUS Http2Frame::EncodeSetting(CodecHttp2* pCodecH2, } else { - pCodecH2->Setting(vecSetting); + pCodecH2->Setting(vecSetting, false); } EncodeFrameHeader(stFrameHead, pBuff); for (uint32 i = 0; i < vecSetting.size(); ++i) @@ -845,9 +851,9 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, stFrameHead.ucFlag |= H2_FRAME_FLAG_PADDED; stFrameHead.uiLength = 5 + oBuffer.ReadableBytes() + strPadding.size(); uiAddtionLength = 5 + strPadding.size(); - if (stFrameHead.uiLength > pCodecH2->GetMaxFrameSize()) + if (stFrameHead.uiLength > pCodecH2->GetMaxEncodeFrameSize()) { - stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); EncodeFrameHeader(stFrameHead, pBuff); uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); @@ -881,9 +887,9 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, { stFrameHead.uiLength = 4 + oBuffer.ReadableBytes(); uiAddtionLength = 4; - if (stFrameHead.uiLength > pCodecH2->GetMaxFrameSize()) + if (stFrameHead.uiLength > pCodecH2->GetMaxEncodeFrameSize()) { - stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); EncodeFrameHeader(stFrameHead, pBuff); // ignore R pBuff->Write(&uiPromiseStreamId, 4); @@ -984,9 +990,9 @@ E_CODEC_STATUS Http2Frame::EncodeContinuation(CodecHttp2* pCodecH2, stFrameHead.uiStreamIdentifier = uiStreamId; while (pHpackBuff->ReadableBytes() > 0) { - if (pHpackBuff->ReadableBytes() > pCodecH2->GetMaxFrameSize()) + if (pHpackBuff->ReadableBytes() > pCodecH2->GetMaxEncodeFrameSize()) { - stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); } else { @@ -1011,9 +1017,10 @@ void Http2Frame::EncodePriority(const tagPriority& stPriority, CBuffer* pBuff) { uint32 uiPriority = stPriority.E; uiPriority <<= 31; - uiPriority |= (CodecUtil::H2N(stPriority.uiDependency)); + uiPriority |= stPriority.uiDependency; + uiPriority = CodecUtil::H2N(uiPriority); pBuff->Write(&uiPriority, 4); - pBuff->Write(&stPriority.ucWeight, 1); + pBuff->WriteByte(stPriority.ucWeight); } } @@ -1038,9 +1045,9 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, if (strPadding.size() > 0) { stFrameHead.uiLength = 1 + uiDataLen + strPadding.size(); - if (stFrameHead.uiLength > pCodecH2->GetMaxFrameSize()) + if (stFrameHead.uiLength > pCodecH2->GetMaxEncodeFrameSize()) { - stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); uiEncodedDataLen = stFrameHead.uiLength - 1 - strPadding.size(); } else @@ -1061,7 +1068,7 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, pBuff->Write(&unNetLength, 1); pBuff->Write(pData, uiEncodedDataLen); pBuff->Write(strPadding.c_str(), strPadding.size()); - pCodecH2->ShrinkSendWindow(uiStreamId, uiEncodedDataLen); + pCodecH2->UpdateSendWindow(uiStreamId, uiEncodedDataLen); EncodeSetStreamState(stFrameHead); } else @@ -1088,9 +1095,9 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, } else { - if (uiDataLen > pCodecH2->GetMaxFrameSize()) + if (uiDataLen > pCodecH2->GetMaxEncodeFrameSize()) { - stFrameHead.uiLength = pCodecH2->GetMaxFrameSize(); + stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); } else { @@ -1107,7 +1114,7 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, uiEncodedDataLen = stFrameHead.uiLength; EncodeFrameHeader(stFrameHead, pBuff); pBuff->Write(pData, uiEncodedDataLen); - pCodecH2->ShrinkSendWindow(uiStreamId, uiEncodedDataLen); + pCodecH2->UpdateSendWindow(uiStreamId, uiEncodedDataLen); EncodeSetStreamState(stFrameHead); } else diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index e68e59e1..e9b9d0e8 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -37,12 +37,14 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, std::string strPadding; if (HTTP_REQUEST == oHttpMsg.type()) { - std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(':')); - if (strSchema.size() > 0) + stPriority.uiDependency = 0; + stPriority.ucWeight = 15; + std::string strScheme = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(':')); + if (strScheme.size() > 0) { auto pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); pHeader->set_name(":scheme"); - pHeader->set_value(strSchema); + pHeader->set_value(strScheme); } else { @@ -244,29 +246,28 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, if (m_bEndHeaders) { eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); - LOG4_TRACE("eStatus = %d", eStatus); if (CODEC_STATUS_OK == eStatus || CODEC_STATUS_PART_OK == eStatus) { if (pCodecH2->IsClient()) { - if (oHttpMsg.stream_id() & 0x01) + if (m_oHttpMsg.stream_id() & 0x01) { - oHttpMsg.set_type(HTTP_RESPONSE); + m_oHttpMsg.set_type(HTTP_RESPONSE); } else { - oHttpMsg.set_type(HTTP_REQUEST); + m_oHttpMsg.set_type(HTTP_REQUEST); } } else { - if (oHttpMsg.stream_id() & 0x01) + if (m_oHttpMsg.stream_id() & 0x01) { - oHttpMsg.set_type(HTTP_REQUEST); + m_oHttpMsg.set_type(HTTP_REQUEST); } else { - oHttpMsg.set_type(HTTP_RESPONSE); + m_oHttpMsg.set_type(HTTP_RESPONSE); } } if (pCodecH2->IsChunkNotice()) @@ -298,7 +299,6 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, return(CODEC_STATUS_ERR); } eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); - LOG4_TRACE("eStatus = %d", eStatus); } return(eStatus); } @@ -422,15 +422,9 @@ void Http2Stream::WindowUpdate(int32 iIncrement) m_iSendWindowSize += iIncrement; } -void Http2Stream::ShrinkRecvWindow(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) +void Http2Stream::UpdateRecvWindow(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) { - m_uiRecvWindowSize -= uiRecvLength; - if (m_uiRecvWindowSize < DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE / 4) - { - m_pFrame->EncodeWindowUpdate(pCodecH2, uiStreamId, - SETTINGS_MAX_INITIAL_WINDOW_SIZE - m_uiRecvWindowSize, pBuff); - } - m_uiRecvWindowSize = SETTINGS_MAX_INITIAL_WINDOW_SIZE; + m_pFrame->EncodeWindowUpdate(pCodecH2, uiStreamId, uiRecvLength, pBuff); } E_CODEC_STATUS Http2Stream::SendWaittingFrameData(CodecHttp2* pCodecH2, CBuffer* pBuff) diff --git a/src/codec/http2/Http2Stream.hpp b/src/codec/http2/Http2Stream.hpp index 24bda484..6d02fac2 100644 --- a/src/codec/http2/Http2Stream.hpp +++ b/src/codec/http2/Http2Stream.hpp @@ -38,7 +38,7 @@ class CodecHttp2; class Http2Stream: public Codec { public: - Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, uint32 m_uiStreamId); + Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, uint32 uiStreamId); virtual ~Http2Stream(); virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) @@ -85,7 +85,7 @@ class Http2Stream: public Codec void WindowInit(uint32 uiWindowSize); void WindowUpdate(int32 iIncrement); - void ShrinkRecvWindow(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff); + void UpdateRecvWindow(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff); E_CODEC_STATUS SendWaittingFrameData(CodecHttp2* pCodecH2, CBuffer* pBuff); private: diff --git a/src/codec/http2/Huffman.cpp b/src/codec/http2/Huffman.cpp index 401e4e07..1937d5c9 100644 --- a/src/codec/http2/Huffman.cpp +++ b/src/codec/http2/Huffman.cpp @@ -7,7 +7,7 @@ * @note * Modify history: ******************************************************************************/ -#include +#include "Huffman.hpp" namespace neb { diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index f107d1e1..081f2a58 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -183,7 +183,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) { if (oHttpMsg.stream_id() > 0) { - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg, eCodecStatus); } } else @@ -320,6 +320,13 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) m_pLabor->GetActorBuilder()->OnError(pChannel, *it, pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); } + auto& mapUncompletedStep = pChannel->m_pImpl->GetStreamStepSeq(); + for (auto it = mapUncompletedStep.begin(); + it != mapUncompletedStep.end(); ++it) + { + m_pLabor->GetActorBuilder()->OnError(pChannel, it->second, + pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); + } } DiscardSocketChannel(pChannel); return(false); @@ -344,7 +351,7 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) { if (oHttpMsg.stream_id() > 0) { - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); + m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg, eCodecStatus); } } else @@ -454,6 +461,13 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) m_pLabor->GetActorBuilder()->OnError(pChannel, *it, pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); } + auto& mapUncompletedStep = pChannel->m_pImpl->GetStreamStepSeq(); + for (auto it = mapUncompletedStep.begin(); + it != mapUncompletedStep.end(); ++it) + { + m_pLabor->GetActorBuilder()->OnError(pChannel, it->second, + pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); + } } DiscardSocketChannel(pChannel); return(false); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 1ec6394d..d8f583de 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -619,10 +619,11 @@ void Manager::CreateWorkerThread() } pWorker->SetLoaderActorBuilder(m_pLoaderActorBuilder); std::thread t(&Worker::Run, pWorker); - t.detach(); std::ostringstream ossThreadId; ossThreadId << t.get_id(); - m_pSessionManager->AddWorkerThreadId(strtoull(ossThreadId.str().c_str(), NULL, 10)); + t.detach(); + uint64 ullThreadId = strtoull(ossThreadId.str().c_str(), NULL, 10); + m_pSessionManager->AddWorkerThreadId(ullThreadId); m_pSessionManager->AddWorkerInfo(i, getpid(), iControlFds[0], iDataFds[0]); std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); From 12b0fd44a9cc1493cfd498b8ae16cbbdc1556ad9 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 9 May 2021 18:25:28 +0800 Subject: [PATCH 133/176] thread id optimization; chain optimization; http2 bug fixed. --- src/actor/ActorBuilder.hpp | 3 +- src/actor/chain/Chain.cpp | 17 ++++--- src/actor/chain/Chain.hpp | 3 +- .../sys_session/SessionWorkerThreadInfo.cpp | 25 ---------- .../sys_session/SessionWorkerThreadInfo.hpp | 46 ------------------- .../sys_session/manager/SessionManager.cpp | 30 ++++++------ .../sys_session/manager/SessionManager.hpp | 14 ++++-- src/codec/http2/CodecHttp2.cpp | 34 +++++++++----- src/codec/http2/CodecHttp2.hpp | 7 +++ src/codec/http2/Http2Frame.cpp | 5 +- src/codec/http2/Http2Stream.cpp | 13 ++++-- src/labor/Labor.hpp | 13 +++++- src/labor/Loader.cpp | 8 ---- src/labor/Loader.hpp | 4 -- src/labor/Manager.cpp | 5 +- src/labor/Worker.cpp | 11 ++++- 16 files changed, 106 insertions(+), 132 deletions(-) delete mode 100644 src/actor/session/sys_session/SessionWorkerThreadInfo.cpp delete mode 100644 src/actor/session/sys_session/SessionWorkerThreadInfo.hpp diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 89c3e66f..d4bd5f8f 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -149,6 +149,7 @@ class ActorBuilder int32 GetStepNum(); bool ReloadCmdConf(); bool AddNetLogMsg(const MsgBody& oMsgBody); + void AddChainConf(const std::string& strChainKey, std::queue >&& queChainBlocks); protected: void AddAssemblyLine(std::shared_ptr pSession); @@ -159,7 +160,6 @@ class ActorBuilder void ExecAssemblyLine(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); void ExecAssemblyLine(std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg); - void AddChainConf(const std::string& strChainKey, std::queue >&& queChainBlocks); void LoadSysCmd(); void BootLoadCmd(CJsonObject& oCmdConf); void DynamicLoad(CJsonObject& oSoConf); @@ -199,6 +199,7 @@ class ActorBuilder friend class Worker; friend class Actor; friend class Dispatcher; + friend class Chain; }; template diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp index 9ca5b466..3e01e6b5 100644 --- a/src/actor/chain/Chain.cpp +++ b/src/actor/chain/Chain.cpp @@ -75,9 +75,9 @@ E_CMD_STATUS Chain::Next() if (m_queChainBlock.empty()) { GetContext()->Done(); + SetContext(nullptr); return(CMD_STATUS_COMPLETED); } - bool bStepInBlock = false; ///< 当前链块存在Step E_CMD_STATUS eResult = CMD_STATUS_START; std::vector& vecTurnBlocks = m_queChainBlock.front(); for (auto iter = vecTurnBlocks.begin(); iter != vecTurnBlocks.end(); ++iter) @@ -109,17 +109,20 @@ E_CMD_STATUS Chain::Next() || Actor::ACT_HTTP_STEP == pSharedActor->GetActorType() || Actor::ACT_REDIS_STEP == pSharedActor->GetActorType()) { - bStepInBlock = true; std::shared_ptr pSharedStep = std::dynamic_pointer_cast(pSharedActor); pSharedStep->SetChainId(GetSequence()); eResult = pSharedStep->Emit(); - if (CMD_STATUS_FAULT == eResult) + if (CMD_STATUS_RUNNING == eResult) { - return(CMD_STATUS_FAULT); + ++m_uiWaitingStep; } - else if (CMD_STATUS_RUNNING == eResult) + else { - ++m_uiWaitingStep; + GetLabor(this)->GetActorBuilder()->RemoveStep(pSharedStep); + if (CMD_STATUS_FAULT == eResult) + { + return(CMD_STATUS_FAULT); + } } } else @@ -144,7 +147,7 @@ E_CMD_STATUS Chain::Next() } m_queChainBlock.pop(); - if (bStepInBlock) + if (m_uiWaitingStep > 0) { return(CMD_STATUS_RUNNING); } diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp index 57a01c9e..6042b4de 100644 --- a/src/actor/chain/Chain.hpp +++ b/src/actor/chain/Chain.hpp @@ -14,6 +14,7 @@ #include #include "actor/Actor.hpp" #include "actor/DynamicCreator.hpp" +#include "actor/ActorSys.hpp" namespace neb { @@ -21,7 +22,7 @@ namespace neb class ActorBuilder; class CJsonObject; -class Chain final: public Actor, +class Chain final: public Actor, public ActorSys, public neb::DynamicCreator { public: diff --git a/src/actor/session/sys_session/SessionWorkerThreadInfo.cpp b/src/actor/session/sys_session/SessionWorkerThreadInfo.cpp deleted file mode 100644 index 5b55b4ca..00000000 --- a/src/actor/session/sys_session/SessionWorkerThreadInfo.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file SessionWorkerThreadInfo.cpp - * @brief 数据上报 - * @author Bwar - * @date: 2020-10-03 - * @note - * Modify history: - ******************************************************************************/ -#include "SessionWorkerThreadInfo.hpp" - -namespace neb -{ - -SessionWorkerThreadInfo::SessionWorkerThreadInfo(const std::string& strSessionId, const std::vector& vecWorkerThreadId) - : neb::Session(strSessionId), m_vecWorkerThreadId(vecWorkerThreadId) -{ -} - -SessionWorkerThreadInfo::~SessionWorkerThreadInfo() -{ -} - -} - diff --git a/src/actor/session/sys_session/SessionWorkerThreadInfo.hpp b/src/actor/session/sys_session/SessionWorkerThreadInfo.hpp deleted file mode 100644 index 893f9a0f..00000000 --- a/src/actor/session/sys_session/SessionWorkerThreadInfo.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file SessionWorkerThreadInfo.hpp - * @brief 数据上报 - * @author Bwar - * @date: 2019-12-22 - * @note - * Modify history: - ******************************************************************************/ -#ifndef ACTOR_SESSION_SYS_SESSION_SESSIONWORKERTHREADINFO_HPP_ -#define ACTOR_SESSION_SYS_SESSION_SESSIONWORKERTHREADINFO_HPP_ - -#include -#include -#include "actor/session/Session.hpp" -#include "actor/ActorSys.hpp" -#include "pb/report.pb.h" - -namespace neb -{ - -class SessionWorkerThreadInfo: public Session, - DynamicCreator&>, - public ActorSys -{ -public: - SessionWorkerThreadInfo(const std::string& strSessionId, const std::vector& vecWorkerThreadId); - virtual ~SessionWorkerThreadInfo(); - - virtual E_CMD_STATUS Timeout() - { - return(CMD_STATUS_RUNNING); - } - - const std::vector& GetWorkerThreadId() const - { - return(m_vecWorkerThreadId); - } - -private: - std::vector m_vecWorkerThreadId; -}; - -} - -#endif /* ACTOR_SESSION_SYS_SESSION_SESSIONWORKERTHREADINFO_HPP_ */ diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 4e100f8d..0aa2c9fe 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -24,6 +24,9 @@ namespace neb { +std::mutex SessionManager::s_mutexWorker; +std::vector SessionManager::s_vecWorkerThreadId; + SessionManager::SessionManager(bool bDirectToLoader) : Session("neb::SessionManager", gc_dNoTimeout), m_bDirectToLoader(bDirectToLoader) { @@ -128,6 +131,7 @@ void SessionManager::AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, i return; } pWorkerAttr->iWorkerIndex = iWorkerIndex; + pWorkerAttr->iWorkerPid = iPid; pWorkerAttr->iControlFd = iControlFd; pWorkerAttr->iDataFd = iDataFd; pWorkerAttr->dBeatTime = GetNowTime(); @@ -194,7 +198,7 @@ Loader* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWo Loader* pLoader = nullptr; try { - pLoader = new Loader(strWorkPath, iControlFd, iDataFd, iWorkerIndex, m_vecWorkerThreadId); + pLoader = new Loader(strWorkPath, iControlFd, iDataFd, iWorkerIndex); } catch(std::bad_alloc& e) { @@ -264,20 +268,6 @@ void SessionManager::SetLoaderActorBuilder(ActorBuilder* pActorBuilder) } } -const std::vector& SessionManager::GetWorkerThreadId() const -{ - return(m_vecWorkerThreadId); -} - -void SessionManager::AddWorkerThreadId(uint64 ullThreadId) -{ - if (std::find(m_vecWorkerThreadId.begin(), m_vecWorkerThreadId.end(), ullThreadId) - == m_vecWorkerThreadId.end()) - { - m_vecWorkerThreadId.push_back(ullThreadId); - } -} - int SessionManager::GetNextWorkerDataFd() { if (m_bDirectToLoader && m_iLoaderDataFd != -1) @@ -582,4 +572,14 @@ bool SessionManager::NewSocketWhenLoaderCreated() return(true); } +void SessionManager::AddWorkerThreadId(uint64 ullThreadId) +{ + std::lock_guard guard(s_mutexWorker); + if (std::find(s_vecWorkerThreadId.begin(), s_vecWorkerThreadId.end(), ullThreadId) + == s_vecWorkerThreadId.end()) + { + s_vecWorkerThreadId.push_back(ullThreadId); + } +} + } /* namespace neb */ diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index 1ef65d5e..4b50e4a7 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -11,6 +11,8 @@ #ifndef SRC_ACTOR_SESSION_SYS_SESSION_MANAGER_SESSIONMANAGER_HPP_ #define SRC_ACTOR_SESSION_SYS_SESSION_MANAGER_SESSIONMANAGER_HPP_ +#include +#include #include "actor/ActorSys.hpp" #include "labor/NodeInfo.hpp" #include "actor/session/Session.hpp" @@ -41,8 +43,6 @@ class SessionManager : public Session, const WorkerInfo* GetWorkerInfo(int32 iWorkerIndex) const; bool SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad); void SetLoaderActorBuilder(ActorBuilder* pActorBuilder); - const std::vector& GetWorkerThreadId() const; - void AddWorkerThreadId(uint64 ullThreadId); int GetNextWorkerDataFd(); std::pair GetMinLoadWorkerDataFd(); bool CheckWorker(); @@ -53,6 +53,12 @@ class SessionManager : public Session, bool NewSocketWhenWorkerCreated(int iWorkerDataFd); bool NewSocketWhenLoaderCreated(); + static const std::vector& GetWorkerThreadId() + { + return(s_vecWorkerThreadId); + } + static void AddWorkerThreadId(uint64 ullThreadId); + private: bool m_bDirectToLoader = false; int m_iLoaderDataFd = -1; @@ -61,8 +67,10 @@ class SessionManager : public Session, std::unordered_map::iterator m_iterWorkerInfo; std::unordered_map m_mapWorkerStartNum; ///< 进程被启动次数,key为WorkerIdx std::unordered_map m_mapWorkerFdPid; ///< 工作进程通信FD对应的进程号 - std::vector m_vecWorkerThreadId; ///< Worker线程ID(线程模式下) std::unordered_map m_mapOnlineNodes; ///< 订阅的节点在线信息 + + static std::mutex s_mutexWorker; + static std::vector s_vecWorkerThreadId; }; } /* namespace neb */ diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index 5b1afebd..d290fdc2 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -100,7 +100,6 @@ void CodecHttp2::ConnectionSetting(CBuffer* pBuff) m_pFrame->EncodeSetting(this, vecSetting, pBuff); m_pFrame->EncodeWindowUpdate(this, 0, 4128769, pBuff); m_pFrame->EncodePing(this, false, 0, 0, pBuff); - m_uiStreamIdGenerate = 2; } } @@ -312,7 +311,9 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR */ if (m_stDecodeFrameHead.uiStreamIdentifier <= m_uiStreamIdGenerate) { - if (H2_FRAME_WINDOW_UPDATE == m_stDecodeFrameHead.ucType) + if (H2_FRAME_WINDOW_UPDATE == m_stDecodeFrameHead.ucType + || H2_FRAME_RST_STREAM == m_stDecodeFrameHead.ucType + || H2_FRAME_PRIORITY == m_stDecodeFrameHead.ucType) { E_CODEC_STATUS eCodecStatus = m_pFrame->Decode(this, m_stDecodeFrameHead, pBuff, oHttpMsg, pReactBuff); if (CODEC_STATUS_PAUSE == eCodecStatus @@ -772,11 +773,11 @@ E_CODEC_STATUS CodecHttp2::PromiseStream(uint32 uiStreamId, CBuffer* pReactBuff) E_CODEC_STATUS CodecHttp2::SendWaittingFrameData(CBuffer* pBuff) { - if (m_pStreamWeightRoot != nullptr) + if (m_bHasWaittingFrame && m_pStreamWeightRoot != nullptr) { E_CODEC_STATUS eStatus = CODEC_STATUS_OK; uint32 uiStreamId = 0; - std::vector vecCompleteStream; + std::vector vecCompletedStream; auto pCurrent = m_pStreamWeightRoot->pFirstChild; while (pCurrent) { @@ -785,17 +786,21 @@ E_CODEC_STATUS CodecHttp2::SendWaittingFrameData(CBuffer* pBuff) if (iter != m_mapStream.end()) { eStatus = iter->second->SendWaittingFrameData(this, pBuff); - if (eStatus == CODEC_STATUS_OK) + if (iter->second->GetStreamState() == H2_STREAM_CLOSE) { - vecCompleteStream.push_back(uiStreamId); - } - else - { - break; + vecCompletedStream.push_back(uiStreamId); } } + if (pCurrent->pFirstChild != nullptr) + { + pCurrent = pCurrent->pFirstChild; + } + else + { + pCurrent = pCurrent->pRightBrother; + } } - for (auto id : vecCompleteStream) + for (auto id : vecCompletedStream) { CloseStream(id); } @@ -804,6 +809,13 @@ E_CODEC_STATUS CodecHttp2::SendWaittingFrameData(CBuffer* pBuff) return(CODEC_STATUS_OK); } +E_CODEC_STATUS CodecHttp2::SendWaittingFrameData( + TreeNode* pStreamWeight, + std::vector& vecCompletedStream, CBuffer* pBuff) +{ + return(CODEC_STATUS_OK); +} + void CodecHttp2::TransferHoldingMsg(HttpMsg* pHoldingHttpMsg) { m_pHoldingHttpMsg = pHoldingHttpMsg; diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index 258e4032..88e15ed8 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -93,7 +93,13 @@ class CodecHttp2: public Codec } E_CODEC_STATUS SendWaittingFrameData(CBuffer* pBuff); + E_CODEC_STATUS SendWaittingFrameData(TreeNode* pStreamWeightNode, + std::vector& vecCompletedStream, CBuffer* pBuff); void TransferHoldingMsg(HttpMsg* pHoldingHttpMsg); + void SetWaittingFrame(bool bHasWaittingFrame) + { + m_bHasWaittingFrame = bHasWaittingFrame; + } protected: uint32 StreamIdGenerate(); @@ -128,6 +134,7 @@ class CodecHttp2: public Codec bool m_bChannelIsClient = false; // 当前编解码器所在channel是作为http客户端还是作为http服务端 bool m_bWantMagic = true; bool m_bChunkNotice = false; // 是否启用分块传输通知(当包体比较大时,部分传输完毕也会通知业务层而无须等待整个http包传输完毕。) + bool m_bHasWaittingFrame = false; uint32 m_uiStreamIdGenerate = 0; uint32 m_uiGoawayLastStreamId = 0; uint32 m_uiSettingsEnablePush = 1; diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index 7cbc9949..f086caa7 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -333,6 +333,7 @@ E_CODEC_STATUS Http2Frame::DecodeRstStream(CodecHttp2* pCodecH2, if (m_pStream != nullptr) { m_pStream->SetState(H2_STREAM_CLOSE); + pCodecH2->RstStream(stFrameHead.uiStreamIdentifier); } return(CODEC_STATUS_PART_ERR); } @@ -540,7 +541,7 @@ E_CODEC_STATUS Http2Frame::DecodeWindowUpdate(CodecHttp2* pCodecH2, else { pCodecH2->WindowUpdate(stFrameHead.uiStreamIdentifier, uiIncrement); - if (stFrameHead.uiStreamIdentifier > 0) + if (m_pStream != nullptr && stFrameHead.uiStreamIdentifier == m_pStream->GetStreamId()) { SendWaittingFrameData(pCodecH2, pReactBuff); } @@ -1084,6 +1085,7 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, return(CODEC_STATUS_PART_ERR); } m_listWaittingFrameData.push_back(pWaittingBuff); + pCodecH2->SetWaittingFrame(true); EncodeFrameHeader(stFrameHead, pWaittingBuff); uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pWaittingBuff->Write(&unNetLength, 1); @@ -1130,6 +1132,7 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, return(CODEC_STATUS_PART_ERR); } m_listWaittingFrameData.push_back(pWaittingBuff); + pCodecH2->SetWaittingFrame(true); uiEncodedDataLen = stFrameHead.uiLength; EncodeFrameHeader(stFrameHead, pWaittingBuff); pWaittingBuff->Write(pData, uiEncodedDataLen); diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index e9b9d0e8..35597c0d 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -103,6 +103,8 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, { LOG4_TRACE("m_eStreamState = %d, stFrameHead.ucType = %u", m_eStreamState, stFrameHead.ucType); m_oHttpMsg.set_stream_id(m_uiStreamId); + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); switch (m_eStreamState) { case H2_STREAM_IDLE: @@ -241,11 +243,9 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, default: break; } - E_CODEC_STATUS eStatus = CODEC_STATUS_OK; LOG4_TRACE("m_eStreamState = %d, stFrameHead.ucType = %u, m_bEndHeaders = %d", m_eStreamState, stFrameHead.ucType, m_bEndHeaders); if (m_bEndHeaders) { - eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); if (CODEC_STATUS_OK == eStatus || CODEC_STATUS_PART_OK == eStatus) { if (pCodecH2->IsClient()) @@ -298,7 +298,6 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u, m_bEndHeaders = %d", m_eStreamState, stFrameHead.ucType, m_bEndHeaders); return(CODEC_STATUS_ERR); } - eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); } return(eStatus); } @@ -424,7 +423,13 @@ void Http2Stream::WindowUpdate(int32 iIncrement) void Http2Stream::UpdateRecvWindow(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) { - m_pFrame->EncodeWindowUpdate(pCodecH2, uiStreamId, uiRecvLength, pBuff); + if (m_eStreamState == H2_STREAM_OPEN + || m_eStreamState == H2_STREAM_IDLE + || m_eStreamState == H2_STREAM_RESERVED_LOCAL + || m_eStreamState == H2_STREAM_RESERVED_REMOTE) + { + m_pFrame->EncodeWindowUpdate(pCodecH2, uiStreamId, uiRecvLength, pBuff); + } } E_CODEC_STATUS Http2Stream::SendWaittingFrameData(CodecHttp2* pCodecH2, CBuffer* pBuff) diff --git a/src/labor/Labor.hpp b/src/labor/Labor.hpp index 0b4677e0..0fd4d264 100644 --- a/src/labor/Labor.hpp +++ b/src/labor/Labor.hpp @@ -12,6 +12,8 @@ #include #include +#include +#include #include "ev.h" #include "pb/msg.pb.h" #include "Definition.hpp" @@ -38,7 +40,7 @@ class Labor }; public: Labor(LABOR_TYPE eLaborType) - : m_eLaborType(eLaborType) + : m_eLaborType(eLaborType), m_iPid(0) { } Labor(const Labor&) = delete; @@ -71,9 +73,18 @@ class Labor { return(false); } + pid_t gettid() + { + if (m_iPid == 0) + { + m_iPid = static_cast(::syscall(SYS_gettid)); + } + return(m_iPid); + } private: LABOR_TYPE m_eLaborType; + int32 m_iPid; }; } /* namespace neb */ diff --git a/src/labor/Loader.cpp b/src/labor/Loader.cpp index b581f57a..305c728f 100644 --- a/src/labor/Loader.cpp +++ b/src/labor/Loader.cpp @@ -20,12 +20,6 @@ Loader::Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int { } -Loader::Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex, const std::vector& vecWorkerThreadId) - : Worker(strWorkPath, iControlFd, iDataFd, iWorkerIndex, Labor::LABOR_LOADER), - m_vecWorkerThreadId(vecWorkerThreadId) -{ -} - Loader::~Loader() { } @@ -35,8 +29,6 @@ bool Loader::InitActorBuilder() LOG4_TRACE(""); if (NewActorBuilder()) { - std::string strSessionId = "neb::SessionWorkerThreadInfo"; - GetActorBuilder()->MakeSharedActor(nullptr, "neb::SessionWorkerThreadInfo", strSessionId, m_vecWorkerThreadId); return(GetActorBuilder()->Init( (const_cast(GetNodeConf()))["load_config"]["loader"]["boot_load"], (const_cast(GetNodeConf()))["load_config"]["loader"]["dynamic_loading"])); diff --git a/src/labor/Loader.hpp b/src/labor/Loader.hpp index 5fa7c51d..117a47ae 100644 --- a/src/labor/Loader.hpp +++ b/src/labor/Loader.hpp @@ -20,14 +20,10 @@ class Loader: public Worker { public: Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex); - Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex, const std::vector& vecWorkerThreadId); virtual ~Loader(); protected: virtual bool InitActorBuilder(); - -private: - std::vector m_vecWorkerThreadId; ///< worker线程ID(线程模式下) }; } /* namespace neb */ diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index d8f583de..9ea4382c 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -59,6 +59,7 @@ Manager::Manager(const std::string& strConfFile) if (m_stNodeInfo.bThreadMode) { CreateWorkerThread(); // Loader可能需要用到Worker线程ID,故先创建Worker + usleep(1000); CreateLoaderThread(); } else @@ -619,11 +620,7 @@ void Manager::CreateWorkerThread() } pWorker->SetLoaderActorBuilder(m_pLoaderActorBuilder); std::thread t(&Worker::Run, pWorker); - std::ostringstream ossThreadId; - ossThreadId << t.get_id(); t.detach(); - uint64 ullThreadId = strtoull(ossThreadId.str().c_str(), NULL, 10); - m_pSessionManager->AddWorkerThreadId(ullThreadId); m_pSessionManager->AddWorkerInfo(i, getpid(), iControlFds[0], iDataFds[0]); std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 7993c458..e0bafee3 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -12,6 +12,7 @@ #ifdef __cplusplus extern "C" { #endif +#include #include "util/process_helper.h" #include "util/proctitle_helper.h" #ifdef __cplusplus @@ -20,6 +21,7 @@ extern "C" { #include "Worker.hpp" #include "ios/Dispatcher.hpp" #include "actor/ActorBuilder.hpp" +#include "actor/session/sys_session/manager/SessionManager.hpp" namespace neb { @@ -43,7 +45,14 @@ Worker::~Worker() void Worker::Run() { - LOG4_DEBUG("%s:%d", __FILE__, __LINE__); + LOG4_TRACE("%s:%d", __FILE__, __LINE__); + if (m_stNodeInfo.bThreadMode) + { + char szThreadName[64] = {0}; + snprintf(szThreadName, sizeof(szThreadName), "%s_W%d", m_oNodeConf("server_name").c_str(), m_stWorkerInfo.iWorkerIndex); + pthread_setname_np(pthread_self(), szThreadName); + SessionManager::AddWorkerThreadId(gettid()); + } if (!CreateEvents()) { From 6cfcd5e2362c9e324719b22c8a96016ed990ab80 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 15 May 2021 18:27:32 +0800 Subject: [PATCH 134/176] Manager to Manager support; http2 dynamic table bug fixed; update CJsonObject. --- src/actor/ActorBuilder.cpp | 15 ++++++ .../cmd/sys_cmd/manager/CmdOnTellWorker.cpp | 3 +- src/channel/SocketChannelImpl.cpp | 6 +-- src/channel/SocketChannelImpl.hpp | 8 +-- src/codec/http2/CodecHttp2.cpp | 18 +++---- src/codec/http2/CodecHttp2.hpp | 2 +- src/codec/http2/Http2Frame.cpp | 13 +++-- src/ios/Dispatcher.cpp | 30 ++++++++--- src/ios/Dispatcher.hpp | 15 +++--- src/util/json/CJsonObject.cpp | 52 +------------------ src/util/json/CJsonObject.hpp | 4 +- src/util/json/cJSON.c | 44 ++++++++++------ 12 files changed, 104 insertions(+), 106 deletions(-) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 648d86bf..ede3f01a 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -993,6 +993,11 @@ bool ActorBuilder::TransformToSharedCmd(Actor* pCreator, std::shared_ptr } return(true); } + else + { + LOG4_ERROR("%s(%d) Init failed.", pSharedCmd->GetActorName().c_str(), pSharedCmd->GetCmd()); + m_mapCmd.erase(ret.first); + } } else { @@ -1021,6 +1026,11 @@ bool ActorBuilder::TransformToSharedModule(Actor* pCreator, std::shared_ptrGetActorName().c_str(), pSharedModule->GetModulePath().c_str()); + m_mapModule.erase(ret.first); + } } else { @@ -1039,6 +1049,11 @@ bool ActorBuilder::TransformToSharedOperator(Actor* pCreator, std::shared_ptrGetActorName().c_str()); + m_mapOperator.erase(ret.first); + } } else { diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp index 5dd3582d..abe85167 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp @@ -34,8 +34,9 @@ bool CmdOnTellWorker::AnyMessage( TargetWorker oOutTargetWorker; if (oInTargetWorker.ParseFromString(oInMsgBody.data())) { - LOG4_DEBUG("AddNodeIdentify(%s, %s)!", oInTargetWorker.node_type().c_str(), + LOG4_TRACE("AddNodeIdentify(%s, %s)!", oInTargetWorker.node_type().c_str(), oInTargetWorker.worker_identify().c_str()); + GetLabor(this)->GetDispatcher()->AddNamedSocketChannel(oInTargetWorker.worker_identify(), pChannel); GetLabor(this)->GetDispatcher()->AddNodeIdentify(oInTargetWorker.node_type(), oInTargetWorker.worker_identify()); oOutTargetWorker.set_worker_identify(GetNodeIdentify()); oOutTargetWorker.set_node_type(GetLabor(this)->GetNodeInfo().strNodeType); diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 309ca0ec..9361c4dc 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -23,7 +23,7 @@ namespace neb SocketChannelImpl::SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) : m_ucChannelStatus(CHANNEL_STATUS_INIT),m_eLastCodecStatus(CODEC_STATUS_OK), m_bIsClientConnection(false), - m_unRemoteWorkerIdx(0), m_iFd(iFd), m_uiSeq(ulSeq), m_uiForeignSeq(0), m_bPipeline(true), + m_iRemoteWorkerIdx(-1), m_iFd(iFd), m_uiSeq(ulSeq), m_uiForeignSeq(0), m_bPipeline(true), m_uiUnitTimeMsgNum(0), m_uiMsgNum(0), m_dActiveTime(0.0), m_dKeepAlive(dKeepAlive), m_pIoWatcher(NULL), m_pTimerWatcher(NULL), @@ -1180,9 +1180,9 @@ void SocketChannelImpl::SetSecretKey(const std::string& strKey) m_pCodec->SetKey(m_strKey); } -void SocketChannelImpl::SetRemoteWorkerIndex(uint16 unRemoteWorkerIndex) +void SocketChannelImpl::SetRemoteWorkerIndex(int16 iRemoteWorkerIndex) { - m_unRemoteWorkerIdx = unRemoteWorkerIndex; + m_iRemoteWorkerIdx = iRemoteWorkerIndex; } Codec* SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive, bool bIsUpgrade) diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 11730285..6329a96e 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -112,9 +112,9 @@ class SocketChannelImpl: public Channel return(m_strRemoteAddr); } - uint16 GetRemoteWorkerIndex() const + int16 GetRemoteWorkerIndex() const { - return(m_unRemoteWorkerIdx); + return(m_iRemoteWorkerIdx); } const std::string& GetClientData() const @@ -209,7 +209,7 @@ class SocketChannelImpl: public Channel void SetSecretKey(const std::string& strKey); - void SetRemoteWorkerIndex(uint16 unRemoteWorkerIndex); + void SetRemoteWorkerIndex(int16 iRemoteWorkerIndex); Codec* SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive, bool bIsUpgrade = false); bool AutoSwitchCodec(); @@ -229,7 +229,7 @@ class SocketChannelImpl: public Channel E_CODEC_STATUS m_eLastCodecStatus; ///< 连接关闭前的最后一个编解码状态(当且仅当连接的应用层读缓冲区有数据未处理完而对端关闭连接时使用) char m_szErrBuff[256]; bool m_bIsClientConnection; - uint16 m_unRemoteWorkerIdx; ///< 对端Worker进程ID,若不涉及则无需关心 + int16 m_iRemoteWorkerIdx; ///< 对端Worker进程ID,若不涉及则无需关心 int32 m_iFd; ///< 文件描述符 uint32 m_uiSeq; ///< 文件描述符创建时对应的序列号 uint32 m_uiForeignSeq; ///< 外来的seq,每个连接的包都是有序的,用作接入Server数据包检查,防止篡包 diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index d290fdc2..c45d6790 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -971,8 +971,8 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF } else { - uint32 uiDynamicTableIndex = DecodingDynamicTableIndex2VectorIndex(uiTableIndex - Http2Header::sc_uiMaxStaticTableLength); - if (uiDynamicTableIndex < m_vecDecodingDynamicTable.size()) + uint32 uiVectorIndex = DecodingDynamicTableIndex2VectorIndex(uiTableIndex - Http2Header::sc_uiMaxStaticTableLength); + if (uiVectorIndex < m_vecDecodingDynamicTable.size()) { if (!Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) { @@ -980,10 +980,10 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF LOG4_ERROR("DecodeStringLiteral failed!"); return(CODEC_STATUS_ERR); } - strHeaderName = m_vecDecodingDynamicTable[uiDynamicTableIndex].Name(); + strHeaderName = m_vecDecodingDynamicTable[uiVectorIndex].Name(); if (H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING & ucFirstByte) { - UpdateDecodingDynamicTable(uiDynamicTableIndex, strHeaderName, strHeaderValue); + UpdateDecodingDynamicTable(uiVectorIndex, strHeaderName, strHeaderValue); } return(CODEC_STATUS_PART_OK); } @@ -1319,12 +1319,12 @@ uint32 CodecHttp2::EncodingDynamicTableIndex2VectorIndex(uint32 uiIndex) return(m_iNextEncodingDynamicTableHeaderIndex + 1 + uiIndex); } -void CodecHttp2::UpdateDecodingDynamicTable(int32 iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue) +void CodecHttp2::UpdateDecodingDynamicTable(int32 iVectorIndex, const std::string& strHeaderName, const std::string& strHeaderValue) { Http2Header oHeader(strHeaderName, strHeaderValue); uint32 uiEntrySize = oHeader.HpackSize(); - if (iDynamicTableIndex < 0 || iDynamicTableIndex >= (int32)m_vecDecodingDynamicTable.size()) // new header + if (iVectorIndex < 0 || iVectorIndex >= (int32)m_vecDecodingDynamicTable.size()) // new header { // if the new header is too big, drop all entries. if (uiEntrySize > m_uiSettingsHeaderTableSize) @@ -1356,7 +1356,7 @@ void CodecHttp2::UpdateDecodingDynamicTable(int32 iDynamicTableIndex, const std: } else { - int32 iAddSize = uiEntrySize - m_vecDecodingDynamicTable[DecodingDynamicTableIndex2VectorIndex(iDynamicTableIndex)].HpackSize(); + int32 iAddSize = uiEntrySize - m_vecDecodingDynamicTable[iVectorIndex].HpackSize(); // if the replacement header is too big, drop all entries. if (iAddSize > (int32)m_uiSettingsHeaderTableSize) { @@ -1368,8 +1368,8 @@ void CodecHttp2::UpdateDecodingDynamicTable(int32 iDynamicTableIndex, const std: // evict headers to the required length. int32 iNeedRecoverSize = (m_uiDecodingDynamicTableSize + iAddSize) - m_uiSettingsHeaderTableSize; uint32 uiEvictedNum = UpdateDecodingDynamicTable(iNeedRecoverSize); - iDynamicTableIndex += DecodingDynamicTableIndex2VectorIndex(iDynamicTableIndex) + uiEvictedNum; - m_vecDecodingDynamicTable[iDynamicTableIndex] = std::move(oHeader); + iVectorIndex += uiEvictedNum; + m_vecDecodingDynamicTable[iVectorIndex] = std::move(oHeader); m_uiDecodingDynamicTableSize += iAddSize; } } diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index 88e15ed8..ed919d7c 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -125,7 +125,7 @@ class CodecHttp2: public Codec void UpdateEncodingDynamicTable(const std::string& strHeaderName, const std::string& strHeaderValue); uint32 UpdateEncodingDynamicTable(int32 iRecoverSize); uint32 EncodingDynamicTableIndex2VectorIndex(uint32 uiIndex); - void UpdateDecodingDynamicTable(int32 iDynamicTableIndex, const std::string& strHeaderName, const std::string& strHeaderValue); + void UpdateDecodingDynamicTable(int32 iVectorIndex, const std::string& strHeaderName, const std::string& strHeaderValue); uint32 UpdateDecodingDynamicTable(int32 iRecoverSize); uint32 DecodingDynamicTableIndex2VectorIndex(uint32 uiIndex); void CloseStream(uint32 uiStreamId); diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index f086caa7..09e05dcf 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -113,12 +113,17 @@ E_CODEC_STATUS Http2Frame::Encode(CodecHttp2* pCodecH2, if (oHttpMsg.body().size() == 0) { bEndStream = true; + eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, stPriority, strPadding, bEndStream, pBuff); + return(eCodecStatus); } - eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, stPriority, strPadding, bEndStream, pBuff); - if (CODEC_STATUS_PART_ERR == eCodecStatus - || CODEC_STATUS_ERR == eCodecStatus) + else { - return(eCodecStatus); + eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, stPriority, strPadding, bEndStream, pBuff); + if (CODEC_STATUS_PART_ERR == eCodecStatus + || CODEC_STATUS_ERR == eCodecStatus) + { + return(eCodecStatus); + } } } if (oHttpMsg.body().size() > 0) diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 081f2a58..93ca4ca6 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -607,20 +607,34 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) { if (CODEC_NEBULA == pChannel->m_pImpl->GetCodecType()) // 系统内部Server间通信 { - if (CHANNEL_STATUS_TRY_CONNECT == pChannel->m_pImpl->GetChannelStatus()) // connect之后的第一个写事件 + if (pChannel->m_pImpl->GetRemoteWorkerIndex() < 0) // connect to Manager { - std::shared_ptr pStepConnectWorker = m_pLabor->GetActorBuilder()->MakeSharedStep( - nullptr, "neb::StepConnectWorker", pChannel, pChannel->m_pImpl->GetRemoteWorkerIndex()); - if (nullptr == pStepConnectWorker) + std::shared_ptr pStepTellWorker + = m_pLabor->GetActorBuilder()->MakeSharedStep(nullptr, "neb::StepTellWorker", pChannel); + if (nullptr == pStepTellWorker) { - LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); return(false); } - if (CMD_STATUS_RUNNING != pStepConnectWorker->Emit(ERR_OK)) + pStepTellWorker->Emit(ERR_OK); + pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + } + else + { + if (CHANNEL_STATUS_TRY_CONNECT == pChannel->m_pImpl->GetChannelStatus()) // connect之后的第一个写事件 { - m_pLabor->GetActorBuilder()->RemoveStep(pStepConnectWorker); + std::shared_ptr pStepConnectWorker = m_pLabor->GetActorBuilder()->MakeSharedStep( + nullptr, "neb::StepConnectWorker", pChannel, pChannel->m_pImpl->GetRemoteWorkerIndex()); + if (nullptr == pStepConnectWorker) + { + LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); + return(false); + } + if (CMD_STATUS_RUNNING != pStepConnectWorker->Emit(ERR_OK)) + { + m_pLabor->GetActorBuilder()->RemoveStep(pStepConnectWorker); + } + return(true); } - return(true); } } else diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 5a87ba07..70ab7554 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -271,11 +271,14 @@ bool Dispatcher::SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, return(false); } std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); - iWorkerIndex = atoi(strWorkerIndex.c_str()); - if (iWorkerIndex > 200) + if (strWorkerIndex.size() > 0) { - strError = "worker index must smaller than 200"; - return(false); + iWorkerIndex = atoi(strWorkerIndex.c_str()); + if (iWorkerIndex > 200) + { + strError = "worker index must smaller than 200"; + return(false); + } } } else @@ -297,7 +300,7 @@ bool Dispatcher::SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, std::string strError; std::string strHost; int iPort = 0; - int iWorkerIndex = 0; + int iWorkerIndex = -1; if (!split(strIdentify, strHost, iPort, iWorkerIndex, strError)) { LOG4_ERROR("%s", strError.c_str()); @@ -312,7 +315,7 @@ bool Dispatcher::SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, std::string strError; std::string strHost; int iPort = 0; - int iWorkerIndex = 0; + int iWorkerIndex = -1; if (!split(strIdentify, strHost, iPort, iWorkerIndex, strError)) { LOG4_ERROR("%s", strError.c_str()); diff --git a/src/util/json/CJsonObject.cpp b/src/util/json/CJsonObject.cpp index 32217508..51ac352a 100644 --- a/src/util/json/CJsonObject.cpp +++ b/src/util/json/CJsonObject.cpp @@ -148,8 +148,6 @@ bool CJsonObject::AddEmptySubObject(const std::string& strKey) } cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); m_pKeyTravers = pFocusData; - m_strLastObjectKey = ""; - m_object_iter = m_mapJsonObjectRef.end(); return(true); } @@ -194,8 +192,6 @@ bool CJsonObject::AddEmptySubArray(const std::string& strKey) } cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); m_pKeyTravers = pFocusData; - m_uiLastArrayIndex = 0; - m_array_iter = m_mapJsonArrayRef.end(); return(true); } @@ -253,10 +249,6 @@ void CJsonObject::ResetTraversing() CJsonObject& CJsonObject::operator[](const std::string& strKey) { - if (strKey == m_strLastObjectKey && m_object_iter != m_mapJsonObjectRef.end()) - { - return(*(m_object_iter->second)); - } #if __cplusplus < 201101L std::map::iterator iter = m_mapJsonObjectRef.find(strKey); #else @@ -294,18 +286,12 @@ CJsonObject& CJsonObject::operator[](const std::string& strKey) } else { - m_object_iter = iter; - m_strLastObjectKey = strKey; return(*(iter->second)); } } CJsonObject& CJsonObject::operator[](unsigned int uiWhich) { - if (uiWhich == m_uiLastArrayIndex && m_array_iter != m_mapJsonArrayRef.end()) - { - return(*(m_array_iter->second)); - } #if __cplusplus < 201101L std::map::iterator iter = m_mapJsonArrayRef.find(uiWhich); #else @@ -343,8 +329,6 @@ CJsonObject& CJsonObject::operator[](unsigned int uiWhich) } else { - m_uiLastArrayIndex = uiWhich; - m_array_iter = iter; return(*(iter->second)); } } @@ -1048,8 +1032,6 @@ bool CJsonObject::Add(const std::string& strKey, const CJsonObject& oJsonObject) m_mapJsonObjectRef.erase(iter); } m_pKeyTravers = pFocusData; - m_strLastObjectKey = ""; - m_object_iter = m_mapJsonObjectRef.end(); return(true); } @@ -1110,8 +1092,6 @@ bool CJsonObject::AddWithMove(const std::string& strKey, CJsonObject& oJsonObjec m_mapJsonObjectRef.erase(iter); } m_pKeyTravers = pFocusData; - m_strLastObjectKey = ""; - m_object_iter = m_mapJsonObjectRef.end(); return(true); } #else @@ -1171,8 +1151,6 @@ bool CJsonObject::Add(const std::string& strKey, CJsonObject&& oJsonObject) m_mapJsonObjectRef.erase(iter); } m_pKeyTravers = pFocusData; - m_strLastObjectKey = ""; - m_object_iter = m_mapJsonObjectRef.end(); return(true); } #endif @@ -1657,8 +1635,6 @@ bool CJsonObject::Delete(const std::string& strKey) m_mapJsonObjectRef.erase(iter); } m_pKeyTravers = pFocusData; - m_strLastObjectKey = ""; - m_object_iter = m_mapJsonObjectRef.end(); return(true); } @@ -1708,8 +1684,6 @@ bool CJsonObject::Replace(const std::string& strKey, const CJsonObject& oJsonObj } m_mapJsonObjectRef.erase(iter); } - m_strLastObjectKey = ""; - m_object_iter = m_mapJsonObjectRef.end(); return(true); } @@ -1757,8 +1731,6 @@ bool CJsonObject::ReplaceWithMove(const std::string& strKey, CJsonObject& oJsonO } m_mapJsonObjectRef.erase(iter); } - m_strLastObjectKey = ""; - m_object_iter = m_mapJsonObjectRef.end(); return(true); } #else @@ -1805,8 +1777,6 @@ bool CJsonObject::Replace(const std::string& strKey, CJsonObject&& oJsonObject) } m_mapJsonObjectRef.erase(iter); } - m_strLastObjectKey = ""; - m_object_iter = m_mapJsonObjectRef.end(); return(true); } #endif @@ -2243,7 +2213,7 @@ bool CJsonObject::ReplaceWithNull(const std::string& strKey) return(true); } -int CJsonObject::GetArraySize() +int CJsonObject::GetArraySize() const { if (m_pJsonData != NULL) { @@ -2638,8 +2608,6 @@ bool CJsonObject::Add(const CJsonObject& oJsonObject) iter++; } } - m_uiLastArrayIndex = 0; - m_array_iter = m_mapJsonArrayRef.end(); return(true); } @@ -2702,8 +2670,6 @@ bool CJsonObject::AddWithMove(CJsonObject& oJsonObject) iter++; } } - m_uiLastArrayIndex = 0; - m_array_iter = m_mapJsonArrayRef.end(); return(true); } #else @@ -2765,8 +2731,6 @@ bool CJsonObject::Add(CJsonObject&& oJsonObject) iter++; } } - m_uiLastArrayIndex = 0; - m_array_iter = m_mapJsonArrayRef.end(); return(true); } #endif @@ -3203,8 +3167,6 @@ bool CJsonObject::AddAsFirst(const CJsonObject& oJsonObject) } m_mapJsonArrayRef.erase(iter++); } - m_uiLastArrayIndex = 0; - m_array_iter = m_mapJsonArrayRef.end(); return(true); } @@ -3259,8 +3221,6 @@ bool CJsonObject::AddAsFirstWithMove(CJsonObject& oJsonObject) } m_mapJsonArrayRef.erase(iter++); } - m_uiLastArrayIndex = 0; - m_array_iter = m_mapJsonArrayRef.end(); return(true); } #else @@ -3314,8 +3274,6 @@ bool CJsonObject::AddAsFirst(CJsonObject&& oJsonObject) } m_mapJsonArrayRef.erase(iter++); } - m_uiLastArrayIndex = 0; - m_array_iter = m_mapJsonArrayRef.end(); return(true); } #endif @@ -3741,8 +3699,6 @@ bool CJsonObject::Delete(int iWhich) iter++; } } - m_uiLastArrayIndex = 0; - m_array_iter = m_mapJsonArrayRef.end(); return(true); } @@ -3792,8 +3748,6 @@ bool CJsonObject::Replace(int iWhich, const CJsonObject& oJsonObject) } m_mapJsonArrayRef.erase(iter); } - m_uiLastArrayIndex = 0; - m_array_iter = m_mapJsonArrayRef.end(); return(true); } @@ -3841,8 +3795,6 @@ bool CJsonObject::ReplaceWithMove(int iWhich, CJsonObject& oJsonObject) } m_mapJsonArrayRef.erase(iter); } - m_uiLastArrayIndex = 0; - m_array_iter = m_mapJsonArrayRef.end(); return(true); } #else @@ -3889,8 +3841,6 @@ bool CJsonObject::Replace(int iWhich, CJsonObject&& oJsonObject) } m_mapJsonArrayRef.erase(iter); } - m_uiLastArrayIndex = 0; - m_array_iter = m_mapJsonArrayRef.end(); return(true); } #endif diff --git a/src/util/json/CJsonObject.hpp b/src/util/json/CJsonObject.hpp index 9c97aafb..4f1575eb 100644 --- a/src/util/json/CJsonObject.hpp +++ b/src/util/json/CJsonObject.hpp @@ -138,7 +138,7 @@ class CJsonObject #endif public: // method of json array - int GetArraySize(); + int GetArraySize() const; CJsonObject& operator[](unsigned int uiWhich); std::string operator()(unsigned int uiWhich) const; bool Get(int iWhich, CJsonObject& oJsonObject) const; @@ -207,8 +207,6 @@ class CJsonObject cJSON* m_pKeyTravers; const char* mc_pError; std::string m_strErrMsg; - uint32 m_uiLastArrayIndex; // corresponds with m_array_iter - std::string m_strLastObjectKey; // corresponds with m_object_iter #if __cplusplus < 201101L std::map m_mapJsonArrayRef; std::map::iterator m_array_iter; diff --git a/src/util/json/cJSON.c b/src/util/json/cJSON.c index 9e1b22cf..4b35888e 100644 --- a/src/util/json/cJSON.c +++ b/src/util/json/cJSON.c @@ -118,8 +118,13 @@ void cJSON_Delete(cJSON *c) /* Parse the input text to generate a number, and populate the result into item. */ static const char *parse_number(cJSON *item, const char *num) { - long double n = 0, scale = 0; - int subscale = 0, signsubscale = 1; + int64 n = 0; + long double d = 0.0; + double base = 0.0; + double point = 0.1; + int scale = 0.0; + int subscale = 0; + int signsubscale = 1; item->sign = 1; /* Could use sscanf for this? */ @@ -127,15 +132,22 @@ static const char *parse_number(cJSON *item, const char *num) item->sign = -1, num++; /* Has sign? */ if (*num == '0') num++; /* is zero */ - if (*num >= '1' && *num <= '9') - do - n = (n * 10.0) + (*num++ - '0'); - while (*num >= '0' && *num <= '9'); /* Number? */ + while (*num >= '0' && *num <= '9') + { + n = (n * 10) + (*num++ - '0'); + } + d = n; if (*num == '.' && num[1] >= '0' && num[1] <= '9') { num++; + base = d; do - n = (n * 10.0) + (*num++ - '0'), scale--; + { + d += point * (*num - '0'); + point *= 0.1; + base = (base * 10.0) + (*num - '0'), scale--; + ++num; + } while (*num >= '0' && *num <= '9'); } /* Fractional part? */ if (*num == 'e' || *num == 'E') /* Exponent? */ @@ -151,15 +163,18 @@ static const char *parse_number(cJSON *item, const char *num) if (scale == 0 && subscale == 0) { - item->valuedouble = (double)(item->sign * n); - item->valueint = item->sign * (int64)n; + item->valuedouble = item->sign * d; + item->valueint = item->sign * n; item->type = cJSON_Int; } else { - n = item->sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ - item->valuedouble = (double)n; - item->valueint = (int64)n; + if (subscale != 0) + { + d = item->sign * base * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + } + item->valuedouble = d; + item->valueint = n; item->type = cJSON_Double; } return num; @@ -173,10 +188,7 @@ static char *print_double(cJSON *item) str = (char*) cJSON_malloc(64); /* This is a nice tradeoff. */ if (str) { - if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) - sprintf(str, "%lf", d); - else - sprintf(str, "%f", d); + sprintf(str, "%.15f", d); } return str; } From bc84e3bfa7a308a62d5619fb3b1e3ea3dc0ef84b Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 30 May 2021 18:35:51 +0800 Subject: [PATCH 135/176] add server data report and metrics --- proto/report.proto | 7 + src/Definition.hpp | 3 +- src/actor/ActorBuilder.cpp | 4 +- src/actor/cmd/sys_cmd/ModuleMetrics.cpp | 88 ++++++++ src/actor/cmd/sys_cmd/ModuleMetrics.hpp | 36 ++++ .../session/sys_session/SessionDataReport.cpp | 10 +- .../session/sys_session/SessionDataReport.hpp | 2 +- .../sys_session/manager/SessionManager.cpp | 92 ++++++--- src/actor/step/GrpcStep.hpp | 2 +- src/actor/step/HttpStep.hpp | 2 +- src/actor/step/PbStep.hpp | 2 +- src/actor/step/RawStep.hpp | 2 +- src/actor/step/RedisStep.hpp | 2 +- src/actor/step/Step.hpp | 2 +- src/channel/SocketChannelImpl.cpp | 85 ++++++-- src/codec/http2/CodecHttp2.cpp | 1 + src/ios/Dispatcher.cpp | 30 ++- src/ios/Dispatcher.hpp | 3 + src/labor/Labor.hpp | 4 + src/labor/NodeInfo.hpp | 14 +- src/labor/Worker.cpp | 48 +++-- src/labor/Worker.hpp | 36 ++++ src/pb/report.pb.cc | 189 +++++++++++++++++- src/pb/report.pb.h | 139 +++++++++++++ 24 files changed, 718 insertions(+), 85 deletions(-) create mode 100644 src/actor/cmd/sys_cmd/ModuleMetrics.cpp create mode 100644 src/actor/cmd/sys_cmd/ModuleMetrics.hpp diff --git a/proto/report.proto b/proto/report.proto index 6423ebcf..15ad0aa4 100644 --- a/proto/report.proto +++ b/proto/report.proto @@ -3,8 +3,15 @@ package neb; message ReportRecord { + enum VALUE_TYPE + { + VALUE_ACC = 0; + VALUE_FIXED = 1; + } bytes key = 1; repeated uint64 value = 2; + string item = 3; + VALUE_TYPE value_type = 4; } message Report diff --git a/src/Definition.hpp b/src/Definition.hpp index 8e1523e1..96e9c564 100644 --- a/src/Definition.hpp +++ b/src/Definition.hpp @@ -166,7 +166,8 @@ const uint32 gc_uiMsgHeadSize = 15; const uint32 gc_uiClientMsgHeadSize = 14; const ev_tstamp gc_dNoTimeout = -1; -const ev_tstamp gc_dDefaultTimeout = 0; +const ev_tstamp gc_dConfigTimeout = 0; +const ev_tstamp gc_dDefaultTimeout = 30.0; /** * @brief 命令执行状态 diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index ede3f01a..2529fd60 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -800,7 +800,7 @@ void ActorBuilder::LoadSysCmd() strModulePath = "/health"; MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); strModulePath = "/status"; - MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); + MakeSharedModule(nullptr, "neb::ModuleMetrics", strModulePath); strModulePath = "http_upgrade"; MakeSharedModule(nullptr, "neb::ModuleHttpUpgrade", strModulePath); } @@ -888,7 +888,7 @@ std::shared_ptr ActorBuilder::InitializeSharedActor(Actor* pCreator, std: bool ActorBuilder::TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor) { - pSharedActor->m_dTimeout = (gc_dDefaultTimeout == pSharedActor->m_dTimeout) + pSharedActor->m_dTimeout = (gc_dConfigTimeout == pSharedActor->m_dTimeout) ? m_pLabor->GetNodeInfo().dStepTimeout : pSharedActor->m_dTimeout; ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); if (NULL == timer_watcher) diff --git a/src/actor/cmd/sys_cmd/ModuleMetrics.cpp b/src/actor/cmd/sys_cmd/ModuleMetrics.cpp new file mode 100644 index 00000000..a42a24c5 --- /dev/null +++ b/src/actor/cmd/sys_cmd/ModuleMetrics.cpp @@ -0,0 +1,88 @@ +/******************************************************************************* + * Project: Nebula + * @file ModuleMetrics.cpp + * @brief + * @author Bwar + * @date: 2021-05-30 + * @note + * Modify history: + ******************************************************************************/ + +#include "ModuleMetrics.hpp" +#include +#include "actor/session/sys_session/SessionDataReport.hpp" + +namespace neb +{ + +ModuleMetrics::ModuleMetrics(const std::string& strModulePath) + : Module(strModulePath) +{ +} + +ModuleMetrics::~ModuleMetrics() +{ +} + +bool ModuleMetrics::Init() +{ + m_strApp = GetCustomConf()("app"); + return(true); +} + +bool ModuleMetrics::AnyMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +{ + HttpMsg oOutHttpMsg; + oOutHttpMsg.set_type(HTTP_RESPONSE); + oOutHttpMsg.set_status_code(200); + oOutHttpMsg.set_http_major(oHttpMsg.http_major()); + oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); + oOutHttpMsg.mutable_headers()->insert({"Content-Type", "text/plain;charset=UTF-8"}); + + std::string strSessionId = "neb::SessionDataReport"; + auto pSharedSession = GetSession(strSessionId); + if (pSharedSession == nullptr) + { + LOG4_ERROR("no session named \"neb::SessionDataReport\"!"); + SendTo(pChannel, oOutHttpMsg); + return(false); + } + auto pReportSession = std::dynamic_pointer_cast(pSharedSession); + + std::ostringstream oss; + auto pReport = pReportSession->GetReport(); + if (pReport == nullptr) + { + SendTo(pChannel, oOutHttpMsg); + return(false); + } + for (int i = 0; i < pReport->records_size(); ++i) + { + if (pReport->records(i).item() == "nebula") + { + if (pReport->records(i).value_size() > 0) + { + if (m_strApp.empty()) + { + oss << "nebula{key=\"}" << pReport->records(i).key() + << "\"}" << pReport->records(i).value(0) << "\n"; + } + else + { + oss << "nebula{app=\"" << m_strApp + << "\", key=\"}" << pReport->records(i).key() + << "\"}" << pReport->records(i).value(0) << "\n"; + } + } + else + { + LOG4_TRACE("report item \"%s\" not define.", pReport->records(i).item().c_str()); + } + } + } + oOutHttpMsg.set_body(oss.str()); + SendTo(pChannel, oOutHttpMsg); + return(true); +} + +} diff --git a/src/actor/cmd/sys_cmd/ModuleMetrics.hpp b/src/actor/cmd/sys_cmd/ModuleMetrics.hpp new file mode 100644 index 00000000..9eafda75 --- /dev/null +++ b/src/actor/cmd/sys_cmd/ModuleMetrics.hpp @@ -0,0 +1,36 @@ +/******************************************************************************* + * Project: Nebula + * @file ModuleMetrics.hpp + * @brief + * @author Bwar + * @date: 2021-05-30 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_SYS_CMD_MODULEHEALTH_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_MODULEHEALTH_HPP_ + +#include "actor/cmd/Module.hpp" + +namespace neb +{ + +class ModuleMetrics: public Module, + public DynamicCreator +{ +public: + ModuleMetrics(const std::string& strModulePath); + virtual ~ModuleMetrics(); + + virtual bool Init(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const HttpMsg& oHttpMsg); +private: + std::string m_strApp; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_MODULEHEALTH_HPP_ */ + diff --git a/src/actor/session/sys_session/SessionDataReport.cpp b/src/actor/session/sys_session/SessionDataReport.cpp index e658cf37..87b3b1d1 100644 --- a/src/actor/session/sys_session/SessionDataReport.cpp +++ b/src/actor/session/sys_session/SessionDataReport.cpp @@ -100,6 +100,7 @@ void SessionDataReport::AddReport(const std::shared_ptr pReport) return; } pRecord->set_key(pReport->records(i).key()); + pRecord->set_item(pReport->records(i).item()); for (int j = 0; j < pReport->records(i).value_size(); ++j) { pRecord->add_value(pReport->records(i).value(j)); @@ -112,7 +113,14 @@ void SessionDataReport::AddReport(const std::shared_ptr pReport) { if (j < iter->second->value_size()) { - iter->second->set_value(j, iter->second->value(j) + pReport->records(i).value(j)); + if (pReport->records(i).value_type() == ReportRecord::VALUE_ACC) + { + iter->second->set_value(j, iter->second->value(j) + pReport->records(i).value(j)); + } + else + { + iter->second->set_value(j, pReport->records(i).value(j)); + } } else { diff --git a/src/actor/session/sys_session/SessionDataReport.hpp b/src/actor/session/sys_session/SessionDataReport.hpp index da8144a5..bb114326 100644 --- a/src/actor/session/sys_session/SessionDataReport.hpp +++ b/src/actor/session/sys_session/SessionDataReport.hpp @@ -24,7 +24,7 @@ class SessionDataReport: public Timer, public ActorSys { public: - SessionDataReport(const std::string& strSessionId, ev_tstamp dStatInterval = 60.0); + SessionDataReport(const std::string& strSessionId, ev_tstamp dStatInterval = gc_dDefaultTimeout); virtual ~SessionDataReport(); virtual E_CMD_STATUS Timeout(); diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 0aa2c9fe..ea8299f0 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -12,6 +12,7 @@ #include #include #include +#include "pb/report.pb.h" #include "util/process_helper.h" #include "util/json/CJsonObject.hpp" #include "labor/NodeInfo.hpp" @@ -20,6 +21,7 @@ #include "labor/Loader.hpp" #include "ios/Dispatcher.hpp" #include "actor/cmd/CW.hpp" +#include "actor/session/sys_session/SessionDataReport.hpp" namespace neb { @@ -28,7 +30,7 @@ std::mutex SessionManager::s_mutexWorker; std::vector SessionManager::s_vecWorkerThreadId; SessionManager::SessionManager(bool bDirectToLoader) - : Session("neb::SessionManager", gc_dNoTimeout), m_bDirectToLoader(bDirectToLoader) + : Session("neb::SessionManager", gc_dDefaultTimeout), m_bDirectToLoader(bDirectToLoader) { m_iterWorkerInfo = m_mapWorkerInfo.begin(); } @@ -55,6 +57,46 @@ SessionManager::~SessionManager() E_CMD_STATUS SessionManager::Timeout() { + std::shared_ptr pReport = std::make_shared(); + if (pReport == nullptr) + { + LOG4_ERROR("failed to new Report!"); + return(CMD_STATUS_FAULT); + } + neb::ReportRecord* pRecord = nullptr; + uint32 uiLoad = 0; + uint32 uiConnect = 0; + uint32 uiClient = 0; + for (auto iter = m_mapWorkerInfo.begin(); iter != m_mapWorkerInfo.end(); ++iter) + { + uiLoad += iter->second->uiLoad; + uiConnect += iter->second->uiConnect; + uiClient += iter->second->uiClientNum; + } + pRecord = pReport->add_records(); + pRecord->set_key("load"); + pRecord->set_item("nebula"); + pRecord->add_value(uiLoad); + pRecord->set_value_type(ReportRecord::VALUE_FIXED); + pRecord = pReport->add_records(); + pRecord->set_key("connect"); + pRecord->set_item("nebula"); + pRecord->add_value(uiConnect); + pRecord->set_value_type(ReportRecord::VALUE_FIXED); + pRecord = pReport->add_records(); + pRecord->set_key("client"); + pRecord->set_item("nebula"); + pRecord->add_value(uiClient); + pRecord->set_value_type(ReportRecord::VALUE_FIXED); + std::string strSessionId = "neb::SessionDataReport"; + auto pSharedSession = GetSession(strSessionId); + if (pSharedSession == nullptr) + { + LOG4_ERROR("no session named \"neb::SessionDataReport\"!"); + return(CMD_STATUS_RUNNING); + } + auto pReportSession = std::dynamic_pointer_cast(pSharedSession); + pReportSession->AddReport(pReport); return(CMD_STATUS_RUNNING); } @@ -236,13 +278,13 @@ bool SessionManager::SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad) auto it = m_mapWorkerInfo.find(iPid); if (it != m_mapWorkerInfo.end()) { - oJsonLoad.Get("load", it->second->iLoad); - oJsonLoad.Get("connect", it->second->iConnect); - oJsonLoad.Get("recv_num", it->second->iRecvNum); - oJsonLoad.Get("recv_byte", it->second->iRecvByte); - oJsonLoad.Get("send_num", it->second->iSendNum); - oJsonLoad.Get("send_byte", it->second->iSendByte); - oJsonLoad.Get("client", it->second->iClientNum); + oJsonLoad.Get("load", it->second->uiLoad); + oJsonLoad.Get("connect", it->second->uiConnect); + oJsonLoad.Get("recv_num", it->second->uiRecvNum); + oJsonLoad.Get("recv_byte", it->second->uiRecvByte); + oJsonLoad.Get("send_num", it->second->uiSendNum); + oJsonLoad.Get("send_byte", it->second->uiSendByte); + oJsonLoad.Get("client", it->second->uiClientNum); it->second->dBeatTime = GetNowTime(); it->second->bStartBeatCheck = true; return(true); @@ -326,13 +368,13 @@ std::pair SessionManager::GetMinLoadWorkerDataFd() if (iMinLoad == -1 && iter->second->iDataFd != m_iLoaderDataFd) { iMinLoadWorkerFd = iter->second->iDataFd; - iMinLoad = iter->second->iLoad; + iMinLoad = iter->second->uiLoad; worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); } - else if (iter->second->iLoad < iMinLoad && iter->second->iDataFd != m_iLoaderDataFd) + else if ((int)iter->second->uiLoad < iMinLoad && iter->second->iDataFd != m_iLoaderDataFd) { iMinLoadWorkerFd = iter->second->iDataFd; - iMinLoad = iter->second->iLoad; + iMinLoad = iter->second->uiLoad; worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); } } @@ -493,21 +535,21 @@ void SessionManager::MakeReportData(CJsonObject& oReportData) { continue; } - iLoad += worker_iter->second->iLoad; - iConnect += worker_iter->second->iConnect; - iRecvNum += worker_iter->second->iRecvNum; - iRecvByte += worker_iter->second->iRecvByte; - iSendNum += worker_iter->second->iSendNum; - iSendByte += worker_iter->second->iSendByte; - iClientNum += worker_iter->second->iClientNum; + iLoad += worker_iter->second->uiLoad; + iConnect += worker_iter->second->uiConnect; + iRecvNum += worker_iter->second->uiRecvNum; + iRecvByte += worker_iter->second->uiRecvByte; + iSendNum += worker_iter->second->uiSendNum; + iSendByte += worker_iter->second->uiSendByte; + iClientNum += worker_iter->second->uiClientNum; oMember.Clear(); - oMember.Add("load", worker_iter->second->iLoad); - oMember.Add("connect", worker_iter->second->iConnect); - oMember.Add("recv_num", worker_iter->second->iRecvNum); - oMember.Add("recv_byte", worker_iter->second->iRecvByte); - oMember.Add("send_num", worker_iter->second->iSendNum); - oMember.Add("send_byte", worker_iter->second->iSendByte); - oMember.Add("client", worker_iter->second->iClientNum); + oMember.Add("load", worker_iter->second->uiLoad); + oMember.Add("connect", worker_iter->second->uiConnect); + oMember.Add("recv_num", worker_iter->second->uiRecvNum); + oMember.Add("recv_byte", worker_iter->second->uiRecvByte); + oMember.Add("send_num", worker_iter->second->uiSendNum); + oMember.Add("send_byte", worker_iter->second->uiSendByte); + oMember.Add("client", worker_iter->second->uiClientNum); oReportData["worker"].Add(oMember); } oReportData["node"].Add("load", iLoad); diff --git a/src/actor/step/GrpcStep.hpp b/src/actor/step/GrpcStep.hpp index 0f106bdc..61455a5c 100644 --- a/src/actor/step/GrpcStep.hpp +++ b/src/actor/step/GrpcStep.hpp @@ -18,7 +18,7 @@ namespace neb class GrpcStep: public HttpStep { public: - GrpcStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + GrpcStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); GrpcStep(const GrpcStep&) = delete; GrpcStep& operator=(const GrpcStep&) = delete; virtual ~GrpcStep(); diff --git a/src/actor/step/HttpStep.hpp b/src/actor/step/HttpStep.hpp index 5cfa4046..eb7d20c6 100644 --- a/src/actor/step/HttpStep.hpp +++ b/src/actor/step/HttpStep.hpp @@ -20,7 +20,7 @@ namespace neb class HttpStep: public Step { public: - HttpStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + HttpStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); HttpStep(const HttpStep&) = delete; HttpStep& operator=(const HttpStep&) = delete; virtual ~HttpStep(); diff --git a/src/actor/step/PbStep.hpp b/src/actor/step/PbStep.hpp index 24723c56..cc0a11b3 100644 --- a/src/actor/step/PbStep.hpp +++ b/src/actor/step/PbStep.hpp @@ -20,7 +20,7 @@ class ActorBuilder; class PbStep: public Step { public: - PbStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + PbStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); PbStep(const PbStep&) = delete; PbStep& operator=(const PbStep&) = delete; virtual ~PbStep(); diff --git a/src/actor/step/RawStep.hpp b/src/actor/step/RawStep.hpp index 2748adc2..5fb02d2e 100644 --- a/src/actor/step/RawStep.hpp +++ b/src/actor/step/RawStep.hpp @@ -21,7 +21,7 @@ namespace neb class RawStep: public Step { public: - RawStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + RawStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); RawStep(const RawStep&) = delete; RawStep& operator=(const RawStep&) = delete; virtual ~RawStep(); diff --git a/src/actor/step/RedisStep.hpp b/src/actor/step/RedisStep.hpp index d095e117..a56c0e9f 100644 --- a/src/actor/step/RedisStep.hpp +++ b/src/actor/step/RedisStep.hpp @@ -26,7 +26,7 @@ typedef RedisReply RedisRequest; class RedisStep: public Step { public: - RedisStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + RedisStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); RedisStep(const RedisStep&) = delete; RedisStep& operator=(const RedisStep&) = delete; virtual ~RedisStep(); diff --git a/src/actor/step/Step.hpp b/src/actor/step/Step.hpp index 407154ee..25ddae99 100644 --- a/src/actor/step/Step.hpp +++ b/src/actor/step/Step.hpp @@ -22,7 +22,7 @@ class Chain; class Step: public Actor { public: - Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dDefaultTimeout); + Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); Step(const Step&) = delete; Step& operator=(const Step&) = delete; virtual ~Step(); diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index 9361c4dc..c8268ab9 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -210,12 +210,16 @@ E_CODEC_STATUS SocketChannelImpl::Send() do { iWrittenLen = Write(m_pSendBuff, m_iErrno); - iHadWrittenLen += iWrittenLen; + if (iWrittenLen > 0) + { + iHadWrittenLen += iWrittenLen; + } } while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); if (iHadWrittenLen >= 0) { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) { @@ -327,12 +331,16 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& do { iWrittenLen = Write(m_pSendBuff, m_iErrno); - iHadWrittenLen += iWrittenLen; + if (iWrittenLen > 0) + { + iHadWrittenLen += iWrittenLen; + } } while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); if (iHadWrittenLen >= 0) { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) { @@ -377,6 +385,12 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); return(CODEC_STATUS_ERR); } + if ((oHttpMsg.http_major() == 2 && m_pCodec->GetCodecType() != CODEC_HTTP2) + || (oHttpMsg.http_major() < 2 && m_pCodec->GetCodecType() != CODEC_HTTP && m_pCodec->GetCodecType() != CODEC_HTTP_CUSTOM)) + { + LOG4_ERROR("codec type not match!"); + return(CODEC_STATUS_ERR); + } E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; switch (m_ucChannelStatus) { @@ -452,12 +466,16 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq do { iWrittenLen = Write(m_pSendBuff, m_iErrno); - iHadWrittenLen += iWrittenLen; + if (iWrittenLen > 0) + { + iHadWrittenLen += iWrittenLen; + } } while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); if (iHadWrittenLen >= 0) { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) { @@ -495,6 +513,11 @@ E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepS LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); return(CODEC_STATUS_ERR); } + if (m_pCodec->GetCodecType() != CODEC_RESP) + { + LOG4_ERROR("codec type not match!"); + return(CODEC_STATUS_ERR); + } E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; switch (m_ucChannelStatus) { @@ -538,12 +561,16 @@ E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepS do { iWrittenLen = Write(m_pSendBuff, m_iErrno); - iHadWrittenLen += iWrittenLen; + if (iWrittenLen > 0) + { + iHadWrittenLen += iWrittenLen; + } } while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); if (iHadWrittenLen >= 0) { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) { @@ -619,12 +646,16 @@ E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint3 do { iWrittenLen = Write(m_pSendBuff, m_iErrno); - iHadWrittenLen += iWrittenLen; + if (iWrittenLen > 0) + { + iHadWrittenLen += iWrittenLen; + } } while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); if (iHadWrittenLen >= 0) { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) { @@ -671,11 +702,15 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) do { iReadLen = Read(m_pRecvBuff, m_iErrno); - iHadReadLen += iReadLen; + if (iReadLen > 0) + { + iHadReadLen += iReadLen; + } LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); } while(iReadLen > 0); + m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen); if (iReadLen == 0) { m_eLastCodecStatus = CODEC_STATUS_EOF; @@ -691,8 +726,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) else { m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); + LOG4_ERROR("recv from %s[fd %d] remote %s error %d: %s", + m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), + m_iErrno, m_strErrMsg.c_str()); m_eLastCodecStatus = CODEC_STATUS_INT; //return(CODEC_STATUS_INT); } @@ -795,9 +831,13 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) iReadLen = Read(m_pRecvBuff, m_iErrno); LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - iHadReadLen += iReadLen; + if (iReadLen > 0) + { + iHadReadLen += iReadLen; + } } while (iReadLen > 0); + m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen); if (iReadLen == 0) { @@ -813,8 +853,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) else { m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); + LOG4_ERROR("recv from %s[fd %d] remote %s error %d: %s", + m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), + m_iErrno, m_strErrMsg.c_str()); m_eLastCodecStatus = CODEC_STATUS_INT; } } @@ -907,9 +948,13 @@ E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) iReadLen = Read(m_pRecvBuff, m_iErrno); LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - iHadReadLen += iReadLen; + if (iReadLen > 0) + { + iHadReadLen += iReadLen; + } } while (iReadLen > 0); + m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen); if (iReadLen == 0) { @@ -925,8 +970,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) else { m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); + LOG4_ERROR("recv from %s[fd %d] remote %s error %d: %s", + m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), + m_iErrno, m_strErrMsg.c_str()); m_eLastCodecStatus = CODEC_STATUS_INT; } } @@ -977,9 +1023,13 @@ E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) iReadLen = Read(m_pRecvBuff, m_iErrno); LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - iHadReadLen += iReadLen; + if (iReadLen > 0) + { + iHadReadLen += iReadLen; + } } while (iReadLen > 0); + m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen); if (iReadLen == 0) { @@ -995,8 +1045,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) else { m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); + LOG4_ERROR("recv from %s[fd %d] remote %s error %d: %s", + m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), + m_iErrno, m_strErrMsg.c_str()); m_eLastCodecStatus = CODEC_STATUS_INT; } } diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index c45d6790..a9663dce 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -127,6 +127,7 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { stSetting.unIdentifier = it->first; stSetting.uiValue = it->second; + vecSetting.push_back(stSetting); } m_pFrame->EncodeSetting(this, vecSetting, pBuff); } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 93ca4ca6..5a9c99cf 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -11,7 +11,6 @@ #include "Dispatcher.hpp" #include #include "Definition.hpp" -#include "labor/Labor.hpp" #include "labor/Manager.hpp" #include "labor/Worker.hpp" #include "actor/Actor.hpp" @@ -183,16 +182,19 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) { if (oHttpMsg.stream_id() > 0) { + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg, eCodecStatus); } } else { + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); } } else if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close { + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); } else @@ -216,6 +218,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) if (CODEC_STATUS_OK == eCodecStatus) { + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oRedisMsg); } else @@ -241,6 +244,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) if (CODEC_STATUS_OK == eCodecStatus) { + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oBuff); } else @@ -273,12 +277,13 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) && CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType() && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 { - LOG4_DEBUG("invalid request, please login first!"); + LOG4_TRACE("invalid request, please login first!"); DiscardSocketChannel(pChannel); return(false); } } */ + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); } else @@ -351,17 +356,20 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) { if (oHttpMsg.stream_id() > 0) { + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg, eCodecStatus); } } else { + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); } eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); } if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close { + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); } } @@ -373,6 +381,7 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) eCodecStatus = pChannel->m_pImpl->Fetch(oRedisMsg); if (CODEC_STATUS_OK == eCodecStatus) { + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oRedisMsg); } else @@ -390,6 +399,7 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) eCodecStatus = pChannel->m_pImpl->Fetch(oBuff); if (CODEC_STATUS_OK == eCodecStatus) { + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oBuff); } else @@ -414,12 +424,13 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) && CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType() && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 { - LOG4_DEBUG("invalid request, please login first!"); + LOG4_TRACE("invalid request, please login first!"); DiscardSocketChannel(pChannel); return(false); } } */ + m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); } else @@ -785,6 +796,7 @@ bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBod return(false); } E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); + m_pLabor->IoStatAddSendNum(pChannel->GetFd()); if (CODEC_STATUS_OK == eStatus) { RemoveIoWriteEvent(pChannel); @@ -880,6 +892,7 @@ std::shared_ptr Dispatcher::StressSend(const std::string& strIden AddIoWriteEvent(pChannel); pChannel->m_pImpl->SetRemoteAddr(strHost); E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); + m_pLabor->IoStatAddSendNum(pChannel->GetFd()); if (CODEC_STATUS_OK != eCodecStatus && CODEC_STATUS_PAUSE != eCodecStatus && CODEC_STATUS_WANT_WRITE != eCodecStatus @@ -915,6 +928,7 @@ bool Dispatcher::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) } } E_CODEC_STATUS eStatus = m_iterLoaderAndWorkerChannel->second->m_pImpl->Send(iCmd, uiSeq, oMsgBody); + //m_pLabor->IoStatAddSendNum(pChannel->GetFd()); m_pLastActivityChannel = m_iterLoaderAndWorkerChannel->second; if (CODEC_STATUS_OK == eStatus) { @@ -981,7 +995,7 @@ bool Dispatcher::DiscardNamedChannel(const std::string& strIdentify) auto named_iter = m_mapNamedSocketChannel.find(strIdentify); if (named_iter == m_mapNamedSocketChannel.end()) { - LOG4_DEBUG("no channel match %s.", strIdentify.c_str()); + LOG4_TRACE("no channel match %s.", strIdentify.c_str()); return(false); } else @@ -1278,7 +1292,7 @@ void Dispatcher::Destroy() std::shared_ptr Dispatcher::CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient, bool bWithSsl) { - LOG4_DEBUG("iFd %d, codec_type %d, with_ssl = %d", iFd, eCodecType, bWithSsl); + LOG4_TRACE("iFd %d, codec_type %d, with_ssl = %d", iFd, eCodecType, bWithSsl); auto iter = m_mapSocketChannel.find(iFd); if (iter == m_mapSocketChannel.end()) @@ -1320,7 +1334,7 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b { if (pChannel == nullptr) { - LOG4_DEBUG("pChannel not exist!"); + LOG4_TRACE("pChannel not exist!"); return(false); } @@ -1346,7 +1360,7 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b bool bCloseResult = pChannel->m_pImpl->Close(); if (bCloseResult) { - LOG4_DEBUG("%s disconnect, fd %d, channel_seq %u, identify %s", + LOG4_TRACE("%s disconnect, fd %d, channel_seq %u, identify %s", pChannel->m_pImpl->GetRemoteAddr().c_str(), pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence(), pChannel->m_pImpl->GetIdentify().c_str()); @@ -1592,7 +1606,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(); if (iWorkerDataFd > 0) { - LOG4_DEBUG("send new fd %d to worker communication fd %d", + LOG4_TRACE("send new fd %d to worker communication fd %d", iAcceptFd, iWorkerDataFd); int iCodec = m_pLabor->GetNodeInfo().eCodec; int iErrno = SocketChannel::SendChannelFd(iWorkerDataFd, iAcceptFd, iFamily, iCodec, m_pLogger); diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 70ab7554..3c9374fc 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -47,6 +47,7 @@ extern "C" { #include "util/process_helper.h" #include "pb/msg.pb.h" +#include "labor/Labor.hpp" #include "channel/SocketChannel.hpp" #include "logger/NetLogger.hpp" #include "Nodes.hpp" @@ -225,6 +226,7 @@ template bool Dispatcher::SendTo(std::shared_ptr pChannel, Targs&&... args) { E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(std::forward(args)...); + m_pLabor->IoStatAddSendNum(pChannel->GetFd()); m_pLastActivityChannel = pChannel; switch (eStatus) { @@ -433,6 +435,7 @@ bool Dispatcher::AutoSend( pChannel->m_pImpl->SetRemoteAddr(strHost); pChannel->m_pImpl->SetPipeline(bPipeline); E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(std::forward(args)...); + m_pLabor->IoStatAddSendNum(pChannel->GetFd()); m_pLastActivityChannel = pChannel; if (CODEC_STATUS_OK != eCodecStatus && CODEC_STATUS_PAUSE != eCodecStatus diff --git a/src/labor/Labor.hpp b/src/labor/Labor.hpp index 0fd4d264..9d36ebc8 100644 --- a/src/labor/Labor.hpp +++ b/src/labor/Labor.hpp @@ -73,6 +73,10 @@ class Labor { return(false); } + virtual void IoStatAddRecvNum(int iFd){} + virtual void IoStatAddRecvBytes(int iFd, uint32 uiBytes){} + virtual void IoStatAddSendNum(int iFd){} + virtual void IoStatAddSendBytes(int iFd, uint32 uiBytes){} pid_t gettid() { if (m_iPid == 0) diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index 9068af5b..dfa1178f 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -57,13 +57,13 @@ struct WorkerInfo int iWorkerIndex = 0; ///< 工作进程序号 int iControlFd = -1; ///< 与Manager进程通信的文件描述符(控制流) int iDataFd = -1; ///< 与Manager进程通信的文件描述符(数据流) - int32 iLoad = 0; ///< 负载 - int32 iConnect = 0; ///< 连接数量 - int32 iRecvNum = 0; ///< 接收数据包数量 - int32 iRecvByte = 0; ///< 接收字节数 - int32 iSendNum = 0; ///< 发送数据包数量 - int32 iSendByte = 0; ///< 发送字节数 - int32 iClientNum = 0; ///< 客户端数量 + uint32 uiLoad = 0; ///< 负载 + uint32 uiConnect = 0; ///< 连接数量 + uint32 uiRecvNum = 0; ///< 接收数据包数量 + uint32 uiRecvByte = 0; ///< 接收字节数 + uint32 uiSendNum = 0; ///< 发送数据包数量 + uint32 uiSendByte = 0; ///< 发送字节数 + uint32 uiClientNum = 0; ///< 客户端数量 ev_tstamp dBeatTime = 0.0; ///< 心跳时间 bool bStartBeatCheck = 0.0; ///< 是否需要心跳检查,worker或loader进程启动时可能需要加载数据而处于繁忙状态无法响应Manager的心跳,需等待其就绪之后才开始心跳检查。 diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index e0bafee3..68e4d519 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -22,6 +22,7 @@ extern "C" { #include "ios/Dispatcher.hpp" #include "actor/ActorBuilder.hpp" #include "actor/session/sys_session/manager/SessionManager.hpp" +#include "pb/report.pb.h" namespace neb { @@ -103,24 +104,45 @@ bool Worker::CheckParent() exit(0); } } + m_stWorkerInfo.uiConnect = m_pDispatcher->GetConnectionNum(); + m_stWorkerInfo.uiClientNum = m_pDispatcher->GetClientNum(); MsgBody oMsgBody; CJsonObject oJsonLoad; - m_stWorkerInfo.iConnect = m_pDispatcher->GetConnectionNum(); - m_stWorkerInfo.iClientNum = m_pDispatcher->GetClientNum(); - oJsonLoad.Add("load", int32(m_stWorkerInfo.iConnect + m_pActorBuilder->GetStepNum())); - oJsonLoad.Add("connect", m_stWorkerInfo.iConnect); - oJsonLoad.Add("recv_num", m_stWorkerInfo.iRecvNum); - oJsonLoad.Add("recv_byte", m_stWorkerInfo.iRecvByte); - oJsonLoad.Add("send_num", m_stWorkerInfo.iSendNum); - oJsonLoad.Add("send_byte", m_stWorkerInfo.iSendByte); - oJsonLoad.Add("client", m_stWorkerInfo.iClientNum); + neb::Report oReport; + auto pRecord = oReport.add_records(); + pRecord->set_key("recv_num"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiRecvNum); + pRecord = oReport.add_records(); + pRecord->set_key("recv_byte"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiRecvByte); + pRecord = oReport.add_records(); + pRecord->set_key("send_num"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiSendNum); + pRecord = oReport.add_records(); + pRecord->set_key("send_byte"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiSendByte); + oJsonLoad.Add("load", int32(m_stWorkerInfo.uiConnect + m_pActorBuilder->GetStepNum())); + oJsonLoad.Add("connect", m_stWorkerInfo.uiConnect); + oJsonLoad.Add("recv_num", m_stWorkerInfo.uiRecvNum); + oJsonLoad.Add("recv_byte", m_stWorkerInfo.uiRecvByte); + oJsonLoad.Add("send_num", m_stWorkerInfo.uiSendNum); + oJsonLoad.Add("send_byte", m_stWorkerInfo.uiSendByte); + oJsonLoad.Add("client", m_stWorkerInfo.uiClientNum); oMsgBody.set_data(oJsonLoad.ToString()); LOG4_TRACE("%s", oJsonLoad.ToString().c_str()); m_pDispatcher->SendTo(m_pManagerControlChannel, CMD_REQ_UPDATE_WORKER_LOAD, GetSequence(), oMsgBody); - m_stWorkerInfo.iRecvNum = 0; - m_stWorkerInfo.iRecvByte = 0; - m_stWorkerInfo.iSendNum = 0; - m_stWorkerInfo.iSendByte = 0; + std::string strReport; + oReport.SerializeToString(&strReport); + oMsgBody.set_data(strReport); + m_pDispatcher->SendDataReport(CMD_REQ_DATA_REPORT, GetSequence(), oMsgBody); + m_stWorkerInfo.uiRecvNum = 0; + m_stWorkerInfo.uiRecvByte = 0; + m_stWorkerInfo.uiSendNum = 0; + m_stWorkerInfo.uiSendByte = 0; return(true); } diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 95318203..765b390c 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -99,6 +99,42 @@ class Worker: public Labor const WorkerInfo& GetWorkerInfo() const; std::shared_ptr GetManagerControlChannel(); bool SetCustomConf(const CJsonObject& oJsonConf); + virtual void IoStatAddRecvNum(int iFd) + { + if (iFd == m_pManagerControlChannel->GetFd() + || iFd == m_pManagerDataChannel->GetFd()) + { + return; + } + ++m_stWorkerInfo.uiRecvNum; + } + virtual void IoStatAddRecvBytes(int iFd, uint32 uiBytes) + { + if (iFd == m_pManagerControlChannel->GetFd() + || iFd == m_pManagerDataChannel->GetFd()) + { + return; + } + m_stWorkerInfo.uiRecvByte += uiBytes; + } + virtual void IoStatAddSendNum(int iFd) + { + if (iFd == m_pManagerControlChannel->GetFd() + || iFd == m_pManagerDataChannel->GetFd()) + { + return; + } + ++m_stWorkerInfo.uiSendNum; + } + virtual void IoStatAddSendBytes(int iFd, uint32 uiBytes) + { + if (iFd == m_pManagerControlChannel->GetFd() + || iFd == m_pManagerDataChannel->GetFd()) + { + return; + } + m_stWorkerInfo.uiSendByte += uiBytes; + } template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); diff --git a/src/pb/report.pb.cc b/src/pb/report.pb.cc index 3dbf9a7f..c10fc34a 100644 --- a/src/pb/report.pb.cc +++ b/src/pb/report.pb.cc @@ -24,6 +24,7 @@ namespace { const ::google::protobuf::Descriptor* ReportRecord_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* ReportRecord_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* ReportRecord_VALUE_TYPE_descriptor_ = NULL; const ::google::protobuf::Descriptor* Report_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* Report_reflection_ = NULL; @@ -39,9 +40,11 @@ void protobuf_AssignDesc_report_2eproto() { "report.proto"); GOOGLE_CHECK(file != NULL); ReportRecord_descriptor_ = file->message_type(0); - static const int ReportRecord_offsets_[2] = { + static const int ReportRecord_offsets_[4] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ReportRecord, key_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ReportRecord, value_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ReportRecord, item_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ReportRecord, value_type_), }; ReportRecord_reflection_ = ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( @@ -54,6 +57,7 @@ void protobuf_AssignDesc_report_2eproto() { sizeof(ReportRecord), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ReportRecord, _internal_metadata_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ReportRecord, _is_default_instance_)); + ReportRecord_VALUE_TYPE_descriptor_ = ReportRecord_descriptor_->enum_type(0); Report_descriptor_ = file->message_type(1); static const int Report_offsets_[1] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Report, records_), @@ -105,9 +109,12 @@ void protobuf_AddDesc_report_2eproto() { GOOGLE_PROTOBUF_VERIFY_VERSION; ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( - "\n\014report.proto\022\003neb\"*\n\014ReportRecord\022\013\n\003k" - "ey\030\001 \001(\014\022\r\n\005value\030\002 \003(\004\",\n\006Report\022\"\n\007rec" - "ords\030\001 \003(\0132\021.neb.ReportRecordb\006proto3", 117); + "\n\014report.proto\022\003neb\"\230\001\n\014ReportRecord\022\013\n\003" + "key\030\001 \001(\014\022\r\n\005value\030\002 \003(\004\022\014\n\004item\030\003 \001(\t\0220" + "\n\nvalue_type\030\004 \001(\0162\034.neb.ReportRecord.VA" + "LUE_TYPE\",\n\nVALUE_TYPE\022\r\n\tVALUE_ACC\020\000\022\017\n" + "\013VALUE_FIXED\020\001\",\n\006Report\022\"\n\007records\030\001 \003(" + "\0132\021.neb.ReportRecordb\006proto3", 228); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "report.proto", &protobuf_RegisterTypes); ReportRecord::default_instance_ = new ReportRecord(); @@ -126,9 +133,32 @@ struct StaticDescriptorInitializer_report_2eproto { // =================================================================== +const ::google::protobuf::EnumDescriptor* ReportRecord_VALUE_TYPE_descriptor() { + protobuf_AssignDescriptorsOnce(); + return ReportRecord_VALUE_TYPE_descriptor_; +} +bool ReportRecord_VALUE_TYPE_IsValid(int value) { + switch(value) { + case 0: + case 1: + return true; + default: + return false; + } +} + +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +const ReportRecord_VALUE_TYPE ReportRecord::VALUE_ACC; +const ReportRecord_VALUE_TYPE ReportRecord::VALUE_FIXED; +const ReportRecord_VALUE_TYPE ReportRecord::VALUE_TYPE_MIN; +const ReportRecord_VALUE_TYPE ReportRecord::VALUE_TYPE_MAX; +const int ReportRecord::VALUE_TYPE_ARRAYSIZE; +#endif // !defined(_MSC_VER) || _MSC_VER >= 1900 #if !defined(_MSC_VER) || _MSC_VER >= 1900 const int ReportRecord::kKeyFieldNumber; const int ReportRecord::kValueFieldNumber; +const int ReportRecord::kItemFieldNumber; +const int ReportRecord::kValueTypeFieldNumber; #endif // !defined(_MSC_VER) || _MSC_VER >= 1900 ReportRecord::ReportRecord() @@ -154,6 +184,8 @@ void ReportRecord::SharedCtor() { ::google::protobuf::internal::GetEmptyString(); _cached_size_ = 0; key_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + item_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_type_ = 0; } ReportRecord::~ReportRecord() { @@ -163,6 +195,7 @@ ReportRecord::~ReportRecord() { void ReportRecord::SharedDtor() { key_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + item_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); if (this != default_instance_) { } } @@ -195,6 +228,8 @@ ReportRecord* ReportRecord::New(::google::protobuf::Arena* arena) const { void ReportRecord::Clear() { // @@protoc_insertion_point(message_clear_start:neb.ReportRecord) key_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + item_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_type_ = 0; value_.Clear(); } @@ -234,6 +269,39 @@ bool ReportRecord::MergePartialFromCodedStream( } else { goto handle_unusual; } + if (input->ExpectTag(26)) goto parse_item; + break; + } + + // optional string item = 3; + case 3: { + if (tag == 26) { + parse_item: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_item())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->item().data(), this->item().length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "neb.ReportRecord.item")); + } else { + goto handle_unusual; + } + if (input->ExpectTag(32)) goto parse_value_type; + break; + } + + // optional .neb.ReportRecord.VALUE_TYPE value_type = 4; + case 4: { + if (tag == 32) { + parse_value_type: + int value; + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>( + input, &value))); + set_value_type(static_cast< ::neb::ReportRecord_VALUE_TYPE >(value)); + } else { + goto handle_unusual; + } if (input->ExpectAtEnd()) goto success; break; } @@ -278,6 +346,22 @@ void ReportRecord::SerializeWithCachedSizes( this->value(i), output); } + // optional string item = 3; + if (this->item().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->item().data(), this->item().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "neb.ReportRecord.item"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 3, this->item(), output); + } + + // optional .neb.ReportRecord.VALUE_TYPE value_type = 4; + if (this->value_type() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteEnum( + 4, this->value_type(), output); + } + // @@protoc_insertion_point(serialize_end:neb.ReportRecord) } @@ -305,6 +389,23 @@ ::google::protobuf::uint8* ReportRecord::InternalSerializeWithCachedSizesToArray WriteUInt64NoTagToArray(this->value(i), target); } + // optional string item = 3; + if (this->item().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->item().data(), this->item().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "neb.ReportRecord.item"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 3, this->item(), target); + } + + // optional .neb.ReportRecord.VALUE_TYPE value_type = 4; + if (this->value_type() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( + 4, this->value_type(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:neb.ReportRecord) return target; } @@ -320,6 +421,19 @@ int ReportRecord::ByteSize() const { this->key()); } + // optional string item = 3; + if (this->item().size() > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->item()); + } + + // optional .neb.ReportRecord.VALUE_TYPE value_type = 4; + if (this->value_type() != 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::EnumSize(this->value_type()); + } + // repeated uint64 value = 2; { int data_size = 0; @@ -370,6 +484,13 @@ void ReportRecord::MergeFrom(const ReportRecord& from) { key_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.key_); } + if (from.item().size() > 0) { + + item_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.item_); + } + if (from.value_type() != 0) { + set_value_type(from.value_type()); + } } void ReportRecord::CopyFrom(const ::google::protobuf::Message& from) { @@ -398,6 +519,8 @@ void ReportRecord::Swap(ReportRecord* other) { void ReportRecord::InternalSwap(ReportRecord* other) { key_.Swap(&other->key_); value_.UnsafeArenaSwap(&other->value_); + item_.Swap(&other->item_); + std::swap(value_type_, other->value_type_); _internal_metadata_.Swap(&other->_internal_metadata_); std::swap(_cached_size_, other->_cached_size_); } @@ -487,6 +610,64 @@ ReportRecord::mutable_value() { return &value_; } +// optional string item = 3; +void ReportRecord::clear_item() { + item_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& ReportRecord::item() const { + // @@protoc_insertion_point(field_get:neb.ReportRecord.item) + return item_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void ReportRecord::set_item(const ::std::string& value) { + + item_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:neb.ReportRecord.item) +} + void ReportRecord::set_item(const char* value) { + + item_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:neb.ReportRecord.item) +} + void ReportRecord::set_item(const char* value, size_t size) { + + item_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:neb.ReportRecord.item) +} + ::std::string* ReportRecord::mutable_item() { + + // @@protoc_insertion_point(field_mutable:neb.ReportRecord.item) + return item_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* ReportRecord::release_item() { + // @@protoc_insertion_point(field_release:neb.ReportRecord.item) + + return item_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void ReportRecord::set_allocated_item(::std::string* item) { + if (item != NULL) { + + } else { + + } + item_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), item); + // @@protoc_insertion_point(field_set_allocated:neb.ReportRecord.item) +} + +// optional .neb.ReportRecord.VALUE_TYPE value_type = 4; +void ReportRecord::clear_value_type() { + value_type_ = 0; +} + ::neb::ReportRecord_VALUE_TYPE ReportRecord::value_type() const { + // @@protoc_insertion_point(field_get:neb.ReportRecord.value_type) + return static_cast< ::neb::ReportRecord_VALUE_TYPE >(value_type_); +} + void ReportRecord::set_value_type(::neb::ReportRecord_VALUE_TYPE value) { + + value_type_ = value; + // @@protoc_insertion_point(field_set:neb.ReportRecord.value_type) +} + #endif // PROTOBUF_INLINE_NOT_IN_HEADERS // =================================================================== diff --git a/src/pb/report.pb.h b/src/pb/report.pb.h index 35094e0d..f878aa3c 100644 --- a/src/pb/report.pb.h +++ b/src/pb/report.pb.h @@ -26,6 +26,7 @@ #include #include #include +#include #include // @@protoc_insertion_point(includes) @@ -39,6 +40,27 @@ void protobuf_ShutdownFile_report_2eproto(); class Report; class ReportRecord; +enum ReportRecord_VALUE_TYPE { + ReportRecord_VALUE_TYPE_VALUE_ACC = 0, + ReportRecord_VALUE_TYPE_VALUE_FIXED = 1, + ReportRecord_VALUE_TYPE_ReportRecord_VALUE_TYPE_INT_MIN_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32min, + ReportRecord_VALUE_TYPE_ReportRecord_VALUE_TYPE_INT_MAX_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32max +}; +bool ReportRecord_VALUE_TYPE_IsValid(int value); +const ReportRecord_VALUE_TYPE ReportRecord_VALUE_TYPE_VALUE_TYPE_MIN = ReportRecord_VALUE_TYPE_VALUE_ACC; +const ReportRecord_VALUE_TYPE ReportRecord_VALUE_TYPE_VALUE_TYPE_MAX = ReportRecord_VALUE_TYPE_VALUE_FIXED; +const int ReportRecord_VALUE_TYPE_VALUE_TYPE_ARRAYSIZE = ReportRecord_VALUE_TYPE_VALUE_TYPE_MAX + 1; + +const ::google::protobuf::EnumDescriptor* ReportRecord_VALUE_TYPE_descriptor(); +inline const ::std::string& ReportRecord_VALUE_TYPE_Name(ReportRecord_VALUE_TYPE value) { + return ::google::protobuf::internal::NameOfEnum( + ReportRecord_VALUE_TYPE_descriptor(), value); +} +inline bool ReportRecord_VALUE_TYPE_Parse( + const ::std::string& name, ReportRecord_VALUE_TYPE* value) { + return ::google::protobuf::internal::ParseNamedEnum( + ReportRecord_VALUE_TYPE_descriptor(), name, value); +} // =================================================================== class ReportRecord : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:neb.ReportRecord) */ { @@ -99,6 +121,32 @@ class ReportRecord : public ::google::protobuf::Message /* @@protoc_insertion_po // nested types ---------------------------------------------------- + typedef ReportRecord_VALUE_TYPE VALUE_TYPE; + static const VALUE_TYPE VALUE_ACC = + ReportRecord_VALUE_TYPE_VALUE_ACC; + static const VALUE_TYPE VALUE_FIXED = + ReportRecord_VALUE_TYPE_VALUE_FIXED; + static inline bool VALUE_TYPE_IsValid(int value) { + return ReportRecord_VALUE_TYPE_IsValid(value); + } + static const VALUE_TYPE VALUE_TYPE_MIN = + ReportRecord_VALUE_TYPE_VALUE_TYPE_MIN; + static const VALUE_TYPE VALUE_TYPE_MAX = + ReportRecord_VALUE_TYPE_VALUE_TYPE_MAX; + static const int VALUE_TYPE_ARRAYSIZE = + ReportRecord_VALUE_TYPE_VALUE_TYPE_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + VALUE_TYPE_descriptor() { + return ReportRecord_VALUE_TYPE_descriptor(); + } + static inline const ::std::string& VALUE_TYPE_Name(VALUE_TYPE value) { + return ReportRecord_VALUE_TYPE_Name(value); + } + static inline bool VALUE_TYPE_Parse(const ::std::string& name, + VALUE_TYPE* value) { + return ReportRecord_VALUE_TYPE_Parse(name, value); + } + // accessors ------------------------------------------------------- // optional bytes key = 1; @@ -124,6 +172,23 @@ class ReportRecord : public ::google::protobuf::Message /* @@protoc_insertion_po ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >* mutable_value(); + // optional string item = 3; + void clear_item(); + static const int kItemFieldNumber = 3; + const ::std::string& item() const; + void set_item(const ::std::string& value); + void set_item(const char* value); + void set_item(const char* value, size_t size); + ::std::string* mutable_item(); + ::std::string* release_item(); + void set_allocated_item(::std::string* item); + + // optional .neb.ReportRecord.VALUE_TYPE value_type = 4; + void clear_value_type(); + static const int kValueTypeFieldNumber = 4; + ::neb::ReportRecord_VALUE_TYPE value_type() const; + void set_value_type(::neb::ReportRecord_VALUE_TYPE value); + // @@protoc_insertion_point(class_scope:neb.ReportRecord) private: @@ -132,6 +197,8 @@ class ReportRecord : public ::google::protobuf::Message /* @@protoc_insertion_po ::google::protobuf::internal::ArenaStringPtr key_; ::google::protobuf::RepeatedField< ::google::protobuf::uint64 > value_; mutable int _value_cached_byte_size_; + ::google::protobuf::internal::ArenaStringPtr item_; + int value_type_; mutable int _cached_size_; friend void protobuf_AddDesc_report_2eproto(); friend void protobuf_AssignDesc_report_2eproto(); @@ -310,6 +377,64 @@ ReportRecord::mutable_value() { return &value_; } +// optional string item = 3; +inline void ReportRecord::clear_item() { + item_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& ReportRecord::item() const { + // @@protoc_insertion_point(field_get:neb.ReportRecord.item) + return item_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void ReportRecord::set_item(const ::std::string& value) { + + item_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:neb.ReportRecord.item) +} +inline void ReportRecord::set_item(const char* value) { + + item_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:neb.ReportRecord.item) +} +inline void ReportRecord::set_item(const char* value, size_t size) { + + item_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:neb.ReportRecord.item) +} +inline ::std::string* ReportRecord::mutable_item() { + + // @@protoc_insertion_point(field_mutable:neb.ReportRecord.item) + return item_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* ReportRecord::release_item() { + // @@protoc_insertion_point(field_release:neb.ReportRecord.item) + + return item_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void ReportRecord::set_allocated_item(::std::string* item) { + if (item != NULL) { + + } else { + + } + item_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), item); + // @@protoc_insertion_point(field_set_allocated:neb.ReportRecord.item) +} + +// optional .neb.ReportRecord.VALUE_TYPE value_type = 4; +inline void ReportRecord::clear_value_type() { + value_type_ = 0; +} +inline ::neb::ReportRecord_VALUE_TYPE ReportRecord::value_type() const { + // @@protoc_insertion_point(field_get:neb.ReportRecord.value_type) + return static_cast< ::neb::ReportRecord_VALUE_TYPE >(value_type_); +} +inline void ReportRecord::set_value_type(::neb::ReportRecord_VALUE_TYPE value) { + + value_type_ = value; + // @@protoc_insertion_point(field_set:neb.ReportRecord.value_type) +} + // ------------------------------------------------------------------- // Report @@ -352,6 +477,20 @@ Report::records() const { } // namespace neb +#ifndef SWIG +namespace google { +namespace protobuf { + +template <> struct is_proto_enum< ::neb::ReportRecord_VALUE_TYPE> : ::google::protobuf::internal::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::neb::ReportRecord_VALUE_TYPE>() { + return ::neb::ReportRecord_VALUE_TYPE_descriptor(); +} + +} // namespace protobuf +} // namespace google +#endif // SWIG + // @@protoc_insertion_point(global_scope) #endif // PROTOBUF_report_2eproto__INCLUDED From d453d22a1f68bda94840041eaa69ab0ef4ad85c7 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 5 Jun 2021 19:24:07 +0800 Subject: [PATCH 136/176] fix bugs in the last submission --- src/actor/step/sys_step/StepConnectWorker.cpp | 6 +++--- src/actor/step/sys_step/StepConnectWorker.hpp | 6 +++--- src/labor/Worker.hpp | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/actor/step/sys_step/StepConnectWorker.cpp b/src/actor/step/sys_step/StepConnectWorker.cpp index d987eb2b..20982ffa 100644 --- a/src/actor/step/sys_step/StepConnectWorker.cpp +++ b/src/actor/step/sys_step/StepConnectWorker.cpp @@ -13,8 +13,8 @@ namespace neb { -StepConnectWorker::StepConnectWorker(std::shared_ptr pChannel, uint16 unRemoteWorkerIndex) - : m_pChannel(pChannel), m_unRemoteWorkerIndex(unRemoteWorkerIndex) +StepConnectWorker::StepConnectWorker(std::shared_ptr pChannel, int16 iRemoteWorkerIndex) + : m_pChannel(pChannel), m_iRemoteWorkerIndex(iRemoteWorkerIndex) { } @@ -29,7 +29,7 @@ E_CMD_STATUS StepConnectWorker::Emit( { MsgHead oMsgHead; MsgBody oMsgBody; - oMsgBody.set_data(std::to_string((int)m_unRemoteWorkerIndex)); + oMsgBody.set_data(std::to_string((int)m_iRemoteWorkerIndex)); SendTo(m_pChannel, CMD_REQ_CONNECT_TO_WORKER, GetSequence(), oMsgBody); return(CMD_STATUS_COMPLETED); } diff --git a/src/actor/step/sys_step/StepConnectWorker.hpp b/src/actor/step/sys_step/StepConnectWorker.hpp index eca6e2bc..084ece09 100644 --- a/src/actor/step/sys_step/StepConnectWorker.hpp +++ b/src/actor/step/sys_step/StepConnectWorker.hpp @@ -16,10 +16,10 @@ namespace neb { -class StepConnectWorker: public PbStep, public DynamicCreator, uint16> +class StepConnectWorker: public PbStep, public DynamicCreator, int16> { public: - StepConnectWorker(std::shared_ptr pChannel, uint16 unRemoteWorkerIndex); + StepConnectWorker(std::shared_ptr pChannel, int16 iRemoteWorkerIndex); virtual ~StepConnectWorker(); virtual E_CMD_STATUS Emit( @@ -37,7 +37,7 @@ class StepConnectWorker: public PbStep, public DynamicCreator m_pChannel; - uint16 m_unRemoteWorkerIndex; + int16 m_iRemoteWorkerIndex; }; } /* namespace neb */ diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 765b390c..ed8d88c4 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -101,6 +101,10 @@ class Worker: public Labor bool SetCustomConf(const CJsonObject& oJsonConf); virtual void IoStatAddRecvNum(int iFd) { + if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) + { + return; + } if (iFd == m_pManagerControlChannel->GetFd() || iFd == m_pManagerDataChannel->GetFd()) { @@ -110,6 +114,10 @@ class Worker: public Labor } virtual void IoStatAddRecvBytes(int iFd, uint32 uiBytes) { + if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) + { + return; + } if (iFd == m_pManagerControlChannel->GetFd() || iFd == m_pManagerDataChannel->GetFd()) { @@ -119,6 +127,10 @@ class Worker: public Labor } virtual void IoStatAddSendNum(int iFd) { + if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) + { + return; + } if (iFd == m_pManagerControlChannel->GetFd() || iFd == m_pManagerDataChannel->GetFd()) { @@ -128,6 +140,10 @@ class Worker: public Labor } virtual void IoStatAddSendBytes(int iFd, uint32 uiBytes) { + if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) + { + return; + } if (iFd == m_pManagerControlChannel->GetFd() || iFd == m_pManagerDataChannel->GetFd()) { From ff9298e78443281758204180a17874e647645a34 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 19 Jun 2021 16:56:55 +0800 Subject: [PATCH 137/176] http2 add data frame when http body is empty. --- src/codec/Codec.hpp | 1 + src/codec/http2/CodecHttp2.cpp | 138 ++++++++++++++++++++++++-------- src/codec/http2/Http2Frame.cpp | 85 ++++++++++---------- src/codec/http2/Http2Frame.hpp | 2 +- src/codec/http2/Http2Stream.cpp | 14 ++-- 5 files changed, 155 insertions(+), 85 deletions(-) diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index c8cbd4ab..f0205596 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -45,6 +45,7 @@ enum E_CODEC_TYPE CODEC_NEBULA_IN_NODE = 8, ///< 节点各进程间通信协议,与CODEC_NEBULA协议相同,使用的编解码类也相同,只为区别节点内部连接与外部连接 CODEC_RESP = 9, ///< redis数据传输协议resp CODEC_HTTP2 = 10, ///< http2编解码 + CODEC_DIRECT = 11, ///< 虚拟编解码类型,用于SelfChannel,以参数方式直接传递数据包 }; /** diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index a9663dce..5cbeab76 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -77,10 +77,13 @@ void CodecHttp2::ConnectionSetting(CBuffer* pBuff) stSetting.uiValue = 4194304; m_uiSettingsMaxDecodeFrameSize = 4194304; vecSetting.push_back(stSetting); + stSetting.unIdentifier = H2_SETTINGS_MAX_CONCURRENT_STREAMS; + stSetting.uiValue = 100; + vecSetting.push_back(stSetting); m_pFrame->EncodeSetting(this, vecSetting, pBuff); m_pFrame->EncodeWindowUpdate(this, 0, 4128769, pBuff); // In HTTP/2, 1 on client is reserved for Upgrade. - m_uiStreamIdGenerate = 1; + //m_uiStreamIdGenerate = 1; } else { @@ -193,6 +196,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR { if (pBuff->ReadableBytes() >= 24) { + LOG4_TRACE("%s", pBuff->GetRawReadBuffer()); std::string strMagic; strMagic.assign(pBuff->GetRawReadBuffer(), 24); if (strMagic == "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") @@ -238,7 +242,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR size_t uiReadIdx = pBuff->GetReadIndex(); Http2Frame::DecodeFrameHeader(pBuff, m_stDecodeFrameHead); LOG4_TRACE("m_stDecodeFrameHead.uiLength = %u, m_stDecodeFrameHead.ucType = %u, m_stDecodeFrameHead.ucFlag = %u, m_stDecodeFrameHead.uiStreamIdentifier = %u", - m_stDecodeFrameHead.uiLength, m_stDecodeFrameHead.ucType, m_stDecodeFrameHead.ucFlag, m_stDecodeFrameHead.uiStreamIdentifier); + m_stDecodeFrameHead.uiLength, (uint32)m_stDecodeFrameHead.ucType, (uint32)m_stDecodeFrameHead.ucFlag, m_stDecodeFrameHead.uiStreamIdentifier); if (m_stDecodeFrameHead.uiLength > m_uiSettingsMaxDecodeFrameSize) { LOG4_TRACE("m_uiSettingsMaxDecodeFrameSize = %u, m_stDecodeFrameHead.uiLength = %u", @@ -299,7 +303,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR "use when a more specific error code is not available."); return(CODEC_STATUS_ERR); } - LOG4_TRACE("m_stDecodeFrameHead.ucType = %u", m_stDecodeFrameHead.ucType); + LOG4_TRACE("m_stDecodeFrameHead.ucType = %u", (uint32)m_stDecodeFrameHead.ucType); auto stream_iter = m_mapStream.find(m_stDecodeFrameHead.uiStreamIdentifier); if (stream_iter == m_mapStream.end()) { @@ -603,6 +607,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu uiReadIndex = pBuff->GetReadIndex(); pBuff->ReadByte(B); pBuff->SetReadIndex(uiReadIndex); + LOG4_TRACE("B = 0x%X", (uint32)B); if (H2_HPACK_CONDITION_INDEXED_HEADER & B) { eStatus = UnpackHeaderIndexed(pBuff, oHttpMsg); @@ -616,27 +621,28 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu eStatus = UnpackHeaderLiteralIndexing(pBuff, B, H2_HPACK_PREFIX_6_BITS, strHeaderName, strHeaderValue, bWithHuffman); + ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); if (eStatus != CODEC_STATUS_PART_OK) { return(eStatus); } - ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); } else if (H2_HPACK_CONDITION_LITERAL_HEADER_NEVER_INDEXED & B) { eStatus = UnpackHeaderLiteralIndexing(pBuff, B, H2_HPACK_PREFIX_4_BITS, strHeaderName, strHeaderValue, bWithHuffman); + ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); if (eStatus != CODEC_STATUS_PART_OK) { return(eStatus); } - ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); oHttpMsg.add_adding_never_index_headers(strHeaderName); } else if (H2_HPACK_CONDITION_DYNAMIC_TABLE_SIZE_UPDATE & B) { uint32 uiTableSize = (uint32)Http2Header::DecodeInt(H2_HPACK_PREFIX_5_BITS, pBuff); + LOG4_TRACE("uiTableSize = %u", uiTableSize); if (uiTableSize > m_uiSettingsHeaderTableSize) { SetErrno(H2_ERR_COMPRESSION_ERROR); @@ -645,7 +651,6 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu " protocol using HPACK!"); return(CODEC_STATUS_ERR); } - ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); UpdateDecodingDynamicTable(uiTableSize); } else // H2_HPACK_CONDITION_LITERAL_HEADER_WITHOUT_INDEXING @@ -653,11 +658,11 @@ E_CODEC_STATUS CodecHttp2::UnpackHeader(uint32 uiHeaderBlockEndPos, CBuffer* pBu eStatus = UnpackHeaderLiteralIndexing(pBuff, B, H2_HPACK_PREFIX_4_BITS, strHeaderName, strHeaderValue, bWithHuffman); + ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); if (eStatus != CODEC_STATUS_PART_OK) { return(eStatus); } - ClassifyHeader(strHeaderName, strHeaderValue, oHttpMsg); oHttpMsg.add_adding_without_index_headers(strHeaderName); } } @@ -908,6 +913,7 @@ void CodecHttp2::ReleaseStreamWeight(TreeNode* pNode) E_CODEC_STATUS CodecHttp2::UnpackHeaderIndexed(CBuffer* pBuff, HttpMsg& oHttpMsg) { uint32 uiTableIndex = (uint32)Http2Header::DecodeInt(H2_HPACK_PREFIX_7_BITS, pBuff); + LOG4_TRACE("uiTableIndex = %u", uiTableIndex); if (uiTableIndex == 0) { SetErrno(H2_ERR_COMPRESSION_ERROR); @@ -949,6 +955,8 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF if (iPrefixMask & ucFirstByte) { uint32 uiTableIndex = (uint32)Http2Header::DecodeInt(iPrefixMask, pBuff); + LOG4_TRACE("uiTableIndex = %u", uiTableIndex); + LOG4_TRACE("Literal Header Field with Incremental Indexing — Indexed Name"); if (uiTableIndex == 0) { SetErrno(H2_ERR_COMPRESSION_ERROR); @@ -957,6 +965,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF } else if (uiTableIndex < Http2Header::sc_uiMaxStaticTableLength) { + LOG4_TRACE("uiTableIndex < Http2Header::sc_uiMaxStaticTableLength"); if (!Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) { SetErrno(H2_ERR_COMPRESSION_ERROR); @@ -999,6 +1008,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF } else // Literal Header Field with Incremental Indexing — New Name { + LOG4_TRACE("Literal Header Field with Incremental Indexing — New Name"); pBuff->SkipBytes(1); if (Http2Header::DecodeStringLiteral(pBuff, strHeaderName, bWithHuffman) && Http2Header::DecodeStringLiteral(pBuff, strHeaderValue, bWithHuffman)) @@ -1017,58 +1027,120 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF void CodecHttp2::ClassifyHeader(const std::string& strHeaderName, const std::string& strHeaderValue, HttpMsg& oHttpMsg) { - if (oHttpMsg.stream_id() & 0x01) + LOG4_TRACE("strHeaderName = %s, strHeaderValue = %s", strHeaderName.c_str(), strHeaderValue.c_str()); + if (IsClient()) { - if (oHttpMsg.body().size() > 0) + if (oHttpMsg.stream_id() & 0x01) { - auto pHeader = oHttpMsg.add_trailer_header(); - pHeader->set_name(strHeaderName); - pHeader->set_value(strHeaderValue); - } - else - { - if (strHeaderName == ":method") + if (oHttpMsg.body().size() > 0) { - if (strHeaderValue == "POST") - { - oHttpMsg.set_method(HTTP_POST); - } - else if (strHeaderValue == "GET") + auto pHeader = oHttpMsg.add_trailer_header(); + pHeader->set_name(strHeaderName); + pHeader->set_value(strHeaderValue); + } + else + { + if (strHeaderName == ":status") { - oHttpMsg.set_method(HTTP_GET); + oHttpMsg.set_status_code(StringConverter::RapidAtoi(strHeaderValue.c_str())); } else { - ;// TODO other http method + oHttpMsg.mutable_headers()->insert({strHeaderName, strHeaderValue}); } } - else if (strHeaderName == ":path") + } + else + { + if (oHttpMsg.body().size() > 0) { - oHttpMsg.set_path(strHeaderValue); + auto pHeader = oHttpMsg.add_trailer_header(); + pHeader->set_name(strHeaderName); + pHeader->set_value(strHeaderValue); } else { - oHttpMsg.mutable_headers()->insert({strHeaderName, strHeaderValue}); + if (strHeaderName == ":method") + { + if (strHeaderValue == "POST") + { + oHttpMsg.set_method(HTTP_POST); + } + else if (strHeaderValue == "GET") + { + oHttpMsg.set_method(HTTP_GET); + } + else + { + ;// TODO other http method + } + } + else if (strHeaderName == ":path") + { + oHttpMsg.set_path(strHeaderValue); + } + else + { + oHttpMsg.mutable_headers()->insert({strHeaderName, strHeaderValue}); + } } } } else { - if (oHttpMsg.body().size() > 0) + if (oHttpMsg.stream_id() & 0x01) { - auto pHeader = oHttpMsg.add_trailer_header(); - pHeader->set_name(strHeaderName); - pHeader->set_value(strHeaderValue); + if (oHttpMsg.body().size() > 0) + { + auto pHeader = oHttpMsg.add_trailer_header(); + pHeader->set_name(strHeaderName); + pHeader->set_value(strHeaderValue); + } + else + { + if (strHeaderName == ":method") + { + if (strHeaderValue == "POST") + { + oHttpMsg.set_method(HTTP_POST); + } + else if (strHeaderValue == "GET") + { + oHttpMsg.set_method(HTTP_GET); + } + else + { + ;// TODO other http method + } + } + else if (strHeaderName == ":path") + { + oHttpMsg.set_path(strHeaderValue); + } + else + { + oHttpMsg.mutable_headers()->insert({strHeaderName, strHeaderValue}); + } + } } else { - if (strHeaderName == ":status") + if (oHttpMsg.body().size() > 0) { - oHttpMsg.set_status_code(StringConverter::RapidAtoi(strHeaderValue.c_str())); + auto pHeader = oHttpMsg.add_trailer_header(); + pHeader->set_name(strHeaderName); + pHeader->set_value(strHeaderValue); } else { - oHttpMsg.mutable_headers()->insert({strHeaderName, strHeaderValue}); + if (strHeaderName == ":status") + { + oHttpMsg.set_status_code(StringConverter::RapidAtoi(strHeaderValue.c_str())); + } + else + { + oHttpMsg.mutable_headers()->insert({strHeaderName, strHeaderValue}); + } } } } diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index 09e05dcf..ff8b1bd5 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -110,39 +110,29 @@ E_CODEC_STATUS Http2Frame::Encode(CodecHttp2* pCodecH2, E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; if (oHttpMsg.headers_size() > 0 || oHttpMsg.pseudo_header_size() > 0) { - if (oHttpMsg.body().size() == 0) + eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, + H2_HEADER_PSEUDO | H2_HEADER_NORMAL, stPriority, strPadding, bEndStream, pBuff); + if (CODEC_STATUS_PART_ERR == eCodecStatus + || CODEC_STATUS_ERR == eCodecStatus) { - bEndStream = true; - eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, stPriority, strPadding, bEndStream, pBuff); return(eCodecStatus); } - else - { - eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, stPriority, strPadding, bEndStream, pBuff); - if (CODEC_STATUS_PART_ERR == eCodecStatus - || CODEC_STATUS_ERR == eCodecStatus) - { - return(eCodecStatus); - } - } } - if (oHttpMsg.body().size() > 0) + if (oHttpMsg.trailer_header_size() == 0) { - if (oHttpMsg.trailer_header_size() == 0) - { - bEndStream = true; - } - eCodecStatus = EncodeData(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, bEndStream, strPadding, pBuff); - if (CODEC_STATUS_PART_ERR == eCodecStatus - || CODEC_STATUS_ERR == eCodecStatus) - { - return(eCodecStatus); - } + bEndStream = true; + } + eCodecStatus = EncodeData(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, bEndStream, strPadding, pBuff); + if (CODEC_STATUS_PART_ERR == eCodecStatus + || CODEC_STATUS_ERR == eCodecStatus) + { + return(eCodecStatus); } if (oHttpMsg.trailer_header_size() > 0) { bEndStream = true; - eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, stPriority, strPadding, bEndStream, pBuff); + eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, + H2_HEADER_TRAILER, stPriority, strPadding, bEndStream, pBuff); } return(eCodecStatus); } @@ -174,7 +164,7 @@ E_CODEC_STATUS Http2Frame::Decode(CodecHttp2* pCodecH2, case H2_FRAME_CONTINUATION: return(DecodeContinuation(pCodecH2, stFrameHead, pBuff, oHttpMsg, pReactBuff)); default: - LOG4_ERROR("unknow frame type %d!", stFrameHead.ucType); + LOG4_ERROR("unknow frame type %d!", (int32)stFrameHead.ucType); EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint detected an" " unspecific protocol error. This error is for use when a more" " specific error code is not available.", pReactBuff); @@ -261,13 +251,17 @@ E_CODEC_STATUS Http2Frame::DecodeHeaders(CodecHttp2* pCodecH2, stPriority.uiDependency &= H2_DATA_MASK_4_BYTE_LOW_31_BIT; pBuff->Read(&stPriority.ucWeight, 1); LOG4_TRACE("SetPriority(identifier = %u, E = %u, ucWeight = %u, uiDependency = %u)", - stFrameHead.uiStreamIdentifier, stPriority.E, stPriority.ucWeight, stPriority.uiDependency); + stFrameHead.uiStreamIdentifier, (uint32)stPriority.E, (uint32)stPriority.ucWeight, stPriority.uiDependency); pCodecH2->SetPriority(stFrameHead.uiStreamIdentifier, stPriority); } E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) { eCodecStatus = pCodecH2->UnpackHeader(uiHeaderBlockEndPos, pBuff, oHttpMsg); + if (CODEC_STATUS_ERR == eCodecStatus) + { + EncodeGoaway(pCodecH2, pCodecH2->GetErrno(), "", pReactBuff); + } } else { @@ -446,6 +440,10 @@ E_CODEC_STATUS Http2Frame::DecodePushPromise(CodecHttp2* pCodecH2, if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) { eCodecStatus = pCodecH2->UnpackHeader(uiHeaderBlockEndPos, pBuff, oHttpMsg); + if (CODEC_STATUS_ERR == eCodecStatus) + { + EncodeGoaway(pCodecH2, pCodecH2->GetErrno(), "", pReactBuff); + } } else { @@ -509,6 +507,14 @@ E_CODEC_STATUS Http2Frame::DecodeGoaway(CodecHttp2* pCodecH2, pBuff->AdvanceReadIndex(stFrameHead.uiLength - 8); pCodecH2->SetGoaway(uiLastStreamId); pCodecH2->SetErrno(iErrCode); + if (strDebugData.empty()) + { + LOG4_ERROR("goaway: last stream id %u, error code %d", uiLastStreamId, iErrCode); + } + else + { + LOG4_ERROR("goaway: last stream id %u, error %d: %s", uiLastStreamId, iErrCode, strDebugData.c_str()); + } return(CODEC_STATUS_ERR); } @@ -578,6 +584,10 @@ E_CODEC_STATUS Http2Frame::DecodeContinuation(CodecHttp2* pCodecH2, oBuffer.Write(oHttpMsg.hpack_data().c_str(), oHttpMsg.hpack_data().size()); oHttpMsg.mutable_hpack_data()->clear(); E_CODEC_STATUS eCodecStatus = pCodecH2->UnpackHeader(oBuffer.GetReadIndex() + oBuffer.ReadableBytes(), &oBuffer, oHttpMsg); + if (CODEC_STATUS_ERR == eCodecStatus) + { + EncodeGoaway(pCodecH2, pCodecH2->GetErrno(), "", pReactBuff); + } return(eCodecStatus); } else @@ -602,7 +612,7 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiDataLen = oHttpMsg.body().size(); uint32 uiHadEncodeDataLen = 0; uint32 uiEncodedDataLen = 0; - while (uiHadEncodeDataLen < uiDataLen) + do { eEncodeStatus = EncodeData(pCodecH2, uiStreamId, pBodyData, uiDataLen - uiHadEncodeDataLen, bEndStream, strPadding, uiEncodedDataLen, pBuff); @@ -613,11 +623,12 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, pBodyData += uiEncodedDataLen; uiHadEncodeDataLen += uiEncodedDataLen; } + while (uiHadEncodeDataLen < uiDataLen); return(eEncodeStatus); } E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, - uint32 uiStreamId, const HttpMsg& oHttpMsg, + uint32 uiStreamId, const HttpMsg& oHttpMsg, int iHeaderType, const tagPriority& stPriority, const std::string& strPadding, bool bEndStream, CBuffer* pBuff) { @@ -633,21 +644,7 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, stFrameHead.ucType = H2_FRAME_HEADERS; stFrameHead.ucFlag = 0; stFrameHead.uiStreamIdentifier = uiStreamId; - if (oHttpMsg.body().size() > 0) - { - if (bEndStream) - { - pCodecH2->PackHeader(oHttpMsg, H2_HEADER_TRAILER, &oBuffer); - } - else - { - pCodecH2->PackHeader(oHttpMsg, H2_HEADER_PSEUDO | H2_HEADER_NORMAL, &oBuffer); - } - } - else - { - pCodecH2->PackHeader(oHttpMsg, H2_HEADER_PSEUDO | H2_HEADER_NORMAL | H2_HEADER_TRAILER, &oBuffer); - } + pCodecH2->PackHeader(oHttpMsg, iHeaderType, &oBuffer); if (strPadding.size() > 0) { uint32 uiAddtionLength = 0; @@ -734,7 +731,7 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); EncodeSetStreamState(stFrameHead); LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucFlag = 0x%X, pBuff->ReadableBytes() = %u", - stFrameHead.uiLength, stFrameHead.ucFlag, pBuff->ReadableBytes()); + stFrameHead.uiLength, (uint32)stFrameHead.ucFlag, pBuff->ReadableBytes()); return(eCodecStatus); } } diff --git a/src/codec/http2/Http2Frame.hpp b/src/codec/http2/Http2Frame.hpp index 4abdedee..64276759 100644 --- a/src/codec/http2/Http2Frame.hpp +++ b/src/codec/http2/Http2Frame.hpp @@ -113,7 +113,7 @@ class Http2Frame: public Codec uint32 uiStreamId, const HttpMsg& oHttpMsg, bool bEndStream, const std::string& strPadding, CBuffer* pBuff); E_CODEC_STATUS EncodeHeaders(CodecHttp2* pCodecH2, - uint32 uiStreamId, const HttpMsg& oHttpMsg, + uint32 uiStreamId, const HttpMsg& oHttpMsg, int iHeaderType, const tagPriority& stPriority, const std::string& strPadding, bool bEndStream, CBuffer* pBuff); E_CODEC_STATUS EncodePriority(CodecHttp2* pCodecH2, diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index 35597c0d..a8b0a586 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -101,7 +101,7 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, const tagH2FrameHead& stFrameHead, CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) { - LOG4_TRACE("m_eStreamState = %d, stFrameHead.ucType = %u", m_eStreamState, stFrameHead.ucType); + LOG4_TRACE("m_eStreamState = %d, stFrameHead.ucType = %u", (int)m_eStreamState, (uint32)stFrameHead.ucType); m_oHttpMsg.set_stream_id(m_uiStreamId); E_CODEC_STATUS eStatus = CODEC_STATUS_OK; eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); @@ -123,7 +123,7 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, "The endpoint detected an unspecific protocol error." " This error is for use when a more specific error " "code is not available.", pReactBuff); - LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", m_eStreamState, stFrameHead.ucType); + LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", (int)m_eStreamState, (uint32)stFrameHead.ucType); return(CODEC_STATUS_ERR); } if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) @@ -224,7 +224,7 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, default: m_pFrame->EncodeRstStream(pCodecH2, stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); - LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", m_eStreamState, stFrameHead.ucType); + LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", (int)m_eStreamState, (uint32)stFrameHead.ucType); return(CODEC_STATUS_PART_ERR); } break; @@ -236,14 +236,14 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, default: m_pFrame->EncodeRstStream(pCodecH2, stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); - LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", m_eStreamState, stFrameHead.ucType); + LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", (int)m_eStreamState, (uint32)stFrameHead.ucType); return(CODEC_STATUS_PART_ERR); } break; default: break; } - LOG4_TRACE("m_eStreamState = %d, stFrameHead.ucType = %u, m_bEndHeaders = %d", m_eStreamState, stFrameHead.ucType, m_bEndHeaders); + LOG4_TRACE("m_eStreamState = %d, stFrameHead.ucType = %u, m_bEndHeaders = %d", m_eStreamState, (uint32)stFrameHead.ucType, m_bEndHeaders); if (m_bEndHeaders) { if (CODEC_STATUS_OK == eStatus || CODEC_STATUS_PART_OK == eStatus) @@ -295,7 +295,7 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, m_pFrame->EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "The endpoint " "detected an unspecific protocol error. This error is for " "use when a more specific error code is not available.", pReactBuff); - LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u, m_bEndHeaders = %d", m_eStreamState, stFrameHead.ucType, m_bEndHeaders); + LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u, m_bEndHeaders = %d", m_eStreamState, (uint32)stFrameHead.ucType, m_bEndHeaders); return(CODEC_STATUS_ERR); } } @@ -304,7 +304,7 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, void Http2Stream::EncodeSetState(const tagH2FrameHead& stFrameHead) { - LOG4_TRACE("stream %u m_eStreamState = %d, stFrameHead.ucType = %u", m_uiStreamId, m_eStreamState, stFrameHead.ucType); + LOG4_TRACE("stream %u m_eStreamState = %d, stFrameHead.ucType = %u", m_uiStreamId, m_eStreamState, (uint32)stFrameHead.ucType); switch (m_eStreamState) { case H2_STREAM_IDLE: From 076d1168c32622e9c310eaee7e54117828809a6a Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 20 Jun 2021 16:30:50 +0800 Subject: [PATCH 138/176] add self channel; replace va_list with variadic template function in logger; http2 header encode bug fixed; add monitor docs. --- README_cn.md | 1 + docs/cn/monitor.md | 117 +++++++++++ src/channel/SelfChannel.cpp | 23 +++ src/channel/SelfChannel.hpp | 93 +++++++++ src/channel/SocketChannel.cpp | 5 + src/channel/SocketChannel.hpp | 15 +- src/channel/SocketChannelImpl.cpp | 52 ++--- src/codec/http2/CodecHttp2.cpp | 1 - src/codec/http2/Http2Frame.cpp | 8 +- src/logger/FileLogger.cpp | 159 +++++++-------- src/logger/FileLogger.hpp | 314 ++++++++++++++++++++++++++++-- src/logger/Logger.hpp | 1 - src/logger/NetLogger.cpp | 70 +------ src/logger/NetLogger.hpp | 269 ++++++++++++++++++++++++- 14 files changed, 914 insertions(+), 214 deletions(-) create mode 100644 docs/cn/monitor.md create mode 100644 src/channel/SelfChannel.cpp create mode 100644 src/channel/SelfChannel.hpp diff --git a/README_cn.md b/README_cn.md index 90bb9fe6..11d8fa55 100644 --- a/README_cn.md +++ b/README_cn.md @@ -66,6 +66,7 @@ * [安装部署说明](docs/cn/install.md) * [Nebula工作原理](how_nebula_works.md) * [配置说明](docs/cn/configuration.md) +* [服务监控](docs/cn/monitor.md) * [协议说明](docs/cn/protocol.md) * 开发组件说明 * [Actor组件概述](docs/cn/actor_overview.md) diff --git a/docs/cn/monitor.md b/docs/cn/monitor.md new file mode 100644 index 00000000..d7dda89d --- /dev/null +++ b/docs/cn/monitor.md @@ -0,0 +1,117 @@ +  Nebula提供了完善的数据上报和监控功能。 + +### 1. 多维度监控建议 +  运维监控可以从几方面考虑: + +* 进程和端口监控,开发者可以自己写脚本做监控,也可以直接把nebula提供的启动脚本配置到crontab里。startup.sh 会通过端口检查进程是否存在,不存在则自动拉起。实际上nebula的进程模型就已十分稳定,业务逻辑基本都在worker进程里,worker进程挂了或者僵死都会被manager重新拉起,外围的启动脚本只是多一重几乎极少用到的保障。 +* Beacon监控,通过nebcli可以获取到集群内各类型服务节点的实时状态,通过状态信息监控集群健康状态。如果信息不够,可以在增加插件获取更多数据,参考Beacon的ModuleAdmin插件可以了解增加监控信息很容易。 +* 把NebulaLogger节点启动起来,集群内的日志会按日志级别和traceid发送到这个分布式日志收集节点,在logger里增加入库或写文件功能,再写个统一的日志扫描脚本做监控,比如扫描error数量,warning数量等。 +* 用prometheus拉取监控数据,用grafana做监控展示。 + +### 2. 进程和端口监控 + +  Nebula启动脚本startup.sh有检查端口启动进程功能,配置到crontab里,如果要到进程不存在的情况会被自动拉起。 + +  如果需要将进程和端口信息监控上报到运维监控平台,可以自行编写shell脚本或python脚本完成。后续提到的Beacon监控和metrics监控可以覆盖脚本扫描进程和端口的功能。 + +  Nebula支持进程模式和线程模式,其中进程模式是推荐使用。进程模式下的Nebula服务健壮性会更好,如果不是必须使用线程模式能满足功能需求的情况下都尽可能使用进程模式。 + +* 线程模式:Manager、Worker、Loader是一个进程的不同线程,Worker和Loader线程启动时由Manager线程创建并detach。 +* 进程模式:Manager、Worker、Loader都是独立进程,Worker和Loader进程是启动时由Manager创建后独立运行,业务逻辑基本都在worker进程里,worker进程挂了或者僵死都会被manager重新拉起。 + +### 3. Beacon监控 + +  Nebula集群的节点会向Beacon上报节点信息数据,Beacon注册中心提供http接口用于查询这些信息,调用Beacon的http接口可以很方便地把这些信息用于报表监控。http接口请求和响应可以参考Beacon的ModuleAdmin实现。 + +  我们还提供了一个用python开发的命令行工具[Nebcli](https://github.com/Bwar/Nebcli)可以很方便查询节点信息。Nebcli也是通过Beacon的http接口获取的数据。Nebcli的部分命令使用参考: + +``` +show nodes # 查看在线节点信息 +show nodes ${node_type} # 查看指定节点类型的在线节点信息 +show node_report ${node_type} # 查看指定类型的节点工作状态(负载、收发数据量等) +show node_report ${node_type} ${node_identify} # 查看指定节点类型指定节点工作状态 +show node_detail ${node_type} # 查看指定类型的节点信息详情(IP地址、工作进程数等) +show node_detail ${node_type} ${node_identify} # 查看指定类型指定节点的信息详情 +show beacon # 查看Beacon节点 +``` + +### 4. 分布式日志监控 + +  把NebulaLogger节点启动起来,集群内的日志会按日志级别和traceid发送到这个分布式日志收集节点,在logger里增加入库或写文件功能,再写个统一的日志扫描脚本做监控,比如扫描error数量,warning数量等。日志上报到NebulaLogger节点是自动的,启动了NebulaLogger节点即可,无需任何其他操作。默认不上报DEBUG和TRACE日志,也不建议上报这两种日志,上报日志级别可以在服务配置文件中net_log_level配置。 + +```json +"log_levels": { "FATAL": 0, "CRITICAL": 1, "ERROR": 2, "NOTICE": 3, "WARNING": 4, "INFO": 5, "DEBUG": 6, "TRACE": 7 }, +"log_level": 7, +"net_log_level": 5, +``` + +### 5. Prometheus&Grafana监控 + +  Nebula框架层有连接数、收发包数、收发字节数统计,并且提供http接口查询,任意一个基于Nebula的服务只要启动了就可以通过接口获取到数据。这是在actor/cmd/sys_cmd/ModuleMetrics实现的。 + +框架层监控指标示例: + +``` +$ curl http://10.16.47.53:16380/status + +nebula{app="fps-user", key="connect"} 269 +nebula{app="fps-user", key="send_byte"} 70497873 +nebula{app="fps-user", key="recv_byte"} 69727450 +nebula{app="fps-user", key="send_num"} 63798 +nebula{app="fps-user", key="recv_num"} 63936 +nebula{app="fps-user", key="client"} 239 +nebula{app="fps-user", key="load"} 269 +``` + +业务层监控指标示例: + +``` +$ curl http://10.16.47.53:16380/metrics + +fps_server{app="fps-user", key="connect"} 269 +fps_server{app="fps-user", key="send_byte"} 70497873 +fps_server{app="fps-user", key="recv_byte"} 69727450 +fps_server{app="fps-user", key="send_num"} 63798 +fps_server{app="fps-user", key="recv_num"} 63936 +fps_server{app="fps-user", key="client"} 239 +fps_latency{app="fps-user", key="HMGET", value_type="p999"} 9 +fps_latency{app="fps-user", key="HMGET", value_type="p99"} 3 +fps_latency{app="fps-user", key="HMGET", value_type="p95"} 1 +fps_latency{app="fps-user", key="HMGET", value_type="p50"} 1 +fps_server{app="fps-user", key="load"} 269 +fps_req{app="fps-user", key="req", value_type="req_num"} 21478 +fps_req{app="fps-user", key="req", value_type="success"} 21305 +fps_req{app="fps-user", key="req", value_type="failed"} 0 +``` + +  框架层路径是/status,prometheus一般是从/metrics拉取数据了,为避免与业务自定义监控路径冲突,框架层用了/status。框架层的监控只要server启动好了就可以随时访问/status获取到。业务层监控可以参考actor/cmd/sys_cmd/ModuleMetrics自行实现。 + +  这些监控的实现都是通过框架内置的数据上报功能完成的,上报的pb数据描述在Nebula/proto/report.proto里: + +```proto +syntax = "proto3"; +package neb; + +message ReportRecord +{ + enum VALUE_TYPE + { + VALUE_ACC = 0; + VALUE_FIXED = 1; + } + bytes key = 1; + repeated uint64 value = 2; + string item = 3; + VALUE_TYPE value_type = 4; +} + +message Report +{ + repeated ReportRecord records = 1; +} +``` + +  Worker将Report上报到Manager(参考Worker::CheckParent()),Manager的actor/cmd/sys_cmd/CmdDataReport收到数据并存放到actor/session/sys_session/SessionDataReport中。ModuleMetrics通过获取SessionDataReport里的数据生成监控指标。 + +  SessionDataReport会定时将Report上报到Beacon节点,如果在Beacon节点的Worker加载CmdDataReport(Manager默认LoadSysCmd()加载CmdDataReport,Beacon节点的Worker需在配置文件的boot_load中配置才会加载)即可获取到集群内所有节点的指标汇总。 + diff --git a/src/channel/SelfChannel.cpp b/src/channel/SelfChannel.cpp new file mode 100644 index 00000000..3d70d024 --- /dev/null +++ b/src/channel/SelfChannel.cpp @@ -0,0 +1,23 @@ +/******************************************************************************* + * Project: Nebula + * @file SelfChannel.cpp + * @brief + * @author Bwar + * @date: 2016年8月10日 + * @note + * Modify history: + ******************************************************************************/ +#include "SelfChannel.hpp" + +namespace neb +{ + +SelfChannel::SelfChannel() +{ +} + +SelfChannel::~SelfChannel() +{ +} + +} diff --git a/src/channel/SelfChannel.hpp b/src/channel/SelfChannel.hpp new file mode 100644 index 00000000..39adccd1 --- /dev/null +++ b/src/channel/SelfChannel.hpp @@ -0,0 +1,93 @@ +/******************************************************************************* + * Project: Nebula + * @file SelfChannel.hpp + * @brief 自我通信通道 + * @author Bwar + * @date: 2021-6-19 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CHANNEL_SELFCHANNEL_HPP_ +#define SRC_CHANNEL_SELFCHANNEL_HPP_ + +#include "SocketChannel.hpp" + +namespace neb +{ + +class SelfChannel: public SocketChannel +{ +public: + SelfChannel(); + virtual ~SelfChannel(); + + virtual bool Init(E_CODEC_TYPE eCodecType, bool bIsClient = false) override + { + return(true); + } + + virtual int GetFd() const override + { + return(0); + } + + virtual bool IsClient() const override + { + return(false); + } + + virtual bool IsPipeline() const override + { + return(false); + } + + virtual const std::string& GetIdentify() const override + { + return(strEmpty); + } + + virtual const std::string& GetRemoteAddr() const override + { + return(strEmpty); + } + + virtual const std::string& GetClientData() const override + { + return(strEmpty); + } + + virtual E_CODEC_TYPE GetCodecType() const override + { + return(CODEC_DIRECT); + } + + void SetStepSeq(uint32 uiStepSeq) + { + m_uiStepSeq = uiStepSeq; + } + + uint32 GetStepSeq() const + { + return(m_uiStepSeq); + } + + bool IsResponse() const + { + return(m_bIsResponse); + } + + void SetResponse() + { + m_bIsResponse = true; + } + +private: + bool m_bIsResponse = false; + uint32 m_uiStepSeq = 0; + std::string strEmpty; +}; + +} /* namespace neb */ + +#endif /* SRC_CHANNEL_SELFCHANNEL_HPP_ */ + diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index 0d91d5f2..aa772e5f 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -20,6 +20,11 @@ namespace neb { +SocketChannel::SocketChannel() + : m_pImpl(nullptr), m_pLogger(nullptr) +{ +} + SocketChannel::SocketChannel(std::shared_ptr pLogger, int iFd, uint32 ulSeq, bool bWithSsl, ev_tstamp dKeepAlive) : m_pImpl(nullptr), m_pLogger(pLogger) { diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index de616d4f..27ca9155 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -30,6 +30,7 @@ class SocketChannel: public Channel, public std::enable_shared_from_this pLogger, int iFd, uint32 ulSeq, bool bWithSsl = false, ev_tstamp dKeepAlive = 10.0); virtual ~SocketChannel(); @@ -38,13 +39,13 @@ class SocketChannel: public Channel, public std::enable_shared_from_thisEncode(oMsgHead, oMsgBody, m_pSendBuff); break; case CHANNEL_STATUS_CLOSED: - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: @@ -310,7 +310,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& } break; default: - LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), m_ucChannelStatus); + LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); return(CODEC_STATUS_OK); } @@ -379,7 +379,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); if (m_pCodec == nullptr) { LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); @@ -416,7 +416,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq } break; case CHANNEL_STATUS_CLOSED: - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: @@ -446,7 +446,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq } break; default: - LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), m_ucChannelStatus); + LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); return(CODEC_STATUS_OK); } @@ -507,7 +507,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepSeq) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); if (m_pCodec == nullptr) { LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); @@ -525,7 +525,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepS eCodecStatus = ((CodecResp*)m_pCodec)->Encode(oRedisMsg, m_pSendBuff); break; case CHANNEL_STATUS_CLOSED: - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: @@ -541,7 +541,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepS } break; default: - LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), m_ucChannelStatus); + LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); return(CODEC_STATUS_OK); } @@ -606,7 +606,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepS E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint32 uiStepSeq) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; switch (m_ucChannelStatus) { @@ -614,7 +614,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint3 m_pSendBuff->Write(pRaw, uiRawSize); break; case CHANNEL_STATUS_CLOSED: - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: @@ -626,7 +626,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint3 eCodecStatus = CODEC_STATUS_PAUSE; break; default: - LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), m_ucChannelStatus); + LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); return(CODEC_STATUS_OK); } @@ -691,7 +691,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint3 E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); if (m_pCodec == nullptr) { LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); @@ -761,7 +761,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) } break; case CHANNEL_STATUS_CLOSED: - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: @@ -786,15 +786,15 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) m_ucChannelStatus = CHANNEL_STATUS_TRANSFER_TO_WORKER; break; default: - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[from %d to %d] may be a fault.", - m_iFd, m_uiSeq, m_ucChannelStatus, CHANNEL_STATUS_ESTABLISHED); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[from %d to %d] may be a fault.", + m_iFd, m_uiSeq, (int)m_ucChannelStatus, CHANNEL_STATUS_ESTABLISHED); m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; break; } } break; default: - LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), m_ucChannelStatus); + LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); return(CODEC_STATUS_ERR); } } @@ -816,7 +816,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); } if (m_pCodec == nullptr) @@ -933,7 +933,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); } if (m_pCodec == nullptr) @@ -1013,7 +1013,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); } int iReadLen = 0; @@ -1081,7 +1081,7 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); } LOG4_TRACE("fetch from fd %d and m_pRecvBuff->ReadableBytes() = %d", @@ -1113,7 +1113,7 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); } // 当http1.0响应包未带Content-Length头时,m_pRecvBuff可读字节数为0,以关闭连接表示数据发送完毕。 @@ -1175,7 +1175,7 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(RedisReply& oRedisReply) LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); } E_CODEC_STATUS eCodecStatus = ((CodecResp*)m_pCodec)->Decode(m_pRecvBuff, oRedisReply); @@ -1203,7 +1203,7 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(CBuffer& oRawBuff) LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) { - LOG4_WARNING("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); return(CODEC_STATUS_EOF); } if (oRawBuff.Write(m_pRecvBuff, m_pRecvBuff->ReadableBytes()) > 0) @@ -1352,7 +1352,7 @@ ev_timer* SocketChannelImpl::MutableTimerWatcher() bool SocketChannelImpl::Close() { - LOG4_TRACE("channel[%d] channel_status %d", m_iFd, m_ucChannelStatus); + LOG4_TRACE("channel[%d] channel_status %d", m_iFd, (int)m_ucChannelStatus); if (CHANNEL_STATUS_CLOSED != m_ucChannelStatus) { m_pSendBuff->Compact(1); diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index 5cbeab76..0da242be 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -191,7 +191,6 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) { LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); - LOG4_TRACE("%s", pBuff->GetRawReadBuffer()); if (m_bWantMagic && !m_bChannelIsClient) { if (pBuff->ReadableBytes() >= 24) diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index ff8b1bd5..e205d47c 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -687,7 +687,7 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); EncodePriority(stPriority, pBuff); - pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); pBuff->Write(strPadding.c_str(), strPadding.size()); EncodeSetStreamState(stFrameHead); return(eCodecStatus); @@ -728,7 +728,7 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, } EncodeFrameHeader(stFrameHead, pBuff); EncodePriority(stPriority, pBuff); - pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); EncodeSetStreamState(stFrameHead); LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucFlag = 0x%X, pBuff->ReadableBytes() = %u", stFrameHead.uiLength, (uint32)stFrameHead.ucFlag, pBuff->ReadableBytes()); @@ -880,7 +880,7 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, pBuff->Write(&unNetLength, 1); // ignore R pBuff->Write(&uiPromiseStreamId, 4); - pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); pBuff->Write(strPadding.c_str(), strPadding.size()); EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); @@ -911,7 +911,7 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, EncodeFrameHeader(stFrameHead, pBuff); // ignore R pBuff->Write(&uiPromiseStreamId, 4); - pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength); + pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); } diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp index 629986aa..a8ef3bb0 100644 --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include "FileLogger.hpp" @@ -34,96 +33,97 @@ FileLogger::FileLogger(const std::string& strLogFile, int iLogLev, #if __GNUC__ < 5 m_szTime = (char*)malloc(20); #endif - m_fp = NULL; OpenLogFile(strLogFile); WriteLog(Logger::NOTICE, __FILE__, __LINE__, __FUNCTION__, "new log instance."); } -int FileLogger::OpenLogFile(const std::string strLogFile) +FileLogger::~FileLogger() { - m_fp = fopen(strLogFile.c_str(), "a+" ); - if(NULL == m_fp) - { - std::cerr << "Can not open file: " << strLogFile << std::endl; - return -1; - } - return 0; +#if __GNUC__ < 5 + free(m_szTime); +#endif + m_fout.close(); } -int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, ...) +int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, const std::string& strContent) { if (iLev > m_iLogLevel) { return 0; } - if(NULL == m_fp) + if(!m_fout.good()) { std::cerr << "Write log error: no log file handle." << std::endl; return -1; } - va_list ap; - va_start(ap, szLogStr); - Vappend(iLev, szFileName, uiFileLine, szFunction, szLogStr, ap); - va_end(ap); - - fprintf(m_fp, "\n"); + AppendLogPattern(iLev, szFileName, uiFileLine, szFunction); + Append(strContent); + m_fout << "\n"; if (m_bAlwaysFlush) { - fflush(m_fp); + m_fout.flush(); } ++m_uiLogNum; return 0; } -int FileLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, ...) +int FileLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, const std::string& strContent) { if (iLev > m_iLogLevel) { return 0; } - if(NULL == m_fp) + if(!m_fout.good()) { std::cerr << "Write log error: no log file handle." << std::endl; return -1; } - va_list ap; - va_start(ap, szLogStr); - Vappend(strTraceId, iLev, szFileName, uiFileLine, szFunction, szLogStr, ap); - va_end(ap); - - fprintf(m_fp, "\n"); - ++m_uiLogNum; + AppendLogPattern(strTraceId, iLev, szFileName, uiFileLine, szFunction); + Append(strContent); + m_fout << "\n"; if (m_bAlwaysFlush) { - fflush(m_fp); + m_fout.flush(); } + ++m_uiLogNum; return 0; } +bool FileLogger::OpenLogFile(const std::string strLogFile) +{ + m_fout.open(strLogFile.c_str(), std::ios::app); + if(!m_fout.good()) + { + std::cerr << "Can not open file: " << strLogFile << std::endl; + return(false); + } + return(true); +} + void FileLogger::ReOpen() { - if (NULL != m_fp) + if (m_fout.good()) { - fclose(m_fp); - m_fp = NULL; + m_fout.close(); } - m_fp = fopen(m_strLogFileBase.c_str(), "a+"); + m_fout.open(m_strLogFileBase.c_str(), std::ios::app); } void FileLogger::RollOver() { - if (NULL != m_fp) + if (m_fout.good()) { - fclose(m_fp); - m_fp = NULL; + m_fout.close(); } std::stringstream ssOldestFile( std::stringstream::in | std::stringstream::out); @@ -148,29 +148,25 @@ void FileLogger::RollOver() rename(m_strLogFileBase.c_str(), strBackupFile.c_str()); } -int FileLogger::Vappend(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, va_list ap) +void FileLogger::AppendLogPattern(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction) { - long file_size = -1; - if (NULL != m_fp) - { - file_size = ftell(m_fp); - } - // if (0 == fstat(m_fd, &sb)) - // { - // file_size = sb.st_size; - // } - if (file_size < 0) - { - ReOpen(); - } - else if (file_size >= m_uiMaxFileSize) - { - RollOver(); - ReOpen(); - } - if (NULL == m_fp) + if (m_uiLogNum % 10000 == 1) { - return -1; + long lFileSize = -1; + lFileSize = m_fout.tellp(); + if (lFileSize < 0) + { + ReOpen(); + } + else if (lFileSize >= m_uiMaxFileSize) + { + RollOver(); + ReOpen(); + } + if (!m_fout.good()) + { + return; + } } auto time_now = std::chrono::system_clock::now(); auto duration_in_ms = std::chrono::duration_cast(time_now.time_since_epoch()); @@ -179,71 +175,54 @@ int FileLogger::Vappend(int iLev, const char* szFileName, unsigned int uiFileLin // There is a bug: The std::get_time and std::put_time manipulators are still missing in gcc 4.9. // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54354 #if __GNUC__ >= 5 - oss << "[" << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S") << "," + oss << "[" << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S") << "." << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" << szFileName << ":" << uiFileLine << "][" << szFunction << "] "; #else strftime(m_szTime, 20, "%Y-%m-%d %H:%M:%S", std::localtime(&t)); - oss << "[" << m_szTime << "," + oss << "[" << m_szTime << "." << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" << szFileName << ":" << uiFileLine << "][" << szFunction << "] "; #endif - fprintf(m_fp, oss.str().c_str()); - //fprintf(m_fp, "[%s] [%s,%03d] ", std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S").c_str(), duration_in_ms.count() % 1000, Logger::LogLevMsg[iLev].c_str()); - vfprintf(m_fp, szLogStr, ap); - fflush(m_fp); - return 0; + Append(oss.str()); } -int FileLogger::Vappend(const std::string& strTraceId, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, va_list ap) +void FileLogger::AppendLogPattern(const std::string& strTraceId, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction) { if (m_uiLogNum % 10000 == 1) { - long file_size = -1; - if (NULL != m_fp) - { - file_size = ftell(m_fp); - } - // if (0 == fstat(m_fd, &sb)) - // { - // file_size = sb.st_size; - // } - if (file_size < 0) + long lFileSize = -1; + lFileSize = m_fout.tellp(); + if (lFileSize < 0) { ReOpen(); } - else if (file_size >= m_uiMaxFileSize) + else if (lFileSize >= m_uiMaxFileSize) { RollOver(); ReOpen(); } - } - if (NULL == m_fp) - { - return -1; + if (!m_fout.good()) + { + return; + } } auto time_now = std::chrono::system_clock::now(); auto duration_in_ms = std::chrono::duration_cast(time_now.time_since_epoch()); auto t = std::chrono::system_clock::to_time_t(time_now); std::ostringstream oss; #if __GNUC__ >= 5 - oss << "[" << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S") << "," + oss << "[" << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S") << "." << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" << szFileName << ":" << uiFileLine << "][" << szFunction << "][" << strTraceId << "] "; #else strftime(m_szTime, 20, "%Y-%m-%d %H:%M:%S", std::localtime(&t)); - oss << "[" << m_szTime << "," + oss << "[" << m_szTime << "." << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" << szFileName << ":" << uiFileLine << "][" << szFunction << "][" << strTraceId << "] "; #endif - fprintf(m_fp, oss.str().c_str()); - //fprintf(m_fp, "[%s] [%s,%03d] ", std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S").c_str(), duration_in_ms.count() % 1000, Logger::LogLevMsg[iLev].c_str()); - vfprintf(m_fp, szLogStr, ap); - if (m_bAlwaysFlush) - { - fflush(m_fp); - } - return 0; + Append(oss.str()); } } /* namespace neb */ + diff --git a/src/logger/FileLogger.hpp b/src/logger/FileLogger.hpp index bb6ab072..fb44e2cd 100644 --- a/src/logger/FileLogger.hpp +++ b/src/logger/FileLogger.hpp @@ -12,6 +12,9 @@ #include #include +#include +#include +#include #include "Logger.hpp" namespace neb @@ -26,13 +29,7 @@ class FileLogger: public Logger unsigned int uiMaxFileSize = neb::gc_uiMaxLogFileSize, unsigned int uiMaxRollFileIndex = neb::gc_uiMaxRollLogFileIndex, bool bAlwaysFlush = true); - virtual ~FileLogger() - { -#if __GNUC__ < 5 - free(m_szTime); -#endif - fclose(m_fp); - } + virtual ~FileLogger(); static FileLogger* Instance(const std::string& strLogFile = "../log/default.log", int iLogLev = Logger::INFO, @@ -51,22 +48,41 @@ class FileLogger: public Logger m_iLogLevel = iLev; } - virtual int WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr = "info", ...); - virtual int WriteLog(const std::string& strTraceId, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr = "info", ...); + template + int WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, const char* szLogFormat, Targs&&... args); + template + int WriteLog(const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, const char* szLogFormat, Targs&&... args); + template + int WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, Targs&&... args); + template + int WriteLog(const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, Targs&&... args); + int WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, const std::string& strContent); + int WriteLog(const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, const std::string& strContent); -private: - int OpenLogFile(const std::string strLogFile); +protected: + bool OpenLogFile(const std::string strLogFile); void ReOpen(); void RollOver(); - int Vappend(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, va_list ap); - int Vappend(const std::string& strTraceId, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, va_list ap); + void AppendLogPattern(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction); + void AppendLogPattern(const std::string& strTraceId, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction); + void Append(){} + template void Append(T&& arg, Targs&&... args); + template void Append(T&& arg); + template void Append(const char* szFormat, T&& arg, Targs&&... args); + template const char* PrintfAppend(const char* szFormat, T&& arg); +private: static FileLogger* s_pInstance; - #if __GNUC__ < 5 char* m_szTime; #endif - FILE* m_fp; + std::ofstream m_fout; int m_iLogLevel; unsigned int m_uiLogNum; unsigned int m_uiMaxFileSize; // 日志文件大小 @@ -75,6 +91,272 @@ class FileLogger: public Logger std::string m_strLogFileBase; // 日志文件基本名(如 log/program_name.log) }; +template +int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, const char* szLogFormat, Targs&&... args) +{ + if (iLev > m_iLogLevel) + { + return 0; + } + + if(!m_fout.good()) + { + std::cerr << "Write log error: no log file handle." << std::endl; + return -1; + } + + AppendLogPattern(iLev, szFileName, uiFileLine, szFunction); + Append(szLogFormat, std::forward(args)...); + m_fout << "\n"; + + if (m_bAlwaysFlush) + { + m_fout.flush(); + } + ++m_uiLogNum; + + return 0; +} + +template +int FileLogger::WriteLog(const std::string& strTraceId, int iLev, + const char* szFileName, unsigned int uiFileLine, const char* szFunction, + const char* szLogFormat, Targs&&... args) +{ + if (iLev > m_iLogLevel) + { + return 0; + } + + if(!m_fout.good()) + { + std::cerr << "Write log error: no log file handle." << std::endl; + return -1; + } + + AppendLogPattern(strTraceId, iLev, szFileName, uiFileLine, szFunction); + Append(szLogFormat, std::forward(args)...); + m_fout << "\n"; + + if (m_bAlwaysFlush) + { + m_fout.flush(); + } + ++m_uiLogNum; + + return 0; +} + +template +int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, Targs&&... args) +{ + if (iLev > m_iLogLevel) + { + return 0; + } + + if(!m_fout.good()) + { + std::cerr << "Write log error: no log file handle." << std::endl; + return -1; + } + + AppendLogPattern(iLev, szFileName, uiFileLine, szFunction); + Append(std::forward(args)...); + m_fout << "\n"; + + if (m_bAlwaysFlush) + { + m_fout.flush(); + } + ++m_uiLogNum; + + return 0; +} + +template +int FileLogger::WriteLog(const std::string& strTraceId, int iLev, + const char* szFileName, unsigned int uiFileLine, const char* szFunction, + Targs&&... args) +{ + if (iLev > m_iLogLevel) + { + return 0; + } + + if(!m_fout.good()) + { + std::cerr << "Write log error: no log file handle." << std::endl; + return -1; + } + + AppendLogPattern(strTraceId, iLev, szFileName, uiFileLine, szFunction); + Append(std::forward(args)...); + m_fout << "\n"; + + if (m_bAlwaysFlush) + { + m_fout.flush(); + } + ++m_uiLogNum; + + return 0; +} + +template +void FileLogger::Append(T&& arg, Targs&&... args) +{ + Append(std::forward(arg)); + Append(std::forward(args)...); +} + +template +void FileLogger::Append(T&& arg) +{ + m_fout << arg; +} + +template +void FileLogger::Append(const char* szFormat, T&& arg, Targs&&... args) +{ + if (szFormat == nullptr) + { + return; + } + const char* pFormat = szFormat; + pFormat = PrintfAppend(pFormat, arg); + if (pFormat == nullptr) + { + Append(std::forward(args)...); + } + else + { + Append(pFormat, std::forward(args)...); + } +} + +template +const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) +{ + if (szFormat == nullptr) + { + return(nullptr); + } + const char* pPos = szFormat; + bool bPlaceholder = false; + std::string strOutStr; + int i = 0; + for (size_t i = 0; ; ++i) + { + switch (pPos[i]) + { + case '\0': + if (i > 0) + { + strOutStr.assign(szFormat, i); + m_fout << strOutStr; + } + m_fout << arg; + return(nullptr); + case '%': + if (bPlaceholder) + { + strOutStr.append("%"); + pPos = szFormat + i + 1; + m_fout << strOutStr; + return(PrintfAppend(pPos, std::forward(arg))); + } + else + { + strOutStr.assign(szFormat, i); + bPlaceholder = true; + } + break; + case 'a': + case 'A': + case 'c': + case 'C': + case 'f': + case 'p': + case 's': + case 'S': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_fout << strOutStr; + m_fout << arg; + return(pPos); + } + break; + case 'u': + case 'd': + case 'i': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_fout << strOutStr; + m_fout << std::dec << arg; + return(pPos); + } + break; + case 'x': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_fout << strOutStr; + m_fout << std::hex << std::nouppercase << arg; + return(pPos); + } + break; + case 'X': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_fout << strOutStr; + m_fout << std::hex << std::uppercase << arg; + return(pPos); + } + break; + case 'o': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_fout << strOutStr; + m_fout << std::oct << arg; + return(pPos); + } + break; + case 'e': + case 'g': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_fout << strOutStr; + m_fout << std::scientific << std::nouppercase << arg; + return(pPos); + } + break; + case 'E': + case 'G': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_fout << strOutStr; + m_fout << std::scientific << std::uppercase << arg; + return(pPos); + } + break; + default: + ; + } + } + strOutStr.assign(szFormat, i); + m_fout << strOutStr; + return(nullptr); +} + } /* namespace neb */ -#endif /* LOGGER_FILELOGGER_HPP_ */ +#endif diff --git a/src/logger/Logger.hpp b/src/logger/Logger.hpp index f0042751..5401290b 100644 --- a/src/logger/Logger.hpp +++ b/src/logger/Logger.hpp @@ -39,7 +39,6 @@ class Logger public: Logger(){}; virtual ~Logger(){}; - virtual int WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* chLogStr = "info", ...) = 0; virtual void SetLogLevel(int iLev) = 0; }; diff --git a/src/logger/NetLogger.cpp b/src/logger/NetLogger.cpp index 095e67ab..b891758e 100644 --- a/src/logger/NetLogger.cpp +++ b/src/logger/NetLogger.cpp @@ -22,10 +22,9 @@ namespace neb NetLogger::NetLogger(const std::string strLogFile, int iLogLev, unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex, unsigned int uiMaxLogLineLen, bool bAlwaysFlush, Labor* pLabor) - : m_pLogBuff(NULL), m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), m_uiMaxLogLineLen(uiMaxFileSize), + : m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), m_uiMaxLogLineLen(uiMaxFileSize), m_bEnableNetLogger(false), m_pLabor(pLabor), m_pLog(nullptr) { - m_pLogBuff = (char*)malloc(m_uiMaxLogLineLen); #if __cplusplus >= 201401L m_pLog = std::make_unique(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex, bAlwaysFlush); #else @@ -37,60 +36,9 @@ NetLogger::~NetLogger() { } -int NetLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, ...) +void NetLogger::SinkLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, const std::string& strLogContent, const std::string& strTraceId) { - if (iLev > m_iLogLevel && iLev > m_iNetLogLevel) - { - return(0); - } - va_list ap; - va_start(ap, szLogStr); - vsnprintf(m_pLogBuff, m_uiMaxLogLineLen, szLogStr, ap); - va_end(ap); - - m_pLog->WriteLog(iLev, szFileName, uiFileLine, szFunction, m_pLogBuff); - - if (iLev > m_iNetLogLevel) - { - return 0; - } - if (m_bEnableNetLogger && m_pLabor) // TODO 当前版本Manager进程的日志不会发送到LOGGER,因为Manager并未存储除Beacon之外的节点信息 - { - MsgBody oMsgBody; - TraceLog oTraceLog; - //oTraceLog.set_log_time(); - oTraceLog.set_node_type(m_pLabor->GetNodeInfo().strNodeType); - oTraceLog.set_node_identify(m_pLabor->GetNodeInfo().strNodeIdentify); - oTraceLog.set_log_level(LogLevMsg[iLev]); - oTraceLog.set_code_file_name(szFileName); - oTraceLog.set_code_file_line(uiFileLine); - oTraceLog.set_code_function(szFunction); - oTraceLog.set_log_content(m_pLogBuff); - //oMsgBody.set_data(oTraceLog.SerializeAsString()); - oTraceLog.SerializeToString(&m_strLogData); - oMsgBody.set_data(m_strLogData); - oMsgBody.mutable_req_target()->set_route(m_pLabor->GetNodeInfo().strNodeIdentify); - m_pLabor->AddNetLogMsg(oMsgBody); - } - - return 0; -} - -int NetLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const char* szLogStr, ...) -{ - va_list ap; - va_start(ap, szLogStr); - vsnprintf(m_pLogBuff, gc_uiMaxLogLineLen, szLogStr, ap); - va_end(ap); - - if (strTraceId == m_pLabor->GetNodeInfo().strNodeIdentify) - { - m_pLog->WriteLog(iLev, szFileName, uiFileLine, szFunction, m_pLogBuff); - } - else - { - m_pLog->WriteLog(strTraceId, iLev, szFileName, uiFileLine, szFunction, m_pLogBuff); - } if (m_bEnableNetLogger && m_pLabor) { MsgBody oMsgBody; @@ -101,23 +49,21 @@ int NetLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szF oTraceLog.set_code_file_name(szFileName); oTraceLog.set_code_file_line(uiFileLine); oTraceLog.set_code_function(szFunction); - oTraceLog.set_log_content(m_pLogBuff); - oMsgBody.set_trace_id(strTraceId); - //oMsgBody.set_data(oTraceLog.SerializeAsString()); - oTraceLog.SerializeToString(&m_strLogData); - oMsgBody.set_data(m_strLogData); + oTraceLog.set_log_content(strLogContent); if (strTraceId.size() > 0) { + oMsgBody.set_trace_id(strTraceId); oMsgBody.mutable_req_target()->set_route(strTraceId); } else { oMsgBody.mutable_req_target()->set_route(m_pLabor->GetNodeInfo().strNodeIdentify); } + oTraceLog.SerializeToString(&m_strLogData); + oMsgBody.set_data(m_strLogData); m_pLabor->AddNetLogMsg(oMsgBody); } - - return 0; } } /* namespace neb */ + diff --git a/src/logger/NetLogger.hpp b/src/logger/NetLogger.hpp index cad61b22..f94329be 100644 --- a/src/logger/NetLogger.hpp +++ b/src/logger/NetLogger.hpp @@ -12,6 +12,8 @@ #include #include +#include +#include "Logger.hpp" #include "FileLogger.hpp" namespace neb @@ -32,12 +34,18 @@ class NetLogger: public Logger Labor* pLabor = nullptr); virtual ~NetLogger(); - int WriteLog(int iLev, - const char* szFileName, unsigned int uiFileLine, const char* szFunction, - const char* szLogStr = "info", ...); - int WriteLog(const std::string& strTraceId, int iLev, - const char* szFileName, unsigned int uiFileLine, const char* szFunction, - const char* szLogStr = "info", ...); + template + int WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, const char* szLogFormat, Targs&&... args); + template + int WriteLog(const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, const char* szLogFormat, Targs&&... args); + template + int WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, Targs&&... args); + template + int WriteLog(const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, Targs&&... args); virtual void SetLogLevel(int iLev) { @@ -55,17 +63,264 @@ class NetLogger: public Logger m_bEnableNetLogger = bEnableNetLogger; } +protected: + void Append(){} + template void Append(T&& arg, Targs&&... args); + template void Append(T&& arg); + template void Append(const char* szFormat, T&& arg, Targs&&... args); + template const char* PrintfAppend(const char* szFormat, T&& arg); + void SinkLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, const std::string& strLogContent, const std::string& strTraceId = ""); + private: - char* m_pLogBuff; int m_iLogLevel; int m_iNetLogLevel; unsigned int m_uiMaxLogLineLen; bool m_bEnableNetLogger; + std::ostringstream m_ossLogContent; Labor* m_pLabor; std::string m_strLogData; ///< 用于提高序列化效率 std::unique_ptr m_pLog; }; +template +int NetLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, const char* szLogFormat, Targs&&... args) +{ + if (iLev > m_iLogLevel && iLev > m_iNetLogLevel) + { + return(0); + } + m_ossLogContent.str(""); + Append(szLogFormat, std::forward(args)...); + + m_pLog->WriteLog(iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); + + if (iLev > m_iNetLogLevel) + { + return 0; + } + SinkLog(iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); + return 0; +} + +template +int NetLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, const char* szLogFormat, Targs&&... args) +{ + if (iLev > m_iLogLevel && iLev > m_iNetLogLevel) + { + return(0); + } + m_ossLogContent.str(""); + Append(szLogFormat, std::forward(args)...); + + m_pLog->WriteLog(strTraceId, iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); + + if (iLev > m_iNetLogLevel) + { + return 0; + } + SinkLog(iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str(), strTraceId); + + return 0; +} + +template +int NetLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, Targs&&... args) +{ + if (iLev > m_iLogLevel && iLev > m_iNetLogLevel) + { + return(0); + } + m_ossLogContent.str(""); + Append(std::forward(args)...); + + m_pLog->WriteLog(iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); + + if (iLev > m_iNetLogLevel) + { + return 0; + } + SinkLog(iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); + return 0; +} + +template +int NetLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, Targs&&... args) +{ + if (iLev > m_iLogLevel && iLev > m_iNetLogLevel) + { + return(0); + } + m_ossLogContent.str(""); + Append(std::forward(args)...); + + m_pLog->WriteLog(strTraceId, iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); + + if (iLev > m_iNetLogLevel) + { + return 0; + } + SinkLog(iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str(), strTraceId); + + return 0; +} + +template +void NetLogger::Append(T&& arg, Targs&&... args) +{ + Append(std::forward(arg)); + Append(std::forward(args)...); +} + +template +void NetLogger::Append(T&& arg) +{ + m_ossLogContent << arg; +} + +template +void NetLogger::Append(const char* szFormat, T&& arg, Targs&&... args) +{ + if (szFormat == nullptr) + { + return; + } + const char* pFormat = szFormat; + pFormat = PrintfAppend(pFormat, std::forward(arg)); + if (pFormat == nullptr) + { + Append(std::forward(args)...); + } + else + { + Append(pFormat, std::forward(args)...); + } +} + +template +const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) +{ + if (szFormat == nullptr) + { + return(nullptr); + } + const char* pPos = szFormat; + bool bPlaceholder = false; + std::string strOutStr; + int i = 0; + for (size_t i = 0; ; ++i) + { + switch (pPos[i]) + { + case '\0': + if (i > 0) + { + strOutStr.assign(szFormat, i); + m_ossLogContent << strOutStr; + } + m_ossLogContent << arg; + return(nullptr); + case '%': + if (bPlaceholder) + { + strOutStr.append("%"); + pPos = szFormat + i + 1; + m_ossLogContent << strOutStr; + return(PrintfAppend(pPos, std::forward(arg))); + } + else + { + strOutStr.assign(szFormat, i); + bPlaceholder = true; + } + break; + case 'a': + case 'A': + case 'c': + case 'C': + case 'f': + case 'p': + case 's': + case 'S': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_ossLogContent << strOutStr; + m_ossLogContent << arg; + return(pPos); + } + break; + case 'u': + case 'd': + case 'i': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_ossLogContent << strOutStr; + m_ossLogContent << std::dec << arg; + return(pPos); + } + break; + case 'x': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_ossLogContent << strOutStr; + m_ossLogContent << std::hex << std::nouppercase << arg; + return(pPos); + } + break; + case 'X': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_ossLogContent << strOutStr; + m_ossLogContent << std::hex << std::uppercase << arg; + return(pPos); + } + break; + case 'o': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_ossLogContent << strOutStr; + m_ossLogContent << std::oct << arg; + return(pPos); + } + break; + case 'e': + case 'g': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_ossLogContent << strOutStr; + m_ossLogContent << std::scientific << std::nouppercase << arg; + return(pPos); + } + break; + case 'E': + case 'G': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_ossLogContent << strOutStr; + m_ossLogContent << std::scientific << std::uppercase << arg; + return(pPos); + } + break; + default: + ; + } + } + strOutStr.assign(szFormat, i); + m_ossLogContent << strOutStr; + return(nullptr); +} + } /* namespace neb */ #endif /* LOGGER_NETLOGGER_HPP_ */ From 1d20f082be404f46fb731832fc95c3d28e234b17 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 20 Jun 2021 17:21:06 +0800 Subject: [PATCH 139/176] add self channel communication --- src/actor/Actor.cpp | 20 ++++ src/actor/Actor.hpp | 8 ++ src/actor/ActorBuilder.cpp | 234 +++++++++++++++++++++++++++++++++++++ src/actor/ActorBuilder.hpp | 4 + src/codec/http2/H2Comm.hpp | 2 +- src/ios/Dispatcher.cpp | 71 +++++++++++ src/ios/Dispatcher.hpp | 25 ++++ 7 files changed, 363 insertions(+), 1 deletion(-) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index b52f53cb..545461e9 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -278,6 +278,26 @@ bool Actor::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) return(m_pLabor->GetDispatcher()->SendDataReport(iCmd, uiSeq, oMsgBody)); } +bool Actor::SendToSelf(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +{ + return(m_pLabor->GetDispatcher()->SendToSelf(iCmd, uiSeq, oMsgBody, GetSequence())); +} + +bool Actor::SendToSelf(const HttpMsg& oHttpMsg) +{ + return(m_pLabor->GetDispatcher()->SendToSelf(oHttpMsg, GetSequence())); +} + +bool Actor::SendToSelf(const RedisMsg& oRedisMsg) +{ + return(m_pLabor->GetDispatcher()->SendToSelf(oRedisMsg, GetSequence())); +} + +bool Actor::SendToSelf(const char* pRaw, uint32 uiRawSize) +{ + return(m_pLabor->GetDispatcher()->SendToSelf(pRaw, uiRawSize, GetSequence())); +} + std::shared_ptr Actor::GetLastActivityChannel() { return(m_pLabor->GetDispatcher()->GetLastActivityChannel()); diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index f7645c49..a31b8775 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -283,6 +283,14 @@ class Actor: public std::enable_shared_from_this virtual bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + /** + * @brief 发送请求到当前worker + */ + bool SendToSelf(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + bool SendToSelf(const HttpMsg& oHttpMsg); + bool SendToSelf(const RedisMsg& oRedisMsg); + bool SendToSelf(const char* pRaw, uint32 uiRawSize); + std::shared_ptr GetLastActivityChannel(); /** diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 2529fd60..304f35ec 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -27,6 +27,7 @@ #include "actor/session/sys_session/SessionLogger.hpp" #include "ios/Dispatcher.hpp" #include "channel/SocketChannel.hpp" +#include "channel/SelfChannel.hpp" namespace neb { @@ -553,6 +554,239 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const CBuf } } +bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) +{ + if (gc_uiCmdReq & oMsgHead.cmd()) // 新请求 + { + auto cmd_iter = m_mapCmd.find(gc_uiCmdBit & oMsgHead.cmd()); + if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) + { + if (oMsgBody.trace_id().length() > 10) + { + cmd_iter->second->SetTraceId(oMsgBody.trace_id()); + } + else + { + std::ostringstream oss; + oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); + cmd_iter->second->SetTraceId(oss.str()); + } + cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); + } + else // 没有对应的cmd,是需由接入层转发的请求 + { + LOG4_ERROR("no handler to dispose cmd %u!", oMsgHead.cmd()); + return(false); + } + } + else // 回调 + { + auto step_iter = m_mapCallbackStep.find(oMsgHead.seq()); + if (step_iter != m_mapCallbackStep.end()) // 步骤回调 + { + LOG4_TRACE("receive message, cmd = %d", + oMsgHead.cmd()); + if (step_iter->second != nullptr) + { + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + LOG4_TRACE("cmd %u, seq %u, step_seq %u, active_time %lf", + oMsgHead.cmd(), oMsgHead.seq(), step_iter->second->GetSequence(), + step_iter->second->GetActiveTime()); + eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); + if (CMD_STATUS_RUNNING != eResult) + { + uint32 uiChainId = step_iter->second->GetChainId(); + RemoveStep(step_iter->second); + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } + } + } + } + } + ExecAssemblyLine(pChannel, oMsgHead, oMsgBody); + } + else + { + snprintf(m_pErrBuff, gc_iErrBuffLen, "no callback or the callback for seq %u had been timeout!", oMsgHead.seq()); + LOG4_WARNING(m_pErrBuff); + return(false); + } + } + return(true); +} + +bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +{ + if (HTTP_REQUEST == oHttpMsg.type()) // 新请求 + { + LOG4_DEBUG("oInHttpMsg.type() = %d, oInHttpMsg.path() = %s", + oHttpMsg.type(), oHttpMsg.path().c_str()); + auto module_iter = m_mapModule.find(oHttpMsg.path()); + if (module_iter == m_mapModule.end()) + { + LOG4_ERROR("no module to dispose %s!", oHttpMsg.path().c_str()); + } + else + { + module_iter->second->AnyMessage(pChannel, oHttpMsg); + } + } + else + { + auto pSelfChannel = std::dynamic_pointer_cast(pChannel); + auto http_step_iter = m_mapCallbackStep.find(pSelfChannel->GetStepSeq()); + if (http_step_iter == m_mapCallbackStep.end()) + { + LOG4_TRACE("no callback for http response from %s!", oHttpMsg.url().c_str()); + } + else + { + E_CMD_STATUS eResult; + http_step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = http_step_iter->second->Callback(pChannel, oHttpMsg); + if (CMD_STATUS_RUNNING != eResult) + { + uint32 uiChainId = http_step_iter->second->GetChainId(); + RemoveStep(http_step_iter->second); + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } + } + } + } + } + } + return(true); +} + +bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const RedisMsg& oRedisMsg) +{ + auto pSelfChannel = std::dynamic_pointer_cast(pChannel); + if (pSelfChannel->IsResponse()) + { + auto cmd_iter = m_mapCmd.find(CMD_REQ_REDIS_PROXY); + if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) + { + auto pRedisCmd = std::dynamic_pointer_cast(cmd_iter->second); + if (pRedisCmd == nullptr) + { + LOG4_ERROR("cmd %d is not a RedisCmd instance!", CMD_REQ_REDIS_PROXY); + return(false); + } + return(pRedisCmd->AnyMessage(pChannel, oRedisMsg)); + } + LOG4_ERROR("no instance of RedisCmd or derived class of RedisCmd found for cmd %d", CMD_REQ_REDIS_PROXY); + return(false); + } + else + { + auto step_iter = m_mapCallbackStep.find(pSelfChannel->GetStepSeq()); + if (step_iter == m_mapCallbackStep.end()) + { + LOG4_TRACE("no callback for redis reply from %s!", pChannel->GetIdentify().c_str()); + return(false); + } + else + { + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = step_iter->second->Callback(pChannel, oRedisMsg); + if (CMD_STATUS_RUNNING != eResult) + { + uint32 uiChainId = step_iter->second->GetChainId(); + RemoveStep(step_iter->second); + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } + } + } + } + return(eResult); + } + } +} + +bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const CBuffer& oBuffer) +{ + auto pSelfChannel = std::dynamic_pointer_cast(pChannel); + if (pSelfChannel->IsResponse()) + { + auto cmd_iter = m_mapCmd.find(CMD_REQ_RAW_DATA); + if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) + { + auto pRawCmd = std::dynamic_pointer_cast(cmd_iter->second); + if (pRawCmd == nullptr) + { + LOG4_ERROR("cmd %d is not a RawCmd instance!", CMD_REQ_RAW_DATA); + return(false); + } + return(pRawCmd->AnyMessage(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes())); + } + LOG4_ERROR("no instance of RawCmd or derived class of RawCmd found for cmd %d", CMD_REQ_RAW_DATA); + return(false); + } + else + { + auto step_iter = m_mapCallbackStep.find(pSelfChannel->GetStepSeq()); + if (step_iter == m_mapCallbackStep.end()) + { + LOG4_TRACE("no callback for raw data reply from %s!", pChannel->GetIdentify().c_str()); + return(false); + } + else + { + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = step_iter->second->Callback(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes()); + if (CMD_STATUS_RUNNING != eResult) + { + uint32 uiChainId = step_iter->second->GetChainId(); + RemoveStep(step_iter->second); + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) + { + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) + { + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } + } + } + } + return(eResult); + } + } +} + bool ActorBuilder::OnError(std::shared_ptr pChannel, uint32 uiStepSeq, int iErrno, const std::string& strErrMsg) { auto step_iter = m_mapCallbackStep.find(uiStepSeq); diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index d4bd5f8f..93cbaf37 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -98,6 +98,10 @@ class ActorBuilder bool OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK); bool OnMessage(std::shared_ptr pChannel, const RedisMsg& oRedisMsg, uint32 uiFinalStepSeq = 0); bool OnMessage(std::shared_ptr pChannel, const CBuffer& oBuffer); + bool OnSelfMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); + bool OnSelfMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + bool OnSelfMessage(std::shared_ptr pChannel, const RedisMsg& oRedisMsg); + bool OnSelfMessage(std::shared_ptr pChannel, const CBuffer& oBuffer); bool OnError(std::shared_ptr pChannel, uint32 uiStepSeq, int iErrno, const std::string& strErrMsg); public: diff --git a/src/codec/http2/H2Comm.hpp b/src/codec/http2/H2Comm.hpp index f3e417b8..82ff94fe 100644 --- a/src/codec/http2/H2Comm.hpp +++ b/src/codec/http2/H2Comm.hpp @@ -78,7 +78,7 @@ struct tagH2FrameHead struct tagPriority { - char E = 0; + uint8 E = 0; uint8 ucWeight = 0; uint32 uiDependency = 0; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 5a9c99cf..478b16f6 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -1685,4 +1685,75 @@ void Dispatcher::EvBreak() ev_break (m_loop, EVBREAK_ALL); } +bool Dispatcher::Deliver(std::shared_ptr pSelfChannel) +{ + return(false); +} + +bool Dispatcher::Deliver(std::shared_ptr pSelfChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, uint32 uiStepSeq) +{ + if (uiStepSeq > 0) + { + pSelfChannel->SetStepSeq(uiStepSeq); + } + else + { + pSelfChannel->SetResponse(); + } + auto pChannel = std::dynamic_pointer_cast(pSelfChannel); + m_pLastActivityChannel = pChannel; + MsgHead oMsgHead; + oMsgHead.set_cmd(iCmd); + oMsgHead.set_seq(uiSeq); + oMsgHead.set_len(oMsgBody.ByteSize()); + return(m_pLabor->GetActorBuilder()->OnSelfMessage(pChannel, oMsgHead, oMsgBody)); +} + +bool Dispatcher::Deliver(std::shared_ptr pSelfChannel, const HttpMsg& oHttpMsg, uint32 uiStepSeq) +{ + if (uiStepSeq > 0) + { + pSelfChannel->SetStepSeq(uiStepSeq); + } + else + { + pSelfChannel->SetResponse(); + } + auto pChannel = std::dynamic_pointer_cast(pSelfChannel); + m_pLastActivityChannel = pChannel; + return(m_pLabor->GetActorBuilder()->OnSelfMessage(pChannel, oHttpMsg)); +} + +bool Dispatcher::Deliver(std::shared_ptr pSelfChannel, const RedisMsg& oRedisMsg, uint32 uiStepSeq) +{ + if (uiStepSeq > 0) + { + pSelfChannel->SetStepSeq(uiStepSeq); + } + else + { + pSelfChannel->SetResponse(); + } + auto pChannel = std::dynamic_pointer_cast(pSelfChannel); + m_pLastActivityChannel = pChannel; + return(m_pLabor->GetActorBuilder()->OnSelfMessage(pChannel, oRedisMsg)); +} + +bool Dispatcher::Deliver(std::shared_ptr pSelfChannel, const char* pRaw, uint32 uiRawSize, uint32 uiStepSeq) +{ + if (uiStepSeq > 0) + { + pSelfChannel->SetStepSeq(uiStepSeq); + } + else + { + pSelfChannel->SetResponse(); + } + auto pChannel = std::dynamic_pointer_cast(pSelfChannel); + m_pLastActivityChannel = pChannel; + CBuffer oBuffer; + oBuffer.Write(pRaw, uiRawSize); + return(m_pLabor->GetActorBuilder()->OnSelfMessage(pChannel, oBuffer)); +} + } diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 3c9374fc..f686f146 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -49,6 +49,7 @@ extern "C" { #include "pb/msg.pb.h" #include "labor/Labor.hpp" #include "channel/SocketChannel.hpp" +#include "channel/SelfChannel.hpp" #include "logger/NetLogger.hpp" #include "Nodes.hpp" @@ -115,6 +116,8 @@ class Dispatcher public: bool AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout = 1.0); template + bool SendToSelf(Targs&&... args); + template bool SendTo(std::shared_ptr pChannel, Targs&&... args); template bool SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); @@ -189,6 +192,11 @@ class Dispatcher bool AcceptServerConn(int iFd); void CheckFailedNode(); void EvBreak(); + bool Deliver(std::shared_ptr pSelfChannel); + bool Deliver(std::shared_ptr pSelfChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, uint32 uiStepSeq = 0); + bool Deliver(std::shared_ptr pSelfChannel, const HttpMsg& oHttpMsg, uint32 uiStepSeq = 0); + bool Deliver(std::shared_ptr pSelfChannel, const RedisMsg& oRedisMsg, uint32 uiStepSeq = 0); + bool Deliver(std::shared_ptr pSelfChannel, const char* pRaw, uint32 uiRawSize, uint32 uiStepSeq = 0); private: char* m_pErrBuff; @@ -222,9 +230,26 @@ void Dispatcher::Logger(int iLogLevel, const char* szFileName, unsigned int uiFi m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } +template +bool Dispatcher::SendToSelf(Targs&&... args) +{ + auto pSelfChannel = std::make_shared(); + return(Deliver(pSelfChannel, std::forward(args)...)); +} + template bool Dispatcher::SendTo(std::shared_ptr pChannel, Targs&&... args) { + if (pChannel->GetCodecType() == CODEC_DIRECT) + { + auto pSelfChannel = std::dynamic_pointer_cast(pChannel); + if (pSelfChannel == nullptr) + { + LOG4_ERROR("channel is not a self channel."); + return(false); + } + return(Deliver(pSelfChannel, std::forward(args)...)); + } E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(std::forward(args)...); m_pLabor->IoStatAddSendNum(pChannel->GetFd()); m_pLastActivityChannel = pChannel; From bc6b4722f5c6b652ea563d7d78f96ee6848b6146 Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 21 Jun 2021 22:19:24 +0800 Subject: [PATCH 140/176] add connection protection --- src/actor/step/sys_step/StepIoTimeout.hpp | 2 +- src/codec/http2/Http2Frame.cpp | 10 +++------- src/ios/Dispatcher.cpp | 11 ++++++++++- src/labor/Manager.cpp | 1 + src/labor/NodeInfo.hpp | 3 ++- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/actor/step/sys_step/StepIoTimeout.hpp b/src/actor/step/sys_step/StepIoTimeout.hpp index 5fd4d607..d5f3693a 100644 --- a/src/actor/step/sys_step/StepIoTimeout.hpp +++ b/src/actor/step/sys_step/StepIoTimeout.hpp @@ -24,7 +24,7 @@ namespace neb * 端发起的心跳机制,心跳时间间隔就是IO超时时间。 */ class StepIoTimeout: public PbStep, - public DynamicCreator >, + public DynamicCreator& >, public ActorSys { public: diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index e205d47c..e1c8b2f8 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -130,9 +130,10 @@ E_CODEC_STATUS Http2Frame::Encode(CodecHttp2* pCodecH2, } if (oHttpMsg.trailer_header_size() > 0) { + tagPriority stEmptyPriority; bEndStream = true; eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, - H2_HEADER_TRAILER, stPriority, strPadding, bEndStream, pBuff); + H2_HEADER_TRAILER, stEmptyPriority, "", bEndStream, pBuff); } return(eCodecStatus); } @@ -740,11 +741,6 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, E_CODEC_STATUS Http2Frame::EncodePriority(CodecHttp2* pCodecH2, uint32 uiStreamId, const tagPriority& stPriority, CBuffer* pBuff) { - if (stPriority.uiDependency == 0x0) - { - pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); - return(CODEC_STATUS_PART_ERR); - } tagH2FrameHead stFrameHead; stFrameHead.cR = 0; stFrameHead.ucType = H2_FRAME_HEADERS; @@ -1016,7 +1012,7 @@ E_CODEC_STATUS Http2Frame::EncodeContinuation(CodecHttp2* pCodecH2, void Http2Frame::EncodePriority(const tagPriority& stPriority, CBuffer* pBuff) { - if (stPriority.uiDependency > 0) + if (stPriority.ucWeight > 0) { uint32 uiPriority = stPriority.E; uiPriority <<= 31; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 478b16f6..d36ec202 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -1300,7 +1300,16 @@ std::shared_ptr Dispatcher::CreateSocketChannel(int iFd, E_CODEC_ std::shared_ptr pChannel = nullptr; try { - pChannel = std::make_shared(m_pLogger, iFd, m_pLabor->GetSequence(), bWithSsl); + if (m_pLabor->GetNodeInfo().dConnectionProtection > 0) + { + pChannel = std::make_shared(m_pLogger, iFd, + m_pLabor->GetSequence(), bWithSsl, m_pLabor->GetNodeInfo().dConnectionProtection); + } + else + { + pChannel = std::make_shared(m_pLogger, iFd, + m_pLabor->GetSequence(), bWithSsl, m_pLabor->GetNodeInfo().dIoTimeout); + } } catch(std::bad_alloc& e) { diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 9ea4382c..e5c5d8a4 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -326,6 +326,7 @@ bool Manager::GetConf() if (m_oLastConf.ToString() != m_oCurrentConf.ToString()) { + m_oCurrentConf.Get("connection_protection", m_stNodeInfo.dConnectionProtection); m_oCurrentConf.Get("io_timeout", m_stNodeInfo.dIoTimeout); m_oCurrentConf.Get("data_report", m_stNodeInfo.dDataReportInterval); if (m_oLastConf.ToString().length() == 0) diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index dfa1178f..ffdca973 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -34,7 +34,8 @@ struct NodeInfo bool bThreadMode = 0; ///< 是否线程模型 bool bIsAccess = false; ///< 是否接入Server bool bChannelVerify = false; ///< 是否需要连接验证 - ev_tstamp dIoTimeout = 10.0; ///< IO(连接)超时配置 + ev_tstamp dConnectionProtection = 0.0; ///< >0时为连接保护时间,新建连接会设置成这个时间,接收到第一个数据包之后改设成dIoTimeout + ev_tstamp dIoTimeout = 60.0; ///< IO(连接)超时配置 ev_tstamp dDataReportInterval = 60.0; ///< 统计数据上报时间间隔 ev_tstamp dMsgStatInterval = 60.0; ///< 客户端连接发送数据包统计时间间隔 ev_tstamp dAddrStatInterval = 60.0; ///< IP地址数据统计时间间隔 From b117dfe8af6a24717c0789762a0467cc564acb5d Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 26 Jun 2021 17:37:04 +0800 Subject: [PATCH 141/176] v1.6.0 release --- README.md | 9 +++ README_cn.md | 11 +++- conf/nebula.json | 4 +- docs/cn/actor.md | 8 +-- docs/cn/actor_overview.md | 10 +-- docs/cn/cmd_and_module.md | 4 +- docs/cn/configuration.md | 5 ++ docs/image/Actor.png | Bin 165681 -> 181130 bytes proto/http.proto | 1 - src/actor/ActorBuilder.cpp | 58 ++++++++--------- src/channel/SocketChannel.cpp | 5 +- src/channel/SocketChannelImpl.cpp | 57 ++++++++++++++--- src/codec/CodecHttp.cpp | 102 ++++++++++-------------------- src/codec/http2/CodecHttp2.cpp | 13 +++- src/codec/http2/Http2Frame.cpp | 23 +++++++ src/codec/http2/Http2Frame.hpp | 3 + src/ios/Dispatcher.cpp | 1 - src/logger/FileLogger.hpp | 10 ++- src/logger/NetLogger.hpp | 10 ++- 19 files changed, 209 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index 62b6cb14..4623cfef 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,15 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v1.6.0 + - add SelfChannel within the Worker, improve the actor model + - add service status monitoring and metrics for prometheus + - add Manager to Manager communication feature + - add initial connection timeout configuration + - replace va_list with variadic templates in logger + - thread id optimization + - chain optimization + - http2 dynamic table and stream priority bug fix #### v1.5.0 - add native http2 server and client - add native grpc server and client diff --git a/README_cn.md b/README_cn.md index 11d8fa55..30c9589f 100644 --- a/README_cn.md +++ b/README_cn.md @@ -109,10 +109,19 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 开发任务 - 完成开发指南 - - 原生支持dubbo、grpc等协议 + - 原生支持dubbo、mqtt等协议 ## 版本历史 +#### v1.6.0 + - 增加用于Worker内通信的SelfChannel,完善actor模型 + - 增加服务状态监控和监控指标获取插件 + - 增加节点之间Manager到Manager通信功能 + - 增加初始连接超时配置 + - 日志组件采用可变模板参数替代va_list,解决日志组件遇到%容易coredump问题 + - 线程ID优化 + - Chain组件优化 + - http2动态表、流优先级bug修复 #### v1.5.0 - 增加原生http2服务端和客户端支持 - 增加原生grpc服务端和客户端支持 diff --git a/conf/nebula.json b/conf/nebula.json index 6efdbd8c..98c35ba6 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -5,7 +5,7 @@ "access_host": "192.168.18.81", "//access_port": "对系统外提供服务监听的端口", "access_port": 9988, - "//access_codec": "接入端编解码器,目前支持CODEC_PRIVATE(4),CODEC_HTTP(3),CODEC_PROTOBUF(2)", + "//access_codec": "接入端编解码器,见codec/Codec.hpp中E_CODEC_TYPE枚举定义" "access_codec": 4, "//need_channel_verify":"是否需要连接验证,如设置为true,第一个消息必须是验证消息,未经验证的连接收到第二个消息会被立即关闭", "need_channel_verify":false, @@ -52,6 +52,8 @@ "addr_permit": { "stat_interval": 60.0, "permit_num": 1000000000 }, "uin_permit": { "stat_interval": 60.0, "permit_num": 600000000 } }, + "//connection_protection": "连接保护时间(单位:秒),当值>0时新建连接设置为这个时间,接收到第一个数据包后改设为io_timeout", + "connection_protection": 0.0, "//io_timeout": "网络IO(连接)超时设置(单位:秒)小数点后面至少保留一位", "io_timeout": 300.0, "//step_timeout": "步骤超时设置(单位:秒)小数点后面至少保留一位", diff --git a/docs/cn/actor.md b/docs/cn/actor.md index bbe4bdec..d0de0f61 100644 --- a/docs/cn/actor.md +++ b/docs/cn/actor.md @@ -29,8 +29,8 @@ public: ACT_PB_STEP = 6, ///< Step步骤对象,处理pb请求或响应 ACT_HTTP_STEP = 7, ///< Step步骤对象,处理http请求或响应 ACT_REDIS_STEP = 8, ///< Step步骤对象,处理redis请求或响应 - ACT_MODEL = 9, ///< Model模型对象,Model(IO无关)与Step(异步IO相关)共同构成功能链 - ACT_CHAIN = 10, ///< Chain链对象,用于将Model和Step组合成功能链 + ACT_OPERATOR = 9, ///< Operator算子对象,Operator(IO无关)与Step(异步IO相关)共同构成功能链 + ACT_CHAIN = 10, ///< Chain链对象,用于将Operator和Step组合成功能链 }; public: @@ -76,7 +76,7 @@ protected: std::shared_ptr GetSession(uint32 uiSessionId); std::shared_ptr GetSession(const std::string& strSessionId); bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); - std::shared_ptr GetModel(const std::string& strModelName); + std::shared_ptr GetOperator(const std::string& strOperatorName); std::shared_ptr GetContext(); void SetContext(std::shared_ptr pContext); void AddAssemblyLine(std::shared_ptr pSession); @@ -141,4 +141,4 @@ private:   GetNodeId()/GetNodeType()/GetCustomConf()等用于获取当前节点、当前进程的一些重要信息,获取用户自定义配置信息。这些函数都非常有用,不用刻意去记,当需要用的时候再到Actor类里找,通常都有,毕竟Nebula是个久经多种业务生产环境使用的框架,功能比较全。再小提示一下,这些函数使得snowflake算法分布式生成唯一ID非常方便。 -   \ No newline at end of file +   diff --git a/docs/cn/actor_overview.md b/docs/cn/actor_overview.md index 38b2f1ae..cd852960 100644 --- a/docs/cn/actor_overview.md +++ b/docs/cn/actor_overview.md @@ -22,10 +22,10 @@   所有业务逻辑均抽象成事件和事件处理,Actor为事件处理者。Actor是所有Actor派生类与Nebula框架的媒介,在框架应用和业务逻辑开发中有至关重要的作用,这在后续会详细讲解,暂且先理解为Actor是所有功能组件的基类。  -  Actor为事件(消息)处理者,所有业务逻辑均抽象成事件和事件处理,Actor分为Cmd、Module、Step、Session、Context、Model、Chain七种不同类型。 业务逻辑代码均通过从除Chain之外的六种不同类型事件处理者派生子类来实现,专注于业务逻辑实现,而无须关注业务逻辑之外的东西。Nebula的前身Starship只有Cmd、Module、Step、Session四种组件,却已成功应用于一个生产环境的IM,这些组件的理解和应用都很容易,Nebula虽然增加了几种组件,但只带来了方便而没增加复杂性。Actor类自身提供各组件与框架交互的成员函数,通过这些成员函数即可完成所有业务逻辑所需的功能,换句话说,基于Nebula框架的业务开发只需了解Actor类的30余成员函数的功能是什么就足够,门槛低到难以想象。 +  Actor为事件(消息)处理者,所有业务逻辑均抽象成事件和事件处理,Actor分为Cmd、Module、Step、Session、Context、Operator、Chain七种不同类型。 业务逻辑代码均通过从除Chain之外的六种不同类型事件处理者派生子类来实现,专注于业务逻辑实现,而无须关注业务逻辑之外的东西。Nebula的前身Starship只有Cmd、Module、Step、Session四种组件,却已成功应用于一个生产环境的IM,这些组件的理解和应用都很容易,Nebula虽然增加了几种组件,但只带来了方便而没增加复杂性。Actor类自身提供各组件与框架交互的成员函数,通过这些成员函数即可完成所有业务逻辑所需的功能,换句话说,基于Nebula框架的业务开发只需了解Actor类的30余成员函数的功能是什么就足够,门槛低到难以想象。  -  Cmd为业务逻辑入口,对应通信协议MsgHead中的cmd命令字,用于选择对应处理逻辑类。一些知名框架把Handler做成一个处理链,一个event过来顺着这个处理链顺序传递,相应的Handler处理完后调用下一个Handler处理,与event无关的Handler收到event时什么都不做就直接调用下一个Handler处理。与之不同的是,Nebula把所有Cmd(Handler)放在一个HashMap里,当收到Msg(event)时,根据cmd命令字(event type)直接交给对应的Cmd类处理,每个事件只会有一个处理者,如果需要多个处理者那就是对应Cmd内部的事情,可以通过Step、Model等组成一条处理链(后续章节会有说明)。与一些框架把编解码器作为Handler的一种加到Handler链里不同,Nebula的Codec编解码器不属于Cmd也不属于Actor,因此也不属于处理链的一部分。Codec会在编解码器章节说明,不在Actor组件这里说明。 +  Cmd为业务逻辑入口,对应通信协议MsgHead中的cmd命令字,用于选择对应处理逻辑类。一些知名框架把Handler做成一个处理链,一个event过来顺着这个处理链顺序传递,相应的Handler处理完后调用下一个Handler处理,与event无关的Handler收到event时什么都不做就直接调用下一个Handler处理。与之不同的是,Nebula把所有Cmd(Handler)放在一个HashMap里,当收到Msg(event)时,根据cmd命令字(event type)直接交给对应的Cmd类处理,每个事件只会有一个处理者,如果需要多个处理者那就是对应Cmd内部的事情,可以通过Step、Operator等组成一条处理链(后续章节会有说明)。与一些框架把编解码器作为Handler的一种加到Handler链里不同,Nebula的Codec编解码器不属于Cmd也不属于Actor,因此也不属于处理链的一部分。Codec会在编解码器章节说明,不在Actor组件这里说明。    Module跟Cmd的作用完全相同,差异只在于Cmd与MsgHead里的cmd命令字对应,Module与HttpMsg里的url_path对应。理解了Cmd也就等于理解了Module。 @@ -40,10 +40,10 @@   Context为上下文数据组件,用于有上下文关系的Actor之间传递数据。与Session在传递数据上的区别在于,Context仅用于有强依赖的上下文之间传递,且传递效率和方便性高于Session。 -  Model为模型和计算组件。 +  Operator为模型和计算组件。 -  Chain为动态调用链管理组件,用于运行时动态管理Step和Model的调用。用Chain管理的调用链除了可动态变化之外,还更为方便,更直观。 +  Chain为动态调用链管理组件,用于运行时动态管理Step和Operator的调用。用Chain管理的调用链除了可动态变化之外,还更为方便,更直观。 -  Actor组件各有各的用途和适用场景,灵活应用这些组件可以满足绝大多数业务需求,这是Nebula号称面向业务的IoC网络框架的根本。同时也因Actor组件设计比较精妙,有些作用可能连Nebula框架设计者也没有发现,有待应用开发者们发掘。当然,发现新的用途并不意味着滥用,与官方文档相悖的用途建议不要使用。 \ No newline at end of file +  Actor组件各有各的用途和适用场景,灵活应用这些组件可以满足绝大多数业务需求,这是Nebula号称面向业务的IoC网络框架的根本。同时也因Actor组件设计比较精妙,有些作用可能连Nebula框架设计者也没有发现,有待应用开发者们发掘。当然,发现新的用途并不意味着滥用,与官方文档相悖的用途建议不要使用。 diff --git a/docs/cn/cmd_and_module.md b/docs/cn/cmd_and_module.md index 3346679b..2ef380cf 100644 --- a/docs/cn/cmd_and_module.md +++ b/docs/cn/cmd_and_module.md @@ -284,7 +284,7 @@ bool ModuleHello::AnyMessage( ### 完整业务流程简析 -  上述Cmd、Module定义和例子是为了说明Cmd和Module两种组件的用途及使用方法,测试Nebula的功能demo有Cmd和Module基本上就可以了。然而,实际的业务应用并不是demo这么简单的,不过再复杂的业务以Actor组件来实现也不难,熟悉了Actor组件甚至能更好地帮助开发者做业务逻辑抽象出来。Nebula的Actor组件有7种,其中Cmd、Module、Step、Session是与Nebula生而俱来的组件,其中Cmd和Module可视作同一种组件,有了这几个组件就可以满足绝大部分复杂业务。Context、Model、Chain是Nebula发展过程中产生的组件,我们暂且先不用关注。 +  上述Cmd、Module定义和例子是为了说明Cmd和Module两种组件的用途及使用方法,测试Nebula的功能demo有Cmd和Module基本上就可以了。然而,实际的业务应用并不是demo这么简单的,不过再复杂的业务以Actor组件来实现也不难,熟悉了Actor组件甚至能更好地帮助开发者做业务逻辑抽象出来。Nebula的Actor组件有7种,其中Cmd、Module、Step、Session是与Nebula生而俱来的组件,其中Cmd和Module可视作同一种组件,有了这几个组件就可以满足绝大部分复杂业务。Context、Operator、Chain是Nebula发展过程中产生的组件,我们暂且先不用关注。   使用Actor组件的普遍业务流程如图: @@ -292,4 +292,4 @@ bool ModuleHello::AnyMessage(   如图所示,客户端请求从Cmd/Module进来,Cmd/Module创建一个步骤Step1完成异步IO操作,在Step1的回调之前,服务节点可以接收并处理其他客户端的请求。Step1完成后可能还需要Step2、Step3才完成整个业务流程,完成后将由最后一个Step给客户端发响应。在整个处理流程中可能需要暂时存储一些数据或共享一些数据,共享数据主要通过Session来实现。Context用于传递各Step所需的上下文,上下文传递也可以通过创建Step时的构造函数参数传递。 -  这是一个通过Actor组件实现业务逻辑的完整流程,再复杂的业务都可通过Actor组件的组合来实现,Step组件的章节还会有更多业务实现流程说明。无论何种业务流程都是通过上图的简单流程根据业务需要发展和衍生出来的,理解了这个简单的业务流程就容易理解后续讲解的业务流程。 \ No newline at end of file +  这是一个通过Actor组件实现业务逻辑的完整流程,再复杂的业务都可通过Actor组件的组合来实现,Step组件的章节还会有更多业务实现流程说明。无论何种业务流程都是通过上图的简单流程根据业务需要发展和衍生出来的,理解了这个简单的业务流程就容易理解后续讲解的业务流程。 diff --git a/docs/cn/configuration.md b/docs/cn/configuration.md index b65d768e..e79f945f 100644 --- a/docs/cn/configuration.md +++ b/docs/cn/configuration.md @@ -36,6 +36,7 @@ "max_log_file_num": 5, "//max_log_file_size": "单个日志文件大小限制", "max_log_file_size": 20480000, + "always_flush_log":true, "log_levels": { "FATAL": 0, "CRITICAL": 1, "ERROR": 2, "NOTICE": 3, "WARNING": 4, "INFO": 5, "DEBUG": 6, "TRACE": 7 }, "log_level": 7, "net_log_level": 6, @@ -44,6 +45,8 @@ "addr_permit": { "stat_interval": 60.0, "permit_num": 1000000000 }, "uin_permit": { "stat_interval": 60.0, "permit_num": 600000000 } }, + "//connection_protection": "连接保护时间(单位:秒),当值>0时新建连接设置为这个时间,接收到第一个数据包后改设为io_timeout", + "connection_protection": 0.0, "//io_timeout": "网络IO(连接)超时设置(单位:秒)小数点后面至少保留一位", "io_timeout": 300.0, "//step_timeout": "步骤超时设置(单位:秒)小数点后面至少保留一位", @@ -148,10 +151,12 @@ * log_path 日志文件存储路径,相对于NEBULA_HOME的路径。 * max_log_file_num 最大日志文件数量,用于日志文件滚动,超出这个数量的日志文件将会被直接删除。 * max_log_file_size 单个日志文件大小限制,用于日志文件滚动,超出这个限制将滚动日志,生成一个新的日志文件。 +* always_flush_log 是否总是刷新日志到磁盘 * log_levels 日志级别枚举,仅用于给log_level和net_log_level配置时参考。 * log_level 本地文件日志级别。框架级别的调试日志用的是LOG4_TRACE(日志量很大,部署生产时请不要打开TRACE级别日志),建议业务的调试日志用LOG4_DEBUG。 * net_log_level 发到LOGTRACE日志跟踪服务的网络日志级别。 * permission 连接或消息发送频率限制。addr_permit为连接限制,限制每个IP在统计时间内连接次数,超出permit_num次数限制的连接会被直接拒绝;uin_permit为消息数量限制,限制每个用户在单位统计时间内permit_num发送消息数量,超出限制数量的消息会被直接丢弃。stat_interval到了之后重新计算。 +* connection_protection 连接保护时间(单位:秒),当值>0时新建连接设置为这个时间,接收到第一个数据包后改设为io_timeout", * io_timeout 网络IO(连接)超时设置(单位:秒)小数点后面至少保留一位,用于触发连接的有效性检查。如果在到达超时时间前连接有数据收或发过,则从最后一次收发数据时间开始重新计算超时时间;如果超时时间到达却一直没有数据收发过,有三种处理情况:(1) 需要做应用层心跳检查,自动发送心跳包,心跳包得到响应,连接得以保持,重新计算超时时间;(2) 需要做应用层心跳检查,自动发送心跳包,心跳包未得到响应,立即断开连接,回收连接所分配资源;(3) 无须做应用层心跳检查,立即断开连接,回收连接所分配资源。 * step_timeout 步骤超时设置(单位:秒)小数点后面至少保留一位,用于请求发出后等待响应的默认超时等待。在代码层可以为每一个发出的请求设置等待超时,通常为Step类的最后一个参数,如果这个参数为缺省值,则配置文件里的step_timeout会作为这个Step发出请求后等待响应的超时时间。 * with_ssl 配置接入服务的对外连接是否需要SSL传输加密,如果需要则配置ssl配置文件路径和相关文件名。 diff --git a/docs/image/Actor.png b/docs/image/Actor.png index cdf847868fe41240eeb9487ea19addff73379dc2..9f097eabb2ef4c3b7827462909a2344e2036762e 100644 GIT binary patch literal 181130 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%0A?&wJvSR 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
S?BQr^((#7R$Vy4by+X*qyR6Kq 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�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

HI!MU}-n=I{-ES5peJ9@=L6Me)ZA9C+5K# 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 Ins!J33LJwHxc~qF diff --git a/proto/http.proto b/proto/http.proto index df2d2106..9978eabc 100644 --- a/proto/http.proto +++ b/proto/http.proto @@ -27,7 +27,6 @@ message HttpMsg Upgrade upgrade = 12; ///< 升级协议 float keep_alive = 13; ///< keep alive time string path = 14; ///< Http Decode时从url中解析出来,不需要人为填充(encode时不需要填) - bool is_decoding = 15; ///< 是否正在解码(true 正在解码, false 未解码或已完成解码) bool chunk_notice = 19; ///< 是否启用分块传输通知(当包体比较大时,部分传输完毕也会通知业务层而无需等待整个http包传输并解码完毕。) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 304f35ec..5fc730fe 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -681,22 +681,6 @@ bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const { auto pSelfChannel = std::dynamic_pointer_cast(pChannel); if (pSelfChannel->IsResponse()) - { - auto cmd_iter = m_mapCmd.find(CMD_REQ_REDIS_PROXY); - if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) - { - auto pRedisCmd = std::dynamic_pointer_cast(cmd_iter->second); - if (pRedisCmd == nullptr) - { - LOG4_ERROR("cmd %d is not a RedisCmd instance!", CMD_REQ_REDIS_PROXY); - return(false); - } - return(pRedisCmd->AnyMessage(pChannel, oRedisMsg)); - } - LOG4_ERROR("no instance of RedisCmd or derived class of RedisCmd found for cmd %d", CMD_REQ_REDIS_PROXY); - return(false); - } - else { auto step_iter = m_mapCallbackStep.find(pSelfChannel->GetStepSeq()); if (step_iter == m_mapCallbackStep.end()) @@ -730,28 +714,28 @@ bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const return(eResult); } } -} - -bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const CBuffer& oBuffer) -{ - auto pSelfChannel = std::dynamic_pointer_cast(pChannel); - if (pSelfChannel->IsResponse()) + else { - auto cmd_iter = m_mapCmd.find(CMD_REQ_RAW_DATA); + auto cmd_iter = m_mapCmd.find(CMD_REQ_REDIS_PROXY); if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) { - auto pRawCmd = std::dynamic_pointer_cast(cmd_iter->second); - if (pRawCmd == nullptr) + auto pRedisCmd = std::dynamic_pointer_cast(cmd_iter->second); + if (pRedisCmd == nullptr) { - LOG4_ERROR("cmd %d is not a RawCmd instance!", CMD_REQ_RAW_DATA); + LOG4_ERROR("cmd %d is not a RedisCmd instance!", CMD_REQ_REDIS_PROXY); return(false); } - return(pRawCmd->AnyMessage(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes())); + return(pRedisCmd->AnyMessage(pChannel, oRedisMsg)); } - LOG4_ERROR("no instance of RawCmd or derived class of RawCmd found for cmd %d", CMD_REQ_RAW_DATA); + LOG4_ERROR("no instance of RedisCmd or derived class of RedisCmd found for cmd %d", CMD_REQ_REDIS_PROXY); return(false); } - else +} + +bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const CBuffer& oBuffer) +{ + auto pSelfChannel = std::dynamic_pointer_cast(pChannel); + if (pSelfChannel->IsResponse()) { auto step_iter = m_mapCallbackStep.find(pSelfChannel->GetStepSeq()); if (step_iter == m_mapCallbackStep.end()) @@ -785,6 +769,22 @@ bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const return(eResult); } } + else + { + auto cmd_iter = m_mapCmd.find(CMD_REQ_RAW_DATA); + if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) + { + auto pRawCmd = std::dynamic_pointer_cast(cmd_iter->second); + if (pRawCmd == nullptr) + { + LOG4_ERROR("cmd %d is not a RawCmd instance!", CMD_REQ_RAW_DATA); + return(false); + } + return(pRawCmd->AnyMessage(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes())); + } + LOG4_ERROR("no instance of RawCmd or derived class of RawCmd found for cmd %d", CMD_REQ_RAW_DATA); + return(false); + } } bool ActorBuilder::OnError(std::shared_ptr pChannel, uint32 uiStepSeq, int iErrno, const std::string& strErrMsg) diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index aa772e5f..efb1e066 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -47,7 +47,10 @@ SocketChannel::SocketChannel(std::shared_ptr pLogger, int iFd, uint32 SocketChannel::~SocketChannel() { - m_pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ""); + if (m_pLogger != nullptr) + { + m_pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ""); + } } bool SocketChannel::Init(E_CODEC_TYPE eCodecType, bool bIsClient) diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index b964b177..b5ebee9a 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -484,6 +484,11 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq m_dActiveTime = m_pLabor->GetNowTime(); if (iNeedWriteLen == iHadWrittenLen) { + if (m_pCodec->GetCodecType() == CODEC_HTTP + && ((CodecHttp*)m_pCodec)->GetKeepAlive() == 0.0) + { + return(CODEC_STATUS_EOF); + } return(eCodecStatus); } else @@ -877,7 +882,8 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) { size_t uiSendBuffLen = m_pSendBuff->ReadableBytes(); eCodecStatus = ((CodecHttp2*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg, m_pSendBuff); - if (m_pSendBuff->ReadableBytes() > uiSendBuffLen) + if (m_pSendBuff->ReadableBytes() > uiSendBuffLen + && (eCodecStatus == CODEC_STATUS_OK || eCodecStatus == CODEC_STATUS_PART_OK)) { Send(); } @@ -916,6 +922,10 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) { if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) { + if (!m_bIsClientConnection) + { + m_pSendBuff->Clear(); + } return(CODEC_STATUS_INVALID); } } @@ -1099,6 +1109,13 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) } LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_uiSeq, oMsgHead.cmd(), oMsgHead.seq()); } + else + { + if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) + { + return(CODEC_STATUS_INVALID); + } + } if ((CODEC_STATUS_PAUSE == eCodecStatus) && (CODEC_STATUS_EOF == m_eLastCodecStatus || CODEC_STATUS_INT == m_eLastCodecStatus)) @@ -1126,7 +1143,8 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) { size_t uiSendBuffLen = m_pSendBuff->ReadableBytes(); eCodecStatus = ((CodecHttp2*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg, m_pSendBuff); - if (m_pSendBuff->ReadableBytes() > uiSendBuffLen) + if (m_pSendBuff->ReadableBytes() > uiSendBuffLen + && (eCodecStatus == CODEC_STATUS_OK || eCodecStatus == CODEC_STATUS_PART_OK)) { Send(); } @@ -1161,6 +1179,13 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) } } } + else + { + if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) + { + return(CODEC_STATUS_INVALID); + } + } if ((CODEC_STATUS_PAUSE == eCodecStatus) && (CODEC_STATUS_EOF == m_eLastCodecStatus || CODEC_STATUS_INT == m_eLastCodecStatus)) @@ -1189,6 +1214,13 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(RedisReply& oRedisReply) m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; } } + else + { + if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) + { + return(CODEC_STATUS_INVALID); + } + } if ((CODEC_STATUS_PAUSE == eCodecStatus) && (CODEC_STATUS_EOF == m_eLastCodecStatus || CODEC_STATUS_INT == m_eLastCodecStatus)) @@ -1256,10 +1288,6 @@ Codec* SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAl pNewCodec = new CodecProto(m_pLogger, eCodecType); pNewCodec->SetKey(m_strKey); break; - case CODEC_PRIVATE: - pNewCodec = new CodecPrivate(m_pLogger, eCodecType); - pNewCodec->SetKey(m_strKey); - break; case CODEC_HTTP: pNewCodec = new CodecHttp(m_pLogger, eCodecType, dKeepAlive); pNewCodec->SetKey(m_strKey); @@ -1273,6 +1301,16 @@ Codec* SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAl } pNewCodec->SetKey(m_strKey); break; + case CODEC_RESP: + pNewCodec = new CodecResp(m_pLogger, eCodecType); + pNewCodec->SetKey(m_strKey); + break; + case CODEC_PRIVATE: + pNewCodec = new CodecPrivate(m_pLogger, eCodecType); + pNewCodec->SetKey(m_strKey); + break; + case CODEC_UNKNOW: + break; default: LOG4_ERROR("no codec defined for code type %d", eCodecType); break; @@ -1283,8 +1321,11 @@ Codec* SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAl LOG4_ERROR("%s", e.what()); return(nullptr); } - DELETE(m_pCodec); - m_pCodec = pNewCodec; + if (pNewCodec != nullptr) + { + DELETE(m_pCodec); + m_pCodec = pNewCodec; + } m_dKeepAlive = dKeepAlive; m_dActiveTime = m_pLabor->GetNowTime(); return(m_pCodec); diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index fe569e85..37ae5d4d 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -563,46 +563,48 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) size_t uiDecodeBuffLen = pBuff->ReadableBytes(); size_t uiLen = http_parser_execute(&m_parser, &m_parser_setting, pDecodeBuff, uiDecodeBuffLen); - if (!oHttpMsg.is_decoding()) + if(m_parser.http_errno == HPE_OK) { - if(m_parser.http_errno != HPE_OK) + pBuff->AdvanceReadIndex(uiLen); + if (HTTP_REQUEST == oHttpMsg.type()) { - LOG4_WARNING("Failed to parse http message for cause:%s", - http_errno_name((http_errno)m_parser.http_errno)); - return(CODEC_STATUS_ERR); + m_iHttpMajor = oHttpMsg.http_major(); + m_iHttpMinor = oHttpMsg.http_minor(); + m_dKeepAlive = (oHttpMsg.keep_alive() > 0) ? oHttpMsg.keep_alive() : m_dKeepAlive; } - pBuff->AdvanceReadIndex(uiLen); - } - else - { - LOG4_TRACE("decoding..."); - return(CODEC_STATUS_PAUSE); - } - if (HTTP_REQUEST == oHttpMsg.type()) - { - m_iHttpMajor = oHttpMsg.http_major(); - m_iHttpMinor = oHttpMsg.http_minor(); - m_dKeepAlive = (oHttpMsg.keep_alive() > 0) ? oHttpMsg.keep_alive() : m_dKeepAlive; - } - auto iter = oHttpMsg.headers().find("Content-Encoding"); - if (iter != oHttpMsg.headers().end()) - { - if ("gzip" == iter->second) + auto iter = oHttpMsg.headers().find("Content-Encoding"); + if (iter != oHttpMsg.headers().end()) { - std::string strData; - if (Gunzip(oHttpMsg.body(), strData)) - { - oHttpMsg.set_body(strData); - } - else + if ("gzip" == iter->second) { - LOG4_WARNING("guzip error!"); - return(CODEC_STATUS_ERR); + std::string strData; + if (Gunzip(oHttpMsg.body(), strData)) + { + oHttpMsg.set_body(strData); + } + else + { + LOG4_WARNING("guzip error!"); + return(CODEC_STATUS_ERR); + } } } + if (!m_bChannelIsClient && !http_should_keep_alive(&m_parser)) + { + oHttpMsg.set_keep_alive(0.0); + m_dKeepAlive = 0.0; + } + LOG4_DEBUG("%s", ToString(oHttpMsg).c_str()); + return(CODEC_STATUS_OK); } - LOG4_DEBUG("%s", ToString(oHttpMsg).c_str()); - return(CODEC_STATUS_OK); + if (m_parser.http_errno == HPE_PAUSED) + { + LOG4_TRACE("decoding..."); + return(CODEC_STATUS_PAUSE); + } + LOG4_WARNING("Failed to parse http message for cause:%s", + http_errno_name((http_errno)m_parser.http_errno)); + return(CODEC_STATUS_ERR); } void CodecHttp::AddHttpHeader(const std::string& strHeaderName, const std::string& strHeaderValue) @@ -674,8 +676,6 @@ bool CodecHttp::CloseRightAway() const int CodecHttp::OnMessageBegin(http_parser *parser) { - HttpMsg* pHttpMsg = (HttpMsg*) parser->data; - pHttpMsg->set_is_decoding(true); return(0); } @@ -755,15 +755,6 @@ int CodecHttp::OnHeaderValue(http_parser *parser, const char *at, size_t len) } else if (std::string("Connection") == strHeadName) { - if (std::string("keep-alive") == strHeadValue) - { - pHttpMsg->set_keep_alive(-1); // 由配置的IoTimeout决定 - } - else if (std::string("close") == strHeadValue) - { - pHttpMsg->set_keep_alive(0.0); - } - size_t uiPos = 0; uiPos = strHeadValue.find_first_of("Upgrade"); if (0 == uiPos) @@ -816,31 +807,6 @@ int CodecHttp::OnMessageComplete(http_parser *parser) } pHttpMsg->set_http_major(parser->http_major); pHttpMsg->set_http_minor(parser->http_minor); - pHttpMsg->set_is_decoding(false); - - if (!http_should_keep_alive(parser)) - { - pHttpMsg->set_keep_alive(0.0); - } - /* - switch ((http_method)pHttpMsg->method()) - { - case HTTP_GET: - case HTTP_OPTIONS: - pHttpMsg->set_is_decoding(false); - break; - default: - if (parser->content_length == 0) - { - pHttpMsg->set_is_decoding(false); - } - else if (parser->content_length == pHttpMsg->body().size()) - { - pHttpMsg->set_is_decoding(false); - } - break; - } - */ return(0); } diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index 0da242be..5d780e80 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -240,8 +240,17 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR size_t uiReadIdx = pBuff->GetReadIndex(); Http2Frame::DecodeFrameHeader(pBuff, m_stDecodeFrameHead); - LOG4_TRACE("m_stDecodeFrameHead.uiLength = %u, m_stDecodeFrameHead.ucType = %u, m_stDecodeFrameHead.ucFlag = %u, m_stDecodeFrameHead.uiStreamIdentifier = %u", - m_stDecodeFrameHead.uiLength, (uint32)m_stDecodeFrameHead.ucType, (uint32)m_stDecodeFrameHead.ucFlag, m_stDecodeFrameHead.uiStreamIdentifier); + LOG4_TRACE("m_stDecodeFrameHead.uiLength = %u, m_stDecodeFrameHead.ucType = %u," + "m_stDecodeFrameHead.ucFlag = %u, m_stDecodeFrameHead.uiStreamIdentifier = %u", + m_stDecodeFrameHead.uiLength, (uint32)m_stDecodeFrameHead.ucType, + (uint32)m_stDecodeFrameHead.ucFlag, m_stDecodeFrameHead.uiStreamIdentifier); + if (!Http2Frame::IsValidFrameType(m_stDecodeFrameHead.ucType)) + { + LOG4_ERROR("invalid frame type %u", (uint32)m_stDecodeFrameHead.ucType); + SetErrno(H2_ERR_PROTOCOL_ERROR); + m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "invalid frame type.", pReactBuff); + return(CODEC_STATUS_ERR); + } if (m_stDecodeFrameHead.uiLength > m_uiSettingsMaxDecodeFrameSize) { LOG4_TRACE("m_uiSettingsMaxDecodeFrameSize = %u, m_stDecodeFrameHead.uiLength = %u", diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index e1c8b2f8..fb5e5739 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -15,6 +15,19 @@ namespace neb { +std::unordered_set Http2Frame::s_setFrameType = { + H2_FRAME_DATA, + H2_FRAME_HEADERS, + H2_FRAME_PRIORITY, + H2_FRAME_RST_STREAM, + H2_FRAME_SETTINGS, + H2_FRAME_PUSH_PROMISE, + H2_FRAME_PING, + H2_FRAME_GOAWAY, + H2_FRAME_WINDOW_UPDATE, + H2_FRAME_CONTINUATION +}; + Http2Frame::Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, Http2Stream* pStream) : Codec(pLogger, eCodecType), m_pStream(pStream) { @@ -102,6 +115,16 @@ E_CODEC_STATUS Http2Frame::EncodeSetting(const std::vector& vecSetti return(CODEC_STATUS_OK); } +bool Http2Frame::IsValidFrameType(uint8 ucFrameType) +{ + auto iter = s_setFrameType.find(ucFrameType); + if (iter == s_setFrameType.end()) + { + return(false); + } + return(true); +} + E_CODEC_STATUS Http2Frame::Encode(CodecHttp2* pCodecH2, const HttpMsg& oHttpMsg, const tagPriority& stPriority, const std::string& strPadding, CBuffer* pBuff) diff --git a/src/codec/http2/Http2Frame.hpp b/src/codec/http2/Http2Frame.hpp index 64276759..25a16bbe 100644 --- a/src/codec/http2/Http2Frame.hpp +++ b/src/codec/http2/Http2Frame.hpp @@ -12,6 +12,7 @@ #include #include +#include #include "Definition.hpp" #include "codec/Codec.hpp" #include "pb/http.pb.h" @@ -57,6 +58,7 @@ class Http2Frame: public Codec static void DecodeFrameHeader(CBuffer* pBuff, tagH2FrameHead& stFrameHead); static void EncodeFrameHeader(const tagH2FrameHead& stFrameHead, CBuffer* pBuff); static E_CODEC_STATUS EncodeSetting(const std::vector& vecSetting, std::string& strSettingFrame); + static bool IsValidFrameType(uint8 ucFrameType); virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) { @@ -147,6 +149,7 @@ class Http2Frame: public Codec friend class CodecHttp2; friend class Http2Stream; + static std::unordered_set s_setFrameType; std::list m_listWaittingFrameData; tagH2FrameHead m_stLastDataFrameHead; Http2Stream* m_pStream; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index d36ec202..77888d0d 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -601,7 +601,6 @@ bool Dispatcher::FdTransfer(int iFd) else { pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - pChannel->m_pImpl->Send(); AddIoTimeout(pChannel, 1.0); // 为了防止大量连接攻击,初始化连接只有一秒即超时,在正常发送第一个数据包之后才采用正常配置的网络IO超时检查 } return(true); diff --git a/src/logger/FileLogger.hpp b/src/logger/FileLogger.hpp index fb44e2cd..e36eac37 100644 --- a/src/logger/FileLogger.hpp +++ b/src/logger/FileLogger.hpp @@ -274,11 +274,19 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) bPlaceholder = true; } break; + case 'f': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_fout << strOutStr; + m_fout << std::fixed << arg; + return(pPos); + } + break; case 'a': case 'A': case 'c': case 'C': - case 'f': case 'p': case 's': case 'S': diff --git a/src/logger/NetLogger.hpp b/src/logger/NetLogger.hpp index f94329be..cbfd893a 100644 --- a/src/logger/NetLogger.hpp +++ b/src/logger/NetLogger.hpp @@ -238,11 +238,19 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) bPlaceholder = true; } break; + case 'f': + if (bPlaceholder) + { + pPos = szFormat + i + 1; + m_ossLogContent << strOutStr; + m_ossLogContent << std::fixed << arg; + return(pPos); + } + break; case 'a': case 'A': case 'c': case 'C': - case 'f': case 'p': case 's': case 'S': From d6d8ba3db1fdae5ff0c6806c21ace3aed90811cf Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 26 Jun 2021 17:42:37 +0800 Subject: [PATCH 142/176] v1.6.0 release, add multi-protocol port --- README.md | 1 + README_cn.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 4623cfef..d227f9f4 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ A simple testing can be start with a NebulaInterface only, and also can be start - add Manager to Manager communication feature - add initial connection timeout configuration - replace va_list with variadic templates in logger + - add multi-protocol port support - thread id optimization - chain optimization - http2 dynamic table and stream priority bug fix diff --git a/README_cn.md b/README_cn.md index 30c9589f..6cd13be7 100644 --- a/README_cn.md +++ b/README_cn.md @@ -119,6 +119,7 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 - 增加节点之间Manager到Manager通信功能 - 增加初始连接超时配置 - 日志组件采用可变模板参数替代va_list,解决日志组件遇到%容易coredump问题 + - 增加一个端口多协议支持 - 线程ID优化 - Chain组件优化 - http2动态表、流优先级bug修复 From 63ed7c31a4d9ae2be7cdb1d6d318b8b19dd8762d Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 3 Jul 2021 16:34:13 +0800 Subject: [PATCH 143/176] CodecResp bug string decode bug fixed; channel disconnect debug log; http2 chunk notice for stream. --- .../sys_session/manager/SessionManager.cpp | 2 ++ src/codec/CodecHttp.cpp | 21 ++++++++++++------- src/codec/CodecResp.cpp | 15 ++++++++++++- src/codec/http2/CodecHttp2.cpp | 1 - src/codec/http2/CodecHttp2.hpp | 5 ----- src/codec/http2/Http2Stream.cpp | 3 ++- src/codec/http2/Http2Stream.hpp | 1 + src/ios/Dispatcher.cpp | 7 +++++++ src/ios/Dispatcher.hpp | 2 ++ 9 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index ea8299f0..94e536e8 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -430,8 +430,10 @@ bool SessionManager::WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& m_mapWorkerFdPid.erase(fd_iter); } auto pControlChannel = GetLabor(this)->GetDispatcher()->GetChannel(worker_iter->second->iControlFd); + LOG4_TRACE("%s", pControlChannel->GetIdentify().c_str()); GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pControlChannel); // 在io事件中已关闭连接,这里可以不需要 auto pDataChannel = GetLabor(this)->GetDispatcher()->GetChannel(worker_iter->second->iDataFd); + LOG4_TRACE("%s", pDataChannel->GetIdentify().c_str()); GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pDataChannel); delete worker_iter->second; m_mapWorkerInfo.erase(worker_iter); diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 37ae5d4d..9dd1c1b9 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -154,17 +154,17 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { m_bChannelIsClient = true; } - if (0 == oHttpMsg.http_major()) - { - LOG4_WARNING("miss http version!"); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } int iWriteSize = 0; int iHadEncodedSize = 0; if (HTTP_REQUEST == oHttpMsg.type()) { + if (0 == oHttpMsg.http_major()) + { + LOG4_WARNING("miss http version!"); + m_mapAddingHttpHeader.clear(); + return(CODEC_STATUS_ERR); + } if (oHttpMsg.url().size() == 0) { LOG4_WARNING("miss url!"); @@ -295,6 +295,11 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) } for (auto h_iter = m_mapAddingHttpHeader.begin(); h_iter != m_mapAddingHttpHeader.end(); ++h_iter) { + if (h_iter->first == "Content-Length" || h_iter->first == "content-length" + || h_iter->first == "Host" || h_iter->first == "host") + { + continue; + } iWriteSize = pBuff->Printf("%s: %s\r\n", h_iter->first.c_str(), h_iter->second.c_str()); if (iWriteSize < 0) { @@ -602,8 +607,8 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) LOG4_TRACE("decoding..."); return(CODEC_STATUS_PAUSE); } - LOG4_WARNING("Failed to parse http message for cause:%s", - http_errno_name((http_errno)m_parser.http_errno)); + LOG4_WARNING("Failed to parse http message for cause:%s, message %s", + http_errno_name((http_errno)m_parser.http_errno), pDecodeBuff); return(CODEC_STATUS_ERR); } diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index 053e6dfc..47665608 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -104,6 +104,7 @@ E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply) oReply.set_type(REDIS_REPLY_ERROR); oReply.set_integer(REDIS_ERR_PROTOCOL); pBuff->SetReadIndex(uiReadIndex); + LOG4_TRACE("cFirstByte = %d", int(cFirstByte)); return(CODEC_STATUS_ERR); } if (CODEC_STATUS_OK != eStatus) @@ -236,6 +237,8 @@ E_CODEC_STATUS CodecResp::DecodeSimpleString(CBuffer* pBuff, RedisReply& oReply) } else { + LOG4_TRACE("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", + uiReadableBytes, i, int(cLastChar), (int)pData[i]); return(CODEC_STATUS_ERR); } break; @@ -271,6 +274,8 @@ E_CODEC_STATUS CodecResp::DecodeError(CBuffer* pBuff, RedisReply& oReply) } else { + LOG4_TRACE("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", + uiReadableBytes, i, int(cLastChar), (int)pData[i]); return(CODEC_STATUS_ERR); } break; @@ -305,6 +310,8 @@ E_CODEC_STATUS CodecResp::DecodeInteger(CBuffer* pBuff, RedisReply& oReply) } else { + LOG4_TRACE("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", + uiReadableBytes, i, int(cLastChar), (int)pData[i]); return(CODEC_STATUS_ERR); } break; @@ -326,7 +333,7 @@ E_CODEC_STATUS CodecResp::DecodeBulkString(CBuffer* pBuff, RedisReply& oReply) bool bGotStringLen = false; const char* pData = pBuff->GetRawReadBuffer(); const char* pPartBegin = pData; - for (size_t i = 0; i <= uiReadableBytes; ++i) + for (size_t i = 0; i < uiReadableBytes; ++i) { switch (pData[i]) { @@ -364,6 +371,8 @@ E_CODEC_STATUS CodecResp::DecodeBulkString(CBuffer* pBuff, RedisReply& oReply) } else { + LOG4_TRACE("uiReadableBytes = %u, i = %d, iStringLen = %d, cLastChar = %d, pData[i] = %d", + uiReadableBytes, i, iStringLen, int(cLastChar), (int)pData[i]); return(CODEC_STATUS_ERR); } break; @@ -435,6 +444,8 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) default: oReply.set_type(REDIS_REPLY_ERROR); oReply.set_integer(REDIS_ERR_PROTOCOL); + LOG4_TRACE("uiReadableBytes = %u, i = %d, cFirstByte = %d, cLastChar = %d, pData[i] = %d", + uiReadableBytes, i, (int)cFirstByte, (int)cLastChar, (int)pData[i]); return(CODEC_STATUS_ERR); } if (CODEC_STATUS_OK != eStatus) @@ -448,6 +459,8 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) } else { + LOG4_TRACE("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", + uiReadableBytes, i, (int)cLastChar, (int)pData[i]); return(CODEC_STATUS_ERR); } break; diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index 5d780e80..5479f66d 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -113,7 +113,6 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) pBuff->Write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24); m_bWantMagic = false; } - m_bChunkNotice = oHttpMsg.chunk_notice(); for (int i = 0; i < oHttpMsg.adding_without_index_headers_size(); ++i) { m_setEncodingWithoutIndexHeaders.insert(oHttpMsg.adding_without_index_headers(i)); diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index ed919d7c..780ebea2 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -61,10 +61,6 @@ class CodecHttp2: public Codec { return(m_bChannelIsClient); } - bool IsChunkNotice() const - { - return(m_bChunkNotice); - } void SetPriority(uint32 uiStreamId, const tagPriority& stPriority); void RstStream(uint32 uiStreamId); E_H2_ERR_CODE Setting(const std::vector& vecSetting, bool bRecvSetting = false); @@ -133,7 +129,6 @@ class CodecHttp2: public Codec private: bool m_bChannelIsClient = false; // 当前编解码器所在channel是作为http客户端还是作为http服务端 bool m_bWantMagic = true; - bool m_bChunkNotice = false; // 是否启用分块传输通知(当包体比较大时,部分传输完毕也会通知业务层而无须等待整个http包传输完毕。) bool m_bHasWaittingFrame = false; uint32 m_uiStreamIdGenerate = 0; uint32 m_uiGoawayLastStreamId = 0; diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index a8b0a586..a38a98ce 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -37,6 +37,7 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, std::string strPadding; if (HTTP_REQUEST == oHttpMsg.type()) { + m_bChunkNotice = oHttpMsg.chunk_notice(); stPriority.uiDependency = 0; stPriority.ucWeight = 15; std::string strScheme = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(':')); @@ -270,7 +271,7 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, m_oHttpMsg.set_type(HTTP_RESPONSE); } } - if (pCodecH2->IsChunkNotice()) + if (m_bChunkNotice) { oHttpMsg = std::move(m_oHttpMsg); } diff --git a/src/codec/http2/Http2Stream.hpp b/src/codec/http2/Http2Stream.hpp index 6d02fac2..8587c3fa 100644 --- a/src/codec/http2/Http2Stream.hpp +++ b/src/codec/http2/Http2Stream.hpp @@ -94,6 +94,7 @@ class Http2Stream: public Codec int32 m_iSendWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; uint32 m_uiRecvWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; bool m_bEndHeaders; + bool m_bChunkNotice = false; // 是否启用分块传输通知(当包体比较大时,部分传输完毕也会通知业务层而无须等待整个http包传输完毕。) std::unique_ptr m_pFrame; HttpMsg m_oHttpMsg; }; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 77888d0d..9c4dde7f 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -333,6 +333,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); } } + LOG4_TRACE("eCodecStatus = %d", eCodecStatus); DiscardSocketChannel(pChannel); return(false); } @@ -480,6 +481,7 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); } } + LOG4_TRACE("eCodecStatus = %d", eCodecStatus); DiscardSocketChannel(pChannel); return(false); } @@ -674,6 +676,7 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) } else { + LOG4_TRACE("%s", pChannel->GetIdentify().c_str()); DiscardSocketChannel(pChannel); } return(true); @@ -897,6 +900,7 @@ std::shared_ptr Dispatcher::StressSend(const std::string& strIden && CODEC_STATUS_WANT_WRITE != eCodecStatus && CODEC_STATUS_WANT_READ != eCodecStatus) { + LOG4_TRACE("eStatus = %d, %s", eCodecStatus, pChannel->GetIdentify().c_str()); DiscardSocketChannel(pChannel); } pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); @@ -968,6 +972,7 @@ std::shared_ptr Dispatcher::GetLastActivityChannel() bool Dispatcher::Disconnect(std::shared_ptr pChannel, bool bChannelNotice) { + LOG4_TRACE("%s", pChannel->GetIdentify().c_str()); return(DiscardSocketChannel(pChannel, bChannelNotice)); } @@ -980,9 +985,11 @@ bool Dispatcher::Disconnect(const std::string& strIdentify, bool bChannelNotice) while (named_iter->second.size() > 1) { channel_iter = named_iter->second.begin(); + LOG4_TRACE("%s", (*channel_iter)->GetIdentify().c_str()); DiscardSocketChannel(*channel_iter, bChannelNotice); } channel_iter = named_iter->second.begin(); + LOG4_TRACE("%s", (*channel_iter)->GetIdentify().c_str()); return(DiscardSocketChannel(*channel_iter, bChannelNotice)); } return(false); diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index f686f146..5a8c5e43 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -266,9 +266,11 @@ bool Dispatcher::SendTo(std::shared_ptr pChannel, Targs&&... args RemoveIoWriteEvent(pChannel); return(true); case CODEC_STATUS_EOF: // a case: http1.0 respone and close + LOG4_TRACE("eStatus = %d, %s", eStatus, pChannel->GetIdentify().c_str()); DiscardSocketChannel(pChannel); return(true); default: + LOG4_TRACE("eStatus = %d, %s", eStatus, pChannel->GetIdentify().c_str()); DiscardSocketChannel(pChannel); return(false); } From 4c865ca9f925af89c463cd360b3adb3417d205a3 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 3 Jul 2021 16:50:24 +0800 Subject: [PATCH 144/176] v1.6.1 release --- README.md | 3 +++ README_cn.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index d227f9f4..ce5bf34e 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,9 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v1.6.1 + - http2 chunk notice for stream + - CodecResp bug string decode bug fixed #### v1.6.0 - add SelfChannel within the Worker, improve the actor model - add service status monitoring and metrics for prometheus diff --git a/README_cn.md b/README_cn.md index 6cd13be7..74275dc1 100644 --- a/README_cn.md +++ b/README_cn.md @@ -113,6 +113,9 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 +#### v1.6.1 + - http2分块响应通知改为作用于流而非连接 + - resp解码字符串遇分包时bug修复 #### v1.6.0 - 增加用于Worker内通信的SelfChannel,完善actor模型 - 增加服务状态监控和监控指标获取插件 From 0ee4b338d5639301351e35c17efccbaf26753af7 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 18 Jul 2021 20:12:46 +0800 Subject: [PATCH 145/176] http chunk decode bug fixed; resp bulk string bug fixed. --- src/Definition.hpp | 3 +- .../sys_session/manager/SessionManager.cpp | 2 - src/channel/SocketChannelImpl.cpp | 109 ++++++++++------- src/codec/CodecHttp.cpp | 114 ++++++++++-------- src/codec/CodecHttp.hpp | 6 + src/codec/CodecResp.cpp | 21 ++-- src/codec/http2/CodecHttp2.cpp | 9 +- src/codec/http2/Http2Stream.cpp | 8 +- src/ios/Dispatcher.cpp | 2 +- 9 files changed, 162 insertions(+), 112 deletions(-) diff --git a/src/Definition.hpp b/src/Definition.hpp index 96e9c564..b95531c3 100644 --- a/src/Definition.hpp +++ b/src/Definition.hpp @@ -193,7 +193,8 @@ enum E_CHANNEL_STATUS CHANNEL_STATUS_WORKER = 4, ///< 连接成功从Manager传送给Worker CHANNEL_STATUS_TELL_WORKER = 5, ///< 将本Worker信息告知对端Worker CHANNEL_STATUS_ESTABLISHED = 6, ///< 与对端Worker的连接就绪(可以正常收发消息) - CHANNEL_STATUS_CLOSED = 7, ///< 被丢弃待回收 + CHANNEL_STATUS_BROKEN = 7, ///< 连接已断开,待回收处理 + CHANNEL_STATUS_CLOSED = 8, ///< 被丢弃待回收 }; } diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 94e536e8..ea8299f0 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -430,10 +430,8 @@ bool SessionManager::WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& m_mapWorkerFdPid.erase(fd_iter); } auto pControlChannel = GetLabor(this)->GetDispatcher()->GetChannel(worker_iter->second->iControlFd); - LOG4_TRACE("%s", pControlChannel->GetIdentify().c_str()); GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pControlChannel); // 在io事件中已关闭连接,这里可以不需要 auto pDataChannel = GetLabor(this)->GetDispatcher()->GetChannel(worker_iter->second->iDataFd); - LOG4_TRACE("%s", pDataChannel->GetIdentify().c_str()); GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pDataChannel); delete worker_iter->second; m_mapWorkerInfo.erase(worker_iter); diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp index b5ebee9a..d7bd5a6f 100644 --- a/src/channel/SocketChannelImpl.cpp +++ b/src/channel/SocketChannelImpl.cpp @@ -171,9 +171,10 @@ bool SocketChannelImpl::NeedAliveCheck() const E_CODEC_STATUS SocketChannelImpl::Send() { LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); return(CODEC_STATUS_EOF); } else if (CHANNEL_STATUS_ESTABLISHED != m_ucChannelStatus) @@ -245,6 +246,7 @@ E_CODEC_STATUS SocketChannelImpl::Send() m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; return(CODEC_STATUS_INT); } } @@ -270,8 +272,10 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& eCodecStatus = m_pCodec->Encode(oMsgHead, oMsgBody, m_pSendBuff); break; case CHANNEL_STATUS_CLOSED: - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - return(CODEC_STATUS_EOF); + case CHANNEL_STATUS_BROKEN: + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: case CHANNEL_STATUS_TRANSFER_TO_WORKER: @@ -310,8 +314,8 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& } break; default: - LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); - return(CODEC_STATUS_OK); + LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); + return(CODEC_STATUS_ERR); } if (CODEC_STATUS_OK != eCodecStatus) @@ -373,6 +377,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; return(CODEC_STATUS_INT); } } @@ -416,8 +421,10 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq } break; case CHANNEL_STATUS_CLOSED: - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - return(CODEC_STATUS_EOF); + case CHANNEL_STATUS_BROKEN: + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: case CHANNEL_STATUS_TRANSFER_TO_WORKER: @@ -446,8 +453,8 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq } break; default: - LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); - return(CODEC_STATUS_OK); + LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); + return(CODEC_STATUS_ERR); } if (CODEC_STATUS_OK != eCodecStatus && CODEC_STATUS_PART_OK != eCodecStatus) @@ -506,6 +513,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; return(CODEC_STATUS_INT); } } @@ -530,7 +538,9 @@ E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepS eCodecStatus = ((CodecResp*)m_pCodec)->Encode(oRedisMsg, m_pSendBuff); break; case CHANNEL_STATUS_CLOSED: - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); + case CHANNEL_STATUS_BROKEN: + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); return(CODEC_STATUS_EOF); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: @@ -546,8 +556,8 @@ E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepS } break; default: - LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); - return(CODEC_STATUS_OK); + LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); + return(CODEC_STATUS_ERR); } if (CODEC_STATUS_OK != eCodecStatus) @@ -605,6 +615,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepS m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; return(CODEC_STATUS_INT); } } @@ -619,8 +630,10 @@ E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint3 m_pSendBuff->Write(pRaw, uiRawSize); break; case CHANNEL_STATUS_CLOSED: - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] send EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - return(CODEC_STATUS_EOF); + case CHANNEL_STATUS_BROKEN: + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: case CHANNEL_STATUS_TRANSFER_TO_WORKER: @@ -631,8 +644,8 @@ E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint3 eCodecStatus = CODEC_STATUS_PAUSE; break; default: - LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); - return(CODEC_STATUS_OK); + LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); + return(CODEC_STATUS_ERR); } if (CODEC_STATUS_OK != eCodecStatus) @@ -690,6 +703,7 @@ E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint3 m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; return(CODEC_STATUS_INT); } } @@ -735,6 +749,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), m_iErrno, m_strErrMsg.c_str()); m_eLastCodecStatus = CODEC_STATUS_INT; + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; //return(CODEC_STATUS_INT); } } @@ -766,8 +781,10 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) } break; case CHANNEL_STATUS_CLOSED: - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - return(CODEC_STATUS_EOF); + case CHANNEL_STATUS_BROKEN: + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); case CHANNEL_STATUS_TELL_WORKER: case CHANNEL_STATUS_WORKER: case CHANNEL_STATUS_TRANSFER_TO_WORKER: @@ -799,7 +816,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) } break; default: - LOG4_ERROR("%s invalid connect status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); + LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); return(CODEC_STATUS_ERR); } } @@ -819,10 +836,11 @@ E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) { LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - return(CODEC_STATUS_EOF); + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); } if (m_pCodec == nullptr) { @@ -862,6 +880,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), m_iErrno, m_strErrMsg.c_str()); m_eLastCodecStatus = CODEC_STATUS_INT; + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; } } @@ -941,10 +960,11 @@ E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) { LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - return(CODEC_STATUS_EOF); + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); } if (m_pCodec == nullptr) { @@ -984,6 +1004,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), m_iErrno, m_strErrMsg.c_str()); m_eLastCodecStatus = CODEC_STATUS_INT; + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; } } @@ -1021,10 +1042,11 @@ E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) { LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - return(CODEC_STATUS_EOF); + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); } int iReadLen = 0; int iHadReadLen = 0; @@ -1059,6 +1081,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), m_iErrno, m_strErrMsg.c_str()); m_eLastCodecStatus = CODEC_STATUS_INT; + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; } } @@ -1089,10 +1112,11 @@ E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) { LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - return(CODEC_STATUS_EOF); + LOG4_TRACE("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); } LOG4_TRACE("fetch from fd %d and m_pRecvBuff->ReadableBytes() = %d", m_iFd, m_pRecvBuff->ReadableBytes()); @@ -1128,10 +1152,11 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) { LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - return(CODEC_STATUS_EOF); + LOG4_TRACE("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); } // 当http1.0响应包未带Content-Length头时,m_pRecvBuff可读字节数为0,以关闭连接表示数据发送完毕。 E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; @@ -1198,10 +1223,11 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) E_CODEC_STATUS SocketChannelImpl::Fetch(RedisReply& oRedisReply) { LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - return(CODEC_STATUS_EOF); + LOG4_TRACE("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); } E_CODEC_STATUS eCodecStatus = ((CodecResp*)m_pCodec)->Decode(m_pRecvBuff, oRedisReply); if (CODEC_STATUS_OK == eCodecStatus) @@ -1233,10 +1259,11 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(RedisReply& oRedisReply) E_CODEC_STATUS SocketChannelImpl::Fetch(CBuffer& oRawBuff) { LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus) + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d] recv EOF.", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - return(CODEC_STATUS_EOF); + LOG4_TRACE("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); } if (oRawBuff.Write(m_pRecvBuff, m_pRecvBuff->ReadableBytes()) > 0) { diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 9dd1c1b9..f33445e9 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -80,7 +80,7 @@ namespace neb CodecHttp::CodecHttp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive) : Codec(pLogger, eCodecType), - m_bChannelIsClient(false), m_uiEncodedNum(0), m_uiDecodedNum(0), + m_bChannelIsClient(false), m_bIsDecoding(false), m_uiEncodedNum(0), m_uiDecodedNum(0), m_iHttpMajor(1), m_iHttpMinor(1), m_dKeepAlive(dKeepAlive) { } @@ -295,6 +295,14 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) } for (auto h_iter = m_mapAddingHttpHeader.begin(); h_iter != m_mapAddingHttpHeader.end(); ++h_iter) { + if (std::string("Content-Encoding") == h_iter->first && std::string("gzip") == h_iter->second) + { + bIsGzip = true; + } + if (std::string("Transfer-Encoding") == h_iter->first && std::string("chunked") == h_iter->second) + { + bIsChunked = true; + } if (h_iter->first == "Content-Length" || h_iter->first == "content-length" || h_iter->first == "Host" || h_iter->first == "host") { @@ -311,14 +319,6 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { iHadEncodedSize += iWriteSize; } - if (std::string("Content-Encoding") == h_iter->first && std::string("gzip") == h_iter->second) - { - bIsGzip = true; - } - if (std::string("Transfer-Encoding") == h_iter->first && std::string("chunked") == h_iter->second) - { - bIsChunked = true; - } } if (oHttpMsg.body().size() > 0) { @@ -552,6 +552,7 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) return(CODEC_STATUS_PAUSE); } ++m_uiDecodedNum; + m_oParsingHttpMsg.Clear(); m_parser_setting.on_message_begin = OnMessageBegin; m_parser_setting.on_url = OnUrl; m_parser_setting.on_status = OnStatus; @@ -562,30 +563,41 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) m_parser_setting.on_message_complete = OnMessageComplete; m_parser_setting.on_chunk_header = OnChunkHeader; m_parser_setting.on_chunk_complete = OnChunkComplete; - m_parser.data = &oHttpMsg; + m_parser.data = this; + m_bIsDecoding = false; http_parser_init(&m_parser, HTTP_BOTH); const char* pDecodeBuff = pBuff->GetRawReadBuffer(); size_t uiDecodeBuffLen = pBuff->ReadableBytes(); size_t uiLen = http_parser_execute(&m_parser, &m_parser_setting, pDecodeBuff, uiDecodeBuffLen); + if (m_parser.http_errno == HPE_PAUSED) + { + LOG4_TRACE("wait for message to complete..."); + return(CODEC_STATUS_PAUSE); + } if(m_parser.http_errno == HPE_OK) { + if (m_bIsDecoding) + { + LOG4_TRACE("wait for message to complete..."); + return(CODEC_STATUS_PAUSE); + } pBuff->AdvanceReadIndex(uiLen); - if (HTTP_REQUEST == oHttpMsg.type()) + if (HTTP_REQUEST == m_oParsingHttpMsg.type()) { - m_iHttpMajor = oHttpMsg.http_major(); - m_iHttpMinor = oHttpMsg.http_minor(); - m_dKeepAlive = (oHttpMsg.keep_alive() > 0) ? oHttpMsg.keep_alive() : m_dKeepAlive; + m_iHttpMajor = m_oParsingHttpMsg.http_major(); + m_iHttpMinor = m_oParsingHttpMsg.http_minor(); + m_dKeepAlive = (m_oParsingHttpMsg.keep_alive() > 0) ? m_oParsingHttpMsg.keep_alive() : m_dKeepAlive; } - auto iter = oHttpMsg.headers().find("Content-Encoding"); - if (iter != oHttpMsg.headers().end()) + auto iter = m_oParsingHttpMsg.headers().find("Content-Encoding"); + if (iter != m_oParsingHttpMsg.headers().end()) { if ("gzip" == iter->second) { std::string strData; - if (Gunzip(oHttpMsg.body(), strData)) + if (Gunzip(m_oParsingHttpMsg.body(), strData)) { - oHttpMsg.set_body(strData); + m_oParsingHttpMsg.set_body(strData); } else { @@ -596,17 +608,13 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) } if (!m_bChannelIsClient && !http_should_keep_alive(&m_parser)) { - oHttpMsg.set_keep_alive(0.0); + m_oParsingHttpMsg.set_keep_alive(0.0); m_dKeepAlive = 0.0; } - LOG4_DEBUG("%s", ToString(oHttpMsg).c_str()); + oHttpMsg = std::move(m_oParsingHttpMsg); + LOG4_TRACE("%s", ToString(oHttpMsg).c_str()); return(CODEC_STATUS_OK); } - if (m_parser.http_errno == HPE_PAUSED) - { - LOG4_TRACE("decoding..."); - return(CODEC_STATUS_PAUSE); - } LOG4_WARNING("Failed to parse http message for cause:%s, message %s", http_errno_name((http_errno)m_parser.http_errno), pDecodeBuff); return(CODEC_STATUS_ERR); @@ -681,13 +689,15 @@ bool CodecHttp::CloseRightAway() const int CodecHttp::OnMessageBegin(http_parser *parser) { + CodecHttp* pCodec = (CodecHttp*)parser->data; + pCodec->m_bIsDecoding = true; return(0); } int CodecHttp::OnUrl(http_parser *parser, const char *at, size_t len) { - HttpMsg* pHttpMsg = (HttpMsg*) parser->data; - pHttpMsg->set_url(at, len); + CodecHttp* pCodec = (CodecHttp*)parser->data; + pCodec->MutableParsingHttpMsg()->set_url(at, len); struct http_parser_url stUrl; if(0 == http_parser_parse_url(at, len, 0, &stUrl)) { @@ -713,7 +723,7 @@ int CodecHttp::OnUrl(http_parser *parser, const char *at, size_t len) { char *path = (char*)malloc(stUrl.field_data[UF_PATH].len); strncpy(path, at+stUrl.field_data[UF_PATH].off, stUrl.field_data[UF_PATH].len); - pHttpMsg->set_path(path, stUrl.field_data[UF_PATH].len); + pCodec->MutableParsingHttpMsg()->set_path(path, stUrl.field_data[UF_PATH].len); free(path); } @@ -725,7 +735,7 @@ int CodecHttp::OnUrl(http_parser *parser, const char *at, size_t len) DecodeParameter(strQuery, mapParam); for (auto it = mapParam.begin(); it != mapParam.end(); ++it) { - (*pHttpMsg->mutable_params())[it->first] = it->second; + (*pCodec->MutableParsingHttpMsg()->mutable_params())[it->first] = it->second; } } } @@ -734,29 +744,29 @@ int CodecHttp::OnUrl(http_parser *parser, const char *at, size_t len) int CodecHttp::OnStatus(http_parser *parser, const char *at, size_t len) { - HttpMsg* pHttpMsg = (HttpMsg*) parser->data; - pHttpMsg->set_status_code(parser->status_code); + CodecHttp* pCodec = (CodecHttp*) parser->data; + pCodec->MutableParsingHttpMsg()->set_status_code(parser->status_code); return(0); } int CodecHttp::OnHeaderField(http_parser *parser, const char *at, size_t len) { - HttpMsg* pHttpMsg = (HttpMsg*) parser->data; - pHttpMsg->set_body(at, len); // 用body暂存head_name,解析完head_value后再填充到head里 + CodecHttp* pCodec = (CodecHttp*) parser->data; + pCodec->MutableParsingHttpMsg()->set_body(at, len); // 用body暂存head_name,解析完head_value后再填充到head里 return(0); } int CodecHttp::OnHeaderValue(http_parser *parser, const char *at, size_t len) { - HttpMsg* pHttpMsg = (HttpMsg*) parser->data; - std::string strHeadName = pHttpMsg->body(); + CodecHttp* pCodec = (CodecHttp*) parser->data; + std::string strHeadName = pCodec->MutableParsingHttpMsg()->body(); std::string strHeadValue; strHeadValue.assign(at, len); - pHttpMsg->set_body(""); - pHttpMsg->mutable_headers()->insert(google::protobuf::MapPair(strHeadName, strHeadValue)); + pCodec->MutableParsingHttpMsg()->set_body(""); + pCodec->MutableParsingHttpMsg()->mutable_headers()->insert(google::protobuf::MapPair(strHeadName, strHeadValue)); if (strHeadName == std::string("Keep-Alive")) { - pHttpMsg->set_keep_alive(atof(strHeadValue.c_str())); + pCodec->MutableParsingHttpMsg()->set_keep_alive(atof(strHeadValue.c_str())); } else if (std::string("Connection") == strHeadName) { @@ -764,16 +774,16 @@ int CodecHttp::OnHeaderValue(http_parser *parser, const char *at, size_t len) uiPos = strHeadValue.find_first_of("Upgrade"); if (0 == uiPos) { - pHttpMsg->mutable_upgrade()->set_is_upgrade(true); + pCodec->MutableParsingHttpMsg()->mutable_upgrade()->set_is_upgrade(true); } else { - pHttpMsg->mutable_upgrade()->set_is_upgrade(false); + pCodec->MutableParsingHttpMsg()->mutable_upgrade()->set_is_upgrade(false); } } else if (std::string("Upgrade") == strHeadName) { - pHttpMsg->mutable_upgrade()->set_protocol(strHeadValue); + pCodec->MutableParsingHttpMsg()->mutable_upgrade()->set_protocol(strHeadValue); } return(0); } @@ -785,33 +795,33 @@ int CodecHttp::OnHeadersComplete(http_parser *parser) int CodecHttp::OnBody(http_parser *parser, const char *at, size_t len) { - HttpMsg* pHttpMsg = (HttpMsg*) parser->data; - if (pHttpMsg->body().size() > 0) + CodecHttp* pCodec = (CodecHttp*) parser->data; + if (pCodec->MutableParsingHttpMsg()->body().size() > 0) { - pHttpMsg->mutable_body()->append(at, len); + pCodec->MutableParsingHttpMsg()->mutable_body()->append(at, len); } else { - pHttpMsg->set_body(at, len); + pCodec->MutableParsingHttpMsg()->set_body(at, len); } return(0); } int CodecHttp::OnMessageComplete(http_parser *parser) { - HttpMsg* pHttpMsg = (HttpMsg*) parser->data; + CodecHttp* pCodec = (CodecHttp*) parser->data; if (0 != parser->status_code) { - pHttpMsg->set_status_code(parser->status_code); - pHttpMsg->set_type(HTTP_RESPONSE); + pCodec->MutableParsingHttpMsg()->set_status_code(parser->status_code); + pCodec->MutableParsingHttpMsg()->set_type(HTTP_RESPONSE); } else { - pHttpMsg->set_method(parser->method); - pHttpMsg->set_type(HTTP_REQUEST); + pCodec->MutableParsingHttpMsg()->set_method(parser->method); + pCodec->MutableParsingHttpMsg()->set_type(HTTP_REQUEST); } - pHttpMsg->set_http_major(parser->http_major); - pHttpMsg->set_http_minor(parser->http_minor); + pCodec->MutableParsingHttpMsg()->set_http_major(parser->http_major); + pCodec->MutableParsingHttpMsg()->set_http_minor(parser->http_minor); return(0); } diff --git a/src/codec/CodecHttp.hpp b/src/codec/CodecHttp.hpp index 9a18346f..24693c6c 100644 --- a/src/codec/CodecHttp.hpp +++ b/src/codec/CodecHttp.hpp @@ -57,8 +57,14 @@ class CodecHttp: public Codec static int OnChunkHeader(http_parser *parser); static int OnChunkComplete(http_parser *parser); + HttpMsg* MutableParsingHttpMsg() + { + return(&m_oParsingHttpMsg); + } + private: bool m_bChannelIsClient; // 当前编解码器所在channel是作为http客户端还是作为http服务端 + bool m_bIsDecoding; // 是否編解碼完成 uint32 m_uiEncodedNum; uint32 m_uiDecodedNum; int32 m_iHttpMajor; diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index 47665608..5e971ab4 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -104,7 +104,7 @@ E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply) oReply.set_type(REDIS_REPLY_ERROR); oReply.set_integer(REDIS_ERR_PROTOCOL); pBuff->SetReadIndex(uiReadIndex); - LOG4_TRACE("cFirstByte = %d", int(cFirstByte)); + LOG4_ERROR("cFirstByte = %d", int(cFirstByte)); return(CODEC_STATUS_ERR); } if (CODEC_STATUS_OK != eStatus) @@ -237,7 +237,7 @@ E_CODEC_STATUS CodecResp::DecodeSimpleString(CBuffer* pBuff, RedisReply& oReply) } else { - LOG4_TRACE("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", + LOG4_ERROR("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", uiReadableBytes, i, int(cLastChar), (int)pData[i]); return(CODEC_STATUS_ERR); } @@ -274,7 +274,7 @@ E_CODEC_STATUS CodecResp::DecodeError(CBuffer* pBuff, RedisReply& oReply) } else { - LOG4_TRACE("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", + LOG4_ERROR("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", uiReadableBytes, i, int(cLastChar), (int)pData[i]); return(CODEC_STATUS_ERR); } @@ -310,7 +310,7 @@ E_CODEC_STATUS CodecResp::DecodeInteger(CBuffer* pBuff, RedisReply& oReply) } else { - LOG4_TRACE("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", + LOG4_ERROR("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", uiReadableBytes, i, int(cLastChar), (int)pData[i]); return(CODEC_STATUS_ERR); } @@ -359,19 +359,16 @@ E_CODEC_STATUS CodecResp::DecodeBulkString(CBuffer* pBuff, RedisReply& oReply) pBuff->AdvanceReadIndex(i + 1); return(CODEC_STATUS_OK); } - if (uiReadableBytes - i < (uint32)iStringLen) + i += (uint32)iStringLen; + if (uiReadableBytes < (i + 1 + 2)) // not enough data (pos i + 1 is the end of bulk string) { return(CODEC_STATUS_PAUSE); } - else - { - i += (uint32)iStringLen; - } } } else { - LOG4_TRACE("uiReadableBytes = %u, i = %d, iStringLen = %d, cLastChar = %d, pData[i] = %d", + LOG4_ERROR("uiReadableBytes = %u, i = %d, iStringLen = %d, cLastChar = %d, pData[i] = %d", uiReadableBytes, i, iStringLen, int(cLastChar), (int)pData[i]); return(CODEC_STATUS_ERR); } @@ -444,7 +441,7 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) default: oReply.set_type(REDIS_REPLY_ERROR); oReply.set_integer(REDIS_ERR_PROTOCOL); - LOG4_TRACE("uiReadableBytes = %u, i = %d, cFirstByte = %d, cLastChar = %d, pData[i] = %d", + LOG4_ERROR("uiReadableBytes = %u, i = %d, cFirstByte = %d, cLastChar = %d, pData[i] = %d", uiReadableBytes, i, (int)cFirstByte, (int)cLastChar, (int)pData[i]); return(CODEC_STATUS_ERR); } @@ -459,7 +456,7 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) } else { - LOG4_TRACE("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", + LOG4_ERROR("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", uiReadableBytes, i, (int)cLastChar, (int)pData[i]); return(CODEC_STATUS_ERR); } diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index 5479f66d..3e82410f 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -588,8 +588,13 @@ void CodecHttp2::UpdateSendWindow(uint32 uiStreamId, uint32 uiSendLength) void CodecHttp2::UpdateRecvWindow(uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) { - m_pFrame->EncodeWindowUpdate(this, 0, uiRecvLength, pBuff); - m_uiRecvWindowSize = SETTINGS_MAX_INITIAL_WINDOW_SIZE; + m_uiRecvWindowSize -= uiRecvLength; + if (m_uiRecvWindowSize < DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE / 4) + { + m_pFrame->EncodeWindowUpdate(this, 0, + SETTINGS_MAX_INITIAL_WINDOW_SIZE - uiRecvLength, pBuff); + m_uiRecvWindowSize = SETTINGS_MAX_INITIAL_WINDOW_SIZE; + } if (uiStreamId > 0) { auto iter = m_mapStream.find(uiStreamId); diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index a38a98ce..bc88a272 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -424,12 +424,18 @@ void Http2Stream::WindowUpdate(int32 iIncrement) void Http2Stream::UpdateRecvWindow(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) { + m_uiRecvWindowSize -= uiRecvLength; if (m_eStreamState == H2_STREAM_OPEN || m_eStreamState == H2_STREAM_IDLE || m_eStreamState == H2_STREAM_RESERVED_LOCAL || m_eStreamState == H2_STREAM_RESERVED_REMOTE) { - m_pFrame->EncodeWindowUpdate(pCodecH2, uiStreamId, uiRecvLength, pBuff); + if (m_uiRecvWindowSize < DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE / 4) + { + m_pFrame->EncodeWindowUpdate(pCodecH2, uiStreamId, + SETTINGS_MAX_INITIAL_WINDOW_SIZE - uiRecvLength, pBuff); + m_uiRecvWindowSize = SETTINGS_MAX_INITIAL_WINDOW_SIZE; + } } } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 9c4dde7f..0f942eba 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -884,7 +884,7 @@ std::shared_ptr Dispatcher::StressSend(const std::string& strIden setsockopt(iFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)); setsockopt(iFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)); setsockopt(iFd, IPPROTO_TCP, TCP_QUICKACK, (void*)&iTcpQuickAck, sizeof(iTcpQuickAck)); - std::shared_ptr pChannel = CreateSocketChannel(iFd, eCodecType); + std::shared_ptr pChannel = CreateSocketChannel(iFd, eCodecType, true); if (nullptr != pChannel) { connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); From dbe62d5b0de598f3f16514e76ce8145d01313510 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 28 Aug 2021 10:43:33 +0800 Subject: [PATCH 146/176] circuit breaker optimization; http2 bug fixed --- src/Definition.hpp | 9 +++ src/actor/Actor.cpp | 23 ++++--- src/actor/Actor.hpp | 9 ++- src/actor/ActorSender.cpp | 14 ++-- src/actor/step/sys_step/StepRedisCluster.cpp | 3 +- .../sys_step/manager/StepReportToBeacon.cpp | 2 +- src/codec/CodecResp.cpp | 2 +- src/codec/http2/CodecHttp2.cpp | 2 - src/codec/http2/Http2Stream.cpp | 4 +- src/ios/Dispatcher.cpp | 23 ++++--- src/ios/Dispatcher.hpp | 68 ++++++++++-------- src/ios/Nodes.cpp | 69 ++++++++++++++++--- src/ios/Nodes.hpp | 3 + src/labor/Manager.cpp | 10 +++ src/labor/NodeInfo.hpp | 1 + 15 files changed, 169 insertions(+), 73 deletions(-) diff --git a/src/Definition.hpp b/src/Definition.hpp index b95531c3..d18df04d 100644 --- a/src/Definition.hpp +++ b/src/Definition.hpp @@ -169,6 +169,15 @@ const ev_tstamp gc_dNoTimeout = -1; const ev_tstamp gc_dConfigTimeout = 0; const ev_tstamp gc_dDefaultTimeout = 30.0; +enum E_SOCKET_TYPE +{ + SOCKET_STREAM = 1, ///< tcp + SOCKET_DGRAM = 2, ///< udp + SOCKET_RAW = 3, ///< + SOCKET_RDW = 4, ///< + SOCKET_SEQPACKET = 5, ///< +}; + /** * @brief 命令执行状态 */ diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 545461e9..cc46d376 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -178,7 +178,7 @@ bool Actor::SendTo(std::shared_ptr pChannel, const char* pRawData bool Actor::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendTo(strIdentify, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); + return(m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); } bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq) @@ -201,17 +201,17 @@ bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMs } if (oHttpMsg.http_major() == 2) { - return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP2, bWithSsl, bPipeline, oHttpMsg, GetSequence())); + return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, SOCKET_STREAM, CODEC_HTTP2, bWithSsl, bPipeline, oHttpMsg, GetSequence())); } else { - return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, GetSequence())); + return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, SOCKET_STREAM, CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, GetSequence())); } } bool Actor::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); + return(m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); } bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) @@ -221,12 +221,12 @@ bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedis bool Actor::SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline) { - return(m_pLabor->GetDispatcher()->SendRoundRobin(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); + return(m_pLabor->GetDispatcher()->SendRoundRobin(strIdentify, SOCKET_STREAM, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); } bool Actor::SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_UNKNOW, bWithSsl, bPipeline, pRawData, uiRawDataSize, GetSequence())); + return(m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, CODEC_UNKNOW, bWithSsl, bPipeline, pRawData, uiRawDataSize, GetSequence())); } bool Actor::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) @@ -238,13 +238,13 @@ bool Actor::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); + return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, SOCKET_STREAM, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); } bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, eCodecType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); + return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, SOCKET_STREAM, eCodecType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); } bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) @@ -258,7 +258,7 @@ bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSe } else if (oMsgBody.req_target().route().length() > 0) { - return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, eCodecType, false, true, oMsgBody.req_target().route(), iCmd, uiSeq, oMsgBody)); + return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, SOCKET_STREAM, eCodecType, false, true, oMsgBody.req_target().route(), iCmd, uiSeq, oMsgBody)); } else { @@ -278,6 +278,11 @@ bool Actor::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) return(m_pLabor->GetDispatcher()->SendDataReport(iCmd, uiSeq, oMsgBody)); } +void Actor::CircuitBreak(const std::string& strIdentify) +{ + m_pLabor->GetDispatcher()->CircuitBreak(strIdentify); +} + bool Actor::SendToSelf(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { return(m_pLabor->GetDispatcher()->SendToSelf(iCmd, uiSeq, oMsgBody, GetSequence())); diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index a31b8775..fb83513c 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -191,7 +191,8 @@ class Actor: public std::enable_shared_from_this * @param oCodecType 编解码方式 * @return 是否发送成功 */ - virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + virtual bool SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, + const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); /** * @brief 发送http请求 @@ -283,6 +284,12 @@ class Actor: public std::enable_shared_from_this virtual bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + /** + * @brief 断路器 + * @note 连接错误框架会自动熔断,如果需要主动熔断(比如超时率太高)可主动调用此函数进行熔断。 + */ + void CircuitBreak(const std::string& strIdentify); + /** * @brief 发送请求到当前worker */ diff --git a/src/actor/ActorSender.cpp b/src/actor/ActorSender.cpp index 2908fb3c..d75e7135 100644 --- a/src/actor/ActorSender.cpp +++ b/src/actor/ActorSender.cpp @@ -57,7 +57,7 @@ bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); - return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); + return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); } bool ActorSender::SendTo(Actor* pActor, const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq) @@ -90,7 +90,7 @@ bool ActorSender::SendTo(Actor* pActor, const std::string& strHost, int iPort, c bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, pActor->GetSequence())); + return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, pActor->GetSequence())); } bool ActorSender::SendToCluster(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) @@ -100,12 +100,12 @@ bool ActorSender::SendToCluster(Actor* pActor, const std::string& strIdentify, c bool ActorSender::SendRoundRobin(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline) { - return(pActor->m_pLabor->GetDispatcher()->SendRoundRobin(strIdentify, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, pActor->GetSequence())); + return(pActor->m_pLabor->GetDispatcher()->SendRoundRobin(strIdentify, SOCKET_STREAM, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, pActor->GetSequence())); } bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, CODEC_UNKNOW, bWithSsl, bPipeline, pRawData, uiRawDataSize, pActor->GetSequence())); + return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, CODEC_UNKNOW, bWithSsl, bPipeline, pRawData, uiRawDataSize, pActor->GetSequence())); } bool ActorSender::SendTo(Actor* pActor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) @@ -117,13 +117,13 @@ bool ActorSender::SendTo(Actor* pActor, int32 iCmd, uint32 uiSeq, const MsgBody& bool ActorSender::SendRoundRobin(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); - return(pActor->m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); + return(pActor->m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, SOCKET_STREAM, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); } bool ActorSender::SendOriented(Actor* pActor, const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); - return(pActor->m_pLabor->GetDispatcher()->SendOriented(strNodeType, eCodecType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); + return(pActor->m_pLabor->GetDispatcher()->SendOriented(strNodeType, SOCKET_STREAM, eCodecType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); } bool ActorSender::SendOriented(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) @@ -137,7 +137,7 @@ bool ActorSender::SendOriented(Actor* pActor, const std::string& strNodeType, in } else if (oMsgBody.req_target().route().length() > 0) { - return(pActor->m_pLabor->GetDispatcher()->SendOriented(strNodeType, eCodecType, false, true, oMsgBody.req_target().route(), iCmd, uiSeq, oMsgBody)); + return(pActor->m_pLabor->GetDispatcher()->SendOriented(strNodeType, SOCKET_STREAM, eCodecType, false, true, oMsgBody.req_target().route(), iCmd, uiSeq, oMsgBody)); } else { diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index e3fd4143..6295466e 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -431,7 +431,8 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, const RedisMsg& oR bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptr pRedisMsg) { LOG4_TRACE("%s", pRedisMsg->DebugString().c_str()); - bool bResult = GetLabor(this)->GetDispatcher()->SendTo(strIdentify, CODEC_RESP, m_bWithSsl, m_bPipeline, (*pRedisMsg.get()), GetSequence()); + bool bResult = GetLabor(this)->GetDispatcher()->SendTo(strIdentify, + SOCKET_STREAM, CODEC_RESP, m_bWithSsl, m_bPipeline, (*pRedisMsg.get()), GetSequence()); if (bResult) { auto iter = m_mapPipelineRequest.find(strIdentify); diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp index cfff4e18..9ddff3dc 100644 --- a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp +++ b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp @@ -46,7 +46,7 @@ E_CMD_STATUS StepReportToBeacon::Emit( } m_pSessionManager->MakeReportData(oReportData); oMsgBody.set_data(oReportData.ToString()); - GetLabor(this)->GetDispatcher()->Broadcast("BEACON", CODEC_NEBULA, false, true, + GetLabor(this)->GetDispatcher()->Broadcast("BEACON", SOCKET_STREAM, CODEC_NEBULA, false, true, (int32)CMD_REQ_NODE_STATUS_REPORT, GetSequence(), oMsgBody); return(CMD_STATUS_RUNNING); } diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index 5e971ab4..f3a9335d 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -104,7 +104,7 @@ E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply) oReply.set_type(REDIS_REPLY_ERROR); oReply.set_integer(REDIS_ERR_PROTOCOL); pBuff->SetReadIndex(uiReadIndex); - LOG4_ERROR("cFirstByte = %d", int(cFirstByte)); + LOG4_TRACE("cFirstByte = %d", int(cFirstByte)); return(CODEC_STATUS_ERR); } if (CODEC_STATUS_OK != eStatus) diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index 3e82410f..b0ec107c 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -215,8 +215,6 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR oHttpMsg = std::move(*m_pHoldingHttpMsg); oHttpMsg.set_http_major(2); oHttpMsg.set_http_minor(0); - oHttpMsg.mutable_headers()->erase( - oHttpMsg.mutable_headers()->find("Connection")); oHttpMsg.mutable_upgrade()->set_is_upgrade(false); oHttpMsg.mutable_upgrade()->set_protocol(""); oHttpMsg.set_stream_id(1); diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index bc88a272..aa0b5a96 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -223,9 +223,9 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, case H2_FRAME_PRIORITY: break; default: + LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", (int)m_eStreamState, (uint32)stFrameHead.ucType); m_pFrame->EncodeRstStream(pCodecH2, stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); - LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", (int)m_eStreamState, (uint32)stFrameHead.ucType); return(CODEC_STATUS_PART_ERR); } break; @@ -235,8 +235,6 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, case H2_FRAME_PRIORITY: break; default: - m_pFrame->EncodeRstStream(pCodecH2, - stFrameHead.uiStreamIdentifier, H2_ERR_STREAM_CLOSED, pReactBuff); LOG4_ERROR("m_eStreamState = %d, stFrameHead.ucType = %u", (int)m_eStreamState, (uint32)stFrameHead.ucType); return(CODEC_STATUS_PART_ERR); } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 0f942eba..830a69a7 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -333,7 +333,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); } } - LOG4_TRACE("eCodecStatus = %d", eCodecStatus); + LOG4_INFO("eCodecStatus = %d", eCodecStatus); DiscardSocketChannel(pChannel); return(false); } @@ -481,7 +481,7 @@ bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); } } - LOG4_TRACE("eCodecStatus = %d", eCodecStatus); + LOG4_INFO("eCodecStatus = %d", eCodecStatus); DiscardSocketChannel(pChannel); return(false); } @@ -676,7 +676,7 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) } else { - LOG4_TRACE("%s", pChannel->GetIdentify().c_str()); + LOG4_INFO("%s fd[%d]", pChannel->GetIdentify().c_str(), pChannel->GetFd()); DiscardSocketChannel(pChannel); } return(true); @@ -787,7 +787,7 @@ bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBod { if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) { - return(Broadcast("BEACON", CODEC_NEBULA, false, true, iCmd, uiSeq, oMsgBody)); + return(Broadcast("BEACON", SOCK_STREAM, CODEC_NEBULA, false, true, iCmd, uiSeq, oMsgBody)); } else { @@ -972,7 +972,7 @@ std::shared_ptr Dispatcher::GetLastActivityChannel() bool Dispatcher::Disconnect(std::shared_ptr pChannel, bool bChannelNotice) { - LOG4_TRACE("%s", pChannel->GetIdentify().c_str()); + LOG4_INFO("%s", pChannel->GetIdentify().c_str()); return(DiscardSocketChannel(pChannel, bChannelNotice)); } @@ -985,11 +985,11 @@ bool Dispatcher::Disconnect(const std::string& strIdentify, bool bChannelNotice) while (named_iter->second.size() > 1) { channel_iter = named_iter->second.begin(); - LOG4_TRACE("%s", (*channel_iter)->GetIdentify().c_str()); + LOG4_INFO("%s", (*channel_iter)->GetIdentify().c_str()); DiscardSocketChannel(*channel_iter, bChannelNotice); } channel_iter = named_iter->second.begin(); - LOG4_TRACE("%s", (*channel_iter)->GetIdentify().c_str()); + LOG4_INFO("%s", (*channel_iter)->GetIdentify().c_str()); return(DiscardSocketChannel(*channel_iter, bChannelNotice)); } return(false); @@ -1088,6 +1088,11 @@ void Dispatcher::DelNodeIdentify(const std::string& strNodeType, const std::stri } } +void Dispatcher::CircuitBreak(const std::string& strIdentify) +{ + m_pSessionNode->NodeFailed(strIdentify); +} + void Dispatcher::SetClientData(std::shared_ptr pChannel, const std::string& strClientData) { pChannel->m_pImpl->SetClientData(strClientData); @@ -1179,7 +1184,7 @@ bool Dispatcher::CreateListenFd(const std::string& strHost, int32 iPort, int& iF struct addrinfo* pAddrCurrent; memset(&stAddrHints, 0, sizeof(struct addrinfo)); stAddrHints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - stAddrHints.ai_socktype = SOCK_STREAM; + stAddrHints.ai_socktype = m_pLabor->GetNodeInfo().iForClientSocketType; stAddrHints.ai_protocol = IPPROTO_IP; stAddrHints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ int iCode = getaddrinfo(strHost.c_str(), @@ -1375,7 +1380,7 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b bool bCloseResult = pChannel->m_pImpl->Close(); if (bCloseResult) { - LOG4_TRACE("%s disconnect, fd %d, channel_seq %u, identify %s", + LOG4_INFO("%s disconnect, fd %d, channel_seq %u, identify %s", pChannel->m_pImpl->GetRemoteAddr().c_str(), pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence(), pChannel->m_pImpl->GetIdentify().c_str()); diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 5a8c5e43..29c98412 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -120,23 +120,22 @@ class Dispatcher template bool SendTo(std::shared_ptr pChannel, Targs&&... args); template - bool SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); + bool SendTo(const std::string& strIdentify, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); template - bool SendTo(const std::string& strHost, int iPort, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); + bool SendTo(const std::string& strHost, int iPort, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); template bool AutoSend(const std::string& strIdentify, const std::string& strHost, - int iPort, int iRemoteWorkerIndex, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); + int iPort, int iRemoteWorkerIndex, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); template - bool SendRoundRobin(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); + bool SendRoundRobin(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); template - bool SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, + bool SendOriented(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args); template - bool SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, + bool SendOriented(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args); template - bool Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); - bool AutoSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + bool Broadcast(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); std::shared_ptr StressSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); @@ -156,6 +155,7 @@ class Dispatcher void DelNamedSocketChannel(const std::string& strIdentify); void AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); void DelNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); + void CircuitBreak(const std::string& strIdentify); void SetClientData(std::shared_ptr pChannel, const std::string& strClientData); bool IsNodeType(const std::string& strNodeIdentify, const std::string& strNodeType); @@ -277,7 +277,7 @@ bool Dispatcher::SendTo(std::shared_ptr pChannel, Targs&&... args } template -bool Dispatcher::SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool Dispatcher::SendTo(const std::string& strIdentify, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) { LOG4_TRACE("identify: %s", strIdentify.c_str()); // 将strIdentify分割的功能只在此SendTo()函数内两处调用,定义为Dispatcher的成员函数语义上不太合适,故定义lambda表达式 @@ -335,7 +335,7 @@ bool Dispatcher::SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, LOG4_ERROR("%s", strError.c_str()); return(false); } - return(AutoSend(strIdentify, strHost, iPort, iWorkerIndex, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + return(AutoSend(strIdentify, strHost, iPort, iWorkerIndex, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); } else { @@ -350,7 +350,7 @@ bool Dispatcher::SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, LOG4_ERROR("%s", strError.c_str()); return(false); } - return(AutoSend(strIdentify, strHost, iPort, iWorkerIndex, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + return(AutoSend(strIdentify, strHost, iPort, iWorkerIndex, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); } else { @@ -366,7 +366,7 @@ bool Dispatcher::SendTo(const std::string& strIdentify, E_CODEC_TYPE eCodecType, } template -bool Dispatcher::SendTo(const std::string& strHost, int iPort, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool Dispatcher::SendTo(const std::string& strHost, int iPort, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) { LOG4_TRACE("host %s port %d", strHost.c_str(), iPort); std::ostringstream ossIdentify; @@ -375,14 +375,14 @@ bool Dispatcher::SendTo(const std::string& strHost, int iPort, E_CODEC_TYPE eCod if (named_iter == m_mapNamedSocketChannel.end()) { LOG4_TRACE("no channel match %s.", ossIdentify.str().c_str()); - return(AutoSend(ossIdentify.str(), strHost, iPort, 0, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + return(AutoSend(ossIdentify.str(), strHost, iPort, 0, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); } else { auto channel_iter = named_iter->second.begin(); if (channel_iter == named_iter->second.end()) { - return(AutoSend(ossIdentify.str(), strHost, iPort, 0, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + return(AutoSend(ossIdentify.str(), strHost, iPort, 0, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); } bool bResult = SendTo((*channel_iter), std::forward(args)...); if (!bPipeline && bResult) @@ -396,7 +396,7 @@ bool Dispatcher::SendTo(const std::string& strHost, int iPort, E_CODEC_TYPE eCod template bool Dispatcher::AutoSend( const std::string& strIdentify, const std::string& strHost, int iPort, - int iRemoteWorkerIndex, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) + int iRemoteWorkerIndex, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) { LOG4_TRACE("%s", strIdentify.c_str()); struct addrinfo stAddrHints; @@ -404,7 +404,7 @@ bool Dispatcher::AutoSend( struct addrinfo* pAddrCurrent; memset(&stAddrHints, 0, sizeof(struct addrinfo)); stAddrHints.ai_family = AF_UNSPEC; - stAddrHints.ai_socktype = SOCK_STREAM; + stAddrHints.ai_socktype = iSocketType; stAddrHints.ai_protocol = IPPROTO_IP; int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); if (0 != iCode) @@ -489,20 +489,24 @@ bool Dispatcher::AutoSend( } template -bool Dispatcher::SendRoundRobin(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; + if (m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) + { + SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline); + } if (m_pSessionNode->GetNode(strNodeType, strOnlineNode)) { - return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); } else { LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) { - return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); } LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); return(false); @@ -510,20 +514,24 @@ bool Dispatcher::SendRoundRobin(const std::string& strNodeType, E_CODEC_TYPE eCo } template -bool Dispatcher::SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args) +bool Dispatcher::SendOriented(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; + if (m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) + { + SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline); + } if (m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) { - return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); } else { LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) { - return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); } LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); return(false); @@ -531,20 +539,24 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCode } template -bool Dispatcher::SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args) +bool Dispatcher::SendOriented(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; + if (m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) + { + SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline); + } if (m_pSessionNode->GetNode(strNodeType, strFactor, strOnlineNode)) { - return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); } else { LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) { - return(SendTo(strOnlineNode, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); + return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); } LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); return(false); @@ -552,7 +564,7 @@ bool Dispatcher::SendOriented(const std::string& strNodeType, E_CODEC_TYPE eCode } template -bool Dispatcher::Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool Dispatcher::Broadcast(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) { LOG4_TRACE("node_type: %s", strNodeType.c_str()); std::unordered_set setOnlineNodes; @@ -561,7 +573,7 @@ bool Dispatcher::Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecTy bool bSendResult = false; for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) { - bSendResult |= SendTo(*node_iter, eCodecType, bWithSsl, bPipeline, std::forward(args)...); + bSendResult |= SendTo(*node_iter, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...); } return(bSendResult); } @@ -582,7 +594,7 @@ bool Dispatcher::Broadcast(const std::string& strNodeType, E_CODEC_TYPE eCodecTy bool bSendResult = false; for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) { - bSendResult |= SendTo(*node_iter, eCodecType, bWithSsl, bPipeline, std::forward(args)...); + bSendResult |= SendTo(*node_iter, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...); } return(bSendResult); } diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index 6ef323ee..4f1bfe42 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -56,14 +56,19 @@ bool Nodes::GetNode(const std::string& strNodeType, const std::string& strHashKe } else { - if (node_type_iter->second->bCheckFailedNode) + if (node_type_iter->second->mapHash2Node.size() == 0) { - node_type_iter->second->bCheckFailedNode = false; if (node_type_iter->second->setFailedNode.size() > 0) { - strNodeIdentify = *(node_type_iter->second->setFailedNode.begin()); + if (node_type_iter->second->itPollingFailed == node_type_iter->second->setFailedNode.end()) + { + node_type_iter->second->itPollingFailed = node_type_iter->second->setFailedNode.begin(); + } + strNodeIdentify = *(node_type_iter->second->itPollingFailed); + node_type_iter->second->itPollingFailed++; return(true); } + return(false); } auto c_iter = node_type_iter->second->mapHash2Node.lower_bound(uiKeyHash); if (c_iter == node_type_iter->second->mapHash2Node.end()) @@ -88,14 +93,19 @@ bool Nodes::GetNode(const std::string& strNodeType, uint32 uiHash, std::string& } else { - if (node_type_iter->second->bCheckFailedNode) + if (node_type_iter->second->mapHash2Node.size() == 0) { - node_type_iter->second->bCheckFailedNode = false; if (node_type_iter->second->setFailedNode.size() > 0) { - strNodeIdentify = *(node_type_iter->second->setFailedNode.begin()); + if (node_type_iter->second->itPollingFailed == node_type_iter->second->setFailedNode.end()) + { + node_type_iter->second->itPollingFailed = node_type_iter->second->setFailedNode.begin(); + } + strNodeIdentify = *(node_type_iter->second->itPollingFailed); + node_type_iter->second->itPollingFailed++; return(true); } + return(false); } auto c_iter = node_type_iter->second->mapHash2Node.lower_bound(uiHash); if (c_iter == node_type_iter->second->mapHash2Node.end()) @@ -120,12 +130,16 @@ bool Nodes::GetNodeInHashRing(const std::string& strNodeType, std::string& strNo } else { - node_type_iter->second->itHashRing++; + if (node_type_iter->second->mapHash2Node.size() == 0) + { + return(false); + } if (node_type_iter->second->itHashRing == node_type_iter->second->mapHash2Node.end()) { node_type_iter->second->itHashRing = node_type_iter->second->mapHash2Node.begin(); } strNodeIdentify = node_type_iter->second->itHashRing->first; + node_type_iter->second->itHashRing++; return(true); } } @@ -139,21 +153,26 @@ bool Nodes::GetNode(const std::string& strNodeType, std::string& strNodeIdentify } else { - if (node_type_iter->second->bCheckFailedNode) + if (node_type_iter->second->mapHash2Node.size() == 0) { - node_type_iter->second->bCheckFailedNode = false; if (node_type_iter->second->setFailedNode.size() > 0) { - strNodeIdentify = *(node_type_iter->second->setFailedNode.begin()); + if (node_type_iter->second->itPollingFailed == node_type_iter->second->setFailedNode.end()) + { + node_type_iter->second->itPollingFailed = node_type_iter->second->setFailedNode.begin(); + } + strNodeIdentify = *(node_type_iter->second->itPollingFailed); + node_type_iter->second->itPollingFailed++; return(true); } + return(false); } - node_type_iter->second->itPollingNode++; if (node_type_iter->second->itPollingNode == node_type_iter->second->mapNode2Hash.end()) { node_type_iter->second->itPollingNode = node_type_iter->second->mapNode2Hash.begin(); } strNodeIdentify = node_type_iter->second->itPollingNode->first; + node_type_iter->second->itPollingNode++; return(true); } } @@ -175,6 +194,33 @@ bool Nodes::GetNode(const std::string& strNodeType, std::unordered_setsecond->bCheckFailedNode) + { + node_type_iter->second->bCheckFailedNode = false; + if (node_type_iter->second->setFailedNode.size() > 0) + { + if (node_type_iter->second->itPollingFailed == node_type_iter->second->setFailedNode.end()) + { + node_type_iter->second->itPollingFailed = node_type_iter->second->setFailedNode.begin(); + } + strNodeIdentify = *(node_type_iter->second->itPollingFailed); + node_type_iter->second->itPollingFailed++; + return(true); + } + } + return(false); + } +} + void Nodes::AddNode(const std::string& strNodeType, const std::string& strNodeIdentify) { auto node_type_iter = m_mapNode.find(strNodeType); @@ -432,6 +478,7 @@ void Nodes::NodeFailed(const std::string& strNodeIdentify) node_type_iter->second->itHashRing = node_type_iter->second->mapHash2Node.begin(); } node_type_iter->second->setFailedNode.insert(strNodeIdentify); + node_type_iter->second->itPollingFailed = node_type_iter->second->setFailedNode.begin(); } } } diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index f9e982ba..29d2fcbc 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -62,6 +62,7 @@ class Nodes std::map mapHash2Node; std::map::const_iterator itHashRing; std::unordered_set setFailedNode; + std::unordered_set::const_iterator itPollingFailed; tagNode(){} ~tagNode(){} @@ -88,6 +89,8 @@ class Nodes bool GetNode(const std::string& strNodeType, std::unordered_set& setNodeIdentify); + bool NodeDetect(const std::string& strNodeType, std::string& strNodeIdentify); + /** * @brief 添加节点 * @note 添加节点信息,每个节点均有一个主节点一个被节点构成。 diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index e5c5d8a4..ad770e59 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -331,6 +331,7 @@ bool Manager::GetConf() m_oCurrentConf.Get("data_report", m_stNodeInfo.dDataReportInterval); if (m_oLastConf.ToString().length() == 0) { + std::string strSocketType = "TCP"; m_stNodeInfo.uiWorkerNum = strtoul(m_oCurrentConf("worker_num").c_str(), NULL, 10); m_oCurrentConf.Get("node_type", m_stNodeInfo.strNodeType); m_oCurrentConf.Get("host", m_stNodeInfo.strHostForServer); @@ -339,6 +340,15 @@ bool Manager::GetConf() m_oCurrentConf.Get("access_port", m_stNodeInfo.iPortForClient); m_oCurrentConf.Get("gateway", m_stNodeInfo.strGateway); m_oCurrentConf.Get("gateway_port", m_stNodeInfo.iGatewayPort); + m_oCurrentConf.Get("access_socket_type", strSocketType); + if (strSocketType == "UDP" || strSocketType == "udp") + { + m_stNodeInfo.iForClientSocketType = SOCK_DGRAM; + } + else + { + m_stNodeInfo.iForClientSocketType = SOCK_STREAM; + } m_stNodeInfo.strNodeIdentify = m_stNodeInfo.strHostForServer + std::string(":") + std::to_string(m_stNodeInfo.iPortForServer); } int32 iCodec; diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index ffdca973..3a51940f 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -30,6 +30,7 @@ struct NodeInfo int32 iMsgPermitNum = 0; ///< 客户端统计时间内允许发送消息数量 int32 iPortForServer = 0; ///< Server间通信监听端口,对应 iS2SListenFd int32 iPortForClient = 0; ///< 对Client通信监听端口,对应 iC2SListenFd + int32 iForClientSocketType = 0; ///< 对Client通信的socket类型 int32 iGatewayPort = 0; ///< 对Client服务的真实端口 bool bThreadMode = 0; ///< 是否线程模型 bool bIsAccess = false; ///< 是否接入Server From 828db06b21f21e3d6960504ee0d39345819e5bba Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 25 Sep 2021 09:49:44 +0800 Subject: [PATCH 147/176] v1.6.2 release --- README.md | 5 +- README_cn.md | 51 +++-- src/actor/step/sys_step/StepRedisCluster.cpp | 195 ++++++++++--------- 3 files changed, 142 insertions(+), 109 deletions(-) diff --git a/README.md b/README.md index ce5bf34e..759e6aa0 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ English | [中文](/README_cn.md)         Nebula is a flexible, high-performance business-oriented IoC distributed network framework developed in C++ language, designed for production environments. It supports multiple application layer communication protocols including proto3, http, https, http2, grpc, and websocket. Nebula makes it easy to deploy a fast and high-performance distributed service with C++, while keeping the same server architecture and APIs. NebulaBootstrap provides out-of-the-box integration with Nebula service, but can be easily extended to serve other types of application. -Nebula is a production level framework and distributed solution project for instant messaging, data collection, real-time computing, message push and other applications, as well as web api services. There were production applications for instant messaging, data acquisition and real-time analysis on line now, and a recommendation engine application for a large user base will be born soon. By the way, using Nebula for toy-level projects is also good for learning network communication. Bwar welcomes more developers to join the Nebula project. Nebula is a proactor development framework(a framework-implemented proactor, not an operating system support). The IO-intensive application on nebula with be good performance. +Nebula is a production level framework and distributed solution project for instant messaging, data collection, real-time computing, message push and other applications, as well as web api services. There were production applications for instant messaging, data acquisition and real-time analysis on line now, and a recommendation engine application for a large user base will be born soon. By the way, using Nebula for toy-level projects is also good for learning network communication. Bwar welcomes more developers to join the Nebula project. Nebula can be used as a single high-performance TCP server, but building a cluster based on Nebula will be truly reflect its value. In order to build distributed service clusters quickly, Nebula Bootstrap cluster solutions including various types of services have been developed. @@ -128,6 +128,9 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v1.6.2 + - circuit breaker optimization + - http chunk decode bug fixed; resp bulk string bug fixed #### v1.6.1 - http2 chunk notice for stream - CodecResp bug string decode bug fixed diff --git a/README_cn.md b/README_cn.md index 74275dc1..2daf714a 100644 --- a/README_cn.md +++ b/README_cn.md @@ -13,27 +13,47 @@ [![](https://travis-ci.org/Bwar/Nebula.svg?branch=master)](https://travis-ci.org/Bwar/Nebula) [![Author](https://img.shields.io/badge/author-@Bwar-blue.svg?style=flat)](cqc@vip.qq.com) ![Platform](https://img.shields.io/badge/platform-Linux-green.svg?style=flat) [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)
1. [概述](#Overview) -2. [功能](#Features) -3. [开始](#GettingStart) -4. [文档](#Documentation) -5. [依赖](#DependOn) -6. [相关项目](#RelatedProject) -7. [开发任务](#TODO) -8. [版本历史](#ChangeLog) -9. [交流与反馈](#Exchange) +2. [生产应用](#UseCase) +3. [功能](#Features) +4. [开始](#GettingStart) +5. [文档](#Documentation) +6. [依赖](#DependOn) +7. [相关项目](#RelatedProject) +8. [开发任务](#TODO) +9. [版本历史](#ChangeLog) +10. [交流与反馈](#Exchange) ## 概述 -  Nebula是一个灵活,高性能的面向业务的IoC分布式网络框架,专为生产环境而设计。Nebula以C\+\+语言开发基于事件驱动型的TCP协议,支持包括proto3、http、https、http2、grpc、websocket多种应用层通信协议。开发Nebula框架的目的是提供一种基于C\+\+快速构建高性能的分布式服务。Nebula自身核心代码只有2万行左右(不计算proto文件生成的代码)。 +  Nebula是一个产线级的网络服务框架和分布式服务解决方案项目,适用于即时通讯、数据采集、实时计算、消息推送、接入网关、web后台服务等应用场景。 -  Nebula可以作为单个高性能TCP服务器使用,不过基于Nebula搭建分布式服务才能真正体现其价值。为了能快速搭建分布式服务,开发了包括各种类型服务的NebulaBootstrap解决方案。 +  原生支持proto3、resp、http、https、http2、grpc、websocket多种应用层通信协议。 -  Nebula是一个产线级的框架和分布式解决方案项目,适用于即时通讯、数据采集、实时计算、消息推送等应用场景,也适用于web后台服务。Nebula已有即时通讯、埋点数据采集及实时分析的生产应用案例,很快将有一个面向庞大用户群的推荐引擎产线应用案例。 +  详细的文档,入门简单,扩展容易,开发效率高。 -  Nebula是个proactor模式开发框架,是proactor不是reactor(框架层实现的proactor而不是操作系统支持),应用于IO密集型的项目可以达到非常好的性能。对用惯了RPC框架的人而言,Nebula跟RPC很不一样,不过使用起来并不会比RPC复杂多少,但比RPC性能要高很多;对了解异步回调编程方式的开发者,Nebula是个非常简单的框架,比写常见的异步回调写法要简单多了。Nebula网络框架的技术分享和交流见[C++网络框架Nebula](https://zhuanlan.zhihu.com/c_216558336) +  依赖的第三方库极少,开发和部署无成本。 -  Nebula从一个从2016年5月至今在生产环境稳定运行的IM底层框架Starship发展而来。Nebula跟Starship框架(也是Bwar一人独立开发)有20%左右的结构相似度,是基于Starship经验全新开发,可以认为Nebula(C++14)是Starship(C++03)的一个高级进化版本,具有Starship的所有优点,没有Starship的所有已发现的缺点,同时提供了更多高级功能。基于Nebula的应用Nebio(埋点数据采集和实时分析项目)在2018年7月底上线并稳定运行。 +  基于channel、类actor模型异于传统rpc调用的无锁并发编程方式,进程&线程模型一键配置。 + +  支持插件式动态加载,不停服务热更新更方便。 + +  不仅是一个网络框架,还是一个十分通用的业务框架。选择Nebula,不必在网络框架之上再做业务框架封装。 + +  代码结构清晰,可读性非常好。 + + +## 部分生产应用(按时间倒序) + +* __计算机设备行业X公司智能设备数据采集服务__:基于http2协议通信向智能设备终端发连接,终端以http2流响应回传所采集的数据。 +* __移动互联网行业O公司特征画像服务__:基于redis协议、grpc协议、http协议通信,带复杂数据逻辑的redis数据代理,既是redis服务端也是redis客户端,高峰tps 33万。 +* __移动互联网行业O公司推荐引擎__:基于grpc协议和redis协议通信,计算密集型服务。 +* __移动互联网行业O公司向量搜索服务__:基于grpc协议和http协议通信,计算密集型服务。 +* __计算机设备行业X公司支付网关__:基于https协议通信,支付网关作为手持设备的https服务端和银行付款api的https客户端。 +* __移动互联网行业O公司http接入网关__:基于http协议,包括条件转发、hash路由、服务熔断与恢复、过载保护、服务降级等功能。 +* __游戏行业G公司即时通讯系统__:基于nebula协议和http协议通信,包括接入服务、逻辑服务、缓存代理服务、数据库代理服务、注册中心、配置中心等。 +* __O2O行业J公司埋点数据采集和实时分析系统__:基于http协议和nebula协议通信,包括接入服务、逻辑服务、数据库代理服务、注册中心。 +* __金融行业N公司即时通讯系统__:基于nebula协议和http协议通信,包括接入服务、逻辑服务、缓存代理服务、数据库代理服务、注册中心、配置中心、日志服务等。 ## 功能 @@ -64,7 +84,7 @@ * [分布式服务示例](docs/cn/nebula_distributed_demo.md) * [安装部署说明](docs/cn/install.md) -* [Nebula工作原理](how_nebula_works.md) +* [Nebula工作原理](docs/cn/how_nebula_works.md) * [配置说明](docs/cn/configuration.md) * [服务监控](docs/cn/monitor.md) * [协议说明](docs/cn/protocol.md) @@ -113,6 +133,9 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 +#### v1.6.2 + - 断路器优化 + - http chunk解码、resp字符串解码、redis集群无可用节点bug修复 #### v1.6.1 - http2分块响应通知改为作用于流而非连接 - resp解码字符串遇分包时bug修复 diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index 6295466e..8814aa38 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -125,9 +125,10 @@ E_CMD_STATUS StepRedisCluster::Callback( { LOG4_TRACE("callback from %s:\n%s", pChannel->GetIdentify().c_str(), oRedisReply.DebugString().c_str()); auto step_iter = m_mapPipelineRequest.find(pChannel->GetIdentify()); - if (step_iter == m_mapPipelineRequest.end() || step_iter->second.size() == 0) + if (step_iter == m_mapPipelineRequest.end()) { LOG4_ERROR("no \"%s\" found in m_mapPipelineStep", pChannel->GetIdentify().c_str()); + return(CMD_STATUS_FAULT); } auto pRedisRequest = step_iter->second.front(); uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); @@ -339,78 +340,81 @@ E_CMD_STATUS StepRedisCluster::ErrBack( { LOG4_ERROR("error %d: %s", iErrno, strErrMsg.c_str()); auto step_iter = m_mapPipelineRequest.find(pChannel->GetIdentify()); - if (step_iter == m_mapPipelineRequest.end() || step_iter->second.size() == 0) + if (step_iter == m_mapPipelineRequest.end()) { - LOG4_INFO("no \"%s\" found in m_mapPipelineStep", pChannel->GetIdentify().c_str()); + LOG4_TRACE("no \"%s\" found in m_mapPipelineStep", pChannel->GetIdentify().c_str()); } - while (step_iter->second.size() > 0) + else { - auto pRedisRequest = step_iter->second.front(); - uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); - step_iter->second.pop(); - if (uiRealStepSeq == GetSequence()) + while (step_iter->second.size() > 0) { - SendCmdClusterSlots(); - } - else - { - auto num_iter = m_mapStepEmitNum.find(uiRealStepSeq); - if (num_iter == m_mapStepEmitNum.end()) // 单key请求的响应 + auto pRedisRequest = step_iter->second.front(); + uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); + step_iter->second.pop(); + if (uiRealStepSeq == GetSequence()) { - GetLabor(this)->GetActorBuilder()->OnError(pChannel, uiRealStepSeq, iErrno, strErrMsg); + SendCmdClusterSlots(); } else { - auto reply_iter = m_mapReply.find(uiRealStepSeq); - if (reply_iter == m_mapReply.end()) + auto num_iter = m_mapStepEmitNum.find(uiRealStepSeq); + if (num_iter == m_mapStepEmitNum.end()) // 单key请求的响应 { - LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); - continue; + GetLabor(this)->GetActorBuilder()->OnError(pChannel, uiRealStepSeq, iErrno, strErrMsg); } - std::vector vecElementIndex; - for (int i = 1; i < pRedisRequest->element_size(); ++i) // element(0).str() is cmd + else { - if (pRedisRequest->element(i).integer() > 0 || i == 1) + auto reply_iter = m_mapReply.find(uiRealStepSeq); + if (reply_iter == m_mapReply.end()) { - vecElementIndex.push_back((uint32)pRedisRequest->element(i).integer()); + LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); + continue; } - } - RedisReply oRedisReply; - oRedisReply.set_type(REDIS_REPLY_ERROR); - oRedisReply.set_str(strErrMsg); - for (uint32 j = 0; j < vecElementIndex.size(); ++j) - { - if (vecElementIndex[j] < reply_iter->second.size()) + std::vector vecElementIndex; + for (int i = 1; i < pRedisRequest->element_size(); ++i) // element(0).str() is cmd { - if (reply_iter->second[vecElementIndex[j]] == nullptr) + if (pRedisRequest->element(i).integer() > 0 || i == 1) { - reply_iter->second[vecElementIndex[j]] = new RedisReply(); + vecElementIndex.push_back((uint32)pRedisRequest->element(i).integer()); } - reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply); } - else + RedisReply oRedisReply; + oRedisReply.set_type(REDIS_REPLY_ERROR); + oRedisReply.set_str(strErrMsg); + for (uint32 j = 0; j < vecElementIndex.size(); ++j) { - LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); + if (vecElementIndex[j] < reply_iter->second.size()) + { + if (reply_iter->second[vecElementIndex[j]] == nullptr) + { + reply_iter->second[vecElementIndex[j]] = new RedisReply(); + } + reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply); + } + else + { + LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); + } } - } - num_iter->second--; - if (num_iter->second == 0) - { - RedisReply oFinalReply; - oFinalReply.set_type(REDIS_REPLY_ARRAY); - for (size_t k = 0; k < reply_iter->second.size(); ++k) + num_iter->second--; + if (num_iter->second == 0) { - oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); - reply_iter->second[k] = nullptr; + RedisReply oFinalReply; + oFinalReply.set_type(REDIS_REPLY_ARRAY); + for (size_t k = 0; k < reply_iter->second.size(); ++k) + { + oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); + reply_iter->second[k] = nullptr; + } + GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); + m_mapStepEmitNum.erase(num_iter); + m_mapReply.erase(reply_iter); } - GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); - m_mapStepEmitNum.erase(num_iter); - m_mapReply.erase(reply_iter); } } } + AskingQueueErrBack(pChannel, iErrno, strErrMsg); } - AskingQueueErrBack(pChannel, iErrno, strErrMsg); return(CMD_STATUS_RUNNING); } @@ -822,73 +826,76 @@ void StepRedisCluster::AskingQueueErrBack( { LOG4_ERROR("error %d: %s", iErrno, strErrMsg.c_str()); auto step_iter = m_mapAskingRequest.find(pChannel->GetIdentify()); - if (step_iter == m_mapAskingRequest.end() || step_iter->second.size() == 0) + if (step_iter == m_mapAskingRequest.end()) { - LOG4_INFO("no \"%s\" found in m_mapPipelineStep", pChannel->GetIdentify().c_str()); + LOG4_TRACE("no \"%s\" found in m_mapPipelineStep", pChannel->GetIdentify().c_str()); } - while (step_iter->second.size() > 0) + else { - auto pRedisRequest = step_iter->second.front(); - uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); - step_iter->second.pop(); - if (uiRealStepSeq == GetSequence()) + while (step_iter->second.size() > 0) { - SendCmdClusterSlots(); - } - else - { - auto num_iter = m_mapStepEmitNum.find(uiRealStepSeq); - if (num_iter == m_mapStepEmitNum.end()) // 单key请求的响应 + auto pRedisRequest = step_iter->second.front(); + uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); + step_iter->second.pop(); + if (uiRealStepSeq == GetSequence()) { - GetLabor(this)->GetActorBuilder()->OnError(pChannel, uiRealStepSeq, iErrno, strErrMsg); + SendCmdClusterSlots(); } else { - auto reply_iter = m_mapReply.find(uiRealStepSeq); - if (reply_iter == m_mapReply.end()) + auto num_iter = m_mapStepEmitNum.find(uiRealStepSeq); + if (num_iter == m_mapStepEmitNum.end()) // 单key请求的响应 { - LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); - continue; + GetLabor(this)->GetActorBuilder()->OnError(pChannel, uiRealStepSeq, iErrno, strErrMsg); } - std::vector vecElementIndex; - for (int i = 1; i < pRedisRequest->element_size(); ++i) // element(0).str() is cmd + else { - if (pRedisRequest->element(i).integer() > 0 || i == 1) + auto reply_iter = m_mapReply.find(uiRealStepSeq); + if (reply_iter == m_mapReply.end()) { - vecElementIndex.push_back((uint32)pRedisRequest->element(i).integer()); + LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); + continue; } - } - RedisReply oRedisReply; - oRedisReply.set_type(REDIS_REPLY_ERROR); - oRedisReply.set_str(strErrMsg); - for (uint32 j = 0; j < vecElementIndex.size(); ++j) - { - if (vecElementIndex[j] < reply_iter->second.size()) + std::vector vecElementIndex; + for (int i = 1; i < pRedisRequest->element_size(); ++i) // element(0).str() is cmd { - if (reply_iter->second[vecElementIndex[j]] == nullptr) + if (pRedisRequest->element(i).integer() > 0 || i == 1) { - reply_iter->second[vecElementIndex[j]] = new RedisReply(); + vecElementIndex.push_back((uint32)pRedisRequest->element(i).integer()); } - reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply); } - else + RedisReply oRedisReply; + oRedisReply.set_type(REDIS_REPLY_ERROR); + oRedisReply.set_str(strErrMsg); + for (uint32 j = 0; j < vecElementIndex.size(); ++j) { - LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); + if (vecElementIndex[j] < reply_iter->second.size()) + { + if (reply_iter->second[vecElementIndex[j]] == nullptr) + { + reply_iter->second[vecElementIndex[j]] = new RedisReply(); + } + reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply); + } + else + { + LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); + } } - } - num_iter->second--; - if (num_iter->second == 0) - { - RedisReply oFinalReply; - oFinalReply.set_type(REDIS_REPLY_ARRAY); - for (size_t k = 0; k < reply_iter->second.size(); ++k) + num_iter->second--; + if (num_iter->second == 0) { - oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); - reply_iter->second[k] = nullptr; + RedisReply oFinalReply; + oFinalReply.set_type(REDIS_REPLY_ARRAY); + for (size_t k = 0; k < reply_iter->second.size(); ++k) + { + oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); + reply_iter->second[k] = nullptr; + } + GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); + m_mapStepEmitNum.erase(num_iter); + m_mapReply.erase(reply_iter); } - GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); - m_mapStepEmitNum.erase(num_iter); - m_mapReply.erase(reply_iter); } } } From 3ab72f15419db87dde81571c3ad65962e919635b Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 2 Jan 2022 17:52:52 +0800 Subject: [PATCH 148/176] =?UTF-8?q?1.=20=E4=BC=98=E5=8C=96IO=E9=80=9A?= =?UTF-8?q?=E4=BF=A1=EF=BC=8C=E7=BC=96=E8=A7=A3=E7=A0=81=E5=99=A8=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=8C=96=E3=80=82=202.=20=E7=A7=BB=E9=99=A4Channel?= =?UTF-8?q?=E5=92=8CActor=E7=9A=84shared=5Ffrom=5Fthis=EF=BC=8C=E6=94=B9?= =?UTF-8?q?=E7=94=A8ChannelWatcher=E5=92=8CActorWatcher=E6=9B=BF=E4=BB=A3?= =?UTF-8?q?=EF=BC=8C=E4=BB=A5=E6=8F=90=E9=AB=98=E6=80=A7=E8=83=BD=E3=80=82?= =?UTF-8?q?=203.=20=E7=A7=BB=E9=99=A4=E5=8A=A8=E6=80=81=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E7=9A=84=E5=8D=B8=E8=BD=BD=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=88=E4=BC=9A=E9=99=8D=E4=BD=8E=E6=80=A7=E8=83=BD=EF=BC=89?= =?UTF-8?q?=E3=80=82=204.=20=E8=A3=B8=E6=95=B0=E6=8D=AE=E7=BC=96=E8=A7=A3?= =?UTF-8?q?=E7=A0=81=E7=8B=AC=E7=AB=8B=E6=88=90=E7=BC=96=E8=A7=A3=E7=A0=81?= =?UTF-8?q?=E5=99=A8=E3=80=82=205.=20=E5=A2=9E=E5=8A=A0cassandra=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E7=BC=96=E8=A7=A3=E7=A0=81=E5=99=A8=E3=80=82?= =?UTF-8?q?=206.=20=E5=A2=9E=E5=8A=A0redis=20cluster=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E6=94=AF=E6=8C=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Definition.hpp | 10 +- src/Makefile_ssl | 87 + src/actor/Actor.cpp | 96 +- src/actor/Actor.hpp | 67 +- src/actor/ActorBuilder.cpp | 1022 ++---------- src/actor/ActorBuilder.hpp | 25 +- src/actor/ActorSender.cpp | 58 +- src/actor/ActorSender.hpp | 2 + src/actor/cmd/sys_cmd/CmdDataReport.cpp | 2 +- src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp | 10 +- src/actor/session/Session.cpp | 49 +- src/actor/session/Session.hpp | 23 - .../session/sys_session/SessionDataReport.cpp | 15 +- .../sys_session/manager/SessionManager.cpp | 4 +- .../sys_session/manager/SessionManager.hpp | 5 +- src/actor/step/CassStep.cpp | 45 + src/actor/step/CassStep.hpp | 42 + src/actor/step/GrpcStep.cpp | 4 +- src/actor/step/GrpcStep.hpp | 2 +- src/actor/step/HttpStep.cpp | 4 +- src/actor/step/HttpStep.hpp | 2 +- src/actor/step/PbStep.cpp | 4 +- src/actor/step/PbStep.hpp | 2 +- src/actor/step/RawStep.cpp | 4 +- src/actor/step/RawStep.hpp | 2 +- src/actor/step/RedisStep.cpp | 11 +- src/actor/step/RedisStep.hpp | 8 +- src/actor/step/Step.cpp | 21 +- src/actor/step/Step.hpp | 16 +- src/actor/step/sys_step/StepRedisCluster.cpp | 88 +- src/actor/step/sys_step/StepRedisCluster.hpp | 2 + .../sys_step/manager/StepReportToBeacon.cpp | 6 +- src/channel/SelfChannel.hpp | 5 - src/channel/SocketChannel.cpp | 156 +- src/channel/SocketChannel.hpp | 48 +- src/channel/SocketChannelImpl.cpp | 1460 ----------------- src/channel/SocketChannelImpl.hpp | 801 ++++++++- src/channel/SocketChannelSslImpl.hpp | 55 +- ...elSslImpl.cpp => SocketChannelSslImpl.inl} | 417 +---- src/channel/SslContext.cpp | 184 +++ src/channel/SslContext.hpp | 73 + src/codec/Codec.cpp | 17 +- src/codec/Codec.hpp | 45 +- src/codec/CodecFactory.cpp | 403 +++++ src/codec/CodecFactory.hpp | 142 ++ src/codec/CodecHttp.cpp | 83 +- src/codec/CodecHttp.hpp | 18 +- src/codec/CodecPrivate.cpp | 34 +- src/codec/CodecPrivate.hpp | 14 +- src/codec/CodecProto.cpp | 85 +- src/codec/CodecProto.hpp | 56 +- src/codec/CodecRaw.cpp | 55 + src/codec/CodecRaw.hpp | 38 + src/codec/CodecResp.cpp | 20 +- src/codec/CodecResp.hpp | 15 +- src/codec/CodecWsExtentJson.cpp | 16 +- src/codec/CodecWsExtentJson.hpp | 14 +- src/codec/CodecWsExtentPb.cpp | 16 +- src/codec/CodecWsExtentPb.hpp | 14 +- src/codec/cass/CassComm.hpp | 183 +++ src/codec/cass/CassMessage.cpp | 25 + src/codec/cass/CassMessage.hpp | 70 + src/codec/cass/CassRequest.cpp | 147 ++ src/codec/cass/CassRequest.hpp | 102 ++ src/codec/cass/CassResponse.cpp | 280 ++++ src/codec/cass/CassResponse.hpp | 74 + src/codec/cass/CodecCass.cpp | 244 +++ src/codec/cass/CodecCass.hpp | 85 + src/codec/cass/result/CassResult.cpp | 66 + src/codec/cass/result/CassResult.hpp | 164 ++ src/codec/cass/result/CassRow.cpp | 44 + src/codec/cass/result/CassRow.hpp | 47 + src/codec/http2/CodecHttp2.cpp | 39 +- src/codec/http2/CodecHttp2.hpp | 22 +- src/codec/http2/Http2Frame.cpp | 5 +- src/codec/http2/Http2Frame.hpp | 4 +- src/codec/http2/Http2Stream.cpp | 12 +- src/codec/http2/Http2Stream.hpp | 3 +- src/ios/ActorWatcher.cpp | 63 + src/ios/ActorWatcher.hpp | 54 + src/ios/ChannelWatcher.cpp | 90 + src/ios/ChannelWatcher.hpp | 64 + src/ios/Dispatcher.cpp | 658 ++------ src/ios/Dispatcher.hpp | 413 +---- src/ios/IO.hpp | 1170 +++++++++++++ src/ios/Nodes.cpp | 33 + src/ios/Nodes.hpp | 6 + src/labor/Manager.cpp | 3 +- src/labor/Worker.cpp | 68 +- src/labor/Worker.hpp | 2 + src/logger/FileLogger.cpp | 13 +- src/logger/FileLogger.hpp | 16 + src/type/Notations.cpp | 578 +++++++ src/type/Notations.hpp | 187 +++ 94 files changed, 6854 insertions(+), 4202 deletions(-) create mode 100644 src/Makefile_ssl create mode 100644 src/actor/step/CassStep.cpp create mode 100644 src/actor/step/CassStep.hpp delete mode 100644 src/channel/SocketChannelImpl.cpp rename src/channel/{SocketChannelSslImpl.cpp => SocketChannelSslImpl.inl} (61%) create mode 100644 src/channel/SslContext.cpp create mode 100644 src/channel/SslContext.hpp create mode 100644 src/codec/CodecFactory.cpp create mode 100644 src/codec/CodecFactory.hpp create mode 100644 src/codec/CodecRaw.cpp create mode 100644 src/codec/CodecRaw.hpp create mode 100644 src/codec/cass/CassComm.hpp create mode 100644 src/codec/cass/CassMessage.cpp create mode 100644 src/codec/cass/CassMessage.hpp create mode 100644 src/codec/cass/CassRequest.cpp create mode 100644 src/codec/cass/CassRequest.hpp create mode 100644 src/codec/cass/CassResponse.cpp create mode 100644 src/codec/cass/CassResponse.hpp create mode 100644 src/codec/cass/CodecCass.cpp create mode 100644 src/codec/cass/CodecCass.hpp create mode 100644 src/codec/cass/result/CassResult.cpp create mode 100644 src/codec/cass/result/CassResult.hpp create mode 100644 src/codec/cass/result/CassRow.cpp create mode 100644 src/codec/cass/result/CassRow.hpp create mode 100644 src/ios/ActorWatcher.cpp create mode 100644 src/ios/ActorWatcher.hpp create mode 100644 src/ios/ChannelWatcher.cpp create mode 100644 src/ios/ChannelWatcher.hpp create mode 100644 src/ios/IO.hpp create mode 100644 src/type/Notations.cpp create mode 100644 src/type/Notations.hpp diff --git a/src/Definition.hpp b/src/Definition.hpp index d18df04d..fbbeea0d 100644 --- a/src/Definition.hpp +++ b/src/Definition.hpp @@ -101,9 +101,17 @@ #define LOG4_NOTICE(args...) Logger(neb::Logger::NOTICE, __FILE__, __LINE__, __FUNCTION__, ##args) #define LOG4_INFO(args...) Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, ##args) #define LOG4_CRITICAL(args...) Logger(neb::Logger::CRITICAL, __FILE__, __LINE__, __FUNCTION__, ##args) +#ifdef _TRACE #define LOG4_DEBUG(args...) Logger(neb::Logger::DEBUG, __FILE__, __LINE__, __FUNCTION__, ##args) #define LOG4_TRACE(args...) Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ##args) -//#define LOG4_TRACE(...) Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) +#else +#ifdef _DEBUG +#define LOG4_DEBUG(args...) Logger(neb::Logger::DEBUG, __FILE__, __LINE__, __FUNCTION__, ##args) +#else +#define LOG4_DEBUG(args...) /**/ +#endif +#define LOG4_TRACE(args...) /**/ +#endif typedef int8_t int8; typedef uint8_t uint8; diff --git a/src/Makefile_ssl b/src/Makefile_ssl new file mode 100644 index 00000000..fee7f1ce --- /dev/null +++ b/src/Makefile_ssl @@ -0,0 +1,87 @@ +CC = gcc +CXX = g++ +CFLAGS = -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic -D__GUNC__ -fPIC +cplusplus_version=$(shell g++ -dumpversion | awk '{if ($$NF > 5.0) print "c++14"; else print "c++11";}') +CXXFLAG = -std=$(cplusplus_version) -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic --no-gnu-unique -D_GNU_SOURCE=1 -D_REENTRANT -D__GUNC__ -fPIC -DNODE_BEAT=10.0 -DWITH_OPENSSL -D_DEBUG + +ifeq ($(unit_test),y) +CXXFLAG += -DUNIT_TEST +endif + +ARCH:=$(shell uname -m) + +ARCH32:=i686 +ARCH64:=x86_64 + +ifeq ($(ARCH),$(ARCH64)) + SYSTEM_LIB_PATH:=/usr/lib64:/usr/local/lib64 +else + SYSTEM_LIB_PATH:=/usr/lib:/usr/local/lib +endif +LIB3RD_PATH = ../../NebulaDepend + +NEBULA_PATH = .. + +VPATH = $(NEBULA_PATH)/src +SUB_DIRS := $(foreach dir, $(VPATH), $(shell find $(dir) -maxdepth 5 -type d)) +DIRS := $(SUB_DIRS) + + +INC := $(INC) \ + -I $(LIB3RD_PATH)/include \ + -I $(NEBULA_PATH)/src + + +LDFLAGS := $(LDFLAGS) -D_LINUX_OS_ \ + -L$(LIB3RD_PATH)/lib -lcryptopp \ + -L$(LIB3RD_PATH)/lib -lev \ + -L$(LIB3RD_PATH)/lib -lprotobuf \ + -L$(LIB3RD_PATH)/lib -lssl \ + -L$(SYSTEM_LIB_PATH) -lc -lrt -ldl + +SUB_INCLUDE = channel ios labor pb mydis logger +DEEP_SUB_INCLUDE = actor util codec +CPP_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cpp)) +CC_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cc)) +C_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c)) +OBJS = $(patsubst %.cpp,%.o,$(CPP_SRCS)) $(patsubst %.c,%.o,$(C_SRCS)) $(patsubst %.cc,%.o,$(CC_SRCS)) + +TARGET = libnebula.so + +all: $(TARGET) + +libnebula.so: $(OBJS) + $(CXX) -fPIE -rdynamic -shared -lstdc++ -g -o $@ $^ $(LDFLAGS) + mkdir -p $(NEBULA_PATH)/include $(NEBULA_PATH)/lib + @for dir in $(SUB_INCLUDE); \ + do \ + mkdir -p $(NEBULA_PATH)/include/$$dir; \ + for f in `ls $(NEBULA_PATH)/src/$$dir/*.*`; \ + do \ + if [[ $$f =~ ".hpp" || $$f =~ ".h" || $$f =~ ".inl" ]]; then cp $$f $(NEBULA_PATH)/include/$$dir/; fi; \ + done; \ + done + @for d in `find $(DEEP_SUB_INCLUDE) -type d`; \ + do \ + mkdir -p $(NEBULA_PATH)/include/$$d; \ + for f in `ls $(NEBULA_PATH)/src/$$d/*.*`; \ + do \ + if [[ $$f =~ ".hpp" || $$f =~ ".h" || $$f =~ ".inl" ]]; then cp $$f $(NEBULA_PATH)/include/$$d/; fi; \ + done; \ + done + cp -f $(NEBULA_PATH)/src/*.hpp $(NEBULA_PATH)/include/ + cp -f $@ $(NEBULA_PATH)/lib/ + +%.o:%.cpp + $(CXX) $(INC) $(CXXFLAG) -c -o $@ $< $(LDFLAGS) +%.o:%.cc + $(CXX) $(INC) $(CXXFLAG) -c -o $@ $< $(LDFLAGS) +%.o:%.c + $(CC) $(INC) $(CFLAGS) -c -o $@ $< $(LDFLAGS) +clean: + rm -f $(OBJS) + rm -f $(TARGET) + rm -rf $(NEBULA_PATH)/include + rm -f $(NEBULA_PATH)/lib/libnebula.* + + diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index cc46d376..42c1a145 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -10,7 +10,9 @@ #include "actor/Actor.hpp" #include +#include "ios/ActorWatcher.hpp" #include "ios/Dispatcher.hpp" +#include "ios/IO.hpp" #include "actor/session/Session.hpp" #include "actor/step/Step.hpp" #include "labor/Worker.hpp" @@ -19,19 +21,27 @@ namespace neb { Actor::Actor(ACTOR_TYPE eActorType, ev_tstamp dTimeout) - : m_eActorType(eActorType), + : m_eActorType(eActorType), m_uiActorStatus(ACT_STATUS_UNREGISTER), m_uiSequence(0), m_dActiveTime(0.0), m_dTimeout(dTimeout), - m_pLabor(nullptr), m_pTimerWatcher(NULL), m_pContext(nullptr) + m_pLabor(nullptr), m_pWatcher(nullptr), m_pContext(nullptr) { } Actor::~Actor() { - FREE(m_pTimerWatcher); + if (m_pWatcher != nullptr) + { + DELETE(m_pWatcher); + } LOG4_TRACE("eActorType %d, seq %u, actor name \"%s\"", m_eActorType, GetSequence(), m_strActorName.c_str()); } +bool Actor::RegisterActor(const std::string& strActorName, Actor* pNewActor, Actor* pCreator) +{ + return(m_pLabor->GetActorBuilder()->RegisterActor(strActorName, pNewActor, pCreator)); +} + uint32 Actor::GetSequence() { if ((ACT_CMD == m_eActorType || ACT_MODULE == m_eActorType) // Cmd和Module总是获取最新Seq @@ -143,42 +153,37 @@ void Actor::SetContext(std::shared_ptr pContext) m_pContext = pContext; } -void Actor::AddAssemblyLine(std::shared_ptr pSession) -{ - m_pLabor->GetActorBuilder()->AddAssemblyLine(pSession); -} - bool Actor::SendTo(std::shared_ptr pChannel) { - return(m_pLabor->GetDispatcher()->SendTo(pChannel)); + return(IO::Send(pChannel)); } bool Actor::SendTo(std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendTo(pChannel, iCmd, uiSeq, oMsgBody)); + return(IO::SendResponse(this, pChannel, iCmd, uiSeq, oMsgBody)); } bool Actor::SendTo(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) { (const_cast(oHttpMsg)).mutable_headers()->insert({"x-trace-id", GetTraceId()}); - return(m_pLabor->GetDispatcher()->SendTo(pChannel, oHttpMsg, 0)); + return(IO::SendResponse(this, pChannel, oHttpMsg)); } bool Actor::SendTo(std::shared_ptr pChannel, const RedisReply& oRedisReply) { - return(m_pLabor->GetDispatcher()->SendTo(pChannel, oRedisReply, 0)); + return(IO::SendResponse(this, pChannel, oRedisReply)); } bool Actor::SendTo(std::shared_ptr pChannel, const char* pRawData, uint32 uiRawDataSize) { - return(m_pLabor->GetDispatcher()->SendTo(pChannel, pRawData, uiRawDataSize, 0)); + return(IO::SendResponse(this, pChannel, pRawData, uiRawDataSize)); } bool Actor::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); + return(IO::SendTo(this, strIdentify, SOCKET_STREAM, false, true, iCmd, uiSeq, oMsgBody)); } bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq) @@ -201,17 +206,17 @@ bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMs } if (oHttpMsg.http_major() == 2) { - return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, SOCKET_STREAM, CODEC_HTTP2, bWithSsl, bPipeline, oHttpMsg, GetSequence())); + return(IO::SendTo(this, strHost, iPort, SOCKET_STREAM, bWithSsl, bPipeline, oHttpMsg)); } else { - return(m_pLabor->GetDispatcher()->SendTo(strHost, iPort, SOCKET_STREAM, CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, GetSequence())); + return(IO::SendTo(this, strHost, iPort, SOCKET_STREAM, bWithSsl, bPipeline, oHttpMsg)); } } bool Actor::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); + return(IO::SendTo(this, strIdentify, SOCKET_STREAM, bWithSsl, bPipeline, oRedisMsg)); } bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) @@ -221,12 +226,7 @@ bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedis bool Actor::SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline) { - return(m_pLabor->GetDispatcher()->SendRoundRobin(strIdentify, SOCKET_STREAM, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, GetSequence())); -} - -bool Actor::SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) -{ - return(m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, CODEC_UNKNOW, bWithSsl, bPipeline, pRawData, uiRawDataSize, GetSequence())); + return(IO::SendRoundRobin(this, strIdentify, bWithSsl, bPipeline, oRedisMsg)); } bool Actor::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) @@ -235,40 +235,40 @@ bool Actor::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) return(m_pLabor->GetDispatcher()->SendTo(iCmd, uiSeq, oMsgBody)); } -bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) +bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, SOCKET_STREAM, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); + return(IO::SendRoundRobin(this, strNodeType, false, true, iCmd, uiSeq, oMsgBody)); } -bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) +bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, SOCKET_STREAM, eCodecType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); + return(IO::SendOriented(this, strNodeType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); } -bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) +bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { (const_cast(oMsgBody)).set_trace_id(GetTraceId()); if (oMsgBody.has_req_target()) { if (0 != oMsgBody.req_target().route_id()) { - return(SendOriented(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody, eCodecType)); + return(SendOriented(strNodeType, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody)); } else if (oMsgBody.req_target().route().length() > 0) { - return(m_pLabor->GetDispatcher()->SendOriented(strNodeType, SOCKET_STREAM, eCodecType, false, true, oMsgBody.req_target().route(), iCmd, uiSeq, oMsgBody)); + return(IO::SendOriented(this, strNodeType, false, true, oMsgBody.req_target().route(), iCmd, uiSeq, oMsgBody)); } else { - return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); + return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody)); } } else { LOG4_ERROR("the request message has no req_target."); - return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); + return(SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody)); } } @@ -285,22 +285,17 @@ void Actor::CircuitBreak(const std::string& strIdentify) bool Actor::SendToSelf(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - return(m_pLabor->GetDispatcher()->SendToSelf(iCmd, uiSeq, oMsgBody, GetSequence())); + return(IO::SendToSelf(this, iCmd, uiSeq, oMsgBody)); } bool Actor::SendToSelf(const HttpMsg& oHttpMsg) { - return(m_pLabor->GetDispatcher()->SendToSelf(oHttpMsg, GetSequence())); + return(IO::SendToSelf(this, oHttpMsg)); } bool Actor::SendToSelf(const RedisMsg& oRedisMsg) { - return(m_pLabor->GetDispatcher()->SendToSelf(oRedisMsg, GetSequence())); -} - -bool Actor::SendToSelf(const char* pRaw, uint32 uiRawSize) -{ - return(m_pLabor->GetDispatcher()->SendToSelf(pRaw, uiRawSize, GetSequence())); + return(IO::SendToSelf(this, oRedisMsg)); } std::shared_ptr Actor::GetLastActivityChannel() @@ -322,6 +317,11 @@ int32 Actor::GetStepNum() const return(m_pLabor->GetActorBuilder()->GetStepNum()); } +void Actor::ResetTimeout(ev_tstamp dTimeout) +{ + m_dTimeout = dTimeout; +} + void Actor::SetLabor(Labor* pLabor) { m_pLabor = pLabor; @@ -336,18 +336,20 @@ uint32 Actor::ForceNewSequence() return(m_uiSequence); } -ev_timer* Actor::MutableTimerWatcher() +ActorWatcher* Actor::MutableWatcher() { - if (NULL == m_pTimerWatcher) + if (nullptr == m_pWatcher) { - m_pTimerWatcher = (ev_timer*)malloc(sizeof(ev_timer)); - if (NULL != m_pTimerWatcher) + try + { + m_pWatcher = new ActorWatcher(); + } + catch(std::bad_alloc& e) { - memset(m_pTimerWatcher, 0, sizeof(ev_timer)); - m_pTimerWatcher->data = this; // (void*)(Actor*) + LOG4_ERROR("new ActorWatcher error: %s", e.what()); } } - return(m_pTimerWatcher); + return(m_pWatcher); } void Actor::SetActorName(const std::string& strActorName) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index fb83513c..e082f11d 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -40,12 +40,16 @@ namespace neb typedef RedisReply RedisMsg; class Labor; +class ActorWatcher; class Dispatcher; +template class IO; class ActorBuilder; class ActorSender; class ActorSys; class SocketChannel; +template class SocketChannelImpl; +class CodecRaw; class Actor; class Cmd; class Module; @@ -67,12 +71,21 @@ class Actor: public std::enable_shared_from_this ACT_SESSION = 3, ///< Session会话对象 ACT_TIMER = 4, ///< 定时器对象 ACT_CONTEXT = 5, ///< 会话上下文对象 - ACT_PB_STEP = 6, ///< Step步骤对象,处理pb请求或响应 - ACT_HTTP_STEP = 7, ///< Step步骤对象,处理http请求或响应 - ACT_REDIS_STEP = 8, ///< Step步骤对象,处理redis请求或响应 - ACT_RAW_STEP = 9, ///< Step步骤对象,处理未经编解码的裸数据 - ACT_OPERATOR = 10, ///< Operator算子对象,Operator(IO无关)与Step(异步IO相关)共同构成功能链 - ACT_CHAIN = 11, ///< Chain链对象,用于将Operator和Step组合成功能链 + ACT_OPERATOR = 6, ///< Operator算子对象,Operator(IO无关)与Step(异步IO相关)共同构成功能链 + ACT_CHAIN = 7, ///< Chain链对象,用于将Operator和Step组合成功能链 + ACT_PB_STEP = 8, ///< Step步骤对象,处理pb请求或响应 + ACT_HTTP_STEP = 9, ///< Step步骤对象,处理http请求或响应 + ACT_REDIS_STEP = 10, ///< Step步骤对象,处理redis请求或响应 + ACT_RAW_STEP = 11, ///< Step步骤对象,处理未经编解码的裸数据 + }; + + enum ACTOR_STATUS + { + ACT_STATUS_UNREGISTER = 0x00, ///< 未向框架注册的Actor + ACT_STATUS_SHARED = 0x01, ///< 完全由框架通过shared_ptr管理的actor,v1.7之前的版本只有这种类型 + ACT_STATUS_ACTIVITY = 0x02, ///< 通过Register()方法向框架注册的有效actor + ACT_STATUS_OBSOLETE = 0x04, ///< 废弃的actor,可以被回收或重置后再复用 + ACT_STATUS_DYNAMIC_LOAD = 0x010, ///< 动态加载的actor }; public: @@ -87,12 +100,18 @@ class Actor: public std::enable_shared_from_this template std::shared_ptr MakeSharedContext(const std::string& strContextName, Targs&&... args); template std::shared_ptr MakeSharedChain(const std::string& strChainName, Targs&&... args); template std::shared_ptr MakeSharedActor(const std::string& strActorName, Targs&&... args); + bool RegisterActor(const std::string& strActorName, Actor* pNewActor, Actor* pCreator); ACTOR_TYPE GetActorType() const { return(m_eActorType); } + uint32 GetActorStatus() const + { + return(m_uiActorStatus); + } + const std::string& GetActorName() const { return(m_strActorName); @@ -131,6 +150,10 @@ class Actor: public std::enable_shared_from_this void AddAssemblyLine(std::shared_ptr pSession); protected: + virtual bool WantResponse() const + { + return(false); + } /** * @brief 连接成功后发送 * @note 当前Server往另一个Server发送数据而两Server之间没有可用连接时,框架层向对端发起连接(发起连接 @@ -234,18 +257,6 @@ class Actor: public std::enable_shared_from_this */ virtual bool SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = false); - /** - * @brief 发送raw请求 - * @note 只有RawStep及其派生类才能调用此方法。 - * @param strIdentify Channel通道标识 - * @param pRawData裸数据 - * @param bWithSsl 是否需要SSL - * @param bPipeline 是否支持pipeline - * @param uiStepSeq 应用层无用参数,框架层的系统Actor会用到 - * @return 是否发送成功 - */ - virtual bool SendTo(const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl = false, bool bPipeline = false, uint32 uiStepSeq = 0); - /** * @brief 从worker发送到loader或从loader发送到worker * @param iCmd 发送的命令字 @@ -262,10 +273,9 @@ class Actor: public std::enable_shared_from_this * @param iCmd 发送的命令字 * @param uiSeq 发送的数据包seq * @param oMsgBody 数据包体 - * @param oCodecType 编解码方式 * @return 是否发送成功 */ - virtual bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + virtual bool SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); /** * @brief 以取模方式选择发送到同一类型节点 @@ -275,12 +285,11 @@ class Actor: public std::enable_shared_from_this * @param iCmd 发送的命令字 * @param uiSeq 发送的数据包seq * @param oMsgBody 数据包体 - * @param oCodecType 编解码方式 * @return 是否发送成功 */ - virtual bool SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + virtual bool SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - virtual bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); + virtual bool SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); virtual bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); @@ -296,7 +305,6 @@ class Actor: public std::enable_shared_from_this bool SendToSelf(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); bool SendToSelf(const HttpMsg& oHttpMsg); bool SendToSelf(const RedisMsg& oRedisMsg); - bool SendToSelf(const char* pRaw, uint32 uiRawSize); std::shared_ptr GetLastActivityChannel(); @@ -309,6 +317,8 @@ class Actor: public std::enable_shared_from_this int32 GetStepNum() const; + void ResetTimeout(ev_tstamp dTimeout); + protected: virtual void SetActiveTime(ev_tstamp dActiveTime) { @@ -328,17 +338,20 @@ class Actor: public std::enable_shared_from_this private: void SetLabor(Labor* pLabor); uint32 ForceNewSequence(); - ev_timer* MutableTimerWatcher(); + ActorWatcher* MutableWatcher(); void SetActorName(const std::string& strActorName); void SetTraceId(const std::string& strTraceId); + void SetActorStatus(uint32 uiActorStatus); + void UnsetActorStatus(uint32 uiActorStatus); private: ACTOR_TYPE m_eActorType; + uint32 m_uiActorStatus; uint32 m_uiSequence; ev_tstamp m_dActiveTime; ev_tstamp m_dTimeout; Labor* m_pLabor; - ev_timer* m_pTimerWatcher; + ActorWatcher* m_pWatcher; std::string m_strActorName; std::string m_strTraceId; // for log trace std::shared_ptr m_pContext; @@ -348,6 +361,8 @@ class Actor: public std::enable_shared_from_this friend class ActorSys; friend class ActorSender; friend class Chain; + template friend class SocketChannelImpl; + template friend class IO; }; template diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 5fc730fe..f6537932 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -20,14 +20,18 @@ #include "step/PbStep.hpp" #include "step/RedisStep.hpp" #include "step/RawStep.hpp" +//#include "step/CassStep.hpp" #include "cmd/RedisCmd.hpp" #include "cmd/RawCmd.hpp" #include "operator/Operator.hpp" #include "chain/Chain.hpp" #include "actor/session/sys_session/SessionLogger.hpp" +#include "ios/ActorWatcher.hpp" #include "ios/Dispatcher.hpp" +#include "ios/IO.hpp" #include "channel/SocketChannel.hpp" #include "channel/SelfChannel.hpp" +#include "codec/CodecProto.hpp" namespace neb { @@ -44,12 +48,6 @@ ActorBuilder::~ActorBuilder() m_mapCallbackStep.clear(); m_mapCallbackSession.clear(); - for (auto so_iter = m_mapLoadedSo.begin(); - so_iter != m_mapLoadedSo.end(); ++so_iter) - { - DELETE(so_iter->second); - } - m_mapLoadedSo.clear(); if (m_pErrBuff != nullptr) { free(m_pErrBuff); @@ -62,6 +60,13 @@ bool ActorBuilder::Init(CJsonObject& oBootLoadConf, CJsonObject& oDynamicLoadCon LoadSysCmd(); BootLoadCmd(oBootLoadConf); DynamicLoad(oDynamicLoadConf); + std::string strReportSessionId = "neb::SessionDataReport"; + auto pSession = GetSession(strReportSessionId); + if (pSession == nullptr) + { + MakeSharedSession(nullptr, "neb::SessionDataReport", + strReportSessionId, (ev_tstamp)m_pLabor->GetNodeInfo().dDataReportInterval); + } return(true); } @@ -75,8 +80,9 @@ void ActorBuilder::StepTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, { if (watcher->data != NULL) { - Step* pStep = (Step*)watcher->data; - pStep->m_pLabor->GetActorBuilder()->OnStepTimeout(std::dynamic_pointer_cast(pStep->shared_from_this())); + auto pWatcher = static_cast(watcher->data); + auto pStep = std::static_pointer_cast(pWatcher->GetActor()); + pStep->m_pLabor->GetActorBuilder()->OnStepTimeout(pStep); } } @@ -84,8 +90,9 @@ void ActorBuilder::SessionTimeoutCallback(struct ev_loop* loop, ev_timer* watche { if (watcher->data != NULL) { - Session* pSession = (Session*)watcher->data; - pSession->m_pLabor->GetActorBuilder()->OnSessionTimeout(std::dynamic_pointer_cast(pSession->shared_from_this())); + auto pWatcher = static_cast(watcher->data); + auto pSession = std::static_pointer_cast(pWatcher->GetActor()); + pSession->m_pLabor->GetActorBuilder()->OnSessionTimeout(pSession); } } @@ -93,43 +100,35 @@ void ActorBuilder::ChainTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, { if (watcher->data != NULL) { - Chain* pChain = (Chain*)watcher->data; - pChain->m_pLabor->GetActorBuilder()->OnChainTimeout(std::dynamic_pointer_cast(pChain->shared_from_this())); + auto pWatcher = static_cast(watcher->data); + auto pChain = std::static_pointer_cast(pWatcher->GetActor()); + pChain->m_pLabor->GetActorBuilder()->OnChainTimeout(pChain); } } bool ActorBuilder::OnStepTimeout(std::shared_ptr pStep) { - ev_timer* watcher = pStep->MutableTimerWatcher(); - ev_tstamp after = pStep->GetActiveTime() - m_pLabor->GetNowTime() + pStep->GetTimeout(); - if (after > 0) // 在定时时间内被重新刷新过,重新设置定时器 + ev_timer* watcher = pStep->MutableWatcher()->MutableTimerWatcher(); + LOG4_TRACE("seq %lu: active_time %lf, now_time %lf, lifetime %lf", + pStep->GetSequence(), pStep->GetActiveTime(), m_pLabor->GetNowTime(), pStep->GetTimeout()); + E_CMD_STATUS eResult = pStep->Timeout(); + if (CMD_STATUS_RUNNING == eResult) { + ev_tstamp after = pStep->GetTimeout(); m_pLabor->GetDispatcher()->RefreshEvent(watcher, after); return(true); } - else // 步骤已超时 + else { - LOG4_TRACE("seq %lu: active_time %lf, now_time %lf, lifetime %lf", - pStep->GetSequence(), pStep->GetActiveTime(), m_pLabor->GetNowTime(), pStep->GetTimeout()); - E_CMD_STATUS eResult = pStep->Timeout(); - if (CMD_STATUS_RUNNING == eResult) - { - ev_tstamp after = pStep->GetTimeout(); - m_pLabor->GetDispatcher()->RefreshEvent(watcher, after); - return(true); - } - else - { - RemoveChain(pStep->GetChainId()); - RemoveStep(pStep); - return(true); - } + RemoveChain(pStep->GetChainId()); + RemoveStep(pStep); + return(true); } } bool ActorBuilder::OnSessionTimeout(std::shared_ptr pSession) { - ev_timer* watcher = pSession->MutableTimerWatcher(); + ev_timer* watcher = pSession->MutableWatcher()->MutableTimerWatcher(); //LOG4_TRACE("CHECK watchar = 0x%x", watcher); ev_tstamp after = pSession->GetActiveTime() - m_pLabor->GetNowTime() + pSession->GetTimeout(); if (after > 0) // 定时时间内被重新刷新过,重新设置定时器 @@ -155,7 +154,7 @@ bool ActorBuilder::OnSessionTimeout(std::shared_ptr pSession) bool ActorBuilder::OnChainTimeout(std::shared_ptr pChain) { - ev_timer* watcher = pChain->MutableTimerWatcher(); + ev_timer* watcher = pChain->MutableWatcher()->MutableTimerWatcher(); LOG4_TRACE("CHECK watchar = 0x%x", watcher); ev_tstamp after = pChain->GetActiveTime() - m_pLabor->GetNowTime() + pChain->GetTimeout(); if (after > 0) // 定时时间内被重新刷新过,重新设置定时器 @@ -248,7 +247,7 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH LOG4_ERROR(m_pErrBuff); oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); - m_pLabor->GetDispatcher()->SendTo(pChannel, oMsgHead.cmd() + 1, oMsgHead.seq(), oOutMsgBody); + IO::SendResponse(m_pLabor->GetDispatcher(), pChannel, oMsgHead.cmd() + 1, oMsgHead.seq(), oOutMsgBody); return(false); } } @@ -271,520 +270,61 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH } else { - snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); - LOG4_ERROR(m_pErrBuff); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); - oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); - m_pLabor->GetDispatcher()->SendTo(pChannel, oMsgHead.cmd() + 1, oMsgHead.seq(), oOutMsgBody); - return(false); - } - } - } - } - } - else // 回调 - { - pChannel->m_pImpl->PopStepSeq(); - auto step_iter = m_mapCallbackStep.find(oMsgHead.seq()); - if (step_iter != m_mapCallbackStep.end()) // 步骤回调 - { - LOG4_TRACE("receive message, cmd = %d", - oMsgHead.cmd()); - if (step_iter->second != nullptr) - { - E_CMD_STATUS eResult; - step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - LOG4_TRACE("cmd %u, seq %u, step_seq %u, active_time %lf", - oMsgHead.cmd(), oMsgHead.seq(), step_iter->second->GetSequence(), - step_iter->second->GetActiveTime()); - eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); - if (CMD_STATUS_RUNNING != eResult) - { - uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); - if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - } - ExecAssemblyLine(pChannel, oMsgHead, oMsgBody); - } - else - { - snprintf(m_pErrBuff, gc_iErrBuffLen, "no callback or the callback for seq %u had been timeout!", oMsgHead.seq()); - LOG4_WARNING(m_pErrBuff); - return(false); - } - } - return(true); -} - -bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, E_CODEC_STATUS eCodecStatus) -{ - if (HTTP_REQUEST == oHttpMsg.type()) // 新请求 - { - LOG4_DEBUG("oInHttpMsg.type() = %d, oInHttpMsg.path() = %s", - oHttpMsg.type(), oHttpMsg.path().c_str()); - if (oHttpMsg.has_upgrade() && oHttpMsg.upgrade().is_upgrade()) - { - auto module_iter = m_mapModule.find("http_upgrade"); - if (module_iter != m_mapModule.end()) - { - std::ostringstream oss; - oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); - module_iter->second->SetTraceId(oss.str()); - if (module_iter->second->AnyMessage(pChannel, oHttpMsg)) - { - return(true); - } - } - } - auto module_iter = m_mapModule.find(oHttpMsg.path()); - if (module_iter == m_mapModule.end()) - { - module_iter = m_mapModule.find("/switch"); - if (module_iter == m_mapModule.end()) - { - module_iter = m_mapModule.find("/route"); - if (module_iter == m_mapModule.end()) - { - HttpMsg oOutHttpMsg; - snprintf(m_pErrBuff, gc_iErrBuffLen, "no module to dispose %s!", oHttpMsg.path().c_str()); - LOG4_WARNING(m_pErrBuff); - oOutHttpMsg.set_type(HTTP_RESPONSE); - oOutHttpMsg.set_status_code(404); - oOutHttpMsg.set_http_major(oHttpMsg.http_major()); - oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); - m_pLabor->GetDispatcher()->SendTo(pChannel, oOutHttpMsg, 0); - } - else - { - std::ostringstream oss; - oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); - module_iter->second->SetTraceId(oss.str()); - module_iter->second->AnyMessage(pChannel, oHttpMsg); - } - } - else - { - std::ostringstream oss; - oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); - module_iter->second->SetTraceId(oss.str()); - module_iter->second->AnyMessage(pChannel, oHttpMsg); - } - } - else - { - std::ostringstream oss; - oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); - module_iter->second->SetTraceId(oss.str()); - module_iter->second->AnyMessage(pChannel, oHttpMsg); - } - } - else - { - auto http_step_iter = m_mapCallbackStep.find(pChannel->m_pImpl->PopStepSeq(oHttpMsg.stream_id(), eCodecStatus)); - if (!pChannel->IsPipeline() && pChannel->m_pImpl->GetPipelineStepSeq().empty()) - { - m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); - } - if (http_step_iter == m_mapCallbackStep.end()) - { - LOG4_TRACE("no callback for http response from %s!", oHttpMsg.url().c_str()); - } - else - { - E_CMD_STATUS eResult; - http_step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = http_step_iter->second->Callback(pChannel, oHttpMsg); - if (CMD_STATUS_RUNNING != eResult) - { - uint32 uiChainId = http_step_iter->second->GetChainId(); - RemoveStep(http_step_iter->second); - if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - } - } - return(true); -} - -bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const RedisMsg& oRedisMsg, uint32 uiFinalStepSeq) -{ - if (pChannel->IsClient()) - { - std::unordered_map>::iterator step_iter; - if (uiFinalStepSeq == 0) // callback from SocketChannel by redis msg - { - step_iter = m_mapCallbackStep.find(pChannel->m_pImpl->PopStepSeq()); - } - else // callback from StepRedisCluster - { - step_iter = m_mapCallbackStep.find(uiFinalStepSeq); - } - if (!pChannel->IsPipeline() && pChannel->m_pImpl->GetPipelineStepSeq().empty()) - { - m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. - } - if (step_iter == m_mapCallbackStep.end()) - { - LOG4_TRACE("no callback for redis reply from %s!", pChannel->GetIdentify().c_str()); - return(false); - } - else - { - E_CMD_STATUS eResult; - step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = step_iter->second->Callback(pChannel, oRedisMsg); - if (CMD_STATUS_RUNNING != eResult) - { - uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); - if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - return(eResult); - } - } - else - { - auto cmd_iter = m_mapCmd.find(CMD_REQ_REDIS_PROXY); - if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) - { - auto pRedisCmd = std::dynamic_pointer_cast(cmd_iter->second); - if (pRedisCmd == nullptr) - { - LOG4_ERROR("cmd %d is not a RedisCmd instance!", CMD_REQ_REDIS_PROXY); - return(false); - } - return(pRedisCmd->AnyMessage(pChannel, oRedisMsg)); - } - LOG4_ERROR("no instance of RedisCmd or derived class of RedisCmd found for cmd %d", CMD_REQ_REDIS_PROXY); - return(false); - } -} - -bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const CBuffer& oBuffer) -{ - if (pChannel->IsClient()) - { - auto step_iter = m_mapCallbackStep.find(pChannel->m_pImpl->PopStepSeq()); - if (!pChannel->IsPipeline() && pChannel->m_pImpl->GetPipelineStepSeq().empty()) - { - m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. - } - if (step_iter == m_mapCallbackStep.end()) - { - LOG4_TRACE("no callback for raw data reply from %s!", pChannel->GetIdentify().c_str()); - return(false); - } - else - { - E_CMD_STATUS eResult; - step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = step_iter->second->Callback(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes()); - if (CMD_STATUS_RUNNING != eResult) - { - uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); - if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - return(eResult); - } - } - else - { - auto cmd_iter = m_mapCmd.find(CMD_REQ_RAW_DATA); - if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) - { - auto pRawCmd = std::dynamic_pointer_cast(cmd_iter->second); - if (pRawCmd == nullptr) - { - LOG4_ERROR("cmd %d is not a RawCmd instance!", CMD_REQ_RAW_DATA); - return(false); - } - return(pRawCmd->AnyMessage(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes())); - } - LOG4_ERROR("no instance of RawCmd or derived class of RawCmd found for cmd %d", CMD_REQ_RAW_DATA); - return(false); - } -} - -bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) -{ - if (gc_uiCmdReq & oMsgHead.cmd()) // 新请求 - { - auto cmd_iter = m_mapCmd.find(gc_uiCmdBit & oMsgHead.cmd()); - if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) - { - if (oMsgBody.trace_id().length() > 10) - { - cmd_iter->second->SetTraceId(oMsgBody.trace_id()); - } - else - { - std::ostringstream oss; - oss << m_pLabor->GetNodeInfo().uiNodeId << "." << m_pLabor->GetNowTime() << "." << m_pLabor->GetSequence(); - cmd_iter->second->SetTraceId(oss.str()); - } - cmd_iter->second->AnyMessage(pChannel, oMsgHead, oMsgBody); - } - else // 没有对应的cmd,是需由接入层转发的请求 - { - LOG4_ERROR("no handler to dispose cmd %u!", oMsgHead.cmd()); - return(false); - } - } - else // 回调 - { - auto step_iter = m_mapCallbackStep.find(oMsgHead.seq()); - if (step_iter != m_mapCallbackStep.end()) // 步骤回调 - { - LOG4_TRACE("receive message, cmd = %d", - oMsgHead.cmd()); - if (step_iter->second != nullptr) - { - E_CMD_STATUS eResult; - step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - LOG4_TRACE("cmd %u, seq %u, step_seq %u, active_time %lf", - oMsgHead.cmd(), oMsgHead.seq(), step_iter->second->GetSequence(), - step_iter->second->GetActiveTime()); - eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); - if (CMD_STATUS_RUNNING != eResult) - { - uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); - if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - } - ExecAssemblyLine(pChannel, oMsgHead, oMsgBody); - } - else - { - snprintf(m_pErrBuff, gc_iErrBuffLen, "no callback or the callback for seq %u had been timeout!", oMsgHead.seq()); - LOG4_WARNING(m_pErrBuff); - return(false); - } - } - return(true); -} - -bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg) -{ - if (HTTP_REQUEST == oHttpMsg.type()) // 新请求 - { - LOG4_DEBUG("oInHttpMsg.type() = %d, oInHttpMsg.path() = %s", - oHttpMsg.type(), oHttpMsg.path().c_str()); - auto module_iter = m_mapModule.find(oHttpMsg.path()); - if (module_iter == m_mapModule.end()) - { - LOG4_ERROR("no module to dispose %s!", oHttpMsg.path().c_str()); - } - else - { - module_iter->second->AnyMessage(pChannel, oHttpMsg); - } - } - else - { - auto pSelfChannel = std::dynamic_pointer_cast(pChannel); - auto http_step_iter = m_mapCallbackStep.find(pSelfChannel->GetStepSeq()); - if (http_step_iter == m_mapCallbackStep.end()) - { - LOG4_TRACE("no callback for http response from %s!", oHttpMsg.url().c_str()); - } - else - { - E_CMD_STATUS eResult; - http_step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = http_step_iter->second->Callback(pChannel, oHttpMsg); - if (CMD_STATUS_RUNNING != eResult) - { - uint32 uiChainId = http_step_iter->second->GetChainId(); - RemoveStep(http_step_iter->second); - if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - } - } - return(true); -} - -bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const RedisMsg& oRedisMsg) -{ - auto pSelfChannel = std::dynamic_pointer_cast(pChannel); - if (pSelfChannel->IsResponse()) - { - auto step_iter = m_mapCallbackStep.find(pSelfChannel->GetStepSeq()); - if (step_iter == m_mapCallbackStep.end()) - { - LOG4_TRACE("no callback for redis reply from %s!", pChannel->GetIdentify().c_str()); - return(false); - } - else - { - E_CMD_STATUS eResult; - step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = step_iter->second->Callback(pChannel, oRedisMsg); - if (CMD_STATUS_RUNNING != eResult) - { - uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); - if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - return(eResult); - } - } - else - { - auto cmd_iter = m_mapCmd.find(CMD_REQ_REDIS_PROXY); - if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) - { - auto pRedisCmd = std::dynamic_pointer_cast(cmd_iter->second); - if (pRedisCmd == nullptr) - { - LOG4_ERROR("cmd %d is not a RedisCmd instance!", CMD_REQ_REDIS_PROXY); - return(false); + snprintf(m_pErrBuff, gc_iErrBuffLen, "no handler to dispose cmd %u!", oMsgHead.cmd()); + LOG4_ERROR(m_pErrBuff); + oOutMsgBody.mutable_rsp_result()->set_code(ERR_UNKNOWN_CMD); + oOutMsgBody.mutable_rsp_result()->set_msg(m_pErrBuff); + IO::SendResponse(m_pLabor->GetDispatcher(), pChannel, oMsgHead.cmd() + 1, oMsgHead.seq(), oOutMsgBody); + return(false); + } + } } - return(pRedisCmd->AnyMessage(pChannel, oRedisMsg)); } - LOG4_ERROR("no instance of RedisCmd or derived class of RedisCmd found for cmd %d", CMD_REQ_REDIS_PROXY); - return(false); } -} - -bool ActorBuilder::OnSelfMessage(std::shared_ptr pChannel, const CBuffer& oBuffer) -{ - auto pSelfChannel = std::dynamic_pointer_cast(pChannel); - if (pSelfChannel->IsResponse()) + else // 回调 { - auto step_iter = m_mapCallbackStep.find(pSelfChannel->GetStepSeq()); - if (step_iter == m_mapCallbackStep.end()) - { - LOG4_TRACE("no callback for raw data reply from %s!", pChannel->GetIdentify().c_str()); - return(false); - } - else + pChannel->m_pImpl->PopStepSeq(); + auto step_iter = m_mapCallbackStep.find(oMsgHead.seq()); + if (step_iter != m_mapCallbackStep.end()) // 步骤回调 { - E_CMD_STATUS eResult; - step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = step_iter->second->Callback(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes()); - if (CMD_STATUS_RUNNING != eResult) + LOG4_TRACE("receive message, cmd = %d", + oMsgHead.cmd()); + if (step_iter->second != nullptr) { - uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); - if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + LOG4_TRACE("cmd %u, seq %u, step_seq %u, active_time %lf", + oMsgHead.cmd(), oMsgHead.seq(), step_iter->second->GetSequence(), + step_iter->second->GetActiveTime()); + eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); + if (CMD_STATUS_RUNNING != eResult) { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) + uint32 uiChainId = step_iter->second->GetChainId(); + m_pLabor->GetDispatcher()->DelEvent(step_iter->second->MutableWatcher()->MutableTimerWatcher()); + step_iter->second->MutableWatcher()->Reset(); + m_mapCallbackStep.erase(step_iter); + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) { - chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) + auto chain_iter = m_mapChain.find(uiChainId); + if (chain_iter != m_mapChain.end()) { - RemoveChain(uiChainId); + chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + RemoveChain(uiChainId); + } } } } } - return(eResult); } - } - else - { - auto cmd_iter = m_mapCmd.find(CMD_REQ_RAW_DATA); - if (cmd_iter != m_mapCmd.end() && cmd_iter->second != nullptr) + else { - auto pRawCmd = std::dynamic_pointer_cast(cmd_iter->second); - if (pRawCmd == nullptr) - { - LOG4_ERROR("cmd %d is not a RawCmd instance!", CMD_REQ_RAW_DATA); - return(false); - } - return(pRawCmd->AnyMessage(pChannel, oBuffer.GetRawReadBuffer(), oBuffer.ReadableBytes())); + return(false); } - LOG4_ERROR("no instance of RawCmd or derived class of RawCmd found for cmd %d", CMD_REQ_RAW_DATA); - return(false); } + return(true); } bool ActorBuilder::OnError(std::shared_ptr pChannel, uint32 uiStepSeq, int iErrno, const std::string& strErrMsg) @@ -800,7 +340,9 @@ bool ActorBuilder::OnError(std::shared_ptr pChannel, uint32 uiSte if (CMD_STATUS_RUNNING != eResult) { uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); + m_pLabor->GetDispatcher()->DelEvent(step_iter->second->MutableWatcher()->MutableTimerWatcher()); + step_iter->second->MutableWatcher()->Reset(); + m_mapCallbackStep.erase(step_iter); if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) { auto chain_iter = m_mapChain.find(uiChainId); @@ -816,7 +358,6 @@ bool ActorBuilder::OnError(std::shared_ptr pChannel, uint32 uiSte } } } - ExecAssemblyLine(pChannel, iErrno, strErrMsg); return(true); } else @@ -827,11 +368,6 @@ bool ActorBuilder::OnError(std::shared_ptr pChannel, uint32 uiSte } } -void ActorBuilder::AddAssemblyLine(std::shared_ptr pSession) -{ - m_setAssemblyLine.insert(pSession); -} - void ActorBuilder::RemoveStep(std::shared_ptr pStep) { if (nullptr == pStep) @@ -839,31 +375,8 @@ void ActorBuilder::RemoveStep(std::shared_ptr pStep) return; } std::unordered_map >::iterator callback_iter; - for (auto step_seq_iter = pStep->m_setPreStepSeq.begin(); - step_seq_iter != pStep->m_setPreStepSeq.end(); ) - { - callback_iter = m_mapCallbackStep.find(*step_seq_iter); - if (callback_iter == m_mapCallbackStep.end()) - { - pStep->m_setPreStepSeq.erase(step_seq_iter++); - } - else - { - LOG4_DEBUG("step %u had pre step %u running, delay delete callback.", pStep->GetSequence(), *step_seq_iter); - ResetTimeout(std::dynamic_pointer_cast(pStep)); - return; - } - } - auto class_iter = m_mapLoadedStep.find(pStep->GetActorName()); - if (class_iter != m_mapLoadedStep.end()) - { - auto id_iter = class_iter->second.find(pStep->GetSequence()); - if (id_iter != class_iter->second.end()) - { - class_iter->second.erase(id_iter); - } - } - m_pLabor->GetDispatcher()->DelEvent(pStep->MutableTimerWatcher()); + m_pLabor->GetDispatcher()->DelEvent(pStep->MutableWatcher()->MutableTimerWatcher()); + pStep->MutableWatcher()->Reset(); callback_iter = m_mapCallbackStep.find(pStep->GetSequence()); if (callback_iter != m_mapCallbackStep.end()) { @@ -878,16 +391,8 @@ void ActorBuilder::RemoveSession(std::shared_ptr pSession) { return; } - auto class_iter = m_mapLoadedSession.find(pSession->GetActorName()); - if (class_iter != m_mapLoadedSession.end()) - { - auto id_iter = class_iter->second.find(pSession->GetSessionId()); - if (id_iter != class_iter->second.end()) - { - class_iter->second.erase(id_iter); - } - } - m_pLabor->GetDispatcher()->DelEvent(pSession->MutableTimerWatcher()); + m_pLabor->GetDispatcher()->DelEvent(pSession->MutableWatcher()->MutableTimerWatcher()); + pSession->MutableWatcher()->Reset(); auto iter = m_mapCallbackSession.find(pSession->GetSessionId()); if (iter != m_mapCallbackSession.end()) { @@ -902,7 +407,8 @@ void ActorBuilder::RemoveChain(uint32 uiChainId) if (chain_iter != m_mapChain.end()) { std::shared_ptr pChain = chain_iter->second; - m_pLabor->GetDispatcher()->DelEvent(pChain->MutableTimerWatcher()); + m_pLabor->GetDispatcher()->DelEvent(pChain->MutableWatcher()->MutableTimerWatcher()); + pChain->MutableWatcher()->Reset(); m_mapChain.erase(chain_iter); } } @@ -928,84 +434,6 @@ void ActorBuilder::ChannelNotice(std::shared_ptr pChannel, const } } -void ActorBuilder::ExecAssemblyLine(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) -{ - for (auto session_iter = m_setAssemblyLine.begin(); session_iter != m_setAssemblyLine.end(); ++session_iter) - { - uint32 uiStepSeq = 0; - do - { - uiStepSeq = (*session_iter)->PopWaitingStep(); - auto step_iter = m_mapCallbackStep.find(uiStepSeq); - if (step_iter != m_mapCallbackStep.end()) - { - E_CMD_STATUS eResult; - step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = (std::dynamic_pointer_cast(step_iter->second))->Callback(pChannel, oMsgHead, oMsgBody); - if (CMD_STATUS_RUNNING != eResult) - { - uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); - if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - } - } - while(uiStepSeq > 0); - } - m_setAssemblyLine.clear(); -} - -void ActorBuilder::ExecAssemblyLine(std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) -{ - for (auto session_iter = m_setAssemblyLine.begin(); session_iter != m_setAssemblyLine.end(); ++session_iter) - { - uint32 uiStepSeq = 0; - do - { - uiStepSeq = (*session_iter)->PopWaitingStep(); - auto step_iter = m_mapCallbackStep.find(uiStepSeq); - if (step_iter != m_mapCallbackStep.end()) - { - E_CMD_STATUS eResult; - step_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = (std::dynamic_pointer_cast(step_iter->second))->ErrBack(pChannel, iErrno, strErrMsg); - if (CMD_STATUS_RUNNING != eResult) - { - uint32 uiChainId = step_iter->second->GetChainId(); - RemoveStep(step_iter->second); - if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) - { - auto chain_iter = m_mapChain.find(uiChainId); - if (chain_iter != m_mapChain.end()) - { - chain_iter->second->SetActiveTime(m_pLabor->GetNowTime()); - eResult = chain_iter->second->Next(); - if (CMD_STATUS_RUNNING != eResult) - { - RemoveChain(uiChainId); - } - } - } - } - } - } - while(uiStepSeq > 0); - } - m_setAssemblyLine.clear(); -} - void ActorBuilder::AddChainConf(const std::string& strChainKey, std::queue >&& queChainBlocks) { m_mapChainConf.insert(std::make_pair(strChainKey, std::move(queChainBlocks))); @@ -1063,9 +491,14 @@ std::shared_ptr ActorBuilder::InitializeSharedActor(Actor* pCreator, std: pSharedActor->SetLabor(m_pLabor); pSharedActor->SetActiveTime(m_pLabor->GetNowTime()); pSharedActor->SetActorName(strActorName); - if (nullptr != pCreator && pSharedActor->GetActorType() != Actor::ACT_CONTEXT) + pSharedActor->SetActorStatus(Actor::ACT_STATUS_SHARED); + if (nullptr != pCreator) { - pSharedActor->SetContext(pCreator->GetContext()); + if (pSharedActor->GetActorType() != Actor::ACT_CONTEXT) + { + pSharedActor->SetContext(pCreator->GetContext()); + } + pSharedActor->SetActorStatus(Actor::ACT_STATUS_DYNAMIC_LOAD | pCreator->GetActorStatus()); } switch (pSharedActor->GetActorType()) { @@ -1113,7 +546,7 @@ std::shared_ptr ActorBuilder::InitializeSharedActor(Actor* pCreator, std: } break; default: - LOG4_ERROR("\"%s\" must be a Step, a Session, a Operator, a Cmd or a Module.", + LOG4_ERROR("\"%s\" must be a Step, a Session, an Operator, a Cmd or a Module.", strActorName.c_str()); return(nullptr); } @@ -1124,8 +557,14 @@ bool ActorBuilder::TransformToSharedStep(Actor* pCreator, std::shared_ptr { pSharedActor->m_dTimeout = (gc_dConfigTimeout == pSharedActor->m_dTimeout) ? m_pLabor->GetNodeInfo().dStepTimeout : pSharedActor->m_dTimeout; - ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); - if (NULL == timer_watcher) + auto pWatcher = pSharedActor->MutableWatcher(); + if (nullptr == pWatcher) + { + return(false); + } + pWatcher->Set(pSharedActor); + ev_timer* timer_watcher = pWatcher->MutableTimerWatcher(); + if (nullptr == timer_watcher) { return(false); } @@ -1140,14 +579,6 @@ bool ActorBuilder::TransformToSharedStep(Actor* pCreator, std::shared_ptr pSharedActor->ForceNewSequence(); } std::shared_ptr pSharedStep = std::dynamic_pointer_cast(pSharedActor); - for (auto iter = pSharedStep->m_setNextStepSeq.begin(); iter != pSharedStep->m_setNextStepSeq.end(); ++iter) - { - auto callback_iter = m_mapCallbackStep.find(*iter); - if (callback_iter != m_mapCallbackStep.end()) - { - callback_iter->second->m_setPreStepSeq.insert(pSharedStep->GetSequence()); - } - } auto ret = m_mapCallbackStep.insert(std::make_pair(pSharedStep->GetSequence(), pSharedStep)); if (ret.second) @@ -1158,11 +589,6 @@ bool ActorBuilder::TransformToSharedStep(Actor* pCreator, std::shared_ptr } LOG4_TRACE("Step(seq %u, active_time %lf, lifetime %lf) register successful.", pSharedStep->GetSequence(), pSharedStep->GetActiveTime(), pSharedStep->GetTimeout()); - auto step_class_iter = m_mapLoadedStep.find(pSharedStep->GetActorName()); - if (step_class_iter != m_mapLoadedStep.end()) - { - step_class_iter->second.insert(pSharedStep->GetSequence()); - } return(true); } else @@ -1174,8 +600,14 @@ bool ActorBuilder::TransformToSharedStep(Actor* pCreator, std::shared_ptr bool ActorBuilder::TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor) { - ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); - if (NULL == timer_watcher) + auto pWatcher = pSharedActor->MutableWatcher(); + if (nullptr == pWatcher) + { + return(false); + } + pWatcher->Set(pSharedActor); + ev_timer* timer_watcher = pWatcher->MutableTimerWatcher(); + if (nullptr == timer_watcher) { return(false); } @@ -1194,11 +626,6 @@ bool ActorBuilder::TransformToSharedSession(Actor* pCreator, std::shared_ptrGetDispatcher()->AddEvent(timer_watcher, SessionTimeoutCallback, pSharedSession->m_dTimeout); } - auto session_class_iter = m_mapLoadedSession.find(pSharedSession->GetActorName()); - if (session_class_iter != m_mapLoadedSession.end()) - { - session_class_iter->second.insert(pSharedSession->GetSessionId()); - } return(true); } else @@ -1220,11 +647,6 @@ bool ActorBuilder::TransformToSharedCmd(Actor* pCreator, std::shared_ptr { if (pSharedCmd->Init()) { - auto cmd_class_iter = m_mapLoadedCmd.find(pSharedCmd->GetActorName()); - if (cmd_class_iter != m_mapLoadedCmd.end()) - { - cmd_class_iter->second.insert(pSharedCmd->GetCmd()); - } return(true); } else @@ -1253,11 +675,6 @@ bool ActorBuilder::TransformToSharedModule(Actor* pCreator, std::shared_ptrInit()) { - auto module_class_iter = m_mapLoadedModule.find(pSharedModule->GetActorName()); - if (module_class_iter != m_mapLoadedModule.end()) - { - module_class_iter->second.insert(pSharedModule->GetModulePath()); - } return(true); } else @@ -1298,8 +715,14 @@ bool ActorBuilder::TransformToSharedOperator(Actor* pCreator, std::shared_ptr pSharedActor) { - ev_timer* timer_watcher = pSharedActor->MutableTimerWatcher(); - if (NULL == timer_watcher) + auto pWatcher = pSharedActor->MutableWatcher(); + if (nullptr == pWatcher) + { + return(false); + } + pWatcher->Set(pSharedActor); + ev_timer* timer_watcher = pWatcher->MutableTimerWatcher(); + if (nullptr == timer_watcher) { return(false); } @@ -1352,6 +775,23 @@ bool ActorBuilder::TransformToSharedChain(Actor* pCreator, std::shared_ptrSetLabor(m_pLabor); + pNewActor->SetActiveTime(m_pLabor->GetNowTime()); + pNewActor->SetActorName(strActorName); + pNewActor->SetActorStatus(Actor::ACT_STATUS_ACTIVITY); + if (nullptr != pCreator && pNewActor->GetActorType() != Actor::ACT_CONTEXT) + { + pNewActor->SetContext(pCreator->GetContext()); + } + return(true); +} + bool ActorBuilder::SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq, bool bEnableReadOnly) { bool bSendResult = false; @@ -1431,7 +871,7 @@ std::shared_ptr ActorBuilder::GetOperator(const std::string& strOperat bool ActorBuilder::ResetTimeout(std::shared_ptr pSharedActor) { - ev_timer* watcher = pSharedActor->MutableTimerWatcher(); + ev_timer* watcher = pSharedActor->MutableWatcher()->MutableTimerWatcher(); ev_tstamp after = m_pLabor->GetNowTime() + pSharedActor->GetTimeout(); m_pLabor->GetDispatcher()->RefreshEvent(watcher, after); return(true); @@ -1442,6 +882,11 @@ int32 ActorBuilder::GetStepNum() return((int32)m_mapCallbackStep.size()); } +std::shared_ptr ActorBuilder::GetLogger() const +{ + return(m_pLogger); +} + bool ActorBuilder::ReloadCmdConf() { for (auto cmd_iter = m_mapCmd.begin(); cmd_iter != m_mapCmd.end(); ++cmd_iter) @@ -1500,99 +945,29 @@ void ActorBuilder::DynamicLoad(CJsonObject& oDynamicLoadingConf) oDynamicLoadingConf[i].Get("load", bIsload); if (bIsload) { - if (oDynamicLoadingConf[i].Get("so_path", strSoPath) && oDynamicLoadingConf[i].Get("version", strVersion)) + std::string strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); + if (m_pLabor->GetNodeInfo().bThreadMode && Labor::LABOR_MANAGER != m_pLabor->GetLaborType()) { - so_iter = m_mapLoadedSo.find(strSoPath); - if (so_iter == m_mapLoadedSo.end()) - { - std::string strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") - + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); - if (m_pLabor->GetNodeInfo().bThreadMode && Labor::LABOR_MANAGER != m_pLabor->GetLaborType()) - { - LoadDynamicSymbol(oDynamicLoadingConf[i]); - } - else - { - if (0 != access(strSoFile.c_str(), F_OK)) - { - strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); - if (0 != access(strSoFile.c_str(), F_OK)) - { - LOG4_WARNING("%s not exist!", strSoFile.c_str()); - continue; - } - } - pSo = LoadSo(strSoFile, strVersion); - if (pSo != nullptr) - { - LOG4_INFO("succeed in loading %s", strSoFile.c_str()); - m_mapLoadedSo.insert(std::make_pair(strSoPath, pSo)); - LoadDynamicSymbol(oDynamicLoadingConf[i]); - } - } - } - else - { - if (strVersion != so_iter->second->strVersion) // 版本升级,先卸载再加载 - { - std::string strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") - + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); - if (m_pLabor->GetNodeInfo().bThreadMode && Labor::LABOR_MANAGER != m_pLabor->GetLaborType()) - { - UnloadDynamicSymbol(oDynamicLoadingConf[i]); - LoadDynamicSymbol(oDynamicLoadingConf[i]); - } - else - { - if (0 != access(strSoFile.c_str(), F_OK)) - { - strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); - if (0 != access(strSoFile.c_str(), F_OK)) - { - LOG4_WARNING("%s not exist!", strSoFile.c_str()); - continue; - } - } - UnloadDynamicSymbol(oDynamicLoadingConf[i]); - dlclose(so_iter->second->pSoHandle); - delete so_iter->second; - pSo = LoadSo(strSoFile, strVersion); - if (pSo != nullptr) - { - LOG4_INFO("succeed in loading %s", strSoFile.c_str()); - so_iter->second = pSo; - LoadDynamicSymbol(oDynamicLoadingConf[i]); - } - else - { - m_mapLoadedSo.erase(so_iter); - } - } - } - } + LoadDynamicSymbol(oDynamicLoadingConf[i]); } - } - else // 卸载动态库 - { - if (oDynamicLoadingConf[i].Get("so_path", strSoPath)) + else { - if (m_pLabor->GetNodeInfo().bThreadMode && Labor::LABOR_MANAGER != m_pLabor->GetLaborType()) - { - UnloadDynamicSymbol(oDynamicLoadingConf[i]); - } - else + if (0 != access(strSoFile.c_str(), F_OK)) { - so_iter = m_mapLoadedSo.find(strSoPath); - if (so_iter != m_mapLoadedSo.end()) + strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path"); + if (0 != access(strSoFile.c_str(), F_OK)) { - UnloadDynamicSymbol(oDynamicLoadingConf[i]); - dlclose(so_iter->second->pSoHandle); - delete so_iter->second; - m_mapLoadedSo.erase(so_iter); - LOG4_INFO("unload %s.%s", strSoPath.c_str(), - oDynamicLoadingConf[i]("version").c_str()); + LOG4_WARNING("%s not exist!", strSoFile.c_str()); + continue; } } + pSo = LoadSo(strSoFile, strVersion); + if (pSo != nullptr) + { + LOG4_INFO("succeed in loading %s", strSoFile.c_str()); + LoadDynamicSymbol(oDynamicLoadingConf[i]); + } } } } @@ -1631,110 +1006,15 @@ void ActorBuilder::LoadDynamicSymbol(CJsonObject& oOneSoConf) for (int i = 0; i < oOneSoConf["cmd"].GetArraySize(); ++i) { std::unordered_set setCmd; - m_mapLoadedCmd.insert(std::make_pair(oOneSoConf["cmd"][i]("class"), setCmd)); oOneSoConf["cmd"][i].Get("cmd", iCmd); MakeSharedCmd(nullptr, oOneSoConf["cmd"][i]("class"), (int32)iCmd); } for (int j = 0; j < oOneSoConf["module"].GetArraySize(); ++j) { std::unordered_set setModule; - m_mapLoadedModule.insert(std::make_pair(oOneSoConf["module"][j]("class"), setModule)); oOneSoConf["module"][j].Get("path", strUrlPath); MakeSharedModule(nullptr, oOneSoConf["module"][j]("class"), strUrlPath); } - for (int k = 0; k < oOneSoConf["session"].GetArraySize(); ++k) - { - std::unordered_set setSession; - m_mapLoadedSession.insert(std::make_pair(oOneSoConf["session"](k), setSession)); - } - for (int l = 0; l < oOneSoConf["step"].GetArraySize(); ++l) - { - std::unordered_set setStep; - m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); - } -} - -void ActorBuilder::UnloadDynamicSymbol(CJsonObject& oOneSoConf) -{ - for (int i = 0; i < oOneSoConf["cmd"].GetArraySize(); ++i) - { - auto class_iter = m_mapLoadedCmd.find(oOneSoConf["cmd"][i]("class")); - if (class_iter != m_mapLoadedCmd.end()) - { - for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) - { - auto cmd_iter = m_mapCmd.find(*id_iter); - if (cmd_iter != m_mapCmd.end()) - { - m_mapCmd.erase(cmd_iter); - } - } - class_iter->second.clear(); - m_mapLoadedCmd.erase(class_iter); - } - } - for (int j = 0; j < oOneSoConf["module"].GetArraySize(); ++j) - { - auto class_iter = m_mapLoadedModule.find(oOneSoConf["module"][j]("class")); - if (class_iter != m_mapLoadedModule.end()) - { - for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) - { - auto module_iter = m_mapModule.find(*id_iter); - if (module_iter != m_mapModule.end()) - { - m_mapModule.erase(module_iter); - } - } - class_iter->second.clear(); - m_mapLoadedModule.erase(class_iter); - } - } - for (int k = 0; k < oOneSoConf["session"].GetArraySize(); ++k) - { - auto class_iter = m_mapLoadedSession.find(oOneSoConf["session"](k)); - if (class_iter != m_mapLoadedSession.end()) - { - for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) - { - auto session_iter = m_mapCallbackSession.find(*id_iter); - if (session_iter != m_mapCallbackSession.end()) - { - m_mapCallbackSession.erase(session_iter); - } - } - class_iter->second.clear(); - m_mapLoadedSession.erase(class_iter); - } - } - for (int l = 0; l < oOneSoConf["step"].GetArraySize(); ++l) - { - auto class_iter = m_mapLoadedStep.find(oOneSoConf["step"](l)); - if (class_iter != m_mapLoadedStep.end()) - { - for (auto id_iter = class_iter->second.begin(); id_iter != class_iter->second.end(); ++id_iter) - { - auto step_iter = m_mapCallbackStep.find(*id_iter); - if (step_iter != m_mapCallbackStep.end()) - { - step_iter->second->Timeout(); - m_mapCallbackStep.erase(step_iter); - } - } - class_iter->second.clear(); - m_mapLoadedStep.erase(class_iter); - } - std::unordered_set setStep; - m_mapLoadedStep.insert(std::make_pair(oOneSoConf["step"](l), setStep)); - } - for (int k = 0; k < oOneSoConf["model"].GetArraySize(); ++k) - { - auto class_iter = m_mapOperator.find(oOneSoConf["model"](k)); - if (class_iter != m_mapOperator.end()) - { - m_mapOperator.erase(class_iter); - } - } } } /* namespace neb */ diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 93cbaf37..87cd0fa7 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -45,6 +45,7 @@ namespace neb { +class CJsonObject; typedef RedisReply RedisMsg; class Manager; @@ -66,8 +67,7 @@ class SessionLogger; class SocketChannel; class RedisChannel; class Dispatcher; - -class CJsonObject; +template class IO; class ActorBuilder { @@ -95,13 +95,6 @@ class ActorBuilder bool OnSessionTimeout(std::shared_ptr pSession); bool OnChainTimeout(std::shared_ptr pChain); bool OnMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); - bool OnMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg, E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK); - bool OnMessage(std::shared_ptr pChannel, const RedisMsg& oRedisMsg, uint32 uiFinalStepSeq = 0); - bool OnMessage(std::shared_ptr pChannel, const CBuffer& oBuffer); - bool OnSelfMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); - bool OnSelfMessage(std::shared_ptr pChannel, const HttpMsg& oHttpMsg); - bool OnSelfMessage(std::shared_ptr pChannel, const RedisMsg& oRedisMsg); - bool OnSelfMessage(std::shared_ptr pChannel, const CBuffer& oBuffer); bool OnError(std::shared_ptr pChannel, uint32 uiStepSeq, int iErrno, const std::string& strErrMsg); public: @@ -142,6 +135,7 @@ class ActorBuilder bool TransformToSharedSession(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedOperator(Actor* pCreator, std::shared_ptr pSharedActor); bool TransformToSharedChain(Actor* pCreator, std::shared_ptr pSharedActor); + bool RegisterActor(const std::string& strActorName, Actor* pNewActor, Actor* pCreator = nullptr); public: bool SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq, bool bEnableReadOnly); @@ -151,25 +145,22 @@ class ActorBuilder virtual std::shared_ptr GetOperator(const std::string& strOperatorName); virtual bool ResetTimeout(std::shared_ptr pSharedActor); int32 GetStepNum(); + std::shared_ptr GetLogger() const; bool ReloadCmdConf(); bool AddNetLogMsg(const MsgBody& oMsgBody); void AddChainConf(const std::string& strChainKey, std::queue >&& queChainBlocks); protected: - void AddAssemblyLine(std::shared_ptr pSession); void RemoveStep(std::shared_ptr pStep); void RemoveSession(std::shared_ptr pSession); void RemoveChain(uint32 uiChainId); void ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData); - void ExecAssemblyLine(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody); - void ExecAssemblyLine(std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg); void LoadSysCmd(); void BootLoadCmd(CJsonObject& oCmdConf); void DynamicLoad(CJsonObject& oSoConf); tagSo* LoadSo(const std::string& strSoPath, const std::string& strVersion); void LoadDynamicSymbol(CJsonObject& oOneSoConf); - void UnloadDynamicSymbol(CJsonObject& oOneSoConf); private: char* m_pErrBuff; @@ -177,13 +168,6 @@ class ActorBuilder std::shared_ptr m_pLogger; std::shared_ptr m_pSessionLogger; - // dynamic load,use for load and unload. - std::unordered_map m_mapLoadedSo; - std::unordered_map > m_mapLoadedCmd; //key为CmdClassName,value为iCmd集合 - std::unordered_map > m_mapLoadedModule; //key为ModuleClassName,value为strModulePath集合 - std::unordered_map > m_mapLoadedStep; //key为StepClassName,value为uiSeq集合 - std::unordered_map > m_mapLoadedSession; //key为SessionClassName,value为strSessionId集合 - // Cmd and Module std::unordered_map > m_mapCmd; std::unordered_map > m_mapModule; @@ -204,6 +188,7 @@ class ActorBuilder friend class Actor; friend class Dispatcher; friend class Chain; + template friend class IO; }; template diff --git a/src/actor/ActorSender.cpp b/src/actor/ActorSender.cpp index d75e7135..3822fe4b 100644 --- a/src/actor/ActorSender.cpp +++ b/src/actor/ActorSender.cpp @@ -15,6 +15,7 @@ //#include "actor/session/Session.hpp" //#include "actor/step/Step.hpp" //#include "labor/Worker.hpp" +#include "ios/IO.hpp" namespace neb { @@ -27,37 +28,42 @@ ActorSender::~ActorSender() { } +void ActorSender::SetAuth(Actor* pActor, const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword) +{ + pActor->m_pLabor->GetDispatcher()->SetAuth(strNodeType, strAuth, strPassword); +} + bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel) { - return(pActor->m_pLabor->GetDispatcher()->SendTo(pChannel)); + return(IO::Send(pChannel)); } bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); - return(pActor->m_pLabor->GetDispatcher()->SendTo(pChannel, iCmd, uiSeq, oMsgBody)); + return(IO::SendResponse(pActor, pChannel, iCmd, uiSeq, oMsgBody)); } bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, const HttpMsg& oHttpMsg) { (const_cast(oHttpMsg)).mutable_headers()->insert({"x-trace-id", pActor->GetTraceId()}); - return(pActor->m_pLabor->GetDispatcher()->SendTo(pChannel, oHttpMsg, 0)); + return(IO::SendResponse(pActor, pChannel, oHttpMsg)); } bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, const RedisReply& oRedisReply) { - return(pActor->m_pLabor->GetDispatcher()->SendTo(pChannel, oRedisReply, 0)); + return(IO::SendResponse(pActor, pChannel, oRedisReply)); } bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, const char* pRawData, uint32 uiRawDataSize) { - return(pActor->m_pLabor->GetDispatcher()->SendTo(pChannel, pRawData, uiRawDataSize, 0)); + return(IO::SendResponse(pActor, pChannel, pRawData, uiRawDataSize)); } bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); - return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); + return(IO::SendTo(pActor, strIdentify, SOCKET_STREAM, false, true, iCmd, uiSeq, oMsgBody)); } bool ActorSender::SendTo(Actor* pActor, const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq) @@ -80,17 +86,17 @@ bool ActorSender::SendTo(Actor* pActor, const std::string& strHost, int iPort, c } if (oHttpMsg.http_major() == 2) { - return(pActor->m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP2, bWithSsl, bPipeline, oHttpMsg, pActor->GetSequence())); + return(IO::SendTo(pActor, strHost, iPort, bWithSsl, bPipeline, oHttpMsg)); } else { - return(pActor->m_pLabor->GetDispatcher()->SendTo(strHost, iPort, CODEC_HTTP, bWithSsl, bPipeline, oHttpMsg, pActor->GetSequence())); + return(IO::SendTo(pActor, strHost, iPort, bWithSsl, bPipeline, oHttpMsg)); } } bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, pActor->GetSequence())); + return(IO::SendTo(pActor, strIdentify, SOCKET_STREAM, bWithSsl, bPipeline, oRedisMsg)); } bool ActorSender::SendToCluster(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) @@ -100,12 +106,12 @@ bool ActorSender::SendToCluster(Actor* pActor, const std::string& strIdentify, c bool ActorSender::SendRoundRobin(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline) { - return(pActor->m_pLabor->GetDispatcher()->SendRoundRobin(strIdentify, SOCKET_STREAM, CODEC_RESP, bWithSsl, bPipeline, oRedisMsg, pActor->GetSequence())); + return(IO::SendRoundRobin(pActor, strIdentify, bWithSsl, bPipeline, oRedisMsg)); } bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(pActor->m_pLabor->GetDispatcher()->SendTo(strIdentify, SOCKET_STREAM, CODEC_UNKNOW, bWithSsl, bPipeline, pRawData, uiRawDataSize, pActor->GetSequence())); + return(IO::SendTo(pActor, strIdentify, SOCKET_STREAM, bWithSsl, bPipeline, pRawData, uiRawDataSize)); } bool ActorSender::SendTo(Actor* pActor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) @@ -117,13 +123,24 @@ bool ActorSender::SendTo(Actor* pActor, int32 iCmd, uint32 uiSeq, const MsgBody& bool ActorSender::SendRoundRobin(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); - return(pActor->m_pLabor->GetDispatcher()->SendRoundRobin(strNodeType, SOCKET_STREAM, eCodecType, false, true, iCmd, uiSeq, oMsgBody)); + return(IO::SendRoundRobin(pActor, strNodeType, false, true, iCmd, uiSeq, oMsgBody)); } bool ActorSender::SendOriented(Actor* pActor, const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); - return(pActor->m_pLabor->GetDispatcher()->SendOriented(strNodeType, SOCKET_STREAM, eCodecType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); + if (eCodecType == CODEC_NEBULA) + { + return(IO::SendOriented(pActor, strNodeType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); + } + else if (eCodecType == CODEC_NEBULA_IN_NODE) + { + return(IO::SendOriented(pActor, strNodeType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); + } + else + { + return(IO::SendOriented(pActor, strNodeType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); + } } bool ActorSender::SendOriented(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) @@ -137,11 +154,22 @@ bool ActorSender::SendOriented(Actor* pActor, const std::string& strNodeType, in } else if (oMsgBody.req_target().route().length() > 0) { - return(pActor->m_pLabor->GetDispatcher()->SendOriented(strNodeType, SOCKET_STREAM, eCodecType, false, true, oMsgBody.req_target().route(), iCmd, uiSeq, oMsgBody)); + if (eCodecType == CODEC_NEBULA) + { + return(IO::SendOriented(pActor, strNodeType, false, true, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody)); + } + else if (eCodecType == CODEC_NEBULA_IN_NODE) + { + return(IO::SendOriented(pActor, strNodeType, false, true, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody)); + } + else + { + return(IO::SendOriented(pActor, strNodeType, false, true, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody)); + } } else { - return(pActor->SendRoundRobin(strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); + return(SendRoundRobin(pActor, strNodeType, iCmd, uiSeq, oMsgBody, eCodecType)); } } else diff --git a/src/actor/ActorSender.hpp b/src/actor/ActorSender.hpp index d20521f0..cafbbd50 100644 --- a/src/actor/ActorSender.hpp +++ b/src/actor/ActorSender.hpp @@ -39,6 +39,8 @@ class ActorSender ActorSender(); virtual ~ActorSender(); + static void SetAuth(Actor* pActor, const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword); + static bool SendTo(Actor* pActor, std::shared_ptr pChannel); // send pb message diff --git a/src/actor/cmd/sys_cmd/CmdDataReport.cpp b/src/actor/cmd/sys_cmd/CmdDataReport.cpp index b6d62bff..5fb5053b 100644 --- a/src/actor/cmd/sys_cmd/CmdDataReport.cpp +++ b/src/actor/cmd/sys_cmd/CmdDataReport.cpp @@ -49,7 +49,7 @@ bool CmdDataReport::AnyMessage( if (pReport == nullptr) { LOG4_ERROR("failed to new Report!"); - return(CMD_STATUS_FAULT); + return(false); } if (!pReport->ParseFromString(oInMsgBody.data())) { diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp index 8be415b6..89abb3e3 100644 --- a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp +++ b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp @@ -14,6 +14,7 @@ #include #include #include "ios/Dispatcher.hpp" +#include "channel/SocketChannelImpl.hpp" #include "codec/http2/H2Comm.hpp" #include "codec/http2/Http2Frame.hpp" #include "codec/http2/CodecHttp2.hpp" @@ -134,7 +135,8 @@ bool ModuleHttpUpgrade::UpgradeToWebSocket(std::shared_ptr pChann oOutHttpMsg.mutable_headers()->insert(google::protobuf::MapPair("Sec-WebSocket-Extensions", "private-extension")); oOutHttpMsg.mutable_headers()->insert(google::protobuf::MapPair("Sec-WebSocket-Accept", strBase64EncodeAcceptKey)); SendTo(pChannel, oOutHttpMsg); - GetLabor(this)->GetDispatcher()->SwitchCodec(pChannel, CODEC_WS_EXTEND_JSON); + SocketChannelImpl::NewCodec(pChannel, GetLabor(this), + GetLabor(this)->GetActorBuilder()->GetLogger(), CODEC_WS_EXTEND_JSON); } else { @@ -190,11 +192,13 @@ bool ModuleHttpUpgrade::UpgradeToHttp2(std::shared_ptr pChannel, oOutHttpMsg.set_body(strSettingFrame); SendTo(pChannel, oOutHttpMsg); - auto pCodec = GetLabor(this)->GetDispatcher()->SwitchCodec(pChannel, CODEC_HTTP2, true); - if (pCodec == nullptr) + bool bRes = SocketChannelImpl::NewCodec(pChannel, GetLabor(this), + GetLabor(this)->GetActorBuilder()->GetLogger(), CODEC_HTTP2); + if (!bRes) { return(false); } + auto pCodec = pChannel->GetCodec(); ((CodecHttp2*)pCodec)->Setting(vecSetting, false); return(true); } diff --git a/src/actor/session/Session.cpp b/src/actor/session/Session.cpp index e67de718..73964ee3 100644 --- a/src/actor/session/Session.cpp +++ b/src/actor/session/Session.cpp @@ -15,8 +15,7 @@ namespace neb { Session::Session(uint32 uiSessionId, ev_tstamp dSessionTimeout) - : Actor(Actor::ACT_SESSION, dSessionTimeout), - m_bDataReady(false), m_bDataLoading(false) + : Actor(Actor::ACT_SESSION, dSessionTimeout) { std::ostringstream oss; oss << uiSessionId; @@ -25,14 +24,12 @@ Session::Session(uint32 uiSessionId, ev_tstamp dSessionTimeout) Session::Session(const std::string& strSessionId, ev_tstamp dSessionTimeout) : Actor(Actor::ACT_SESSION, dSessionTimeout), - m_bDataReady(false), m_bDataLoading(false), m_strSessionId(strSessionId) { } Session::Session(ACTOR_TYPE eActorType, uint32 ulSessionId, ev_tstamp dSessionTimeout) - : Actor(eActorType, dSessionTimeout), - m_bDataReady(false), m_bDataLoading(false) + : Actor(eActorType, dSessionTimeout) { std::ostringstream oss; oss << ulSessionId; @@ -41,7 +38,6 @@ Session::Session(ACTOR_TYPE eActorType, uint32 ulSessionId, ev_tstamp dSessionTi Session::Session(ACTOR_TYPE eActorType, const std::string& strSessionId, ev_tstamp dSessionTimeout) : Actor(eActorType, dSessionTimeout), - m_bDataReady(false), m_bDataLoading(false), m_strSessionId(strSessionId) { } @@ -50,45 +46,4 @@ Session::~Session() { } -bool Session::IsReady(Step* pStep) -{ - if (!m_bDataReady) - { - m_vecWaitingStep.push(pStep->GetSequence()); - } - return(m_bDataReady); -} - -void Session::SetReady() -{ - m_bDataReady = true; - m_bDataLoading = false; - if (m_vecWaitingStep.size() > 0) - { - AddAssemblyLine(std::dynamic_pointer_cast(shared_from_this())); - } -} - -bool Session::IsLoading() -{ - return(m_bDataLoading); -} - -void Session::SetLoading() -{ - m_bDataLoading = true; - m_bDataReady = false; -} - -uint32 Session::PopWaitingStep() -{ - uint32 uiStepSeq = 0; - if (m_vecWaitingStep.size() > 0) - { - uiStepSeq = m_vecWaitingStep.front(); - m_vecWaitingStep.pop(); - } - return(uiStepSeq); -} - } /* namespace neb */ diff --git a/src/actor/session/Session.hpp b/src/actor/session/Session.hpp index e35eea46..e282044b 100644 --- a/src/actor/session/Session.hpp +++ b/src/actor/session/Session.hpp @@ -38,26 +38,6 @@ class Session: public Actor return(m_strSessionId); } - /** - * @brief 检查Session内数据是否已加载 - * @note 为满足数据共享并确保同一数据在同一个Worker内只需从 - * 外部存储中加载一次,提供了IsReady(),SetReady(),IsLoading(), - * SetLoading()四个方法。如果一个或若干个Step获取到一个已创建好 - * 的Session,则需先调用IsReady()判断数据是否就绪,若就绪则直接 - * 从Session中读取,若未就绪则调用IsLoading()判断数据是否正在加 - * 载,若正在加载则直接return(CMD_STATUS_RUNNING)(框架会在数据 - * 加载完毕后调用该Step的Callback),否则加载数据并且SetLoading(), - * 数据加载完毕后SetReady()。 - * @param pStep 调用IsReady()方法的调用者Step指针,用于记录 - * 哪些Step依赖于Session的数据,在数据就绪时由框架主动调用 - * 依赖这些数据的Step回调而不需要等到超时才回调。 - */ - bool IsReady(Step* pStep); - - void SetReady(); - bool IsLoading(); - void SetLoading(); - protected: // 这两个构造函数专为Timer而用,其他Session子类不可使用 Session(ACTOR_TYPE eActorType, uint32 ulSessionId, ev_tstamp dSessionTimeout = 60.0); @@ -67,10 +47,7 @@ class Session: public Actor uint32 PopWaitingStep(); private: - bool m_bDataReady; - bool m_bDataLoading; std::string m_strSessionId; - std::queue m_vecWaitingStep; friend class ActorBuilder; }; diff --git a/src/actor/session/sys_session/SessionDataReport.cpp b/src/actor/session/sys_session/SessionDataReport.cpp index 87b3b1d1..bffd3a79 100644 --- a/src/actor/session/sys_session/SessionDataReport.cpp +++ b/src/actor/session/sys_session/SessionDataReport.cpp @@ -45,6 +45,10 @@ SessionDataReport::~SessionDataReport() E_CMD_STATUS SessionDataReport::Timeout() { LOG4_TRACE(""); + if (Labor::LABOR_MANAGER != GetLabor(this)->GetLaborType()) + { + ((Worker*)GetLabor(this))->DataReport(); + } if (m_mapDataCollect.empty()) { return(CMD_STATUS_RUNNING); @@ -68,13 +72,10 @@ E_CMD_STATUS SessionDataReport::Timeout() m_pReport->mutable_records()->AddAllocated(iter->second); iter->second = nullptr; } - if (Labor::LABOR_MANAGER == GetLabor(this)->GetLaborType()) - { - MsgBody oMsgBody; - m_pReport->SerializeToString(&m_strReport); - oMsgBody.set_data(m_strReport); - SendDataReport(CMD_REQ_DATA_REPORT, GetSequence(), oMsgBody); - } + MsgBody oMsgBody; + m_pReport->SerializeToString(&m_strReport); + oMsgBody.set_data(m_strReport); + SendDataReport(CMD_REQ_DATA_REPORT, GetSequence(), oMsgBody); m_uiNodeReportUpdatingIndex = 1 - m_uiNodeReportUpdatingIndex; m_vecNodeReport[m_uiNodeReportUpdatingIndex].clear(); m_mapDataCollect.clear(); diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index ea8299f0..abbcd409 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -29,8 +29,8 @@ namespace neb std::mutex SessionManager::s_mutexWorker; std::vector SessionManager::s_vecWorkerThreadId; -SessionManager::SessionManager(bool bDirectToLoader) - : Session("neb::SessionManager", gc_dDefaultTimeout), m_bDirectToLoader(bDirectToLoader) +SessionManager::SessionManager(bool bDirectToLoader, ev_tstamp dStatInterval) + : Session("neb::SessionManager", dStatInterval), m_bDirectToLoader(bDirectToLoader) { m_iterWorkerInfo = m_mapWorkerInfo.begin(); } diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index 4b50e4a7..596e8027 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -23,10 +23,11 @@ namespace neb class Loader; class SessionManager : public Session, - public DynamicCreator, public ActorSys + public DynamicCreator, + public ActorSys { public: - SessionManager(bool bDirectToLoader); + SessionManager(bool bDirectToLoader, ev_tstamp dStatInterval); virtual ~SessionManager(); virtual E_CMD_STATUS Timeout(); diff --git a/src/actor/step/CassStep.cpp b/src/actor/step/CassStep.cpp new file mode 100644 index 00000000..fbda357a --- /dev/null +++ b/src/actor/step/CassStep.cpp @@ -0,0 +1,45 @@ +/******************************************************************************* + * Project: Nebula + * @file CassStep.cpp + * @brief + * @author Bwar + * @date: 2021-12-11 + * @note + * Modify history: + ******************************************************************************/ +#include "actor/step/CassStep.hpp" + +namespace neb +{ + +CassStep::CassStep(ev_tstamp dTimeout) + : Step(ACT_REDIS_STEP, dTimeout) +{ +} + +CassStep::~CassStep() +{ +} + +E_CMD_STATUS CassStep::Callback(std::shared_ptr pChannel, + const CassMessage& oCassMsg) +{ + auto& oCassResponse = static_cast(oCassMsg); + if (oCassResponse.IsSuccess()) + { + return(Callback(pChannel, oCassResponse.GetResult(), oCassResponse.GetErrCode(), oCassResponse.GetErrMsg())); + } + else + { + if (oCassResponse.GetErrCode() == 0) + { + return(Callback(pChannel, oCassResponse.GetResult(), -1, oCassResponse.GetErrMsg())); + } + else + { + return(Callback(pChannel, oCassResponse.GetResult(), oCassResponse.GetErrCode(), oCassResponse.GetErrMsg())); + } + } +} + +} /* namespace neb */ diff --git a/src/actor/step/CassStep.hpp b/src/actor/step/CassStep.hpp new file mode 100644 index 00000000..31dadd66 --- /dev/null +++ b/src/actor/step/CassStep.hpp @@ -0,0 +1,42 @@ +/******************************************************************************* + * Project: Nebula + * @file CassStep.hpp + * @brief + * @author Bwar + * @date: 2021-12-11 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_STEP_CASSSTEP_HPP_ +#define SRC_ACTOR_STEP_CASSSTEP_HPP_ + +#include "Step.hpp" +#include "codec/cass/CassMessage.hpp" +#include "codec/cass/CassRequest.hpp" +#include "codec/cass/CassResponse.hpp" +#include "codec/cass/result/CassResult.hpp" + +namespace neb +{ + +class CassStep: public Step +{ +public: + CassStep(ev_tstamp dTimeout = gc_dConfigTimeout); + CassStep(const CassStep&) = delete; + CassStep& operator=(const CassStep&) = delete; + virtual ~CassStep(); + + virtual E_CMD_STATUS Callback( + std::shared_ptr pChannel, + const CassMessage& oCassMsg); + + virtual E_CMD_STATUS Callback( + std::shared_ptr pChannel, + const CassResult& oCassResult, + int iErrCode, const std::string& strErrMsg) = 0; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_STEP_CASSSTEP_HPP_ */ diff --git a/src/actor/step/GrpcStep.cpp b/src/actor/step/GrpcStep.cpp index 94b875b6..19a6dff6 100644 --- a/src/actor/step/GrpcStep.cpp +++ b/src/actor/step/GrpcStep.cpp @@ -13,8 +13,8 @@ namespace neb { -GrpcStep::GrpcStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) - : HttpStep(pNextStep, dTimeout) +GrpcStep::GrpcStep(ev_tstamp dTimeout) + : HttpStep(dTimeout) { } diff --git a/src/actor/step/GrpcStep.hpp b/src/actor/step/GrpcStep.hpp index 61455a5c..193d4490 100644 --- a/src/actor/step/GrpcStep.hpp +++ b/src/actor/step/GrpcStep.hpp @@ -18,7 +18,7 @@ namespace neb class GrpcStep: public HttpStep { public: - GrpcStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); + GrpcStep(ev_tstamp dTimeout = gc_dConfigTimeout); GrpcStep(const GrpcStep&) = delete; GrpcStep& operator=(const GrpcStep&) = delete; virtual ~GrpcStep(); diff --git a/src/actor/step/HttpStep.cpp b/src/actor/step/HttpStep.cpp index 40db8a16..f790dc3f 100644 --- a/src/actor/step/HttpStep.cpp +++ b/src/actor/step/HttpStep.cpp @@ -13,8 +13,8 @@ namespace neb { -HttpStep::HttpStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) - : Step(ACT_HTTP_STEP, pNextStep, dTimeout) +HttpStep::HttpStep(ev_tstamp dTimeout) + : Step(ACT_HTTP_STEP, dTimeout) { } diff --git a/src/actor/step/HttpStep.hpp b/src/actor/step/HttpStep.hpp index eb7d20c6..61e826e9 100644 --- a/src/actor/step/HttpStep.hpp +++ b/src/actor/step/HttpStep.hpp @@ -20,7 +20,7 @@ namespace neb class HttpStep: public Step { public: - HttpStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); + HttpStep(ev_tstamp dTimeout = gc_dConfigTimeout); HttpStep(const HttpStep&) = delete; HttpStep& operator=(const HttpStep&) = delete; virtual ~HttpStep(); diff --git a/src/actor/step/PbStep.cpp b/src/actor/step/PbStep.cpp index 0bf8f296..c8c77bbb 100644 --- a/src/actor/step/PbStep.cpp +++ b/src/actor/step/PbStep.cpp @@ -12,8 +12,8 @@ namespace neb { -PbStep::PbStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) - : Step(Actor::ACT_PB_STEP, pNextStep, dTimeout) +PbStep::PbStep(ev_tstamp dTimeout) + : Step(Actor::ACT_PB_STEP, dTimeout) { } diff --git a/src/actor/step/PbStep.hpp b/src/actor/step/PbStep.hpp index cc0a11b3..021d0769 100644 --- a/src/actor/step/PbStep.hpp +++ b/src/actor/step/PbStep.hpp @@ -20,7 +20,7 @@ class ActorBuilder; class PbStep: public Step { public: - PbStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); + PbStep(ev_tstamp dTimeout = gc_dConfigTimeout); PbStep(const PbStep&) = delete; PbStep& operator=(const PbStep&) = delete; virtual ~PbStep(); diff --git a/src/actor/step/RawStep.cpp b/src/actor/step/RawStep.cpp index ddb37435..b807db46 100644 --- a/src/actor/step/RawStep.cpp +++ b/src/actor/step/RawStep.cpp @@ -12,8 +12,8 @@ namespace neb { -RawStep::RawStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) - : Step(ACT_REDIS_STEP, pNextStep, dTimeout) +RawStep::RawStep(ev_tstamp dTimeout) + : Step(ACT_REDIS_STEP, dTimeout) { } diff --git a/src/actor/step/RawStep.hpp b/src/actor/step/RawStep.hpp index 5fb02d2e..7d7d55b4 100644 --- a/src/actor/step/RawStep.hpp +++ b/src/actor/step/RawStep.hpp @@ -21,7 +21,7 @@ namespace neb class RawStep: public Step { public: - RawStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); + RawStep(ev_tstamp dTimeout = gc_dConfigTimeout); RawStep(const RawStep&) = delete; RawStep& operator=(const RawStep&) = delete; virtual ~RawStep(); diff --git a/src/actor/step/RedisStep.cpp b/src/actor/step/RedisStep.cpp index a6ae4d86..c4d82eb1 100644 --- a/src/actor/step/RedisStep.cpp +++ b/src/actor/step/RedisStep.cpp @@ -13,8 +13,8 @@ namespace neb { -RedisStep::RedisStep(std::shared_ptr pNextStep, ev_tstamp dTimeout) - : Step(ACT_REDIS_STEP, pNextStep, dTimeout) +RedisStep::RedisStep(ev_tstamp dTimeout) + : Step(ACT_REDIS_STEP, dTimeout) { } @@ -57,7 +57,7 @@ std::string RedisStep::CmdToString() const return strCmd; } -const RedisRequest& RedisStep::GenrateRedisRequest() +const RedisRequest& RedisStep::GenerateRedisRequest() { m_oRedisRequest.Clear(); m_oRedisRequest.set_type(REDIS_REPLY_ARRAY); @@ -73,6 +73,11 @@ const RedisRequest& RedisStep::GenrateRedisRequest() return(m_oRedisRequest); } +const RedisRequest& RedisStep::GetRedisRequest() const +{ + return(m_oRedisRequest); +} + std::shared_ptr RedisStep::MutableRedisRequest() { auto pRequest = std::make_shared(); diff --git a/src/actor/step/RedisStep.hpp b/src/actor/step/RedisStep.hpp index a56c0e9f..46e2ae82 100644 --- a/src/actor/step/RedisStep.hpp +++ b/src/actor/step/RedisStep.hpp @@ -12,7 +12,7 @@ #include #include -#include "actor/step/Step.hpp" +#include "Step.hpp" #include "pb/redis.pb.h" namespace neb @@ -26,7 +26,7 @@ typedef RedisReply RedisRequest; class RedisStep: public Step { public: - RedisStep(std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); + RedisStep(ev_tstamp dTimeout = gc_dConfigTimeout); RedisStep(const RedisStep&) = delete; RedisStep& operator=(const RedisStep&) = delete; virtual ~RedisStep(); @@ -80,7 +80,9 @@ class RedisStep: public Step return(m_vecCmdArguments); } - const RedisRequest& GenrateRedisRequest(); + const RedisRequest& GenerateRedisRequest(); + + const RedisRequest& GetRedisRequest() const; protected: std::shared_ptr MutableRedisRequest(); diff --git a/src/actor/step/Step.cpp b/src/actor/step/Step.cpp index a4e3d89d..024a08c1 100644 --- a/src/actor/step/Step.cpp +++ b/src/actor/step/Step.cpp @@ -12,20 +12,14 @@ namespace neb { -Step::Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep, ev_tstamp dTimeout) +Step::Step(Actor::ACTOR_TYPE eActorType, ev_tstamp dTimeout) : Actor(eActorType, dTimeout), m_uiChainId(0) { - if (nullptr != pNextStep) - { - m_setNextStepSeq.insert(pNextStep->GetSequence()); - } } Step::~Step() { - m_setNextStepSeq.clear(); - m_setPreStepSeq.clear(); } E_CMD_STATUS Step::Callback(std::shared_ptr pChannel, @@ -72,19 +66,6 @@ E_CMD_STATUS Step::Callback(std::shared_ptr pChannel, return(CMD_STATUS_FAULT); } -void Step::NextStep(int iErrno, const std::string& strErrMsg, void* data) -{ - if (iErrno != ERR_OK) - { - return; - } - - for (auto it = m_setNextStepSeq.begin(); it != m_setNextStepSeq.end(); ++it) - { - ExecStep(*it, iErrno, strErrMsg, data); - } -} - void Step::SetChainId(uint32 uiChainId) { m_uiChainId = uiChainId; diff --git a/src/actor/step/Step.hpp b/src/actor/step/Step.hpp index 25ddae99..aa5f41c2 100644 --- a/src/actor/step/Step.hpp +++ b/src/actor/step/Step.hpp @@ -22,7 +22,7 @@ class Chain; class Step: public Actor { public: - Step(Actor::ACTOR_TYPE eActorType, std::shared_ptr pNextStep = nullptr, ev_tstamp dTimeout = gc_dConfigTimeout); + Step(Actor::ACTOR_TYPE eActorType, ev_tstamp dTimeout = gc_dConfigTimeout); Step(const Step&) = delete; Step& operator=(const Step&) = delete; virtual ~Step(); @@ -60,23 +60,21 @@ class Step: public Actor virtual E_CMD_STATUS Callback(std::shared_ptr pChannel, const char* pRawData, uint32 uiRawDataSize); -protected: - /** - * @brief 执行当前步骤接下来的步骤 - */ - void NextStep(int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); - uint32 GetChainId() const { return(m_uiChainId); } +protected: + virtual bool WantResponse() const + { + return(true); + } + private: void SetChainId(uint32 uiChainId); uint32 m_uiChainId; - std::unordered_set m_setNextStepSeq; - std::unordered_set m_setPreStepSeq; friend class ActorBuilder; friend class Chain; diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index 8814aa38..c38c7e61 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -13,6 +13,7 @@ #include #include "util/StringCoder.hpp" #include "ios/Dispatcher.hpp" +#include "ios/IO.hpp" #include "util/StringCoder.hpp" #include "util/encrypt/crc16.h" @@ -102,8 +103,7 @@ const std::unordered_set StepRedisCluster::s_setMultipleKeyValueCmd StepRedisCluster::StepRedisCluster( const std::string& strIdentify, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) - : RedisStep(nullptr, 10.0), - m_bWithSsl(bWithSsl), m_bPipeline(bPipeline), m_bEnableReadOnly(bEnableReadOnly), + : m_bWithSsl(bWithSsl), m_bPipeline(bPipeline), m_bEnableReadOnly(bEnableReadOnly), m_uiAddressIndex(0), m_strIdentify(strIdentify) { @@ -143,10 +143,40 @@ E_CMD_STATUS StepRedisCluster::Callback( { CmdReadOnlyCallback(pChannel, pChannel->GetIdentify(), oRedisReply); } + else if (pRedisRequest->element(0).str() == "AUTH") + { + if (REDIS_REPLY_ERROR == oRedisReply.type()) + { + CmdErrBack(pChannel, oRedisReply.type(), oRedisReply.str()); + } + else + { + if (m_mapSlot2Node.size() == 0) + { + SendCmdClusterSlots(); + } + } + } else { - CmdClusterSlotsCallback(oRedisReply); - SendWaittingRequest(); + if (REDIS_REPLY_ERROR == oRedisReply.type()) + { + std::vector vecMsg; + Split(oRedisReply.str(), " ", vecMsg); + if (vecMsg.size() >= 3) + { + if (vecMsg[0] == "NOAUTH") + { + Auth(pChannel->GetIdentify(), nullptr); + return(CMD_STATUS_RUNNING); + } + } + } + else + { + CmdClusterSlotsCallback(oRedisReply); + SendWaittingRequest(); + } } } else @@ -172,9 +202,14 @@ E_CMD_STATUS StepRedisCluster::Callback( SendCmdAsking(vecMsg[2]); return(CMD_STATUS_RUNNING); } + if (vecMsg[0] == "NOAUTH") + { + Auth(pChannel->GetIdentify(), pRedisRequest); + return(CMD_STATUS_RUNNING); + } } } - GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oRedisReply, uiRealStepSeq); + IO::OnResponse(GetLabor(this)->GetActorBuilder(), pChannel, uiRealStepSeq, oRedisReply); } else // 多key请求的响应 { @@ -224,7 +259,7 @@ E_CMD_STATUS StepRedisCluster::Callback( oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); reply_iter->second[k] = nullptr; } - GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); + IO::OnResponse(GetLabor(this)->GetActorBuilder(), pChannel, uiRealStepSeq, oFinalReply); m_mapStepEmitNum.erase(num_iter); m_mapReply.erase(reply_iter); } @@ -294,7 +329,7 @@ E_CMD_STATUS StepRedisCluster::Callback( oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); reply_iter->second[k] = nullptr; } - GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); + IO::OnResponse(GetLabor(this)->GetActorBuilder(), pChannel, uiRealStepSeq, oFinalReply); m_mapStepEmitNum.erase(num_iter); m_mapReply.erase(reply_iter); } @@ -306,6 +341,7 @@ E_CMD_STATUS StepRedisCluster::Callback( E_CMD_STATUS StepRedisCluster::Timeout() { + SendCmdClusterSlots(); for (auto timeout_iter = m_mapTimeoutStep.begin(); timeout_iter != m_mapTimeoutStep.end(); ) { if (GetNowTime() - timeout_iter->first >= GetTimeout()) @@ -339,6 +375,14 @@ E_CMD_STATUS StepRedisCluster::ErrBack( std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) { LOG4_ERROR("error %d: %s", iErrno, strErrMsg.c_str()); + SendCmdClusterSlots(); + CmdErrBack(pChannel, iErrno, strErrMsg); + return(CMD_STATUS_RUNNING); +} + +void StepRedisCluster::CmdErrBack( + std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) +{ auto step_iter = m_mapPipelineRequest.find(pChannel->GetIdentify()); if (step_iter == m_mapPipelineRequest.end()) { @@ -406,7 +450,7 @@ E_CMD_STATUS StepRedisCluster::ErrBack( oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); reply_iter->second[k] = nullptr; } - GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); + IO::OnResponse(GetLabor(this)->GetActorBuilder(), pChannel, uiRealStepSeq, oFinalReply); m_mapStepEmitNum.erase(num_iter); m_mapReply.erase(reply_iter); } @@ -415,7 +459,6 @@ E_CMD_STATUS StepRedisCluster::ErrBack( } AskingQueueErrBack(pChannel, iErrno, strErrMsg); } - return(CMD_STATUS_RUNNING); } bool StepRedisCluster::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, @@ -435,8 +478,8 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, const RedisMsg& oR bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptr pRedisMsg) { LOG4_TRACE("%s", pRedisMsg->DebugString().c_str()); - bool bResult = GetLabor(this)->GetDispatcher()->SendTo(strIdentify, - SOCKET_STREAM, CODEC_RESP, m_bWithSsl, m_bPipeline, (*pRedisMsg.get()), GetSequence()); + bool bResult = IO::SendTo(this, strIdentify, + SOCKET_STREAM, m_bWithSsl, m_bPipeline, (*pRedisMsg.get())); if (bResult) { auto iter = m_mapPipelineRequest.find(strIdentify); @@ -892,7 +935,7 @@ void StepRedisCluster::AskingQueueErrBack( oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); reply_iter->second[k] = nullptr; } - GetLabor(this)->GetActorBuilder()->OnMessage(pChannel, oFinalReply, uiRealStepSeq); + IO::OnResponse(GetLabor(this)->GetActorBuilder(), pChannel, uiRealStepSeq, oFinalReply); m_mapStepEmitNum.erase(num_iter); m_mapReply.erase(reply_iter); } @@ -1039,6 +1082,25 @@ void StepRedisCluster::AddToAskingQueue(const std::string& strIdentify, std::sha } } -} /* namespace neb */ +bool StepRedisCluster::Auth(const std::string& strIdentify, std::shared_ptr pRedisMsg) +{ + std::string strAuth; + std::string strPassword; + GetLabor(this)->GetDispatcher()->GetAuth(strIdentify, strAuth, strPassword); + SetCmd("AUTH"); + Append(strPassword); + auto pRedisRequest = MutableRedisRequest(); + pRedisRequest->set_integer(GetSequence()); + if (pRedisMsg == nullptr) + { + return(SendTo(strIdentify, pRedisRequest)); + } + else + { + SendTo(strIdentify, pRedisRequest); + return(SendTo(strIdentify, pRedisMsg)); + } +} +} /* namespace neb */ diff --git a/src/actor/step/sys_step/StepRedisCluster.hpp b/src/actor/step/sys_step/StepRedisCluster.hpp index 81f18006..1b87dda0 100644 --- a/src/actor/step/sys_step/StepRedisCluster.hpp +++ b/src/actor/step/sys_step/StepRedisCluster.hpp @@ -93,10 +93,12 @@ class StepRedisCluster: public RedisStep, void CmdReadOnlyCallback(std::shared_ptr pChannel, const std::string& strIdentify, const RedisReply& oRedisReply); void AskingQueueErrBack(std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg); + void CmdErrBack(std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg); bool Dispatch(const RedisMsg& oRedisMsg, uint32 uiStepSeq); void SendWaittingRequest(); void RegisterStep(uint32 uiStepSeq); void AddToAskingQueue(const std::string& strIdentify, std::shared_ptr pRedisMsg); + bool Auth(const std::string& strIdentify, std::shared_ptr pRedisMsg); private: bool m_bWithSsl; ///< 是否支持SSL diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp index 9ddff3dc..713ce494 100644 --- a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp +++ b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp @@ -10,14 +10,14 @@ #include "StepReportToBeacon.hpp" #include "labor/NodeInfo.hpp" -#include "ios/Dispatcher.hpp" +#include "ios/IO.hpp" #include "actor/session/sys_session/manager/SessionManager.hpp" namespace neb { StepReportToBeacon::StepReportToBeacon(ev_tstamp dTimeout) - : PbStep(nullptr, dTimeout) + : PbStep(dTimeout) { } @@ -46,7 +46,7 @@ E_CMD_STATUS StepReportToBeacon::Emit( } m_pSessionManager->MakeReportData(oReportData); oMsgBody.set_data(oReportData.ToString()); - GetLabor(this)->GetDispatcher()->Broadcast("BEACON", SOCKET_STREAM, CODEC_NEBULA, false, true, + IO::Broadcast(this, "BEACON", false, true, (int32)CMD_REQ_NODE_STATUS_REPORT, GetSequence(), oMsgBody); return(CMD_STATUS_RUNNING); } diff --git a/src/channel/SelfChannel.hpp b/src/channel/SelfChannel.hpp index 39adccd1..14cee536 100644 --- a/src/channel/SelfChannel.hpp +++ b/src/channel/SelfChannel.hpp @@ -21,11 +21,6 @@ class SelfChannel: public SocketChannel SelfChannel(); virtual ~SelfChannel(); - virtual bool Init(E_CODEC_TYPE eCodecType, bool bIsClient = false) override - { - return(true); - } - virtual int GetFd() const override { return(0); diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index efb1e066..93ceab6b 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -7,41 +7,49 @@ * @note * Modify history: ******************************************************************************/ +#include "SocketChannel.hpp" #include #include #include #include #include #include -#include "pb/msg.pb.h" -#include "pb/http.pb.h" -#include "SocketChannel.hpp" +#include "ios/ChannelWatcher.hpp" +#include "SocketChannelImpl.hpp" +#ifdef WITH_OPENSSL +#include "SocketChannelSslImpl.hpp" +#endif +#include "codec/CodecProto.hpp" namespace neb { SocketChannel::SocketChannel() - : m_pImpl(nullptr), m_pLogger(nullptr) + : m_bIsClient(false), m_bWithSsl(false), m_pImpl(nullptr), m_pLogger(nullptr), m_pWatcher(nullptr) { } -SocketChannel::SocketChannel(std::shared_ptr pLogger, int iFd, uint32 ulSeq, bool bWithSsl, ev_tstamp dKeepAlive) - : m_pImpl(nullptr), m_pLogger(pLogger) +SocketChannel::SocketChannel(Labor* pLabor, std::shared_ptr pLogger, int iFd, uint32 ulSeq, bool bWithSsl, bool bIsClient, ev_tstamp dKeepAlive) + : m_bIsClient(bIsClient), m_bWithSsl(bWithSsl), m_pImpl(nullptr), m_pLogger(nullptr), m_pWatcher(nullptr) { if (bWithSsl) { #ifdef WITH_OPENSSL pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "with openssl, create SocekChannelSslImpl."); - m_pImpl = std::dynamic_pointer_cast(std::make_shared(this, pLogger, iFd, ulSeq, dKeepAlive)); + auto pImpl = std::make_shared>(pLabor, pLogger, iFd, ulSeq, dKeepAlive); + pImpl->Init(bIsClient); + m_pImpl = std::static_pointer_cast(pImpl); #else pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "without openssl, SocekChannelImpl instead."); - m_pImpl = std::make_shared(this, pLogger, iFd, ulSeq, dKeepAlive); + auto pImpl = std::make_shared>(pLabor, pLogger, iFd, ulSeq, dKeepAlive); + m_pImpl = std::static_pointer_cast(pImpl); #endif } else { pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "create SocekChannelImpl."); - m_pImpl = std::make_shared(this, pLogger, iFd, ulSeq, dKeepAlive); + auto pImpl = std::make_shared>(pLabor, pLogger, iFd, ulSeq, dKeepAlive); + m_pImpl = std::static_pointer_cast(pImpl); } } @@ -51,48 +59,154 @@ SocketChannel::~SocketChannel() { m_pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ""); } + if (nullptr != m_pWatcher) + { + delete m_pWatcher; + m_pWatcher = nullptr; + } } -bool SocketChannel::Init(E_CODEC_TYPE eCodecType, bool bIsClient) +bool SocketChannel::IsClient() const { - return(m_pImpl->Init(eCodecType, bIsClient)); + return(m_bIsClient); } +bool SocketChannel::WithSsl() const +{ + return(m_bWithSsl); +} int SocketChannel::GetFd() const { + if (m_pImpl == nullptr) + { + return(-1); + } return(m_pImpl->GetFd()); } -bool SocketChannel::IsClient() const +uint32 SocketChannel::GetSequence() const { - return(m_pImpl->IsClient()); + if (m_pImpl == nullptr) + { + return(0); + } + return(m_pImpl->GetSequence()); } bool SocketChannel::IsPipeline() const { + if (m_pImpl == nullptr) + { + return(false); + } return(m_pImpl->IsPipeline()); } const std::string& SocketChannel::GetIdentify() const { + if (m_pImpl == nullptr) + { + return(m_strEmpty); + } return(m_pImpl->GetIdentify()); } const std::string& SocketChannel::GetRemoteAddr() const { + if (m_pImpl == nullptr) + { + return(m_strEmpty); + } return(m_pImpl->GetRemoteAddr()); } const std::string& SocketChannel::GetClientData() const { + if (m_pImpl == nullptr) + { + return(m_strEmpty); + } return(m_pImpl->GetClientData()); } E_CODEC_TYPE SocketChannel::GetCodecType() const { + if (m_pImpl == nullptr) + { + return(CODEC_UNKNOW); + } return(m_pImpl->GetCodecType()); } +uint8 SocketChannel::GetChannelStatus() const +{ + if (m_pImpl == nullptr) + { + return(0); + } + return(m_pImpl->GetChannelStatus()); +} + +uint32 SocketChannel::PopStepSeq(uint32 uiStreamId, E_CODEC_STATUS eStatus) +{ + if (m_pImpl == nullptr) + { + return(0); + } + return(m_pImpl->PopStepSeq(uiStreamId, eStatus)); +} + +bool SocketChannel::PipelineIsEmpty() const +{ + if (m_pImpl == nullptr) + { + return(0); + } + return(m_pImpl->PipelineIsEmpty()); +} + +int SocketChannel::GetErrno() const +{ + if (m_pImpl == nullptr) + { + return(0); + } + return(m_pImpl->GetErrno()); +} + +const std::string& SocketChannel::GetErrMsg() const +{ + if (m_pImpl == nullptr) + { + return(m_strEmpty); + } + return(m_pImpl->GetErrMsg()); +} + +Codec* SocketChannel::GetCodec() const +{ + if (m_pImpl == nullptr) + { + return(nullptr); + } + return(m_pImpl->GetCodec()); +} + +bool SocketChannel::InitImpl(std::shared_ptr pImpl) +{ + if (pImpl == nullptr) + { + return(false); + } + m_pImpl = pImpl; + return(true); +} + +Labor* SocketChannel::GetLabor() +{ + return(nullptr); +} + int SocketChannel::SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, std::shared_ptr pLogger) { ssize_t n; @@ -229,4 +343,20 @@ int SocketChannel::RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, in return(ERR_OK); } +ChannelWatcher* SocketChannel::MutableWatcher() +{ + if (nullptr == m_pWatcher) + { + try + { + m_pWatcher = new ChannelWatcher(); + } + catch(std::bad_alloc& e) + { + m_pLogger->WriteLog(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "new ChannelWatcher error %s", e.what()); + } + } + return(m_pWatcher); +} + } diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index 27ca9155..52679947 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -10,17 +10,24 @@ #ifndef SRC_CHANNEL_SOCKETCHANNEL_HPP_ #define SRC_CHANNEL_SOCKETCHANNEL_HPP_ -#include "SocketChannelImpl.hpp" -#ifdef WITH_OPENSSL -#include "SocketChannelSslImpl.hpp" -#endif +#include +#include +#include "Channel.hpp" +#include "codec/Codec.hpp" namespace neb { class Dispatcher; +class ChannelWatcher; +class Labor; +class CodecFactory; +class CodecProto; -class SocketChannel: public Channel, public std::enable_shared_from_this +template class IO; +template class SocketChannelImpl; + +class SocketChannel: public Channel { public: struct tagChannelCtx @@ -31,31 +38,48 @@ class SocketChannel: public Channel, public std::enable_shared_from_this pLogger, int iFd, uint32 ulSeq, bool bWithSsl = false, ev_tstamp dKeepAlive = 10.0); + SocketChannel(Labor* pLabor, std::shared_ptr pLogger, int iFd, uint32 ulSeq, bool bWithSsl, bool bIsClient, ev_tstamp dKeepAlive = 10.0); virtual ~SocketChannel(); static int SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, std::shared_ptr pLogger); static int RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, int& iCodecType, std::shared_ptr pLogger); - virtual bool Init(E_CODEC_TYPE eCodecType, bool bIsClient = false); - - virtual int GetFd() const; virtual bool IsClient() const; + virtual bool WithSsl() const; + virtual int GetFd() const; + virtual uint32 GetSequence() const; virtual bool IsPipeline() const; virtual const std::string& GetIdentify() const; virtual const std::string& GetRemoteAddr() const; virtual const std::string& GetClientData() const; virtual E_CODEC_TYPE GetCodecType() const; + virtual uint8 GetChannelStatus() const; + virtual uint32 PopStepSeq(uint32 uiStreamId = 0, E_CODEC_STATUS eStatus = CODEC_STATUS_OK); + virtual bool PipelineIsEmpty() const; + virtual int GetErrno() const; + virtual const std::string& GetErrMsg() const; + virtual Codec* GetCodec() const; + ChannelWatcher* MutableWatcher(); + +protected: + virtual Labor* GetLabor(); + bool InitImpl(std::shared_ptr pImpl); private: + bool m_bIsClient; + bool m_bWithSsl; + std::string m_strEmpty; // Hide most of the channel implementation for Actors - // 以m_pImpl对Actor及其派生类隐藏框架层才需要的Channel大部分实现 - //std::unique_ptr m_pImpl; - std::shared_ptr m_pImpl; + std::shared_ptr m_pImpl; std::shared_ptr m_pLogger; + ChannelWatcher* m_pWatcher; friend class Dispatcher; friend class ActorBuilder; + friend class CodecFactory; + friend class CodecProto; + template friend class IO; + template friend class SocketChannelImpl; }; } /* namespace neb */ diff --git a/src/channel/SocketChannelImpl.cpp b/src/channel/SocketChannelImpl.cpp deleted file mode 100644 index d7bd5a6f..00000000 --- a/src/channel/SocketChannelImpl.cpp +++ /dev/null @@ -1,1460 +0,0 @@ -/******************************************************************************* - * Project: Nebula - * @file SocketChannelImpl.cpp - * @brief - * @author Bwar - * @date: 2016年8月10日 - * @note - * Modify history: - ******************************************************************************/ -#include -#include "codec/CodecProto.hpp" -#include "codec/CodecPrivate.hpp" -#include "codec/CodecHttp.hpp" -#include "codec/http2/CodecHttp2.hpp" -#include "codec/CodecResp.hpp" -#include "labor/Labor.hpp" -#include "labor/Manager.hpp" -#include "logger/NetLogger.hpp" -#include "SocketChannelImpl.hpp" - -namespace neb -{ - -SocketChannelImpl::SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) - : m_ucChannelStatus(CHANNEL_STATUS_INIT),m_eLastCodecStatus(CODEC_STATUS_OK), m_bIsClientConnection(false), - m_iRemoteWorkerIdx(-1), m_iFd(iFd), m_uiSeq(ulSeq), m_uiForeignSeq(0), m_bPipeline(true), - m_uiUnitTimeMsgNum(0), m_uiMsgNum(0), - m_dActiveTime(0.0), m_dKeepAlive(dKeepAlive), - m_pIoWatcher(NULL), m_pTimerWatcher(NULL), - m_pRecvBuff(nullptr), m_pSendBuff(nullptr), m_pWaitForSendBuff(nullptr), - m_pCodec(nullptr), m_pHoldingHttpMsg(nullptr), m_iErrno(0), m_pLabor(nullptr), m_pSocketChannel(pSocketChannel), m_pLogger(pLogger) -{ - memset(m_szErrBuff, 0, sizeof(m_szErrBuff)); -} - -SocketChannelImpl::~SocketChannelImpl() -{ - LOG4_TRACE("SocketChannelImpl::~SocketChannelImpl() fd %d, seq %u", m_iFd, m_uiSeq); - m_listPipelineStepSeq.clear(); - if (CHANNEL_STATUS_CLOSED != m_ucChannelStatus) - { - Close(); - } - FREE(m_pIoWatcher); - FREE(m_pTimerWatcher); - DELETE(m_pRecvBuff); - DELETE(m_pSendBuff); - DELETE(m_pWaitForSendBuff); - DELETE(m_pHoldingHttpMsg); - DELETE(m_pCodec); -} - -bool SocketChannelImpl::Init(E_CODEC_TYPE eCodecType, bool bIsClient) -{ - LOG4_TRACE("fd[%d], codec_type[%d]", m_iFd, eCodecType); - m_bIsClientConnection = bIsClient; - try - { - if (m_pRecvBuff == nullptr) - { - m_pRecvBuff = new CBuffer(); - } - if (m_pSendBuff == nullptr) - { - m_pSendBuff = new CBuffer(); - } - if (m_pWaitForSendBuff == nullptr) - { - m_pWaitForSendBuff = new CBuffer(); - } - if (m_pCodec != nullptr) - { - DELETE(m_pCodec); - } - switch (eCodecType) - { - case CODEC_NEBULA: - case CODEC_PROTO: - case CODEC_NEBULA_IN_NODE: - m_pCodec = new CodecProto(m_pLogger, eCodecType); - m_pCodec->SetKey(m_strKey); - break; - case CODEC_HTTP: - m_pCodec = new CodecHttp(m_pLogger, eCodecType); - m_pCodec->SetKey(m_strKey); - break; - case CODEC_HTTP2: - m_pCodec = new CodecHttp2(m_pLogger, eCodecType, m_bIsClientConnection); - ((CodecHttp2*)m_pCodec)->ConnectionSetting(m_pSendBuff); - m_pCodec->SetKey(m_strKey); - break; - case CODEC_RESP: - m_pCodec = new CodecResp(m_pLogger, eCodecType); - m_pCodec->SetKey(m_strKey); - break; - case CODEC_PRIVATE: - m_pCodec = new CodecPrivate(m_pLogger, eCodecType); - m_pCodec->SetKey(m_strKey); - break; - case CODEC_UNKNOW: - break; - default: - LOG4_ERROR("no codec defined for code type %d", eCodecType); - break; - } - } - catch(std::bad_alloc& e) - { - LOG4_ERROR("%s", e.what()); - return(false); - } - m_dActiveTime = m_pLabor->GetNowTime(); - return(true); -} - -E_CODEC_TYPE SocketChannelImpl::GetCodecType() const -{ - return(m_pCodec->GetCodecType()); -} - -uint32 SocketChannelImpl::PopStepSeq(uint32 uiStreamId, E_CODEC_STATUS eCodecStatus) -{ - if (m_listPipelineStepSeq.empty()) - { - auto iter = m_mapStreamStepSeq.find(uiStreamId); - if (iter != m_mapStreamStepSeq.end()) - { - uint32 uiStepSeq = iter->second; - if (CODEC_STATUS_OK == eCodecStatus) - { - m_mapStreamStepSeq.erase(iter); - } - return(uiStepSeq); - } - return(0); - } - else - { - uint32 uiStepSeq = m_listPipelineStepSeq.front(); - if (CODEC_STATUS_OK == eCodecStatus) - { - m_listPipelineStepSeq.pop_front(); - } - return(uiStepSeq); - } -} - -ev_tstamp SocketChannelImpl::GetKeepAlive() -{ - if (CODEC_HTTP == m_pCodec->GetCodecType()) - { - if (((CodecHttp*)m_pCodec)->GetKeepAlive() >= 0.0) - { - return(((CodecHttp*)m_pCodec)->GetKeepAlive()); - } - } - return(m_dKeepAlive); -} - -bool SocketChannelImpl::NeedAliveCheck() const -{ - if (CODEC_HTTP == m_pCodec->GetCodecType() - || CODEC_NEBULA == m_pCodec->GetCodecType() - || CODEC_NEBULA_IN_NODE == m_pCodec->GetCodecType()) - { - return(false); - } - return(true); -} - -E_CODEC_STATUS SocketChannelImpl::Send() -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) - { - LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_EOF); - } - else if (CHANNEL_STATUS_ESTABLISHED != m_ucChannelStatus) - { - return(CODEC_STATUS_PAUSE); - } - if (m_pCodec == nullptr) - { - LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); - return(CODEC_STATUS_ERR); - } - int iNeedWriteLen = 0; - iNeedWriteLen = m_pSendBuff->ReadableBytes(); - if (0 == iNeedWriteLen) - { - iNeedWriteLen = m_pWaitForSendBuff->ReadableBytes(); - if (0 == iNeedWriteLen) - { - LOG4_TRACE("no data need to send."); - return(CODEC_STATUS_OK); - } - else - { - CBuffer* pExchangeBuff = m_pSendBuff; - m_pSendBuff = m_pWaitForSendBuff; - m_pWaitForSendBuff = pExchangeBuff; - m_pWaitForSendBuff->Compact(1); - } - } - - m_dActiveTime = m_pLabor->GetNowTime(); - int iHadWrittenLen = 0; - int iWrittenLen = 0; - do - { - iWrittenLen = Write(m_pSendBuff, m_iErrno); - if (iWrittenLen > 0) - { - iHadWrittenLen += iWrittenLen; - } - } - while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); - LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); - if (iHadWrittenLen >= 0) - { - m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); - if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ - && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) - { - m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); - } - m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iHadWrittenLen && 0 == m_pWaitForSendBuff->ReadableBytes()) - { - return(CODEC_STATUS_OK); - } - else - { - return(CODEC_STATUS_PAUSE); - } - } - else - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - return(CODEC_STATUS_PAUSE); - } - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); - m_ucChannelStatus = CHANNEL_STATUS_BROKEN; - return(CODEC_STATUS_INT); - } -} - -E_CODEC_STATUS SocketChannelImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d], cmd[%u], seq[%u]", m_iFd, m_uiSeq, iCmd, uiSeq); - if (m_pCodec == nullptr) - { - LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); - return(CODEC_STATUS_ERR); - } - E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; - int32 iMsgBodyLen = oMsgBody.ByteSize(); - MsgHead oMsgHead; - oMsgHead.set_cmd(iCmd); - oMsgHead.set_seq(uiSeq); - iMsgBodyLen = (iMsgBodyLen > 0) ? iMsgBodyLen : -1; // proto3里int赋值为0会在指定固定大小的message时有问题 - oMsgHead.set_len(iMsgBodyLen); - switch (m_ucChannelStatus) - { - case CHANNEL_STATUS_ESTABLISHED: - eCodecStatus = m_pCodec->Encode(oMsgHead, oMsgBody, m_pSendBuff); - break; - case CHANNEL_STATUS_CLOSED: - case CHANNEL_STATUS_BROKEN: - LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_ERR); - case CHANNEL_STATUS_TELL_WORKER: - case CHANNEL_STATUS_WORKER: - case CHANNEL_STATUS_TRANSFER_TO_WORKER: - case CHANNEL_STATUS_CONNECTED: - case CHANNEL_STATUS_TRY_CONNECT: - case CHANNEL_STATUS_INIT: - { - switch (iCmd) - { - case CMD_RSP_TELL_WORKER: - m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - eCodecStatus = m_pCodec->Encode(oMsgHead, oMsgBody, m_pSendBuff); - break; - case CMD_REQ_TELL_WORKER: - m_ucChannelStatus = CHANNEL_STATUS_TELL_WORKER; - eCodecStatus = m_pCodec->Encode(oMsgHead, oMsgBody, m_pSendBuff); - break; - case CMD_RSP_CONNECT_TO_WORKER: - m_ucChannelStatus = CHANNEL_STATUS_WORKER; - eCodecStatus = m_pCodec->Encode(oMsgHead, oMsgBody, m_pSendBuff); - break; - case CMD_REQ_CONNECT_TO_WORKER: - m_ucChannelStatus = CHANNEL_STATUS_TRANSFER_TO_WORKER; - eCodecStatus = m_pCodec->Encode(oMsgHead, oMsgBody, m_pSendBuff); - break; - default: - eCodecStatus = m_pCodec->Encode(oMsgHead, oMsgBody, m_pWaitForSendBuff); - if (CODEC_STATUS_OK == eCodecStatus && (gc_uiCmdReq & iCmd)) - { - eCodecStatus = CODEC_STATUS_PAUSE; - m_listPipelineStepSeq.push_back(uiSeq); - } - break; - } - } - break; - default: - LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); - return(CODEC_STATUS_ERR); - } - - if (CODEC_STATUS_OK != eCodecStatus) - { - return(eCodecStatus); - } - - int iNeedWriteLen = m_pSendBuff->ReadableBytes(); - if (iNeedWriteLen <= 0) - { - return(eCodecStatus); - } - - errno = 0; - int iHadWrittenLen = 0; - int iWrittenLen = 0; - do - { - iWrittenLen = Write(m_pSendBuff, m_iErrno); - if (iWrittenLen > 0) - { - iHadWrittenLen += iWrittenLen; - } - } - while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); - LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); - if (iHadWrittenLen >= 0) - { - m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); - if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ - && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) - { - m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); - } - m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iHadWrittenLen) - { - if (CMD_RSP_TELL_WORKER == iCmd) - { - return(Send()); - } - else - { - return(CODEC_STATUS_OK); - } - } - else - { - return(CODEC_STATUS_PAUSE); - } - } - else - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - return(CODEC_STATUS_PAUSE); - } - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); - m_ucChannelStatus = CHANNEL_STATUS_BROKEN; - return(CODEC_STATUS_INT); - } -} - -E_CODEC_STATUS SocketChannelImpl::Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - if (m_pCodec == nullptr) - { - LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); - return(CODEC_STATUS_ERR); - } - if ((oHttpMsg.http_major() == 2 && m_pCodec->GetCodecType() != CODEC_HTTP2) - || (oHttpMsg.http_major() < 2 && m_pCodec->GetCodecType() != CODEC_HTTP && m_pCodec->GetCodecType() != CODEC_HTTP_CUSTOM)) - { - LOG4_ERROR("codec type not match!"); - return(CODEC_STATUS_ERR); - } - E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; - switch (m_ucChannelStatus) - { - case CHANNEL_STATUS_ESTABLISHED: - if (CODEC_HTTP == m_pCodec->GetCodecType()) - { - eCodecStatus = ((CodecHttp*)m_pCodec)->Encode(oHttpMsg, m_pSendBuff); - } - else - { - eCodecStatus = ((CodecHttp2*)m_pCodec)->Encode(oHttpMsg, m_pSendBuff); - } - if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) - { - if (CODEC_HTTP == m_pCodec->GetCodecType()) - { - m_listPipelineStepSeq.push_back(uiStepSeq); - } - else - { - m_mapStreamStepSeq.insert(std::make_pair(((CodecHttp2*)m_pCodec)->GetLastStreamId(), uiStepSeq)); - } - } - break; - case CHANNEL_STATUS_CLOSED: - case CHANNEL_STATUS_BROKEN: - LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_ERR); - case CHANNEL_STATUS_TELL_WORKER: - case CHANNEL_STATUS_WORKER: - case CHANNEL_STATUS_TRANSFER_TO_WORKER: - case CHANNEL_STATUS_CONNECTED: - case CHANNEL_STATUS_TRY_CONNECT: - case CHANNEL_STATUS_INIT: - if (CODEC_HTTP == m_pCodec->GetCodecType()) - { - eCodecStatus = ((CodecHttp*)m_pCodec)->Encode(oHttpMsg, m_pWaitForSendBuff); - } - else - { - eCodecStatus = ((CodecHttp2*)m_pCodec)->Encode(oHttpMsg, m_pWaitForSendBuff); - } - if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) - { - eCodecStatus = CODEC_STATUS_PAUSE; - if (CODEC_HTTP == m_pCodec->GetCodecType()) - { - m_listPipelineStepSeq.push_back(uiStepSeq); - } - else - { - m_mapStreamStepSeq.insert(std::make_pair(((CodecHttp2*)m_pCodec)->GetLastStreamId(), uiStepSeq)); - } - } - break; - default: - LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); - return(CODEC_STATUS_ERR); - } - - if (CODEC_STATUS_OK != eCodecStatus && CODEC_STATUS_PART_OK != eCodecStatus) - { - return(eCodecStatus); - } - - int iNeedWriteLen = m_pSendBuff->ReadableBytes(); - if (iNeedWriteLen <= 0) - { - return(eCodecStatus); - } - - int iHadWrittenLen = 0; - int iWrittenLen = 0; - do - { - iWrittenLen = Write(m_pSendBuff, m_iErrno); - if (iWrittenLen > 0) - { - iHadWrittenLen += iWrittenLen; - } - } - while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); - LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); - if (iHadWrittenLen >= 0) - { - m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); - if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ - && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) - { - m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); - } - m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iHadWrittenLen) - { - if (m_pCodec->GetCodecType() == CODEC_HTTP - && ((CodecHttp*)m_pCodec)->GetKeepAlive() == 0.0) - { - return(CODEC_STATUS_EOF); - } - return(eCodecStatus); - } - else - { - return(CODEC_STATUS_PAUSE); - } - } - else - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - return(CODEC_STATUS_PAUSE); - } - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); - m_ucChannelStatus = CHANNEL_STATUS_BROKEN; - return(CODEC_STATUS_INT); - } -} - -E_CODEC_STATUS SocketChannelImpl::Send(const RedisMsg& oRedisMsg, uint32 uiStepSeq) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - if (m_pCodec == nullptr) - { - LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); - return(CODEC_STATUS_ERR); - } - if (m_pCodec->GetCodecType() != CODEC_RESP) - { - LOG4_ERROR("codec type not match!"); - return(CODEC_STATUS_ERR); - } - E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; - switch (m_ucChannelStatus) - { - case CHANNEL_STATUS_ESTABLISHED: - eCodecStatus = ((CodecResp*)m_pCodec)->Encode(oRedisMsg, m_pSendBuff); - break; - case CHANNEL_STATUS_CLOSED: - case CHANNEL_STATUS_BROKEN: - LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_EOF); - case CHANNEL_STATUS_TELL_WORKER: - case CHANNEL_STATUS_WORKER: - case CHANNEL_STATUS_TRANSFER_TO_WORKER: - case CHANNEL_STATUS_CONNECTED: - case CHANNEL_STATUS_TRY_CONNECT: - case CHANNEL_STATUS_INIT: - eCodecStatus = ((CodecResp*)m_pCodec)->Encode(oRedisMsg, m_pWaitForSendBuff); - if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) - { - eCodecStatus = CODEC_STATUS_PAUSE; - m_listPipelineStepSeq.push_back(uiStepSeq); - } - break; - default: - LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); - return(CODEC_STATUS_ERR); - } - - if (CODEC_STATUS_OK != eCodecStatus) - { - return(eCodecStatus); - } - - int iNeedWriteLen = m_pSendBuff->ReadableBytes(); - if (iNeedWriteLen <= 0) - { - return(eCodecStatus); - } - - int iHadWrittenLen = 0; - int iWrittenLen = 0; - do - { - iWrittenLen = Write(m_pSendBuff, m_iErrno); - if (iWrittenLen > 0) - { - iHadWrittenLen += iWrittenLen; - } - } - while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); - LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); - if (iHadWrittenLen >= 0) - { - m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); - if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ - && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) - { - m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); - } - if (uiStepSeq > 0) - { - m_listPipelineStepSeq.push_back(uiStepSeq); - } - m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iHadWrittenLen) - { - return(CODEC_STATUS_OK); - } - else - { - return(CODEC_STATUS_PAUSE); - } - } - else - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - return(CODEC_STATUS_PAUSE); - } - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); - m_ucChannelStatus = CHANNEL_STATUS_BROKEN; - return(CODEC_STATUS_INT); - } -} - -E_CODEC_STATUS SocketChannelImpl::Send(const char* pRaw, uint32 uiRawSize, uint32 uiStepSeq) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; - switch (m_ucChannelStatus) - { - case CHANNEL_STATUS_ESTABLISHED: - m_pSendBuff->Write(pRaw, uiRawSize); - break; - case CHANNEL_STATUS_CLOSED: - case CHANNEL_STATUS_BROKEN: - LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_ERR); - case CHANNEL_STATUS_TELL_WORKER: - case CHANNEL_STATUS_WORKER: - case CHANNEL_STATUS_TRANSFER_TO_WORKER: - case CHANNEL_STATUS_CONNECTED: - case CHANNEL_STATUS_TRY_CONNECT: - case CHANNEL_STATUS_INIT: - m_pWaitForSendBuff->Write(pRaw, uiRawSize); - eCodecStatus = CODEC_STATUS_PAUSE; - break; - default: - LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); - return(CODEC_STATUS_ERR); - } - - if (CODEC_STATUS_OK != eCodecStatus) - { - return(eCodecStatus); - } - - int iNeedWriteLen = m_pSendBuff->ReadableBytes(); - if (iNeedWriteLen <= 0) - { - return(eCodecStatus); - } - - int iHadWrittenLen = 0; - int iWrittenLen = 0; - do - { - iWrittenLen = Write(m_pSendBuff, m_iErrno); - if (iWrittenLen > 0) - { - iHadWrittenLen += iWrittenLen; - } - } - while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); - LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); - if (iHadWrittenLen >= 0) - { - m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); - if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ - && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) - { - m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); - } - if (uiStepSeq > 0) - { - m_listPipelineStepSeq.push_back(uiStepSeq); - } - m_dActiveTime = m_pLabor->GetNowTime(); - if (iNeedWriteLen == iHadWrittenLen) - { - return(CODEC_STATUS_OK); - } - else - { - return(CODEC_STATUS_PAUSE); - } - } - else - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - return(CODEC_STATUS_PAUSE); - } - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), - m_iFd, m_iErrno, m_strErrMsg.c_str()); - m_ucChannelStatus = CHANNEL_STATUS_BROKEN; - return(CODEC_STATUS_INT); - } -} - -E_CODEC_STATUS SocketChannelImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); - if (m_pCodec == nullptr) - { - LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); - return(CODEC_STATUS_ERR); - } - int iReadLen = 0; - int iHadReadLen = 0; - do - { - iReadLen = Read(m_pRecvBuff, m_iErrno); - if (iReadLen > 0) - { - iHadReadLen += iReadLen; - } - LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", - m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - } - while(iReadLen > 0); - m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen); - if (iReadLen == 0) - { - m_eLastCodecStatus = CODEC_STATUS_EOF; - } - else if (iReadLen < 0) - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - m_eLastCodecStatus = CODEC_STATUS_PAUSE; - //return(CODEC_STATUS_PAUSE); - } - else - { - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] remote %s error %d: %s", - m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), - m_iErrno, m_strErrMsg.c_str()); - m_eLastCodecStatus = CODEC_STATUS_INT; - m_ucChannelStatus = CHANNEL_STATUS_BROKEN; - //return(CODEC_STATUS_INT); - } - } - - if (iHadReadLen > 0) - { - if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ - && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) - { - m_pRecvBuff->Compact(m_pRecvBuff->ReadableBytes() * 2); - } - m_dActiveTime = m_pLabor->GetNowTime(); - E_CODEC_STATUS eCodecStatus = m_pCodec->Decode(m_pRecvBuff, oMsgHead, oMsgBody); - if (CODEC_STATUS_OK == eCodecStatus) - { - switch (m_ucChannelStatus) - { - case CHANNEL_STATUS_ESTABLISHED: - if ((gc_uiCmdReq & oMsgHead.cmd()) && (m_strClientData.size() > 0)) - { - m_uiForeignSeq = oMsgHead.seq(); - ++m_uiUnitTimeMsgNum; - ++m_uiMsgNum; - oMsgBody.set_add_on(m_strClientData); - if (m_uiMsgNum == 1) - { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - } - } - break; - case CHANNEL_STATUS_CLOSED: - case CHANNEL_STATUS_BROKEN: - LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_ERR); - case CHANNEL_STATUS_TELL_WORKER: - case CHANNEL_STATUS_WORKER: - case CHANNEL_STATUS_TRANSFER_TO_WORKER: - case CHANNEL_STATUS_CONNECTED: - case CHANNEL_STATUS_TRY_CONNECT: - case CHANNEL_STATUS_INIT: - { - switch (oMsgHead.cmd()) - { - case CMD_RSP_TELL_WORKER: - m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - break; - case CMD_REQ_TELL_WORKER: - m_ucChannelStatus = CHANNEL_STATUS_TELL_WORKER; - break; - case CMD_RSP_CONNECT_TO_WORKER: - m_ucChannelStatus = CHANNEL_STATUS_WORKER; - break; - case CMD_REQ_CONNECT_TO_WORKER: - m_ucChannelStatus = CHANNEL_STATUS_TRANSFER_TO_WORKER; - break; - default: - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[from %d to %d] may be a fault.", - m_iFd, m_uiSeq, (int)m_ucChannelStatus, CHANNEL_STATUS_ESTABLISHED); - m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; - break; - } - } - break; - default: - LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); - return(CODEC_STATUS_ERR); - } - } - else - { - if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) - { - return(CODEC_STATUS_INVALID); - } - } - LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_uiSeq, oMsgHead.cmd(), oMsgHead.seq()); - return(eCodecStatus); - } - return(m_eLastCodecStatus); -} - -E_CODEC_STATUS SocketChannelImpl::Recv(HttpMsg& oHttpMsg) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) - { - LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_ERR); - } - if (m_pCodec == nullptr) - { - LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); - return(CODEC_STATUS_ERR); - } - int iReadLen = 0; - int iHadReadLen = 0; - do - { - iReadLen = Read(m_pRecvBuff, m_iErrno); - LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", - m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - if (iReadLen > 0) - { - iHadReadLen += iReadLen; - } - } - while (iReadLen > 0); - m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen); - - if (iReadLen == 0) - { - m_eLastCodecStatus = CODEC_STATUS_EOF; - } - else if (iReadLen < 0) - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - m_eLastCodecStatus = CODEC_STATUS_PAUSE; - } - else - { - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] remote %s error %d: %s", - m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), - m_iErrno, m_strErrMsg.c_str()); - m_eLastCodecStatus = CODEC_STATUS_INT; - m_ucChannelStatus = CHANNEL_STATUS_BROKEN; - } - } - - if (iHadReadLen > 0) - { - if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ - && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) - { - m_pRecvBuff->Compact(m_pRecvBuff->ReadableBytes() * 2); - } - m_dActiveTime = m_pLabor->GetNowTime(); - E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; - if (CODEC_HTTP == m_pCodec->GetCodecType()) - { - eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); - } - else - { - size_t uiSendBuffLen = m_pSendBuff->ReadableBytes(); - eCodecStatus = ((CodecHttp2*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg, m_pSendBuff); - if (m_pSendBuff->ReadableBytes() > uiSendBuffLen - && (eCodecStatus == CODEC_STATUS_OK || eCodecStatus == CODEC_STATUS_PART_OK)) - { - Send(); - } - } - if (CODEC_STATUS_OK == eCodecStatus) - { - ++m_uiUnitTimeMsgNum; - ++m_uiMsgNum; - if (m_uiMsgNum == 1) - { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; - if (oHttpMsg.has_upgrade() && oHttpMsg.upgrade().is_upgrade()) - { - if (std::string("h2") == oHttpMsg.upgrade().protocol() - || std::string("h2c") == oHttpMsg.upgrade().protocol()) - { - auto h_iter = oHttpMsg.headers().find("HTTP2-Settings"); - if (h_iter != oHttpMsg.headers().end()) - { - try - { - m_pHoldingHttpMsg = new HttpMsg(oHttpMsg); - } - catch (std::bad_alloc& e) - { - LOG4_ERROR("%s", e.what()); - return(CODEC_STATUS_ERR); - } - } - } - } - } - } - else - { - if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) - { - if (!m_bIsClientConnection) - { - m_pSendBuff->Clear(); - } - return(CODEC_STATUS_INVALID); - } - } - if (((CodecHttp*)m_pCodec)->CloseRightAway()) - { - eCodecStatus = CODEC_STATUS_EOF; - } - return(eCodecStatus); - } - return(m_eLastCodecStatus); -} - -E_CODEC_STATUS SocketChannelImpl::Recv(RedisReply& oRedisReply) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) - { - LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_ERR); - } - if (m_pCodec == nullptr) - { - LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); - return(CODEC_STATUS_ERR); - } - int iReadLen = 0; - int iHadReadLen = 0; - do - { - iReadLen = Read(m_pRecvBuff, m_iErrno); - LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", - m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - if (iReadLen > 0) - { - iHadReadLen += iReadLen; - } - } - while (iReadLen > 0); - m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen); - - if (iReadLen == 0) - { - m_eLastCodecStatus = CODEC_STATUS_EOF; - } - else if (iReadLen < 0) - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - m_eLastCodecStatus = CODEC_STATUS_PAUSE; - } - else - { - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] remote %s error %d: %s", - m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), - m_iErrno, m_strErrMsg.c_str()); - m_eLastCodecStatus = CODEC_STATUS_INT; - m_ucChannelStatus = CHANNEL_STATUS_BROKEN; - } - } - - if (iHadReadLen > 0) - { - if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ - && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) - { - m_pRecvBuff->Compact(m_pRecvBuff->ReadableBytes() * 2); - } - m_dActiveTime = m_pLabor->GetNowTime(); - E_CODEC_STATUS eCodecStatus = ((CodecResp*)m_pCodec)->Decode(m_pRecvBuff, oRedisReply); - if (CODEC_STATUS_OK == eCodecStatus) - { - ++m_uiUnitTimeMsgNum; - ++m_uiMsgNum; - if (m_uiMsgNum == 1) - { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; - } - } - else - { - if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) - { - return(CODEC_STATUS_INVALID); - } - } - return(eCodecStatus); - } - return(m_eLastCodecStatus); -} - -E_CODEC_STATUS SocketChannelImpl::Recv(CBuffer& oRawBuff) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) - { - LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_ERR); - } - int iReadLen = 0; - int iHadReadLen = 0; - do - { - iReadLen = Read(m_pRecvBuff, m_iErrno); - LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", - m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); - if (iReadLen > 0) - { - iHadReadLen += iReadLen; - } - } - while (iReadLen > 0); - m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen); - - if (iReadLen == 0) - { - m_eLastCodecStatus = CODEC_STATUS_EOF; - } - else if (iReadLen < 0) - { - if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 - { - m_dActiveTime = m_pLabor->GetNowTime(); - m_eLastCodecStatus = CODEC_STATUS_PAUSE; - } - else - { - m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); - LOG4_ERROR("recv from %s[fd %d] remote %s error %d: %s", - m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), - m_iErrno, m_strErrMsg.c_str()); - m_eLastCodecStatus = CODEC_STATUS_INT; - m_ucChannelStatus = CHANNEL_STATUS_BROKEN; - } - } - - if (iHadReadLen > 0) - { - if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ - && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) - { - m_pRecvBuff->Compact(m_pRecvBuff->ReadableBytes() * 2); - } - m_dActiveTime = m_pLabor->GetNowTime(); - if (oRawBuff.Write(m_pRecvBuff, m_pRecvBuff->ReadableBytes()) > 0) - { - ++m_uiUnitTimeMsgNum; - ++m_uiMsgNum; - if (m_uiMsgNum == 1) - { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; - } - return(CODEC_STATUS_OK); - } - return(CODEC_STATUS_PAUSE); - } - return(m_eLastCodecStatus); -} - -E_CODEC_STATUS SocketChannelImpl::Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) - { - LOG4_TRACE("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_ERR); - } - LOG4_TRACE("fetch from fd %d and m_pRecvBuff->ReadableBytes() = %d", - m_iFd, m_pRecvBuff->ReadableBytes()); - E_CODEC_STATUS eCodecStatus = m_pCodec->Decode(m_pRecvBuff, oMsgHead, oMsgBody); - if (CODEC_STATUS_OK == eCodecStatus) - { - m_uiForeignSeq = oMsgHead.seq(); - ++m_uiUnitTimeMsgNum; - ++m_uiMsgNum; - if (m_uiMsgNum == 1) - { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; - } - LOG4_TRACE("channel_fd[%d], channel_seq[%u], cmd[%u], seq[%u]", m_iFd, m_uiSeq, oMsgHead.cmd(), oMsgHead.seq()); - } - else - { - if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) - { - return(CODEC_STATUS_INVALID); - } - } - if ((CODEC_STATUS_PAUSE == eCodecStatus) - && (CODEC_STATUS_EOF == m_eLastCodecStatus - || CODEC_STATUS_INT == m_eLastCodecStatus)) - { - return(m_eLastCodecStatus); - } - return(eCodecStatus); -} - -E_CODEC_STATUS SocketChannelImpl::Fetch(HttpMsg& oHttpMsg) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) - { - LOG4_TRACE("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_ERR); - } - // 当http1.0响应包未带Content-Length头时,m_pRecvBuff可读字节数为0,以关闭连接表示数据发送完毕。 - E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; - if (CODEC_HTTP == m_pCodec->GetCodecType()) - { - eCodecStatus = ((CodecHttp*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg); - } - else - { - size_t uiSendBuffLen = m_pSendBuff->ReadableBytes(); - eCodecStatus = ((CodecHttp2*)m_pCodec)->Decode(m_pRecvBuff, oHttpMsg, m_pSendBuff); - if (m_pSendBuff->ReadableBytes() > uiSendBuffLen - && (eCodecStatus == CODEC_STATUS_OK || eCodecStatus == CODEC_STATUS_PART_OK)) - { - Send(); - } - } - if (CODEC_STATUS_OK == eCodecStatus) - { - ++m_uiUnitTimeMsgNum; - ++m_uiMsgNum; - if (m_uiMsgNum == 1) - { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; - if (oHttpMsg.has_upgrade() && oHttpMsg.upgrade().is_upgrade()) - { - if (std::string("h2") == oHttpMsg.upgrade().protocol() - || std::string("h2c") == oHttpMsg.upgrade().protocol()) - { - auto h_iter = oHttpMsg.headers().find("HTTP2-Settings"); - if (h_iter != oHttpMsg.headers().end()) - { - try - { - m_pHoldingHttpMsg = new HttpMsg(oHttpMsg); - } - catch (std::bad_alloc& e) - { - LOG4_ERROR("%s", e.what()); - return(CODEC_STATUS_ERR); - } - } - } - } - } - } - else - { - if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) - { - return(CODEC_STATUS_INVALID); - } - } - if ((CODEC_STATUS_PAUSE == eCodecStatus) - && (CODEC_STATUS_EOF == m_eLastCodecStatus - || CODEC_STATUS_INT == m_eLastCodecStatus)) - { - return(m_eLastCodecStatus); - } - return(eCodecStatus); -} - -E_CODEC_STATUS SocketChannelImpl::Fetch(RedisReply& oRedisReply) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) - { - LOG4_TRACE("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_ERR); - } - E_CODEC_STATUS eCodecStatus = ((CodecResp*)m_pCodec)->Decode(m_pRecvBuff, oRedisReply); - if (CODEC_STATUS_OK == eCodecStatus) - { - ++m_uiUnitTimeMsgNum; - ++m_uiMsgNum; - if (m_uiMsgNum == 1) - { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; - } - } - else - { - if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) - { - return(CODEC_STATUS_INVALID); - } - } - if ((CODEC_STATUS_PAUSE == eCodecStatus) - && (CODEC_STATUS_EOF == m_eLastCodecStatus - || CODEC_STATUS_INT == m_eLastCodecStatus)) - { - return(m_eLastCodecStatus); - } - return(eCodecStatus); -} - -E_CODEC_STATUS SocketChannelImpl::Fetch(CBuffer& oRawBuff) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); - if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) - { - LOG4_TRACE("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", - m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); - return(CODEC_STATUS_ERR); - } - if (oRawBuff.Write(m_pRecvBuff, m_pRecvBuff->ReadableBytes()) > 0) - { - ++m_uiUnitTimeMsgNum; - ++m_uiMsgNum; - if (m_uiMsgNum == 1) - { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; - m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; - } - return(CODEC_STATUS_OK); - } - if (CODEC_STATUS_EOF == m_eLastCodecStatus - || CODEC_STATUS_INT == m_eLastCodecStatus) - { - return(m_eLastCodecStatus); - } - return(CODEC_STATUS_PAUSE); -} - -void SocketChannelImpl::SetSecretKey(const std::string& strKey) -{ - m_strKey = strKey; - m_pCodec->SetKey(m_strKey); -} - -void SocketChannelImpl::SetRemoteWorkerIndex(int16 iRemoteWorkerIndex) -{ - m_iRemoteWorkerIdx = iRemoteWorkerIndex; -} - -Codec* SocketChannelImpl::SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive, bool bIsUpgrade) -{ - LOG4_TRACE("channel_fd[%d], channel_seq[%d], codec_type[%d], new_codec_type[%d]", - m_iFd, m_uiSeq, m_pCodec->GetCodecType(), eCodecType); - if (eCodecType == m_pCodec->GetCodecType()) - { - return(m_pCodec); - } - - Codec* pNewCodec = NULL; - try - { - switch (eCodecType) - { - case CODEC_NEBULA: - case CODEC_PROTO: - case CODEC_NEBULA_IN_NODE: - pNewCodec = new CodecProto(m_pLogger, eCodecType); - pNewCodec->SetKey(m_strKey); - break; - case CODEC_HTTP: - pNewCodec = new CodecHttp(m_pLogger, eCodecType, dKeepAlive); - pNewCodec->SetKey(m_strKey); - break; - case CODEC_HTTP2: - pNewCodec = new CodecHttp2(m_pLogger, eCodecType, m_bIsClientConnection); - if (bIsUpgrade) - { - ((CodecHttp2*)pNewCodec)->TransferHoldingMsg(m_pHoldingHttpMsg); - m_pHoldingHttpMsg = nullptr; - } - pNewCodec->SetKey(m_strKey); - break; - case CODEC_RESP: - pNewCodec = new CodecResp(m_pLogger, eCodecType); - pNewCodec->SetKey(m_strKey); - break; - case CODEC_PRIVATE: - pNewCodec = new CodecPrivate(m_pLogger, eCodecType); - pNewCodec->SetKey(m_strKey); - break; - case CODEC_UNKNOW: - break; - default: - LOG4_ERROR("no codec defined for code type %d", eCodecType); - break; - } - } - catch(std::bad_alloc& e) - { - LOG4_ERROR("%s", e.what()); - return(nullptr); - } - if (pNewCodec != nullptr) - { - DELETE(m_pCodec); - m_pCodec = pNewCodec; - } - m_dKeepAlive = dKeepAlive; - m_dActiveTime = m_pLabor->GetNowTime(); - return(m_pCodec); -} - -bool SocketChannelImpl::AutoSwitchCodec() -{ - auto& vecAutoSwitchCodecType = Codec::GetAutoSwitchCodecType(); - for (auto codec_iter = vecAutoSwitchCodecType.begin(); - codec_iter != vecAutoSwitchCodecType.end(); ++codec_iter) - { - if (*codec_iter == GetCodecType()) - { - m_setSkipCodecType.insert(*codec_iter); - continue; - } - else - { - auto skip_iter = m_setSkipCodecType.find(*codec_iter); - if (skip_iter == m_setSkipCodecType.end()) - { - if(SwitchCodec(*codec_iter, -1.0) != nullptr) - { - return(true); - } - return(false); - } - else - { - continue; - } - } - } - return(false); -} - -ev_io* SocketChannelImpl::MutableIoWatcher() -{ - if (NULL == m_pIoWatcher) - { - m_pIoWatcher = (ev_io*)malloc(sizeof(ev_io)); - if (NULL != m_pIoWatcher) - { - memset(m_pIoWatcher, 0, sizeof(ev_io)); - m_pIoWatcher->data = m_pSocketChannel; // (void*)(Channel*) - m_pIoWatcher->fd = GetFd(); - } - } - return(m_pIoWatcher); -} - -ev_timer* SocketChannelImpl::MutableTimerWatcher() -{ - if (NULL == m_pTimerWatcher) - { - m_pTimerWatcher = (ev_timer*)malloc(sizeof(ev_timer)); - if (NULL != m_pTimerWatcher) - { - memset(m_pTimerWatcher, 0, sizeof(ev_timer)); - m_pTimerWatcher->data = m_pSocketChannel; // (void*)(Channel*) - } - } - return(m_pTimerWatcher); -} - -bool SocketChannelImpl::Close() -{ - LOG4_TRACE("channel[%d] channel_status %d", m_iFd, (int)m_ucChannelStatus); - if (CHANNEL_STATUS_CLOSED != m_ucChannelStatus) - { - m_pSendBuff->Compact(1); - m_pWaitForSendBuff->Compact(1); - if (0 == close(m_iFd)) - { - m_ucChannelStatus = CHANNEL_STATUS_CLOSED; - LOG4_TRACE("channel[%d], channel_seq[%u] close successfully.", m_iFd, GetSequence()); - return(true); - } - else - { - LOG4_TRACE("failed to close channel(fd %d), errno %d, it will be close later.", m_iFd, errno); - return(false); - } - } - else - { - LOG4_TRACE("channel(fd %d, seq %u) had been closed before.", m_iFd, GetSequence()); - return(false); - } -} - -int SocketChannelImpl::Write(CBuffer* pBuff, int& iErrno) -{ - LOG4_TRACE("fd[%d], channel_seq[%u]", GetFd(), GetSequence()); - return(pBuff->WriteFD(m_iFd, iErrno)); -} - -int SocketChannelImpl::Read(CBuffer* pBuff, int& iErrno) -{ - LOG4_TRACE("fd[%d], channel_seq[%u]", GetFd(), GetSequence()); - return(pBuff->ReadFD(m_iFd, iErrno)); -} - - -} /* namespace neb */ diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 6329a96e..fcfdc67e 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -25,101 +25,125 @@ #include "util/StreamCodec.hpp" #include "util/json/CJsonObject.hpp" -#include "pb/http.pb.h" -#include "pb/redis.pb.h" #include "codec/Codec.hpp" #include "Channel.hpp" +#include "SocketChannel.hpp" #include "Definition.hpp" #include "logger/NetLogger.hpp" +#include "actor/Actor.hpp" +#include "ios/ChannelWatcher.hpp" +#include "labor/NodeInfo.hpp" namespace neb { -typedef RedisReply RedisMsg; - class Labor; class NetLogger; +class CodecFactory; +class Dispatcher; class SocketChannel; +class Step; -class SocketChannelImpl: public Channel +template +class SocketChannelImpl: public SocketChannel { public: - SocketChannelImpl(SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive = 0.0); + SocketChannelImpl(Labor* pLabor, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive = 0.0); virtual ~SocketChannelImpl(); - virtual bool Init(E_CODEC_TYPE eCodecType, bool bIsClient = false); + static bool NewCodec(std::shared_ptr pChannel, Labor* pLabor, std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + + template + E_CODEC_STATUS SendRequest(uint32 uiStepSeq, Targs&&... args); + + template + E_CODEC_STATUS SendResponse(Targs&&... args); + + template + E_CODEC_STATUS Recv(Targs&&... args); - E_CODEC_TYPE GetCodecType() const; + template + E_CODEC_STATUS Fetch(Targs&&... args); - virtual E_CODEC_STATUS Send(); - virtual E_CODEC_STATUS Send(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - virtual E_CODEC_STATUS Send(const HttpMsg& oHttpMsg, uint32 uiStepSeq); - virtual E_CODEC_STATUS Send(const RedisMsg& oRedisMsg, uint32 uiStepSeq); - virtual E_CODEC_STATUS Send(const char* pRaw, uint32 uiRawSize, uint32 uiStepSeq); - virtual E_CODEC_STATUS Recv(MsgHead& oMsgHead, MsgBody& oMsgBody); - virtual E_CODEC_STATUS Recv(HttpMsg& oHttpMsg); - virtual E_CODEC_STATUS Recv(RedisReply& oRedisReply); - virtual E_CODEC_STATUS Recv(CBuffer& oRawBuff); - E_CODEC_STATUS Fetch(MsgHead& oMsgHead, MsgBody& oMsgBody); - E_CODEC_STATUS Fetch(HttpMsg& oHttpMsg); - E_CODEC_STATUS Fetch(RedisReply& oRedisReply); - E_CODEC_STATUS Fetch(CBuffer& oRawBuff); + E_CODEC_STATUS Send(); template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); public: - int GetFd() const + virtual int GetFd() const { return(m_iFd); } - uint32 GetSequence() const + virtual uint32 GetSequence() const { return(m_uiSeq); } - uint32 PopStepSeq(uint32 uiStreamId = 0, E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK); + virtual bool IsPipeline() const + { + return(m_bPipeline); + } - ev_tstamp GetActiveTime() const + virtual const std::string& GetIdentify() const { - return(m_dActiveTime); + return(m_strIdentify); } - bool IsPipeline() const + virtual const std::string& GetRemoteAddr() const { - return(m_bPipeline); + return(m_strRemoteAddr); } - bool IsClient() const + virtual const std::string& GetClientData() const { - return(m_bIsClientConnection); + return(m_strClientData); } - ev_tstamp GetKeepAlive(); + virtual E_CODEC_TYPE GetCodecType() const; - uint8 GetChannelStatus() const + virtual uint8 GetChannelStatus() const { return(m_ucChannelStatus); } - const std::string& GetIdentify() const + virtual uint32 PopStepSeq(uint32 uiStreamId = 0, E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK); + + virtual bool PipelineIsEmpty() const { - return(m_strIdentify); + return(m_listPipelineStepSeq.empty()); } - const std::string& GetRemoteAddr() const + virtual int GetErrno() const { - return(m_strRemoteAddr); + return(m_iErrno); } - int16 GetRemoteWorkerIndex() const + virtual const std::string& GetErrMsg() const { - return(m_iRemoteWorkerIdx); + return(m_strErrMsg); } - const std::string& GetClientData() const + virtual Codec* GetCodec() const { - return(m_strClientData); + return(m_pCodec); + } + + virtual Labor* GetLabor() + { + return(m_pLabor); + } + + ev_tstamp GetActiveTime() const + { + return(m_dActiveTime); + } + + ev_tstamp GetKeepAlive(); + + int16 GetRemoteWorkerIndex() const + { + return(m_iRemoteWorkerIdx); } bool IsChannelVerify() const @@ -149,27 +173,7 @@ class SocketChannelImpl: public Channel return(m_mapStreamStepSeq); } - Labor* GetLabor() - { - return(m_pLabor); - } - - int GetErrno() const - { - return(m_iErrno); - } - - const std::string& GetErrMsg() const - { - return(m_strErrMsg); - } - public: - void SetLabor(Labor* pLabor) - { - m_pLabor = pLabor; - } - /* void SetActiveTime(ev_tstamp dTime) { @@ -211,35 +215,27 @@ class SocketChannelImpl: public Channel void SetRemoteWorkerIndex(int16 iRemoteWorkerIndex); - Codec* SwitchCodec(E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive, bool bIsUpgrade = false); - bool AutoSwitchCodec(); - - ev_io* MutableIoWatcher(); - - ev_timer* MutableTimerWatcher(); - virtual bool Close(); protected: virtual int Write(CBuffer* pBuff, int& iErrno); virtual int Read(CBuffer* pBuff, int& iErrno); +private: + bool SetCodec(Codec* pCodec); + private: uint8 m_ucChannelStatus; E_CODEC_STATUS m_eLastCodecStatus; ///< 连接关闭前的最后一个编解码状态(当且仅当连接的应用层读缓冲区有数据未处理完而对端关闭连接时使用) char m_szErrBuff[256]; - bool m_bIsClientConnection; int16 m_iRemoteWorkerIdx; ///< 对端Worker进程ID,若不涉及则无需关心 int32 m_iFd; ///< 文件描述符 uint32 m_uiSeq; ///< 文件描述符创建时对应的序列号 - uint32 m_uiForeignSeq; ///< 外来的seq,每个连接的包都是有序的,用作接入Server数据包检查,防止篡包 uint32 m_bPipeline; ///< 是否支持pipeline uint32 m_uiUnitTimeMsgNum; ///< 统计单位时间内接收消息数量 uint32 m_uiMsgNum; ///< 接收消息数量 ev_tstamp m_dActiveTime; ///< 最后一次访问时间 ev_tstamp m_dKeepAlive; ///< 连接保持时间 - ev_io* m_pIoWatcher; ///< 不在结构体析构时回收 - ev_timer* m_pTimerWatcher; ///< 不在结构体析构时回收 CBuffer* m_pRecvBuff; CBuffer* m_pSendBuff; CBuffer* m_pWaitForSendBuff; ///< 等待发送的数据缓冲区(数据到达时,连接并未建立,等连接建立并且pSendBuff发送完毕后立即发送) @@ -255,16 +251,675 @@ class SocketChannelImpl: public Channel std::unordered_map m_mapStreamStepSeq; ///< 等待回调的http2 step seq std::set m_setSkipCodecType; ///< Codec转换需跳过的CodecType Labor* m_pLabor; - SocketChannel* m_pSocketChannel; std::shared_ptr m_pLogger; + + friend class CodecFactory; + friend class Dispatcher; }; +template +SocketChannelImpl::SocketChannelImpl(Labor* pLabor, std::shared_ptr pLogger, + int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) + : m_ucChannelStatus(CHANNEL_STATUS_INIT),m_eLastCodecStatus(CODEC_STATUS_OK), + m_iRemoteWorkerIdx(-1), m_iFd(iFd), m_uiSeq(ulSeq), m_bPipeline(true), + m_uiUnitTimeMsgNum(0), m_uiMsgNum(0), + m_dActiveTime(0.0), m_dKeepAlive(dKeepAlive), + m_pRecvBuff(nullptr), m_pSendBuff(nullptr), m_pWaitForSendBuff(nullptr), + m_pCodec(nullptr), m_pHoldingHttpMsg(nullptr), m_iErrno(0), m_pLabor(pLabor), + m_pLogger(pLogger) +{ + try + { + m_pRecvBuff = new CBuffer(); + m_pSendBuff = new CBuffer(); + m_pWaitForSendBuff = new CBuffer(); + } + catch(std::bad_alloc& e) + { + LOG4_ERROR("%s", e.what()); + } + //m_dActiveTime = m_pLabor->GetNowTime(); + memset(m_szErrBuff, 0, sizeof(m_szErrBuff)); +} + +template +SocketChannelImpl::~SocketChannelImpl() +{ + LOG4_TRACE("SocketChannelImpl::~SocketChannelImpl() fd %d, seq %u", m_iFd, m_uiSeq); + m_listPipelineStepSeq.clear(); + m_mapStreamStepSeq.clear(); + if (CHANNEL_STATUS_CLOSED != m_ucChannelStatus) + { + Close(); + } + FREE(m_pWatcher); + DELETE(m_pRecvBuff); + DELETE(m_pSendBuff); + DELETE(m_pWaitForSendBuff); + DELETE(m_pHoldingHttpMsg); + DELETE(m_pCodec); +} + +template +E_CODEC_TYPE SocketChannelImpl::GetCodecType() const +{ + if (m_pCodec == nullptr) + { + return(CODEC_UNKNOW); + } + return(m_pCodec->GetCodecType()); +} + +template +uint32 SocketChannelImpl::PopStepSeq(uint32 uiStreamId, E_CODEC_STATUS eCodecStatus) +{ + if (m_listPipelineStepSeq.empty()) + { + auto iter = m_mapStreamStepSeq.find(uiStreamId); + if (iter != m_mapStreamStepSeq.end()) + { + uint32 uiStepSeq = iter->second; + if (CODEC_STATUS_OK == eCodecStatus) + { + m_mapStreamStepSeq.erase(iter); + } + return(uiStepSeq); + } + return(0); + } + else + { + uint32 uiStepSeq = m_listPipelineStepSeq.front(); + if (CODEC_STATUS_OK == eCodecStatus) + { + m_listPipelineStepSeq.pop_front(); + } + return(uiStepSeq); + } +} + +template +ev_tstamp SocketChannelImpl::GetKeepAlive() +{ + if (m_pCodec == nullptr) + { + LOG4_ERROR("no codec found, please check codec type is valid."); + return(0.0); + } + if (CODEC_HTTP == m_pCodec->GetCodecType()) + { + if ((static_cast(m_pCodec))->GetKeepAlive() >= 0.0) + { + return((static_cast(m_pCodec))->GetKeepAlive()); + } + } + return(m_dKeepAlive); +} + +template +bool SocketChannelImpl::NeedAliveCheck() const +{ + if (CODEC_HTTP == m_pCodec->GetCodecType() + || CODEC_NEBULA == m_pCodec->GetCodecType() + || CODEC_NEBULA_IN_NODE == m_pCodec->GetCodecType()) + { + return(false); + } + return(true); +} + +template template -void SocketChannelImpl::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) +void SocketChannelImpl::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) { m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } +template +bool SocketChannelImpl::NewCodec(std::shared_ptr pChannel, Labor* pLabor, std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) +{ + if (T::Type() != eCodecType) + { + pLogger->WriteLog(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "Codec %d with codec type %d not match needed codec type %d", + T::Type(), eCodecType); + return(false); + } + Codec* pCodec = nullptr; + try + { + pCodec = new T(pLogger, eCodecType, pChannel); + } + catch(std::bad_alloc& e) + { + return(false); + } + if (pChannel->m_pImpl == nullptr) + { + auto pImpl = std::make_shared>(pLabor, pLogger, pChannel->GetFd(), eCodecType); + pImpl->SetCodec(pCodec); + pChannel->InitImpl(std::static_pointer_cast(pImpl)); + } + else + { + std::static_pointer_cast>(pChannel->m_pImpl)->SetCodec(pCodec); + } + return(true); + +} + +template +E_CODEC_STATUS SocketChannelImpl::Send() +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) + { + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); + } + else if (CHANNEL_STATUS_ESTABLISHED != m_ucChannelStatus) + { + return(CODEC_STATUS_PAUSE); + } + int iNeedWriteLen = 0; + iNeedWriteLen = m_pSendBuff->ReadableBytes(); + if (0 == iNeedWriteLen) + { + iNeedWriteLen = m_pWaitForSendBuff->ReadableBytes(); + if (0 == iNeedWriteLen) + { + LOG4_TRACE("no data need to send."); + return(CODEC_STATUS_OK); + } + else + { + CBuffer* pExchangeBuff = m_pSendBuff; + m_pSendBuff = m_pWaitForSendBuff; + m_pWaitForSendBuff = pExchangeBuff; + m_pWaitForSendBuff->Compact(1); + } + } + + m_dActiveTime = m_pLabor->GetNowTime(); + int iHadWrittenLen = 0; + int iWrittenLen = 0; + do + { + iWrittenLen = Write(m_pSendBuff, m_iErrno); + if (iWrittenLen > 0) + { + iHadWrittenLen += iWrittenLen; + } + } + while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); + LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); + if (iHadWrittenLen >= 0) + { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); + if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ + && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) + { + m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); + } + m_dActiveTime = m_pLabor->GetNowTime(); + if (iNeedWriteLen == iHadWrittenLen && 0 == m_pWaitForSendBuff->ReadableBytes()) + { + return(CODEC_STATUS_OK); + } + else + { + return(CODEC_STATUS_PAUSE); + } + } + else + { + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + { + m_dActiveTime = m_pLabor->GetNowTime(); + return(CODEC_STATUS_PAUSE); + } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; + return(CODEC_STATUS_INT); + } +} + +template +template +E_CODEC_STATUS SocketChannelImpl::SendRequest(uint32 uiStepSeq, Targs&&... args) +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); + if (m_pCodec == nullptr) + { + LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); + return(CODEC_STATUS_ERR); + } + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + switch (m_ucChannelStatus) + { + case CHANNEL_STATUS_ESTABLISHED: + eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pSendBuff); + if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) + { + uint32 uiStreamId = (static_cast(m_pCodec))->GetLastStreamId(); + if (0 == uiStreamId) + { + m_listPipelineStepSeq.push_back(uiStepSeq); + } + else + { + m_mapStreamStepSeq.insert(std::make_pair(uiStreamId, uiStepSeq)); + } + } + break; + case CHANNEL_STATUS_CLOSED: + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); + case CHANNEL_STATUS_TELL_WORKER: + case CHANNEL_STATUS_WORKER: + case CHANNEL_STATUS_TRANSFER_TO_WORKER: + case CHANNEL_STATUS_CONNECTED: + case CHANNEL_STATUS_TRY_CONNECT: + case CHANNEL_STATUS_INIT: + eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pWaitForSendBuff); + if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) + { + eCodecStatus = CODEC_STATUS_PAUSE; + uint32 uiStreamId = (static_cast(m_pCodec))->GetLastStreamId(); + if (0 == uiStreamId) + { + m_listPipelineStepSeq.push_back(uiStepSeq); + } + else + { + m_mapStreamStepSeq.insert(std::make_pair(uiStreamId, uiStepSeq)); + } + } + break; + default: + LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); + return(CODEC_STATUS_ERR); + } + + if (CODEC_STATUS_OK != eCodecStatus && CODEC_STATUS_PART_OK != eCodecStatus) + { + return(eCodecStatus); + } + + int iNeedWriteLen = m_pSendBuff->ReadableBytes(); + if (iNeedWriteLen <= 0) + { + return(eCodecStatus); + } + + int iHadWrittenLen = 0; + int iWrittenLen = 0; + do + { + iWrittenLen = Write(m_pSendBuff, m_iErrno); + if (iWrittenLen > 0) + { + iHadWrittenLen += iWrittenLen; + } + } + while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); + LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); + if (iHadWrittenLen >= 0) + { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); + if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ + && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) + { + m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); + } + m_dActiveTime = m_pLabor->GetNowTime(); + if (iNeedWriteLen == iHadWrittenLen) + { + return(eCodecStatus); + } + else + { + return(CODEC_STATUS_PAUSE); + } + } + else + { + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + { + m_dActiveTime = m_pLabor->GetNowTime(); + return(CODEC_STATUS_PAUSE); + } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; + return(CODEC_STATUS_INT); + } +} + +template +template +E_CODEC_STATUS SocketChannelImpl::SendResponse(Targs&&... args) +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); + if (m_pCodec == nullptr) + { + LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); + return(CODEC_STATUS_ERR); + } + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + switch (m_ucChannelStatus) + { + case CHANNEL_STATUS_ESTABLISHED: + eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pSendBuff); + break; + case CHANNEL_STATUS_CLOSED: + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); + case CHANNEL_STATUS_TELL_WORKER: + case CHANNEL_STATUS_WORKER: + case CHANNEL_STATUS_TRANSFER_TO_WORKER: + case CHANNEL_STATUS_CONNECTED: + case CHANNEL_STATUS_TRY_CONNECT: + case CHANNEL_STATUS_INIT: + eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pWaitForSendBuff); + break; + default: + LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); + return(CODEC_STATUS_ERR); + } + + if (CODEC_STATUS_OK != eCodecStatus && CODEC_STATUS_PART_OK != eCodecStatus) + { + return(eCodecStatus); + } + + int iNeedWriteLen = m_pSendBuff->ReadableBytes(); + if (iNeedWriteLen <= 0) + { + return(eCodecStatus); + } + + int iHadWrittenLen = 0; + int iWrittenLen = 0; + do + { + iWrittenLen = Write(m_pSendBuff, m_iErrno); + if (iWrittenLen > 0) + { + iHadWrittenLen += iWrittenLen; + } + } + while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); + LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); + if (iHadWrittenLen >= 0) + { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); + if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ + && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) + { + m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); + } + m_dActiveTime = m_pLabor->GetNowTime(); + if (iNeedWriteLen == iHadWrittenLen) + { + if (m_pCodec->GetCodecType() == CODEC_HTTP + && (static_cast(m_pCodec))->GetKeepAlive() == 0.0) + { + return(CODEC_STATUS_EOF); + } + return(eCodecStatus); + } + else + { + return(CODEC_STATUS_PAUSE); + } + } + else + { + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + { + m_dActiveTime = m_pLabor->GetNowTime(); + return(CODEC_STATUS_PAUSE); + } + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("send to %s[fd %d] error %d: %s", m_strIdentify.c_str(), + m_iFd, m_iErrno, m_strErrMsg.c_str()); + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; + return(CODEC_STATUS_INT); + } +} + +template +template +E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) + { + LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); + } + if (m_pCodec == nullptr) + { + LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); + return(CODEC_STATUS_ERR); + } + int iReadLen = 0; + int iHadReadLen = 0; + do + { + iReadLen = Read(m_pRecvBuff, m_iErrno); + LOG4_TRACE("recv from fd %d data len %d. and m_pRecvBuff->ReadableBytes() = %d", + m_iFd, iReadLen, m_pRecvBuff->ReadableBytes()); + if (iReadLen > 0) + { + iHadReadLen += iReadLen; + } + } + while (iReadLen > 0); + m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen); + + if (iReadLen == 0) + { + m_eLastCodecStatus = CODEC_STATUS_EOF; + } + else if (iReadLen < 0) + { + if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 + { + m_dActiveTime = m_pLabor->GetNowTime(); + m_eLastCodecStatus = CODEC_STATUS_PAUSE; + } + else + { + m_strErrMsg = strerror_r(m_iErrno, m_szErrBuff, sizeof(m_szErrBuff)); + LOG4_ERROR("recv from %s[fd %d] remote %s error %d: %s", + m_strIdentify.c_str(), m_iFd, m_strRemoteAddr.c_str(), + m_iErrno, m_strErrMsg.c_str()); + m_eLastCodecStatus = CODEC_STATUS_INT; + m_ucChannelStatus = CHANNEL_STATUS_BROKEN; + } + } + + if (iHadReadLen > 0) + { + if (m_pRecvBuff->Capacity() > CBuffer::BUFFER_MAX_READ + && (m_pRecvBuff->ReadableBytes() < m_pRecvBuff->Capacity() / 2)) + { + m_pRecvBuff->Compact(m_pRecvBuff->ReadableBytes() * 2); + } + m_dActiveTime = m_pLabor->GetNowTime(); + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + if (m_pCodec->DecodeWithReactor()) + { + size_t uiSendBuffLen = m_pSendBuff->ReadableBytes(); + eCodecStatus = (static_cast(m_pCodec))->Decode(m_pRecvBuff, std::forward(args)..., m_pSendBuff); + if (m_pSendBuff->ReadableBytes() > uiSendBuffLen + && (eCodecStatus == CODEC_STATUS_OK || eCodecStatus == CODEC_STATUS_PART_OK)) + { + Send(); + } + } + else + { + eCodecStatus = (static_cast(m_pCodec))->Decode(m_pRecvBuff, std::forward(args)...); + } + if (CODEC_STATUS_OK == eCodecStatus) + { + ++m_uiUnitTimeMsgNum; + ++m_uiMsgNum; + if (m_uiMsgNum == 1) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; + } + } + else + { + if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) + { + if (!IsClient()) + { + m_pSendBuff->Clear(); + } + return(CODEC_STATUS_INVALID); + } + } + return(eCodecStatus); + } + return(m_eLastCodecStatus); +} + +template +template +E_CODEC_STATUS SocketChannelImpl::Fetch(Targs&&... args) +{ + LOG4_TRACE("channel_fd[%d], channel_seq[%d]", m_iFd, m_uiSeq); + if (CHANNEL_STATUS_CLOSED == m_ucChannelStatus || CHANNEL_STATUS_BROKEN == m_ucChannelStatus) + { + LOG4_TRACE("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", + m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); + return(CODEC_STATUS_ERR); + } + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + if (m_pCodec->DecodeWithReactor()) + { + size_t uiSendBuffLen = m_pSendBuff->ReadableBytes(); + eCodecStatus = (static_cast(m_pCodec))->Decode(m_pRecvBuff, std::forward(args)..., m_pSendBuff); + if (m_pSendBuff->ReadableBytes() > uiSendBuffLen + && (eCodecStatus == CODEC_STATUS_OK || eCodecStatus == CODEC_STATUS_PART_OK)) + { + Send(); + } + } + else + { + eCodecStatus = (static_cast(m_pCodec))->Decode(m_pRecvBuff, std::forward(args)...); + } + if (CODEC_STATUS_OK == eCodecStatus) + { + ++m_uiUnitTimeMsgNum; + ++m_uiMsgNum; + if (m_uiMsgNum == 1) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; + } + } + else + { + if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) + { + return(CODEC_STATUS_INVALID); + } + } + if ((CODEC_STATUS_PAUSE == eCodecStatus) + && (CODEC_STATUS_EOF == m_eLastCodecStatus + || CODEC_STATUS_INT == m_eLastCodecStatus)) + { + return(m_eLastCodecStatus); + } + return(eCodecStatus); +} + +template +void SocketChannelImpl::SetSecretKey(const std::string& strKey) +{ + m_strKey = strKey; + m_pCodec->SetKey(m_strKey); +} + +template +void SocketChannelImpl::SetRemoteWorkerIndex(int16 iRemoteWorkerIndex) +{ + m_iRemoteWorkerIdx = iRemoteWorkerIndex; +} + +template +bool SocketChannelImpl::Close() +{ + LOG4_TRACE("channel[%d] channel_status %d", m_iFd, (int)m_ucChannelStatus); + if (CHANNEL_STATUS_CLOSED != m_ucChannelStatus) + { + m_pSendBuff->Compact(1); + m_pWaitForSendBuff->Compact(1); + if (0 == close(m_iFd)) + { + m_ucChannelStatus = CHANNEL_STATUS_CLOSED; + LOG4_TRACE("channel[%d], channel_seq[%u] close successfully.", m_iFd, GetSequence()); + return(true); + } + else + { + LOG4_TRACE("failed to close channel(fd %d), errno %d, it will be close later.", m_iFd, errno); + return(false); + } + } + else + { + LOG4_TRACE("channel(fd %d, seq %u) had been closed before.", m_iFd, GetSequence()); + return(false); + } +} + +template +bool SocketChannelImpl::SetCodec(Codec* pCodec) +{ + if (pCodec == nullptr) + { + return(false); + } + if (m_pCodec != nullptr) + { + delete m_pCodec; + } + pCodec->ConnectionSetting(m_pSendBuff); + m_pCodec = pCodec; + return(true); +} + +template +int SocketChannelImpl::Write(CBuffer* pBuff, int& iErrno) +{ + LOG4_TRACE("fd[%d], channel_seq[%u]", GetFd(), GetSequence()); + return(pBuff->WriteFD(m_iFd, iErrno)); +} + +template +int SocketChannelImpl::Read(CBuffer* pBuff, int& iErrno) +{ + LOG4_TRACE("fd[%d], channel_seq[%u]", GetFd(), GetSequence()); + return(pBuff->ReadFD(m_iFd, iErrno)); +} + } /* namespace neb */ #endif /* SRC_CHANNEL_SOCKETCHANNELIMPL_HPP_ */ + diff --git a/src/channel/SocketChannelSslImpl.hpp b/src/channel/SocketChannelSslImpl.hpp index 2fd67ecf..5f7bcb3b 100644 --- a/src/channel/SocketChannelSslImpl.hpp +++ b/src/channel/SocketChannelSslImpl.hpp @@ -11,25 +11,7 @@ #define SRC_CHANNEL_SOCKETCHANNELSSLIMPL_HPP_ #ifdef WITH_OPENSSL -#include -#include -#include -#include -#include -#include -#ifndef OPENSSL_NO_ENGINE -#include -#endif -#include -#include -#ifndef OPENSSL_NO_OCSP -#include -#endif -#include -#include -#include -#include - +#include "SslContext.hpp" #include "SocketChannelImpl.hpp" namespace neb @@ -46,31 +28,29 @@ enum E_SSL_CHANNEL_STATUS SSL_CHANNEL_SHUTDOWN = 6, ///< SslShutDown() done }; -class SocketChannelSslImpl : public SocketChannelImpl +template +class SocketChannelSslImpl : public SocketChannelImpl { public: - SocketChannelSslImpl(SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive = 0.0); + SocketChannelSslImpl(Labor* pLabor, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive = 0.0); virtual ~SocketChannelSslImpl(); - static int SslInit(std::shared_ptr pLogger); - static int SslServerCtxCreate(std::shared_ptr pLogger); - static int SslServerCertificate(std::shared_ptr pLogger, - const std::string& strCertFile, const std::string& strKeyFile); - static void SslFree(); - int SslClientCtxCreate(); int SslCreateConnection(); int SslHandshake(); int SslShutdown(); - virtual bool Init(E_CODEC_TYPE eCodecType, bool bIsClient = false) override; - virtual E_CODEC_STATUS Send() override; ///< 覆盖基类的Send()方法,实现非阻塞socket连接建立后继续建立SSL连接 - virtual E_CODEC_STATUS Send(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) override; - virtual E_CODEC_STATUS Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq) override; - virtual E_CODEC_STATUS Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) override; - virtual E_CODEC_STATUS Recv(HttpMsg& oHttpMsg) override; - //virtual E_CODEC_STATUS Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, HttpMsg& oHttpMsg) override; - virtual bool Close() override; + bool Init(bool bIsClient = false); + template + E_CODEC_STATUS SendRequest(uint32 uiStepSeq, Targs&&... args); + template + E_CODEC_STATUS SendResponse(Targs&&... args); + template + E_CODEC_STATUS Recv(Targs&&... args); + E_CODEC_STATUS Send(); + bool Close(); + template + void Logger(int iLogLevel, const char* szFileName, uint32 uiFileLine, const char* szFunction, Targs&&... args); protected: virtual int Write(CBuffer* pBuff, int& iErrno) override; @@ -80,12 +60,11 @@ class SocketChannelSslImpl : public SocketChannelImpl E_SSL_CHANNEL_STATUS m_eSslChannelStatus; bool m_bIsClientConnection; SSL* m_pSslConnection; - - static SSL_CTX* s_pServerSslCtx; - static SSL_CTX* s_pClientSslCtx; }; } #endif // ifdef WITH_OPENSSL +#include "SocketChannelSslImpl.inl" #endif // SRC_CHANNEL_SOCKETCHANNELSSLIMPL_HPP_ + diff --git a/src/channel/SocketChannelSslImpl.cpp b/src/channel/SocketChannelSslImpl.inl similarity index 61% rename from src/channel/SocketChannelSslImpl.cpp rename to src/channel/SocketChannelSslImpl.inl index d5bf3f31..1a77f7f7 100644 --- a/src/channel/SocketChannelSslImpl.cpp +++ b/src/channel/SocketChannelSslImpl.inl @@ -9,177 +9,35 @@ ******************************************************************************/ #ifdef WITH_OPENSSL -#include "SocketChannelSslImpl.hpp" namespace neb { -SSL_CTX* SocketChannelSslImpl::s_pServerSslCtx = NULL; -SSL_CTX* SocketChannelSslImpl::s_pClientSslCtx = NULL; - -SocketChannelSslImpl::SocketChannelSslImpl( - SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) - : SocketChannelImpl(pSocketChannel, pLogger, iFd, ulSeq, dKeepAlive), +template +SocketChannelSslImpl::SocketChannelSslImpl( + Labor* pLabor, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) + : SocketChannelImpl(pLabor, pLogger, iFd, ulSeq, dKeepAlive), m_eSslChannelStatus(SSL_CHANNEL_INIT), m_bIsClientConnection(false), m_pSslConnection(NULL) { } -SocketChannelSslImpl::~SocketChannelSslImpl() -{ - LOG4_DEBUG("SocketChannelSslImpl::~SocketChannelSslImpl() fd %d, seq %u", GetFd(), GetSequence()); -} - -int SocketChannelSslImpl::SslInit(std::shared_ptr pLogger) -{ -#if OPENSSL_VERSION_NUMBER >= 0x10100003L - - if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) - { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "OPENSSL_init_ssl() failed!"); - return(ERR_SSL_INIT); - } - - /* - * OPENSSL_init_ssl() may leave errors in the error queue - * while returning success - */ - - ERR_clear_error(); - -#else - - OPENSSL_config(NULL); - - SSL_library_init(); - SSL_load_error_strings(); - - OpenSSL_add_all_algorithms(); - -#endif - - return(ERR_OK); -} - -int SocketChannelSslImpl::SslServerCtxCreate(std::shared_ptr pLogger) -{ - pLogger->WriteLog(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, " "); - s_pServerSslCtx = SSL_CTX_new(TLS_server_method()); - - if (s_pServerSslCtx == NULL) - { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "SSL_CTX_new() failed"); - return(ERR_SSL_CTX); - } - - SSL_CTX_set_min_proto_version(s_pServerSslCtx, TLS1_1_VERSION); - -#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG - SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG); -#endif - -#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER - SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); -#endif - -#ifdef SSL_OP_TLS_D5_BUG - SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_TLS_D5_BUG); -#endif - -#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG - SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_TLS_BLOCK_PADDING_BUG); -#endif - -#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS - SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); -#endif - - SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_SINGLE_DH_USE); - -#ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_NO_COMPRESSION); -#endif - -#ifdef SSL_MODE_RELEASE_BUFFERS - SSL_CTX_set_mode(s_pServerSslCtx, SSL_MODE_RELEASE_BUFFERS); -#endif - -#ifdef SSL_MODE_NO_AUTO_CHAIN - SSL_CTX_set_mode(s_pServerSslCtx, SSL_MODE_NO_AUTO_CHAIN); -#endif - - SSL_CTX_set_read_ahead(s_pServerSslCtx, 1); - - return(ERR_OK); -} - -int SocketChannelSslImpl::SslServerCertificate(std::shared_ptr pLogger, - const std::string& strCertFile, const std::string& strKeyFile) -{ - pLogger->WriteLog(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, - "SslServerCertificate(%s, %s)", strCertFile.c_str(), strKeyFile.c_str()); - // 加载使用公钥证书 - if (!SSL_CTX_use_certificate_chain_file(s_pServerSslCtx, strCertFile.c_str())) - { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "SSL_CTX_user_certificate_chain_file(\"%s\") failed!", strCertFile.c_str()); - return(ERR_SSL_CERTIFICATE); - } - - // 加载使用私钥 - if (!SSL_CTX_use_PrivateKey_file(s_pServerSslCtx, strKeyFile.c_str(), SSL_FILETYPE_PEM)) - { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "SSL_CTX_use_PrivateKey_file(\"%s\") failed!", strKeyFile.c_str()); - return(ERR_SSL_CERTIFICATE); - } - - // 检查私钥与证书是否匹配 - if (!SSL_CTX_check_private_key(s_pServerSslCtx)) - { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "SSL_CTX_check_private_key() failed: private key does not match the certificate public key!", strKeyFile.c_str()); - return(ERR_SSL_CERTIFICATE); - } - return(ERR_OK); -} - -void SocketChannelSslImpl::SslFree() -{ - if (s_pServerSslCtx) - { - SSL_CTX_free(s_pServerSslCtx); - s_pServerSslCtx = NULL; - } - if (s_pClientSslCtx) - { - SSL_CTX_free(s_pClientSslCtx); - s_pClientSslCtx = NULL; - } -} - -int SocketChannelSslImpl::SslClientCtxCreate() +template +SocketChannelSslImpl::~SocketChannelSslImpl() { - if (s_pClientSslCtx == NULL) - { - s_pClientSslCtx = SSL_CTX_new(TLS_client_method()); - if (s_pClientSslCtx == NULL) - { - LOG4_ERROR("SSL_CTX_new() failed!"); - return(ERR_SSL_CTX); - } - } - return(ERR_OK); + LOG4_DEBUG("SocketChannelSslImpl::~SocketChannelSslImpl() fd %d, seq %u", + SocketChannelImpl::GetFd(), SocketChannelImpl::GetSequence()); } -int SocketChannelSslImpl::SslCreateConnection() +template +int SocketChannelSslImpl::SslCreateConnection() { if (m_bIsClientConnection) { - m_pSslConnection = SSL_new(s_pClientSslCtx); + m_pSslConnection = SSL_new(SslContext::ClientSslCtx()); } else { - m_pSslConnection = SSL_new(s_pServerSslCtx); + m_pSslConnection = SSL_new(SslContext::ServerSslCtx()); } if (m_pSslConnection == NULL) @@ -188,7 +46,7 @@ int SocketChannelSslImpl::SslCreateConnection() return(ERR_SSL_NEW_CONNECTION); } - if (!SSL_set_fd(m_pSslConnection, GetFd())) + if (!SSL_set_fd(m_pSslConnection, SocketChannelImpl::GetFd())) { LOG4_ERROR("SSL_set_fd() failed!"); return(ERR_SSL_NEW_CONNECTION); @@ -206,13 +64,14 @@ int SocketChannelSslImpl::SslCreateConnection() return(ERR_OK); } -int SocketChannelSslImpl::SslHandshake() +template +int SocketChannelSslImpl::SslHandshake() { LOG4_TRACE(""); int iHandshakeResult = SSL_do_handshake(m_pSslConnection); if (iHandshakeResult == 1) { - LOG4_TRACE("fd %d ssl handshake was successful.", GetFd()); + LOG4_TRACE("fd %d ssl handshake was successful.", SocketChannelImpl::GetFd()); m_eSslChannelStatus = SSL_CHANNEL_ESTABLISHED; return(ERR_OK); } @@ -279,7 +138,8 @@ int SocketChannelSslImpl::SslHandshake() return(ERR_OK); } -int SocketChannelSslImpl::SslShutdown() +template +int SocketChannelSslImpl::SslShutdown() { if (SSL_in_init(m_pSslConnection)) { @@ -333,7 +193,7 @@ int SocketChannelSslImpl::SslShutdown() int iShutdownResult = SSL_shutdown(m_pSslConnection); if (iShutdownResult == 1) { - LOG4_TRACE("fd %d ssl shutdown was successful.", GetFd()); + LOG4_TRACE("fd %d ssl shutdown was successful.", SocketChannelImpl::GetFd()); m_eSslChannelStatus = SSL_CHANNEL_SHUTDOWN; } else // 0 and < 0 @@ -376,11 +236,11 @@ int SocketChannelSslImpl::SslShutdown() if (m_bIsClientConnection) { - SSL_CTX_remove_session(s_pClientSslCtx, SSL_get0_session(m_pSslConnection)); + SSL_CTX_remove_session(SslContext::ClientSslCtx(), SSL_get0_session(m_pSslConnection)); } else { - SSL_CTX_remove_session(s_pServerSslCtx, SSL_get0_session(m_pSslConnection)); + SSL_CTX_remove_session(SslContext::ServerSslCtx(), SSL_get0_session(m_pSslConnection)); } SSL_free(m_pSslConnection); @@ -398,21 +258,10 @@ int SocketChannelSslImpl::SslShutdown() return(ERR_OK); } -bool SocketChannelSslImpl::Init(E_CODEC_TYPE eCodecType, bool bIsClient) +template +bool SocketChannelSslImpl::Init(bool bIsClient) { - if (!SocketChannelImpl::Init(eCodecType, bIsClient)) - { - return(false); - } - if (bIsClient) - { - m_bIsClientConnection = true; - if (ERR_OK != SslClientCtxCreate()) - { - return(false); - } - LOG4_TRACE("SslClientCtxCreate() successfully."); - } + m_bIsClientConnection = bIsClient; if (ERR_OK != SslCreateConnection()) { return(false); @@ -421,143 +270,15 @@ bool SocketChannelSslImpl::Init(E_CODEC_TYPE eCodecType, bool bIsClient) return(true); } -E_CODEC_STATUS SocketChannelSslImpl::Send() -{ - LOG4_TRACE(""); - switch (m_eSslChannelStatus) - { - case SSL_CHANNEL_ESTABLISHED: - return(SocketChannelImpl::Send()); - case SSL_CHANNEL_WANT_READ: - case SSL_CHANNEL_WANT_WRITE: - case SSL_CHANNEL_INIT: - if (ERR_OK == SslHandshake()) - { - if (m_eSslChannelStatus == SSL_CHANNEL_ESTABLISHED) - { - return(SocketChannelImpl::Send()); - } - else if (m_eSslChannelStatus == SSL_CHANNEL_WANT_READ) - { - return(CODEC_STATUS_WANT_READ); - } - else - { - return(CODEC_STATUS_WANT_WRITE); - } - } - else - { - return(CODEC_STATUS_ERR); - } - break; - case SSL_CHANNEL_SHUTING_WANT_READ: - case SSL_CHANNEL_SHUTING_WANT_WRITE: - if (ERR_OK == SslShutdown()) - { - if (m_eSslChannelStatus == SSL_CHANNEL_SHUTDOWN) - { - return(CODEC_STATUS_EOF); - } - else if (m_eSslChannelStatus == SSL_CHANNEL_SHUTING_WANT_READ) - { - return(CODEC_STATUS_WANT_READ); - } - else - { - return(CODEC_STATUS_WANT_WRITE); - } - } - else - { - return(CODEC_STATUS_ERR); - } - break; - case SSL_CHANNEL_SHUTDOWN: - LOG4_ERROR("the ssl channel had been shutdown!"); - return(CODEC_STATUS_ERR); - default: - LOG4_ERROR("invalid ssl channel status!"); - return(CODEC_STATUS_ERR); - } -} - -E_CODEC_STATUS SocketChannelSslImpl::Send(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) -{ - LOG4_TRACE(""); - switch (m_eSslChannelStatus) - { - case SSL_CHANNEL_ESTABLISHED: - return(SocketChannelImpl::Send(iCmd, uiSeq, oMsgBody)); - case SSL_CHANNEL_WANT_READ: - case SSL_CHANNEL_WANT_WRITE: - case SSL_CHANNEL_INIT: - if (ERR_OK == SslHandshake()) - { - if (m_eSslChannelStatus == SSL_CHANNEL_ESTABLISHED) - { - return(SocketChannelImpl::Send(iCmd, uiSeq, oMsgBody)); - } - else if (m_eSslChannelStatus == SSL_CHANNEL_WANT_READ) - { - uint8 ucChannelStatus = GetChannelStatus(); - SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); - SocketChannelImpl::Send(iCmd, uiSeq, oMsgBody); - SetChannelStatus((E_CHANNEL_STATUS)ucChannelStatus); - return(CODEC_STATUS_WANT_READ); - } - else - { - uint8 ucChannelStatus = GetChannelStatus(); - SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); - SocketChannelImpl::Send(iCmd, uiSeq, oMsgBody); - SetChannelStatus((E_CHANNEL_STATUS)ucChannelStatus); - return(CODEC_STATUS_WANT_WRITE); - } - } - else - { - return(CODEC_STATUS_ERR); - } - break; - case SSL_CHANNEL_SHUTING_WANT_READ: - case SSL_CHANNEL_SHUTING_WANT_WRITE: - if (ERR_OK == SslShutdown()) - { - if (m_eSslChannelStatus == SSL_CHANNEL_SHUTDOWN) - { - return(CODEC_STATUS_EOF); - } - else if (m_eSslChannelStatus == SSL_CHANNEL_SHUTING_WANT_READ) - { - return(CODEC_STATUS_WANT_READ); - } - else - { - return(CODEC_STATUS_WANT_WRITE); - } - } - else - { - return(CODEC_STATUS_ERR); - } - break; - case SSL_CHANNEL_SHUTDOWN: - LOG4_ERROR("the ssl channel had been shutdown!"); - return(CODEC_STATUS_ERR); - default: - LOG4_ERROR("invalid ssl channel status!"); - return(CODEC_STATUS_ERR); - } -} - -E_CODEC_STATUS SocketChannelSslImpl::Send(const HttpMsg& oHttpMsg, uint32 ulStepSeq) +template +template +E_CODEC_STATUS SocketChannelSslImpl::SendRequest(uint32 uiStepSeq, Targs&&... args) { LOG4_TRACE(""); switch (m_eSslChannelStatus) { case SSL_CHANNEL_ESTABLISHED: - return(SocketChannelImpl::Send(oHttpMsg, ulStepSeq)); + return(SocketChannelImpl::SendRequest(uiStepSeq, std::forward(args)...)); case SSL_CHANNEL_WANT_READ: case SSL_CHANNEL_WANT_WRITE: case SSL_CHANNEL_INIT: @@ -565,22 +286,22 @@ E_CODEC_STATUS SocketChannelSslImpl::Send(const HttpMsg& oHttpMsg, uint32 ulStep { if (m_eSslChannelStatus == SSL_CHANNEL_ESTABLISHED) { - return(SocketChannelImpl::Send(oHttpMsg, ulStepSeq)); + return(SocketChannelImpl::SendRequest(uiStepSeq, std::forward(args)...)); } else if (m_eSslChannelStatus == SSL_CHANNEL_WANT_READ) { - uint8 ucChannelStatus = GetChannelStatus(); - SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); - SocketChannelImpl::Send(oHttpMsg, ulStepSeq); - SetChannelStatus((E_CHANNEL_STATUS)ucChannelStatus); + uint8 ucChannelStatus = SocketChannelImpl::GetChannelStatus(); + SocketChannelImpl::SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); + SocketChannelImpl::SendRequest(uiStepSeq, std::forward(args)...); + SocketChannelImpl::SetChannelStatus((E_CHANNEL_STATUS)ucChannelStatus); return(CODEC_STATUS_WANT_READ); } else { - uint8 ucChannelStatus = GetChannelStatus(); - SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); - SocketChannelImpl::Send(oHttpMsg, ulStepSeq); - SetChannelStatus((E_CHANNEL_STATUS)ucChannelStatus); + uint8 ucChannelStatus = SocketChannelImpl::GetChannelStatus(); + SocketChannelImpl::SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); + SocketChannelImpl::SendRequest(uiStepSeq, std::forward(args)...); + SocketChannelImpl::SetChannelStatus((E_CHANNEL_STATUS)ucChannelStatus); return(CODEC_STATUS_WANT_WRITE); } } @@ -620,13 +341,15 @@ E_CODEC_STATUS SocketChannelSslImpl::Send(const HttpMsg& oHttpMsg, uint32 ulStep } } -E_CODEC_STATUS SocketChannelSslImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) +template +template +E_CODEC_STATUS SocketChannelSslImpl::SendResponse(Targs&& ...args) { LOG4_TRACE(""); switch (m_eSslChannelStatus) { case SSL_CHANNEL_ESTABLISHED: - return(SocketChannelImpl::Recv(oMsgHead, oMsgBody)); + return(SocketChannelImpl::SendResponse(std::forward(args)...)); case SSL_CHANNEL_WANT_READ: case SSL_CHANNEL_WANT_WRITE: case SSL_CHANNEL_INIT: @@ -634,21 +357,22 @@ E_CODEC_STATUS SocketChannelSslImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) { if (m_eSslChannelStatus == SSL_CHANNEL_ESTABLISHED) { - if (m_bIsClientConnection) - { - return(CODEC_STATUS_WANT_WRITE); - } - else - { - return(SocketChannelImpl::Recv(oMsgHead, oMsgBody)); - } + return(SocketChannelImpl::SendResponse(std::forward(args)...)); } else if (m_eSslChannelStatus == SSL_CHANNEL_WANT_READ) { + uint8 ucChannelStatus = SocketChannelImpl::GetChannelStatus(); + SocketChannelImpl::SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); + SocketChannelImpl::SendResponse(std::forward(args)...); + SocketChannelImpl::SetChannelStatus((E_CHANNEL_STATUS)ucChannelStatus); return(CODEC_STATUS_WANT_READ); } else { + uint8 ucChannelStatus = SocketChannelImpl::GetChannelStatus(); + SocketChannelImpl::SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); + SocketChannelImpl::SendResponse(std::forward(args)...); + SocketChannelImpl::SetChannelStatus((E_CHANNEL_STATUS)ucChannelStatus); return(CODEC_STATUS_WANT_WRITE); } } @@ -688,13 +412,15 @@ E_CODEC_STATUS SocketChannelSslImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody) } } -E_CODEC_STATUS SocketChannelSslImpl::Recv(HttpMsg& oHttpMsg) +template +template +E_CODEC_STATUS SocketChannelSslImpl::Recv(Targs&& ...args) { LOG4_TRACE(""); switch (m_eSslChannelStatus) { case SSL_CHANNEL_ESTABLISHED: - return(SocketChannelImpl::Recv(oHttpMsg)); + return(SocketChannelImpl::Recv(std::forward(args)...)); case SSL_CHANNEL_WANT_READ: case SSL_CHANNEL_WANT_WRITE: case SSL_CHANNEL_INIT: @@ -708,7 +434,7 @@ E_CODEC_STATUS SocketChannelSslImpl::Recv(HttpMsg& oHttpMsg) } else { - return(SocketChannelImpl::Recv(oHttpMsg)); + return(SocketChannelImpl::Recv(std::forward(args)...)); } } else if (m_eSslChannelStatus == SSL_CHANNEL_WANT_READ) @@ -717,6 +443,7 @@ E_CODEC_STATUS SocketChannelSslImpl::Recv(HttpMsg& oHttpMsg) } else { + Send(); return(CODEC_STATUS_WANT_WRITE); } } @@ -739,6 +466,7 @@ E_CODEC_STATUS SocketChannelSslImpl::Recv(HttpMsg& oHttpMsg) } else { + Send(); return(CODEC_STATUS_WANT_WRITE); } } @@ -756,14 +484,14 @@ E_CODEC_STATUS SocketChannelSslImpl::Recv(HttpMsg& oHttpMsg) } } -/* -E_CODEC_STATUS SocketChannelSslImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, HttpMsg& oHttpMsg) +template +E_CODEC_STATUS SocketChannelSslImpl::Send() { LOG4_TRACE(""); switch (m_eSslChannelStatus) { case SSL_CHANNEL_ESTABLISHED: - return(SocketChannelImpl::Recv(oMsgHead, oMsgBody, oHttpMsg)); + return(SocketChannelImpl::Send()); case SSL_CHANNEL_WANT_READ: case SSL_CHANNEL_WANT_WRITE: case SSL_CHANNEL_INIT: @@ -771,7 +499,7 @@ E_CODEC_STATUS SocketChannelSslImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, { if (m_eSslChannelStatus == SSL_CHANNEL_ESTABLISHED) { - return(SocketChannelImpl::Recv(oMsgHead, oMsgBody, oHttpMsg)); + return(SocketChannelImpl::Send()); } else if (m_eSslChannelStatus == SSL_CHANNEL_WANT_READ) { @@ -817,9 +545,9 @@ E_CODEC_STATUS SocketChannelSslImpl::Recv(MsgHead& oMsgHead, MsgBody& oMsgBody, return(CODEC_STATUS_ERR); } } -*/ -bool SocketChannelSslImpl::Close() +template +bool SocketChannelSslImpl::Close() { if (m_eSslChannelStatus != SSL_CHANNEL_SHUTDOWN) { @@ -830,10 +558,11 @@ bool SocketChannelSslImpl::Close() return(false); } } - return(SocketChannelImpl::Close()); + return(SocketChannelImpl::Close()); } -int SocketChannelSslImpl::Write(CBuffer* pBuff, int& iErrno) +template +int SocketChannelSslImpl::Write(CBuffer* pBuff, int& iErrno) { LOG4_TRACE(""); int iNeedWriteLen = (int)pBuff->ReadableBytes(); @@ -854,7 +583,7 @@ int SocketChannelSslImpl::Write(CBuffer* pBuff, int& iErrno) iErrno = EAGAIN; break; case SSL_ERROR_ZERO_RETURN: - LOG4_DEBUG("ssl channel(fd %d) closed by peer.", GetFd()); + LOG4_DEBUG("ssl channel(fd %d) closed by peer.", SocketChannelImpl::GetFd()); break; default: LOG4_ERROR("SSL_write() error code %d, see SSL_get_error() manual for error code detail.", iErrCode); @@ -864,7 +593,8 @@ int SocketChannelSslImpl::Write(CBuffer* pBuff, int& iErrno) return(iWritenLen); } -int SocketChannelSslImpl::Read(CBuffer* pBuff, int& iErrno) +template +int SocketChannelSslImpl::Read(CBuffer* pBuff, int& iErrno) { LOG4_TRACE(""); if (pBuff->WriteableBytes() < 1024) @@ -888,7 +618,7 @@ int SocketChannelSslImpl::Read(CBuffer* pBuff, int& iErrno) iErrno = EAGAIN; break; case SSL_ERROR_ZERO_RETURN: - LOG4_DEBUG("ssl channel(fd %d) closed by peer.", GetFd()); + LOG4_DEBUG("ssl channel(fd %d) closed by peer.", SocketChannelImpl::GetFd()); break; case SSL_ERROR_SYSCALL: LOG4_DEBUG("Some non-recoverable I/O error occurred. The OpenSSL error queue may contain more information on the error. " @@ -902,6 +632,13 @@ int SocketChannelSslImpl::Read(CBuffer* pBuff, int& iErrno) return(iReadLen); } +template +template +void SocketChannelSslImpl::Logger(int32 iLogLevel, const char* szFileName, uint32 uiFileLine, const char* szFunction, Targs&&... args) +{ + SocketChannelImpl::Logger(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); +} + } #endif // ifdef WITH_OPENSSL diff --git a/src/channel/SslContext.cpp b/src/channel/SslContext.cpp new file mode 100644 index 00000000..82f8171b --- /dev/null +++ b/src/channel/SslContext.cpp @@ -0,0 +1,184 @@ +/******************************************************************************* + * Project: Nebula + * @file SslContext.cpp + * @brief + * @author Bwar + * @date: 2021-10-04 + * @note + * Modify history: + ******************************************************************************/ +#include "SslContext.hpp" + +#ifdef WITH_OPENSSL + +namespace neb +{ + +SSL_CTX* SslContext::s_pServerSslCtx = nullptr; +SSL_CTX* SslContext::s_pClientSslCtx = nullptr; + +SslContext::SslContext() +{ + // TODO Auto-generated constructor stub + +} + +SslContext::~SslContext() +{ + // TODO Auto-generated destructor stub +} + +int SslContext::SslInit(std::shared_ptr pLogger) +{ + if (s_pServerSslCtx != nullptr) + { + return(ERR_OK); + } +#if OPENSSL_VERSION_NUMBER >= 0x10100003L + + if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) + { + pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "OPENSSL_init_ssl() failed!"); + return(ERR_SSL_INIT); + } + + /* + * OPENSSL_init_ssl() may leave errors in the error queue + * while returning success + */ + + ERR_clear_error(); + +#else + + OPENSSL_config(NULL); + + SSL_library_init(); + SSL_load_error_strings(); + + OpenSSL_add_all_algorithms(); + +#endif + + return(ERR_OK); +} + +int SslContext::SslClientCtxCreate(std::shared_ptr pLogger) +{ + if (s_pClientSslCtx == nullptr) + { + s_pClientSslCtx = SSL_CTX_new(TLS_client_method()); + if (s_pClientSslCtx == nullptr) + { + pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "SSL_CTX_new() failed!"); + return(ERR_SSL_CTX); + } + } + return(ERR_OK); +} + +int SslContext::SslServerCtxCreate(std::shared_ptr pLogger) +{ + pLogger->WriteLog(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, " "); + if (s_pServerSslCtx != nullptr) + { + return(ERR_OK); + } + s_pServerSslCtx = SSL_CTX_new(TLS_server_method()); + + if (s_pServerSslCtx == nullptr) + { + pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "SSL_CTX_new() failed"); + return(ERR_SSL_CTX); + } + + SSL_CTX_set_min_proto_version(s_pServerSslCtx, TLS1_1_VERSION); + +#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG); +#endif + +#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); +#endif + +#ifdef SSL_OP_TLS_D5_BUG + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_TLS_D5_BUG); +#endif + +#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_TLS_BLOCK_PADDING_BUG); +#endif + +#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); +#endif + + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_SINGLE_DH_USE); + +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(s_pServerSslCtx, SSL_OP_NO_COMPRESSION); +#endif + +#ifdef SSL_MODE_RELEASE_BUFFERS + SSL_CTX_set_mode(s_pServerSslCtx, SSL_MODE_RELEASE_BUFFERS); +#endif + +#ifdef SSL_MODE_NO_AUTO_CHAIN + SSL_CTX_set_mode(s_pServerSslCtx, SSL_MODE_NO_AUTO_CHAIN); +#endif + + SSL_CTX_set_read_ahead(s_pServerSslCtx, 1); + + return(ERR_OK); +} + +int SslContext::SslServerCertificate(std::shared_ptr pLogger, + const std::string& strCertFile, const std::string& strKeyFile) +{ + pLogger->WriteLog(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "SslServerCertificate(%s, %s)", strCertFile.c_str(), strKeyFile.c_str()); + // 加载使用公钥证书 + if (!SSL_CTX_use_certificate_chain_file(s_pServerSslCtx, strCertFile.c_str())) + { + pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "SSL_CTX_user_certificate_chain_file(\"%s\") failed!", strCertFile.c_str()); + return(ERR_SSL_CERTIFICATE); + } + + // 加载使用私钥 + if (!SSL_CTX_use_PrivateKey_file(s_pServerSslCtx, strKeyFile.c_str(), SSL_FILETYPE_PEM)) + { + pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "SSL_CTX_use_PrivateKey_file(\"%s\") failed!", strKeyFile.c_str()); + return(ERR_SSL_CERTIFICATE); + } + + // 检查私钥与证书是否匹配 + if (!SSL_CTX_check_private_key(s_pServerSslCtx)) + { + pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "SSL_CTX_check_private_key() failed: private key does not match the certificate public key!", strKeyFile.c_str()); + return(ERR_SSL_CERTIFICATE); + } + return(ERR_OK); +} + +void SslContext::SslFree() +{ + if (s_pServerSslCtx) + { + SSL_CTX_free(s_pServerSslCtx); + s_pServerSslCtx = nullptr; + } + if (s_pClientSslCtx) + { + SSL_CTX_free(s_pClientSslCtx); + s_pClientSslCtx = nullptr; + } +} + +} /* namespace neb */ + +#endif // #ifdef WITH_OPENSSL + diff --git a/src/channel/SslContext.hpp b/src/channel/SslContext.hpp new file mode 100644 index 00000000..a6a091e7 --- /dev/null +++ b/src/channel/SslContext.hpp @@ -0,0 +1,73 @@ +/******************************************************************************* + * Project: Nebula + * @file SslContext.hpp + * @brief + * @author Bwar + * @date: 2021-10-04 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CHANNEL_SSLCONTEXT_HPP_ +#define SRC_CHANNEL_SSLCONTEXT_HPP_ + +#ifdef WITH_OPENSSL +#include +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_ENGINE +#include +#endif +#include +#include +#ifndef OPENSSL_NO_OCSP +#include +#endif +#include +#include +#include +#include + +#include "Error.hpp" +#include "Definition.hpp" +#include "logger/NetLogger.hpp" + +namespace neb +{ + +class SslContext +{ +public: + SslContext(); + virtual ~SslContext(); + + static int SslInit(std::shared_ptr pLogger); + static int SslClientCtxCreate(std::shared_ptr pLogger); + static int SslServerCtxCreate(std::shared_ptr pLogger); + static int SslServerCertificate(std::shared_ptr pLogger, + const std::string& strCertFile, const std::string& strKeyFile); + static void SslFree(); + + static SSL_CTX* ServerSslCtx() + { + return(s_pServerSslCtx); + } + + static SSL_CTX* ClientSslCtx() + { + return(s_pClientSslCtx); + } + +private: + static SSL_CTX* s_pServerSslCtx; + static SSL_CTX* s_pClientSslCtx; +}; + +} /* namespace neb */ + +#endif // #ifdef WITH_OPENSSL + +#endif /* SRC_CHANNEL_SSLCONTEXT_HPP_ */ + diff --git a/src/codec/Codec.cpp b/src/codec/Codec.cpp index e5f3587a..089b9d1d 100644 --- a/src/codec/Codec.cpp +++ b/src/codec/Codec.cpp @@ -15,10 +15,8 @@ namespace neb { -std::vector Codec::m_vecAutoSwitchCodecType; - -Codec::Codec(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) - : m_pLogger(pLogger), m_iErrno(0), m_eCodecType(eCodecType) +Codec::Codec(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel) + : m_pLogger(pLogger), m_iErrno(0), m_eCodecType(eCodecType), m_pBindChannel(pBindChannel) { } @@ -27,16 +25,6 @@ Codec::~Codec() LOG4_TRACE("codec_type %d", m_eCodecType); } -const std::vector& Codec::GetAutoSwitchCodecType() -{ - return(m_vecAutoSwitchCodecType); -} - -void Codec::AddAutoSwitchCodecType(E_CODEC_TYPE eCodecType) -{ - m_vecAutoSwitchCodecType.push_back(eCodecType); -} - bool Codec::Zip(const std::string& strSrc, std::string& strDest) { /* @@ -246,3 +234,4 @@ bool Codec::AesDecrypt(const std::string& strSrc, std::string& strDest) } } /* namespace neb */ + diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index f0205596..1fdcc3dc 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -46,6 +46,8 @@ enum E_CODEC_TYPE CODEC_RESP = 9, ///< redis数据传输协议resp CODEC_HTTP2 = 10, ///< http2编解码 CODEC_DIRECT = 11, ///< 虚拟编解码类型,用于SelfChannel,以参数方式直接传递数据包 + CODEC_CASS = 12, ///< cassandra scylladb + CODEC_RAW = 13, ///< 裸数据传输 }; /** @@ -68,10 +70,12 @@ enum E_CODEC_STATUS CODEC_STATUS_INT = 9, ///< 连接非正常关闭 }; +class SocketChannel; + class Codec { public: - Codec(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + Codec(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel); virtual ~Codec(); E_CODEC_TYPE GetCodecType() const @@ -79,23 +83,27 @@ class Codec return(m_eCodecType); } - /** - * @brief 字节流编码 - * @param[in] oMsgHead 消息包头 - * @param[in] oMsgBody 消息包体 - * @param[out] pBuff 数据缓冲区 - * @return 编解码状态 - */ - virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) = 0; + virtual uint32 GetLastStreamId() + { + return(0); + } + + virtual ev_tstamp GetKeepAlive() const + { + return(0.0); + } + + virtual bool DecodeWithReactor() const + { + return(false); + } /** - * @brief 字节流解码 - * @param[in,out] pBuff 数据缓冲区 - * @param[out] oMsgHead 消息包头 - * @param[out] oMsgBody 消息包体 - * @return 编解码状态 + * @see CodecHttp2::ConnectionSetting() */ - virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) = 0; + virtual void ConnectionSetting(CBuffer* pBuff) + { + } void SetKey(const std::string& strKey) { @@ -107,8 +115,10 @@ class Codec return(m_iErrno); } - static const std::vector& GetAutoSwitchCodecType(); - static void AddAutoSwitchCodecType(E_CODEC_TYPE eCodecType); + std::shared_ptr GetBindChannel() const + { + return(m_pBindChannel); + } template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); @@ -137,6 +147,7 @@ class Codec private: int32 m_iErrno; E_CODEC_TYPE m_eCodecType; + std::shared_ptr m_pBindChannel; std::string m_strKey; // 密钥 static std::vector m_vecAutoSwitchCodecType; // 自动转换有效的编解码类型 diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp new file mode 100644 index 00000000..8c7c306a --- /dev/null +++ b/src/codec/CodecFactory.cpp @@ -0,0 +1,403 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecFactory.hpp + * @brief + * @author Bwar + * @date: 2021-10-02 + * @note + * Modify history: + ******************************************************************************/ +#include "CodecFactory.hpp" +#include "ios/IO.hpp" + +namespace neb +{ + +std::vector CodecFactory::s_vecAutoSwitchCodec = { + CODEC_UNKNOW, CODEC_HTTP, CODEC_PROTO, CODEC_RESP, CODEC_HTTP2 +}; + +CodecFactory::CodecFactory() +{ +} + +CodecFactory::~CodecFactory() +{ +} + +Codec* CodecFactory::Create(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel) +{ + Codec* pCodec = nullptr; + switch (eCodecType) + { + case CODEC_NEBULA: + case CODEC_PROTO: + case CODEC_NEBULA_IN_NODE: + pCodec = new CodecProto(pLogger, eCodecType, pBindChannel); + break; + case CODEC_HTTP: + pCodec = new CodecHttp(pLogger, eCodecType, pBindChannel); + break; + case CODEC_HTTP2: + pCodec = new CodecHttp2(pLogger, eCodecType, pBindChannel); + break; + case CODEC_RESP: + pCodec = new CodecResp(pLogger, eCodecType, pBindChannel); + break; + case CODEC_PRIVATE: + pCodec = new CodecPrivate(pLogger, eCodecType, pBindChannel); + break; + case CODEC_CASS: + pCodec = new CodecCass(pLogger, eCodecType, pBindChannel); + break; + case CODEC_UNKNOW: + break; + default: + //LOG4_ERROR("no codec defined for code type %d", eCodecType); + break; + } + return(pCodec); +} + +E_CODEC_STATUS CodecFactory::OnEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel) +{ + uint32 uiLastCodecPos = 0; + E_CODEC_TYPE eOriginCodecType = pChannel->GetCodecType(); + E_CODEC_STATUS eCodecStatus = OnEvent(pDispatcher, pChannel, CODEC_STATUS_OK); + while (CODEC_STATUS_INVALID == eCodecStatus) + { + if (!AutoSwitchCodec(pDispatcher, pChannel, eOriginCodecType, uiLastCodecPos)) + { + eCodecStatus = CODEC_STATUS_ERR; + break; + } + eCodecStatus = OnEvent(pDispatcher, pChannel, CODEC_STATUS_INVALID); + } + return(eCodecStatus); +} + +E_CODEC_STATUS CodecFactory::OnEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, E_CODEC_STATUS eLastCodecStatus) +{ + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_ERR; + int iStart = 0; + if (CODEC_STATUS_INVALID == eLastCodecStatus) + { + iStart = 1; + } + switch (pChannel->GetCodecType()) + { + case CODEC_PROTO: + case CODEC_NEBULA: + case CODEC_NEBULA_IN_NODE: + eCodecStatus = OnNebulaEvent(pDispatcher, pChannel, iStart); + break; + case CODEC_HTTP: + case CODEC_HTTP2: + eCodecStatus = OnHttpEvent(pDispatcher, pChannel, iStart); + break; + case CODEC_RESP: + eCodecStatus = OnRedisEvent(pDispatcher, pChannel, iStart); + break; + case CODEC_CASS: + eCodecStatus = OnCassEvent(pDispatcher, pChannel, iStart); + break; + case CODEC_PRIVATE: + break; + case CODEC_UNKNOW: + break; + default: + ; + } + return(eCodecStatus); +} + +bool CodecFactory::OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +{ + MsgHead oMsgHead; + oMsgHead.set_cmd(iCmd); + oMsgHead.set_seq(uiSeq); + oMsgHead.set_len(oMsgBody.ByteSize()); + pChannel->SetStepSeq(uiStepSeq); + return(IO::OnRequest(pDispatcher, std::static_pointer_cast(pChannel), iCmd, oMsgHead, oMsgBody)); +} + +bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +{ + MsgHead oMsgHead; + oMsgHead.set_cmd(iCmd); + oMsgHead.set_seq(uiSeq); + oMsgHead.set_len(oMsgBody.ByteSize()); + return(IO::OnResponse(pDispatcher, std::static_pointer_cast(pChannel), uiSeq, oMsgHead, oMsgBody)); +} + +bool CodecFactory::OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +{ + pChannel->SetStepSeq(uiStepSeq); + return(IO::OnRequest(pDispatcher, std::static_pointer_cast(pChannel), oHttpMsg.path(), oHttpMsg)); +} + +bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const HttpMsg& oHttpMsg) +{ + return(IO::OnResponse(pDispatcher, std::static_pointer_cast(pChannel), pChannel->GetStepSeq(), oHttpMsg)); +} + +bool CodecFactory::OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const RedisMsg& oRedisMsg) +{ + pChannel->SetStepSeq(uiStepSeq); + return(IO::OnRequest(pDispatcher, std::static_pointer_cast(pChannel), (int32)CMD_REQ_REDIS_PROXY, oRedisMsg)); +} + +bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const RedisMsg& oRedisMsg) +{ + return(IO::OnResponse(pDispatcher, std::static_pointer_cast(pChannel), pChannel->GetStepSeq(), oRedisMsg)); +} + +bool CodecFactory::OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const char* pRaw, uint32 uiRawSize) +{ + pChannel->SetStepSeq(uiStepSeq); + return(IO::OnRequest(pDispatcher, std::static_pointer_cast(pChannel), (int32)CMD_REQ_RAW_DATA, pRaw, uiRawSize)); +} + +bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const char* pRaw, uint32 uiRawSize) +{ + return(IO::OnResponse(pDispatcher, std::static_pointer_cast(pChannel), pChannel->GetStepSeq(), pRaw, uiRawSize)); +} + +bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const CassMessage& oCassMsg) +{ + return(IO::OnResponse(pDispatcher, std::static_pointer_cast(pChannel), pChannel->GetStepSeq(), oCassMsg)); +} + +E_CODEC_STATUS CodecFactory::OnNebulaEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart) +{ + E_CODEC_STATUS eCodecStatus; + int i = iStart; + for (; ; ++i) + { + MsgHead oMsgHead; + MsgBody oMsgBody; + if (0 == i) + { + if (CODEC_PROTO == pChannel->GetCodecType()) + { + eCodecStatus = IO::Recv(pDispatcher, pChannel, oMsgHead, oMsgBody); + } + else if (CODEC_NEBULA == pChannel->GetCodecType()) + { + eCodecStatus = IO::Recv(pDispatcher, pChannel, oMsgHead, oMsgBody); + } + else + { + eCodecStatus = IO::Recv(pDispatcher, pChannel, oMsgHead, oMsgBody); + } + } + else + { + if (CODEC_PROTO == pChannel->GetCodecType()) + { + eCodecStatus = IO::Fetch(pDispatcher, pChannel, oMsgHead, oMsgBody); + } + else if (CODEC_NEBULA == pChannel->GetCodecType()) + { + eCodecStatus = IO::Fetch(pDispatcher, pChannel, oMsgHead, oMsgBody); + } + else + { + eCodecStatus = IO::Fetch(pDispatcher, pChannel, oMsgHead, oMsgBody); + } + } + + if (CODEC_STATUS_OK == eCodecStatus) + { + IO::OnMessage(pDispatcher, pChannel, oMsgHead, oMsgBody); + } + else + { + break; + } + } + return(eCodecStatus); +} + +E_CODEC_STATUS CodecFactory::OnHttpEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart) +{ + E_CODEC_STATUS eCodecStatus; + int i = iStart; + for (; ; ++i) + { + HttpMsg oHttpMsg; + if (0 == i) + { + if (CODEC_HTTP == pChannel->GetCodecType()) + { + eCodecStatus = IO::Recv(pDispatcher, pChannel, oHttpMsg); + } + else + { + eCodecStatus = IO::Recv(pDispatcher, pChannel, oHttpMsg); + } + } + else + { + if (CODEC_HTTP == pChannel->GetCodecType()) + { + eCodecStatus = IO::Fetch(pDispatcher, pChannel, oHttpMsg); + } + else + { + eCodecStatus = IO::Fetch(pDispatcher, pChannel, oHttpMsg); + } + } + + if (CODEC_STATUS_OK == eCodecStatus + || CODEC_STATUS_PART_OK == eCodecStatus) + { + if (oHttpMsg.http_major() > 1) + { + if (oHttpMsg.stream_id() > 0) + { + if (HTTP_REQUEST == oHttpMsg.type()) + { + bool bRes = IO::OnRequest(pDispatcher, pChannel, oHttpMsg.path(), oHttpMsg); + if (!bRes) + { + HttpMsg oOutHttpMsg; + oOutHttpMsg.set_type(HTTP_RESPONSE); + oOutHttpMsg.set_status_code(404); + oOutHttpMsg.set_http_major(oHttpMsg.http_major()); + oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); + IO::SendRequest(pDispatcher, 0, pChannel, oOutHttpMsg); + } + } + else + { + IO::OnResponse(pDispatcher, pChannel, oHttpMsg.stream_id(), eCodecStatus, oHttpMsg); + } + } + } + else + { + if (HTTP_REQUEST == oHttpMsg.type()) + { + bool bRes = IO::OnRequest(pDispatcher, pChannel, oHttpMsg.path(), oHttpMsg); + if (!bRes) + { + HttpMsg oOutHttpMsg; + oOutHttpMsg.set_type(HTTP_RESPONSE); + oOutHttpMsg.set_status_code(404); + oOutHttpMsg.set_http_major(oHttpMsg.http_major()); + oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); + IO::SendRequest(pDispatcher, 0, pChannel, oOutHttpMsg); + } + } + else + { + IO::OnResponse(pDispatcher, pChannel, oHttpMsg.stream_id(), eCodecStatus, oHttpMsg); + } + } + } + else if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close + { + IO::OnResponse(pDispatcher, pChannel, oHttpMsg.stream_id(), eCodecStatus, oHttpMsg); + } + else + { + break; + } + } + return(eCodecStatus); +} + +E_CODEC_STATUS CodecFactory::OnRedisEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart) +{ + E_CODEC_STATUS eCodecStatus; + int i = iStart; + for (; ; ++i) + { + RedisMsg oRedisMsg; + if (0 == i) + { + eCodecStatus = IO::Recv(pDispatcher, pChannel, oRedisMsg); + } + else + { + eCodecStatus = IO::Fetch(pDispatcher, pChannel, oRedisMsg); + } + + if (CODEC_STATUS_OK == eCodecStatus) + { + if (pChannel->IsClient()) + { + IO::OnResponse(pDispatcher, pChannel, 0, eCodecStatus, oRedisMsg); + } + else + { + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_REDIS_PROXY, oRedisMsg); + } + } + else + { + break; + } + } + return(eCodecStatus); +} + +E_CODEC_STATUS CodecFactory::OnCassEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart) +{ + E_CODEC_STATUS eCodecStatus; + int i = iStart; + for (; ; ++i) + { + CassResponse oCassResponse; + if (0 == i) + { + eCodecStatus = IO::Recv(pDispatcher, pChannel, oCassResponse); + } + else + { + eCodecStatus = IO::Fetch(pDispatcher, pChannel, oCassResponse); + } + + if (CODEC_STATUS_OK == eCodecStatus) + { + if (CASS_OP_RESULT != oCassResponse.GetOpcode() && CASS_OP_ERROR != oCassResponse.GetOpcode()) + { + continue; + } + IO::OnResponse(pDispatcher, pChannel, oCassResponse.GetStreamId(), eCodecStatus, oCassResponse); + } + else + { + break; + } + } + return(eCodecStatus); +} + +bool CodecFactory::AutoSwitchCodec(Dispatcher* pDispatcher, + std::shared_ptr pChannel, E_CODEC_TYPE eOriginCodecType, uint32& uiLastCodecPos) +{ + for (uint32 i = uiLastCodecPos + 1; i < s_vecAutoSwitchCodec.size(); ++i) + { + if (eOriginCodecType == s_vecAutoSwitchCodec[i]) + { + continue; + } + + Codec* pCodec = Create(pDispatcher->GetLogger(), s_vecAutoSwitchCodec[i], pChannel); + if (pCodec == nullptr) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "failed to new codec with codec type %d", s_vecAutoSwitchCodec[i]); + continue; + } + uiLastCodecPos = i; + std::static_pointer_cast>(pChannel->m_pImpl)->SetCodec(pCodec); + return(true); + } + return(false); +} + +} /* namespace neb */ + diff --git a/src/codec/CodecFactory.hpp b/src/codec/CodecFactory.hpp new file mode 100644 index 00000000..ff2fe1e8 --- /dev/null +++ b/src/codec/CodecFactory.hpp @@ -0,0 +1,142 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecFactory.hpp + * @brief + * @author Bwar + * @date: 2021-10-02 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_CODECFACTORY_HPP_ +#define SRC_CODEC_CODECFACTORY_HPP_ + +#include "codec/Codec.hpp" +#include "codec/CodecProto.hpp" +#include "codec/CodecHttp.hpp" +#include "codec/CodecPrivate.hpp" +#include "codec/CodecResp.hpp" +#include "codec/CodecRaw.hpp" +#include "codec/http2/CodecHttp2.hpp" +#include "codec/cass/CodecCass.hpp" +#include "codec/cass/CassResponse.hpp" + +#include "actor/cmd/Cmd.hpp" +#include "actor/cmd/Module.hpp" +#include "actor/cmd/RedisCmd.hpp" +#include "actor/cmd/RawCmd.hpp" +#include "actor/step/PbStep.hpp" +#include "actor/step/HttpStep.hpp" +#include "actor/step/RedisStep.hpp" +#include "actor/step/RawStep.hpp" +#include "actor/step/CassStep.hpp" + +namespace neb +{ + +class Dispatcher; +class SocketChannel; +class SelfChannel; + +class CodecFactory +{ +public: + CodecFactory(); + virtual ~CodecFactory(); + + static Codec* Create(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel); + + static E_CODEC_STATUS OnEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel); + + static bool OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + static bool OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + + static bool OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + static bool OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const HttpMsg& oHttpMsg); + + static bool OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const RedisMsg& oRedisMsg); + static bool OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const RedisMsg& oRedisMsg); + + static bool OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const char* pRaw, uint32 uiRawSize); + static bool OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const char* pRaw, uint32 uiRawSize); + + //static bool OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const CassMessage& oCassMsg); + static bool OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const CassMessage& oCassMsg); + +protected: + static E_CODEC_STATUS OnEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, E_CODEC_STATUS eLastCodecStatus); + static E_CODEC_STATUS OnNebulaEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart); + static E_CODEC_STATUS OnHttpEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart); + static E_CODEC_STATUS OnRedisEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart); + static E_CODEC_STATUS OnCassEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart); + static bool AutoSwitchCodec(Dispatcher* pDispatcher, std::shared_ptr pChannel, E_CODEC_TYPE eOriginCodecType, uint32& uiLastCodecPos); + +// template +// static bool Recv(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args); +// +// template +// static bool Fetch(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args); + +private: + static std::vector s_vecAutoSwitchCodec; +}; + +/* +template +bool CodecFactory::Recv(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args) +{ + switch (pChannel->GetCodecType()) + { + case CODEC_PROTO: + return(IO::Recv(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_NEBULA: + return(IO::Recv(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_NEBULA_IN_NODE: + return(IO::Recv(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_HTTP: + return(IO::Recv(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_HTTP2: + return(IO::Recv(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_RESP: + return(IO::Recv(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_PRIVATE: + return(IO::Recv(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_CASS: + return(IO::Recv(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_UNKNOW: + default: + return(false); + } +} + +template +bool CodecFactory::Fetch(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args) +{ + switch (pChannel->GetCodecType()) + { + case CODEC_PROTO: + return(IO::Fetch(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_NEBULA: + return(IO::Fetch(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_NEBULA_IN_NODE: + return(IO::Fetch(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_HTTP: + return(IO::Fetch(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_HTTP2: + return(IO::Fetch(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_RESP: + return(IO::Fetch(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_PRIVATE: + return(IO::Fetch(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_CASS: + return(IO::Fetch(pDispatcher, pChannel, std::forward(args)...)); + case CODEC_UNKNOW: + default: + return(false); + } +} +*/ + +} /* namespace neb */ + +#endif /* SRC_CODEC_CODECFACTORY_HPP_ */ + diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index f33445e9..f8e3a373 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -10,6 +10,7 @@ #include #include "util/StringCoder.hpp" #include "logger/NetLogger.hpp" +#include "channel/SocketChannel.hpp" #include "CodecHttp.hpp" #define STATUS_CODE(code, str) case code: return str; @@ -78,9 +79,10 @@ static const char * status_string(int code) namespace neb { -CodecHttp::CodecHttp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive) - : Codec(pLogger, eCodecType), - m_bChannelIsClient(false), m_bIsDecoding(false), m_uiEncodedNum(0), m_uiDecodedNum(0), +CodecHttp::CodecHttp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel, ev_tstamp dKeepAlive) + : Codec(pLogger, eCodecType, pBindChannel), + m_bIsDecoding(false), m_iHttpMajor(1), m_iHttpMinor(1), m_dKeepAlive(dKeepAlive) { } @@ -89,71 +91,15 @@ CodecHttp::~CodecHttp() { } -E_CODEC_STATUS CodecHttp::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) +E_CODEC_STATUS CodecHttp::Encode(CBuffer* pBuff) { - LOG4_TRACE(" "); - ++m_uiEncodedNum; - if (m_uiEncodedNum > m_uiDecodedNum) - { - m_bChannelIsClient = true; - } - HttpMsg oHttpMsg; - if (oHttpMsg.ParseFromString(oMsgBody.data())) - { - oHttpMsg.mutable_headers()->insert(google::protobuf::MapPair("x-cmd", std::to_string(oMsgHead.cmd()))); - oHttpMsg.mutable_headers()->insert(google::protobuf::MapPair("x-seq", std::to_string(oMsgHead.seq()))); - return(Encode(oHttpMsg, pBuff)); - } - else - { - LOG4_ERROR("oHttpMsg.ParseFromString() error!"); - return(CODEC_STATUS_ERR); - } -} - -E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) -{ - LOG4_TRACE(" "); - if (pBuff->ReadableBytes() == 0) - { - LOG4_DEBUG("no data..."); - return(CODEC_STATUS_PAUSE); - } - ++m_uiDecodedNum; - HttpMsg oHttpMsg; - E_CODEC_STATUS eCodecStatus = Decode(pBuff, oHttpMsg); - if (CODEC_STATUS_OK == eCodecStatus) - { - oMsgBody.set_data(oHttpMsg.body()); - oMsgHead.set_len(oMsgBody.ByteSize()); - for (auto it = oHttpMsg.headers().begin(); it != oHttpMsg.headers().end(); ++it) - { - if (std::string("x-cmd") == it->first - || std::string("x-CMD") == it->first - || std::string("x-Cmd") == it->first) - { - oMsgHead.set_cmd(atoi(it->second.c_str())); - } - else if (std::string("x-seq") == it->first - || std::string("x-SEQ") == it->first - || std::string("x-Seq") == it->first) - { - oMsgHead.set_seq(strtoul(it->second.c_str(), NULL, 10)); - } - } - } - return(eCodecStatus); + return(CODEC_STATUS_OK); } E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { LOG4_TRACE("pBuff->ReadableBytes() = %u, ReadIndex = %u, WriteIndex = %u", pBuff->ReadableBytes(), pBuff->GetReadIndex(), pBuff->GetWriteIndex()); - ++m_uiEncodedNum; - if (m_uiEncodedNum > m_uiDecodedNum) - { - m_bChannelIsClient = true; - } int iWriteSize = 0; int iHadEncodedSize = 0; @@ -264,7 +210,7 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { iHadEncodedSize += iWriteSize; } - if (!m_bChannelIsClient) + if (GetBindChannel()->IsClient()) { if (m_dKeepAlive == 0) { @@ -551,7 +497,6 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) { return(CODEC_STATUS_PAUSE); } - ++m_uiDecodedNum; m_oParsingHttpMsg.Clear(); m_parser_setting.on_message_begin = OnMessageBegin; m_parser_setting.on_url = OnUrl; @@ -606,7 +551,7 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) } } } - if (!m_bChannelIsClient && !http_should_keep_alive(&m_parser)) + if (!GetBindChannel()->IsClient() && !http_should_keep_alive(&m_parser)) { m_oParsingHttpMsg.set_keep_alive(0.0); m_dKeepAlive = 0.0; @@ -620,6 +565,12 @@ E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) return(CODEC_STATUS_ERR); } +E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + void CodecHttp::AddHttpHeader(const std::string& strHeaderName, const std::string& strHeaderValue) { m_mapAddingHttpHeader.insert(std::pair(strHeaderName, strHeaderValue)); @@ -647,7 +598,7 @@ const std::string& CodecHttp::ToString(const HttpMsg& oHttpMsg) if (oHttpMsg.status_code() > 0) { char tmp[10]; - sprintf(tmp, " %u\r\n", oHttpMsg.status_code()); + snprintf(tmp, 10, " %d\r\n", oHttpMsg.status_code()); m_strHttpString += tmp; } } @@ -670,7 +621,7 @@ const std::string& CodecHttp::ToString(const HttpMsg& oHttpMsg) bool CodecHttp::CloseRightAway() const { - if (m_bChannelIsClient) + if (GetBindChannel()->IsClient()) { if (m_dKeepAlive == 0.0) { diff --git a/src/codec/CodecHttp.hpp b/src/codec/CodecHttp.hpp index 24693c6c..226ea52d 100644 --- a/src/codec/CodecHttp.hpp +++ b/src/codec/CodecHttp.hpp @@ -20,14 +20,19 @@ namespace neb class CodecHttp: public Codec { public: - CodecHttp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, ev_tstamp dKeepAlive = -1.0); + CodecHttp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel, ev_tstamp dKeepAlive = -1.0); virtual ~CodecHttp(); - virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); - virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); + static E_CODEC_TYPE Type() + { + return(CODEC_HTTP); + } - virtual E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff); - virtual E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg); + E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff); + E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg); + E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff); /** * @brief 添加http头 @@ -63,10 +68,7 @@ class CodecHttp: public Codec } private: - bool m_bChannelIsClient; // 当前编解码器所在channel是作为http客户端还是作为http服务端 bool m_bIsDecoding; // 是否編解碼完成 - uint32 m_uiEncodedNum; - uint32 m_uiDecodedNum; int32 m_iHttpMajor; int32 m_iHttpMinor; ev_tstamp m_dKeepAlive; diff --git a/src/codec/CodecPrivate.cpp b/src/codec/CodecPrivate.cpp index 581571ef..4c30c454 100644 --- a/src/codec/CodecPrivate.cpp +++ b/src/codec/CodecPrivate.cpp @@ -15,8 +15,9 @@ namespace neb { -CodecPrivate::CodecPrivate(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) - : Codec(pLogger, eCodecType) +CodecPrivate::CodecPrivate(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel) + : Codec(pLogger, eCodecType, pBindChannel) { } @@ -24,15 +25,20 @@ CodecPrivate::~CodecPrivate() { } +E_CODEC_STATUS CodecPrivate::Encode(CBuffer* pBuff) +{ + return(CODEC_STATUS_OK); +} + E_CODEC_STATUS CodecPrivate::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) { LOG4_TRACE(" "); tagMsgHead stMsgHead; stMsgHead.version = 1; // version暂时无用 stMsgHead.encript = (unsigned char)(oMsgHead.cmd() >> 24); - stMsgHead.cmd = htons((unsigned short)(gc_uiCmdBit & oMsgHead.cmd())); - stMsgHead.body_len = htonl((unsigned int)oMsgHead.len()); - stMsgHead.seq = htonl(oMsgHead.seq()); + stMsgHead.cmd = CodecUtil::H2N((unsigned short)(gc_uiCmdBit & oMsgHead.cmd())); + stMsgHead.body_len = CodecUtil::H2N((unsigned int)oMsgHead.len()); + stMsgHead.seq = CodecUtil::H2N(oMsgHead.seq()); //stMsgHead.checksum = htons((unsigned short)stMsgHead.checksum); if (oMsgBody.ByteSize() > 1000000) // pb 最大限制 { @@ -123,7 +129,7 @@ E_CODEC_STATUS CodecPrivate::Encode(const MsgHead& oMsgHead, const MsgBody& oMsg if (strEncryptData.size() > 0) // 加密后的数据包 { - stMsgHead.body_len = htonl((unsigned int)strEncryptData.size()); + stMsgHead.body_len = CodecUtil::H2N((unsigned int)strEncryptData.size()); iWriteLen = pBuff->Write(&stMsgHead, iNeedWriteLen); LOG4_TRACE("sizeof(stClientMsgHead) = %d, iWriteLen = %d", sizeof(stMsgHead), iWriteLen); if (iWriteLen != iNeedWriteLen) @@ -144,7 +150,7 @@ E_CODEC_STATUS CodecPrivate::Encode(const MsgHead& oMsgHead, const MsgBody& oMsg } else if (strCompressData.size() > 0) // 压缩后的数据包 { - stMsgHead.body_len = htonl((unsigned int)strCompressData.size()); + stMsgHead.body_len = CodecUtil::H2N((unsigned int)strCompressData.size()); iWriteLen = pBuff->Write(&stMsgHead, iNeedWriteLen); LOG4_TRACE("sizeof(stClientMsgHead) = %d, iWriteLen = %d", sizeof(stMsgHead), iWriteLen); if (iWriteLen != iNeedWriteLen) @@ -198,10 +204,10 @@ E_CODEC_STATUS CodecPrivate::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& tagMsgHead stMsgHead; int iReadIdx = pBuff->GetReadIndex(); pBuff->Read(&stMsgHead, uiHeadSize); - stMsgHead.cmd = ntohs(stMsgHead.cmd); - stMsgHead.body_len = ntohl(stMsgHead.body_len); - stMsgHead.seq = ntohl(stMsgHead.seq); - stMsgHead.checksum = ntohs(stMsgHead.checksum); + stMsgHead.cmd = CodecUtil::N2H(stMsgHead.cmd); + stMsgHead.body_len = CodecUtil::N2H(stMsgHead.body_len); + stMsgHead.seq = CodecUtil::N2H(stMsgHead.seq); + stMsgHead.checksum = CodecUtil::N2H(stMsgHead.checksum); LOG4_TRACE("cmd %u, seq %u, len %u, pBuff->ReadableBytes() %u", stMsgHead.cmd, stMsgHead.seq, stMsgHead.body_len, pBuff->ReadableBytes()); @@ -314,4 +320,10 @@ E_CODEC_STATUS CodecPrivate::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& } } +E_CODEC_STATUS CodecPrivate::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + } /* namespace neb */ diff --git a/src/codec/CodecPrivate.hpp b/src/codec/CodecPrivate.hpp index 37b5092d..d3bbaaad 100644 --- a/src/codec/CodecPrivate.hpp +++ b/src/codec/CodecPrivate.hpp @@ -18,11 +18,19 @@ namespace neb class CodecPrivate: public Codec { public: - CodecPrivate(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + CodecPrivate(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel); virtual ~CodecPrivate(); - virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); - virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); + static E_CODEC_TYPE Type() + { + return(CODEC_PRIVATE); + } + + E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); + E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); + E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff); }; } /* namespace neb */ diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp index 0e5f9126..45b9f44e 100644 --- a/src/codec/CodecProto.cpp +++ b/src/codec/CodecProto.cpp @@ -8,14 +8,17 @@ * Modify history: ******************************************************************************/ -#include "logger/NetLogger.hpp" #include "CodecProto.hpp" +#include "logger/NetLogger.hpp" +#include "channel/SocketChannel.hpp" +#include "channel/SocketChannelImpl.hpp" namespace neb { -CodecProto::CodecProto(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) - : Codec(pLogger, eCodecType) +CodecProto::CodecProto(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel) + : Codec(pLogger, eCodecType, pBindChannel) { } @@ -23,13 +26,22 @@ CodecProto::~CodecProto() { } -E_CODEC_STATUS CodecProto::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) +E_CODEC_STATUS CodecProto::Encode(CBuffer* pBuff) +{ + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecProto::Encode(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, CBuffer* pBuff) { LOG4_TRACE("pBuff->ReadableBytes()=%u, oMsgHead.ByteSize() = %d", pBuff->ReadableBytes(), oMsgHead.ByteSize()); int iHadWriteLen = 0; int iWriteLen = 0; int iNeedWriteLen = gc_uiMsgHeadSize; std::string strTmpData; + MsgHead oMsgHead; + oMsgHead.set_cmd(iCmd); + oMsgHead.set_seq(uiSeq); + oMsgHead.set_len(oMsgBody.ByteSize()); oMsgHead.SerializeToString(&strTmpData); iWriteLen = pBuff->Write(strTmpData.c_str(), gc_uiMsgHeadSize); if (iWriteLen != iNeedWriteLen) @@ -72,7 +84,7 @@ E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oM if (oMsgHead.len() <= 0) // 无包体(心跳包等),nebula在proto3的使用上以-1表示包体长度为0 { pBuff->SkipBytes(gc_uiMsgHeadSize); - return(CODEC_STATUS_OK); + return(ChannelSticky(oMsgHead, oMsgBody)); } if (pBuff->ReadableBytes() >= gc_uiMsgHeadSize + oMsgHead.len()) { @@ -82,7 +94,7 @@ E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oM if (bResult) { pBuff->SkipBytes(gc_uiMsgHeadSize + oMsgHead.len()); - return(CODEC_STATUS_OK); + return(ChannelSticky(oMsgHead, oMsgBody)); } else { @@ -107,4 +119,65 @@ E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oM } } +E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + +E_CODEC_STATUS CodecProto::ChannelSticky(MsgHead& oMsgHead, MsgBody& oMsgBody) +{ + switch (GetBindChannel()->GetChannelStatus()) + { + case CHANNEL_STATUS_ESTABLISHED: + if ((gc_uiCmdReq & oMsgHead.cmd()) && (GetBindChannel()->GetClientData().size() > 0)) + { + m_uiForeignSeq = oMsgHead.seq(); + oMsgBody.set_add_on(GetBindChannel()->GetClientData()); + } + break; + case CHANNEL_STATUS_CLOSED: + case CHANNEL_STATUS_BROKEN: + LOG4_WARNING("%s channel_fd[%d], channel_status[%d] remote %s EOF.", + GetBindChannel()->GetIdentify().c_str(), GetBindChannel()->GetFd(), + (int)GetBindChannel()->GetChannelStatus(), GetBindChannel()->GetRemoteAddr().c_str()); + return(CODEC_STATUS_ERR); + case CHANNEL_STATUS_TELL_WORKER: + case CHANNEL_STATUS_WORKER: + case CHANNEL_STATUS_TRANSFER_TO_WORKER: + case CHANNEL_STATUS_CONNECTED: + case CHANNEL_STATUS_TRY_CONNECT: + case CHANNEL_STATUS_INIT: + { + auto pChannelImpl = std::static_pointer_cast>(GetBindChannel()->m_pImpl); + switch (oMsgHead.cmd()) + { + case CMD_RSP_TELL_WORKER: + pChannelImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + break; + case CMD_REQ_TELL_WORKER: + pChannelImpl->SetChannelStatus(CHANNEL_STATUS_TELL_WORKER); + break; + case CMD_RSP_CONNECT_TO_WORKER: + pChannelImpl->SetChannelStatus(CHANNEL_STATUS_WORKER); + break; + case CMD_REQ_CONNECT_TO_WORKER: + pChannelImpl->SetChannelStatus(CHANNEL_STATUS_TRANSFER_TO_WORKER); + break; + default: + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[from %d to %d] may be a fault.", + m_iFd, m_uiSeq, (int)m_ucChannelStatus, CHANNEL_STATUS_ESTABLISHED); + pChannelImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + break; + } + } + break; + default: + LOG4_ERROR("%s invalid connection status %d!", GetBindChannel()->GetIdentify().c_str(), + (int)GetBindChannel()->GetChannelStatus()); + return(CODEC_STATUS_ERR); + } + return(CODEC_STATUS_OK); +} + } /* namespace neb */ diff --git a/src/codec/CodecProto.hpp b/src/codec/CodecProto.hpp index 6e1ed51d..a8fd14f9 100644 --- a/src/codec/CodecProto.hpp +++ b/src/codec/CodecProto.hpp @@ -18,11 +18,61 @@ namespace neb class CodecProto: public Codec { public: - CodecProto(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + CodecProto(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel); virtual ~CodecProto(); - virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); - virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); + static E_CODEC_TYPE Type() + { + return(CODEC_PROTO); + } + + E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(int iCmd, uint32 uiSeq, const MsgBody& oMsgBody, CBuffer* pBuff); + E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); + E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff); + +protected: + E_CODEC_STATUS ChannelSticky(MsgHead& oMsgHead, MsgBody& oMsgBody); + +private: + uint32 m_uiForeignSeq = 0; +}; + +class CodecNebula: public CodecProto +{ +public: + CodecNebula(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel) + : CodecProto(pLogger, eCodecType, pBindChannel) + { + } + virtual ~CodecNebula() + { + } + + static E_CODEC_TYPE Type() + { + return(CODEC_NEBULA); + } +}; + +class CodecNebulaInNode: public CodecProto +{ +public: + CodecNebulaInNode(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel) + : CodecProto(pLogger, eCodecType, pBindChannel) + { + } + virtual ~CodecNebulaInNode() + { + } + + static E_CODEC_TYPE Type() + { + return(CODEC_NEBULA_IN_NODE); + } }; } /* namespace neb */ diff --git a/src/codec/CodecRaw.cpp b/src/codec/CodecRaw.cpp new file mode 100644 index 00000000..28c2465e --- /dev/null +++ b/src/codec/CodecRaw.cpp @@ -0,0 +1,55 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecRaw.cpp + * @brief + * @author Bwar + * @date: 2016年8月11日 + * @note + * Modify history: + ******************************************************************************/ +#include +#include "logger/NetLogger.hpp" +#include "CodecRaw.hpp" +#include "CodecDefine.hpp" + +namespace neb +{ + +CodecRaw::CodecRaw(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel) + : Codec(pLogger, eCodecType, pBindChannel) +{ +} + +CodecRaw::~CodecRaw() +{ +} + +E_CODEC_STATUS CodecRaw::Encode(CBuffer* pBuff) +{ + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecRaw::Encode(const char* pRaw, uint32 uiRawSize, CBuffer* pBuff) +{ + if (pRaw == nullptr) + { + return(CODEC_STATUS_ERR); + } + pBuff->Write(pRaw, uiRawSize); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecRaw::Decode(CBuffer* pBuff, CBuffer& oBuff) +{ + oBuff.Write(pBuff->GetRawReadBuffer(), pBuff->ReadableBytes()); + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecRaw::Decode(CBuffer* pBuff, CBuffer& oBuff, CBuffer* pReactBuff) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + +} /* namespace neb */ diff --git a/src/codec/CodecRaw.hpp b/src/codec/CodecRaw.hpp new file mode 100644 index 00000000..f66bee48 --- /dev/null +++ b/src/codec/CodecRaw.hpp @@ -0,0 +1,38 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecRaw.hpp + * @brief + * @author Bwar + * @date: 2016年8月11日 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_CODECRAW_HPP_ +#define SRC_CODEC_CODECRAW_HPP_ + +#include "Codec.hpp" + +namespace neb +{ + +class CodecRaw: public Codec +{ +public: + CodecRaw(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel); + virtual ~CodecRaw(); + + static E_CODEC_TYPE Type() + { + return(CODEC_RAW); + } + + E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(const char* pRaw, uint32 uiRawSize, CBuffer* pBuff); + E_CODEC_STATUS Decode(CBuffer* pBuff, CBuffer& oBuff); + E_CODEC_STATUS Decode(CBuffer* pBuff, CBuffer& oBuff, CBuffer* pReactBuff); +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_CODECRAW_HPP_ */ diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index f3a9335d..d738ed56 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -19,8 +19,9 @@ const char CodecResp::RESP_INTEGER = ':'; const char CodecResp::RESP_BULK_STRING = '$'; const char CodecResp::RESP_ARRAY = '*'; -CodecResp::CodecResp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) - : Codec(pLogger, eCodecType) +CodecResp::CodecResp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel) + : Codec(pLogger, eCodecType, pBindChannel) { } @@ -28,14 +29,9 @@ CodecResp::~CodecResp() { } -E_CODEC_STATUS CodecResp::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) +E_CODEC_STATUS CodecResp::Encode(CBuffer* pBuff) { - return(CODEC_STATUS_INVALID); -} - -E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) -{ - return(CODEC_STATUS_INVALID); + return(CODEC_STATUS_OK); } E_CODEC_STATUS CodecResp::Encode(const RedisReply& oReply, CBuffer* pBuff) @@ -471,5 +467,11 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) return(CODEC_STATUS_PAUSE); } +E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply, CBuffer* pReactBuff) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + } diff --git a/src/codec/CodecResp.hpp b/src/codec/CodecResp.hpp index ad78917a..cb92839f 100644 --- a/src/codec/CodecResp.hpp +++ b/src/codec/CodecResp.hpp @@ -21,14 +21,19 @@ class RedisStep; class CodecResp: public neb::Codec { public: - CodecResp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + CodecResp(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel); virtual ~CodecResp(); - virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); - virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); + static E_CODEC_TYPE Type() + { + return(CODEC_RESP); + } - virtual E_CODEC_STATUS Encode(const RedisReply& oReply, CBuffer* pBuff); - virtual E_CODEC_STATUS Decode(CBuffer* pBuff, RedisReply& oReply); + E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(const RedisReply& oReply, CBuffer* pBuff); + E_CODEC_STATUS Decode(CBuffer* pBuff, RedisReply& oReply); + E_CODEC_STATUS Decode(CBuffer* pBuff, RedisReply& oReply, CBuffer* pReactBuff); protected: E_CODEC_STATUS EncodeSimpleString(const RedisReply& oReply, CBuffer* pBuff); diff --git a/src/codec/CodecWsExtentJson.cpp b/src/codec/CodecWsExtentJson.cpp index 29db6be5..0035fec2 100644 --- a/src/codec/CodecWsExtentJson.cpp +++ b/src/codec/CodecWsExtentJson.cpp @@ -14,8 +14,9 @@ namespace neb { -CodecWsExtentJson::CodecWsExtentJson(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) - : Codec(pLogger, eCodecType), +CodecWsExtentJson::CodecWsExtentJson(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel) + : Codec(pLogger, eCodecType, pBindChannel), uiBeatCmd(0), uiBeatSeq(0) { } @@ -24,6 +25,11 @@ CodecWsExtentJson::~CodecWsExtentJson() { } +E_CODEC_STATUS CodecWsExtentJson::Encode(CBuffer* pBuff) +{ + return(CODEC_STATUS_OK); +} + E_CODEC_STATUS CodecWsExtentJson::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) { @@ -424,4 +430,10 @@ E_CODEC_STATUS CodecWsExtentJson::Decode(CBuffer* pBuff, } } +E_CODEC_STATUS CodecWsExtentJson::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + } /* namespace neb */ diff --git a/src/codec/CodecWsExtentJson.hpp b/src/codec/CodecWsExtentJson.hpp index ea938f0e..9e4cbef1 100644 --- a/src/codec/CodecWsExtentJson.hpp +++ b/src/codec/CodecWsExtentJson.hpp @@ -20,11 +20,19 @@ namespace neb class CodecWsExtentJson: public Codec { public: - CodecWsExtentJson(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + CodecWsExtentJson(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel); virtual ~CodecWsExtentJson(); - virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); - virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); + static E_CODEC_TYPE Type() + { + return(CODEC_WS_EXTEND_JSON); + } + + E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); + E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); + E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff); private: uint32 uiBeatCmd; diff --git a/src/codec/CodecWsExtentPb.cpp b/src/codec/CodecWsExtentPb.cpp index 6139d1b9..816d4a92 100644 --- a/src/codec/CodecWsExtentPb.cpp +++ b/src/codec/CodecWsExtentPb.cpp @@ -15,8 +15,9 @@ namespace neb { -CodecWsExtentPb::CodecWsExtentPb(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) - : Codec(pLogger, eCodecType), +CodecWsExtentPb::CodecWsExtentPb(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel) + : Codec(pLogger, eCodecType, pBindChannel), uiBeatCmd(0), uiBeatSeq(0) { } @@ -25,6 +26,11 @@ CodecWsExtentPb::~CodecWsExtentPb() { } +E_CODEC_STATUS CodecWsExtentPb::Encode(CBuffer* pBuff) +{ + return(CODEC_STATUS_OK); +} + E_CODEC_STATUS CodecWsExtentPb::Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) { @@ -415,4 +421,10 @@ E_CODEC_STATUS CodecWsExtentPb::Decode(CBuffer* pBuff, } } +E_CODEC_STATUS CodecWsExtentPb::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + } /* namespace neb */ diff --git a/src/codec/CodecWsExtentPb.hpp b/src/codec/CodecWsExtentPb.hpp index 477b6c7e..856b5883 100644 --- a/src/codec/CodecWsExtentPb.hpp +++ b/src/codec/CodecWsExtentPb.hpp @@ -19,11 +19,19 @@ namespace neb class CodecWsExtentPb: public Codec { public: - CodecWsExtentPb(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); + CodecWsExtentPb(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel); virtual ~CodecWsExtentPb(); - virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); - virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); + static E_CODEC_TYPE Type() + { + return(CODEC_WS_EXTEND_PB); + } + + E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); + E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); + E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff); private: uint32 uiBeatCmd; diff --git a/src/codec/cass/CassComm.hpp b/src/codec/cass/CassComm.hpp new file mode 100644 index 00000000..f839a62f --- /dev/null +++ b/src/codec/cass/CassComm.hpp @@ -0,0 +1,183 @@ +/******************************************************************************* + * Project: Nebula + * @file CassComm.hpp + * @brief + * @author Bwar + * @date: 2021-11-27 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_CASSANDRA_CASSCOMM_HPP_ +#define SRC_CODEC_CASSANDRA_CASSCOMM_HPP_ + +#include "Definition.hpp" + +namespace neb +{ + +/** + * @brief request or response + * @note a single byte that indicates both the direction of the message + * (request or response) and the version of the protocol in use. + */ +enum E_CASS_STREAM_TYPE +{ + CASS_MSG_REQUEST = 0x00, + CASS_MSG_RESPONSE = 0x80, +}; + +/** + * @brief + * @note The version is a single byte that indicates both the direction of + * the message (request or response) and the version of the protocol in use. + * The most significant bit of version is used to define the direction of + * the message: 0 indicates a request, 1 indicates a response. + */ +enum E_CASS_VERSION +{ + CASS_VERSION_BITS = 0x7F, +}; + +/** + * @brief flags + * @note Flags applying to this frame. The flags have the following + * meaning (described by the mask that allows selecting them): + */ +enum E_CASS_FLAGS +{ + /** + * @note Compression flag. If set, the frame body is compressed. The actual + * compression to use should have been set up beforehand through the + * Startup message (which thus cannot be compressed; Section 4.1.1). + */ + CASS_FLAG_COMPRESSION = 0x01, + /** + * @note Tracing flag. For a request frame, this indicates the client requires + * tracing of the request. Note that only QUERY, PREPARE and EXECUTE queries + * support tracing. Other requests will simply ignore the tracing flag if + * set. If a request supports tracing and the tracing flag is set, the response + * to this request will have the tracing flag set and contain tracing information. + * If a response frame has the tracing flag set, its body contains + * a tracing ID. The tracing ID is a [uuid] and is the first thing in + * the frame body. + */ + CASS_FLAG_TRACING = 0x02, + /** + * @note Custom payload flag. For a request or response frame, this indicates + * that a generic key-value custom payload for a custom QueryHandler + * implementation is present in the frame. Such a custom payload is simply + * ignored by the default QueryHandler implementation. + * Currently, only QUERY, PREPARE, EXECUTE and BATCH requests support payload. + * Type of custom payload is [bytes map] (see below). If either or both + * of the tracing and warning flags are set, the custom payload will follow + * those indicated elements in the frame body. If neither are set, the custom + * payload will be the first value in the frame body. + */ + CASS_FLAG_CUSTOM_PAYLOAD = 0x04, + /** + * @note Warning flag. The response contains warnings which were generated + * by the server to go along with this response. + * If a response frame has the warning flag set, its body will contain the + * text of the warnings. The warnings are a [string list] and will be the + * first value in the frame body if the tracing flag is not set, or directly + * after the tracing ID if it is. + */ + CASS_FLAG_WARNING = 0x08, + + // The rest of flags is currently unused and ignored. +}; + +enum E_CASS_OP_CODE +{ + CASS_OP_ERROR = 0x00, ///< + CASS_OP_STARTUP = 0x01, + CASS_OP_READY = 0x02, + CASS_OP_AUTHENTICATE = 0x03, + CASS_OP_OPTIONS = 0x05, + CASS_OP_SUPPORTED = 0x06, + CASS_OP_QUERY = 0x07, + CASS_OP_RESULT = 0x08, + CASS_OP_PREPARE = 0x09, + CASS_OP_EXECUTE = 0x0A, + CASS_OP_REGISTER = 0x0B, + CASS_OP_EVENT = 0x0C, + CASS_OP_BATCH = 0x0D, + CASS_OP_AUTH_CHALLENGE = 0x0E, + CASS_OP_AUTH_RESPONSE = 0x0F, + CASS_OP_AUTH_SUCCESS = 0x10 +}; + +/** + * @brief cassandra error code + * @note an ERROR message is composed of [...] + * (see 4.2.1 for details). The supported error codes, as well as any additional + * information the message may contain after the are described below: + */ +enum E_CASS_ERROR_CODE +{ + CASS_ERR_SERVER = 0x0000, ///< something unexpected happened. This indicates a server-side bug. + CASS_ERR_PROTOCOL = 0x000A, ///< some client message triggered a protocol violation (for instance a QUERY message is sent before a STARTUP one has been sent) + CASS_ERR_AUTHENTICATION = 0x0100, ///< authentication was required and failed. The possible reason for failing depends on the authenticator in use, which may or may not include more detail in the accompanying error message. + CASS_ERR_UNAVAILABLE = 0x1000, ///< Unavailable exception. The rest of the ERROR message body will be + CASS_ERR_OVERLOADED = 0x1001, ///< the request cannot be processed because the coordinator node is overloaded + CASS_ERR_BOOTSTRAPPING = 0x1002, ///< the request was a read request but the coordinator node is bootstrapping + CASS_ERR_TRUNCATE = 0x1003, ///< error during a truncation error. + CASS_ERR_WRITE_TIMEOUT = 0x1100, ///< Timeout exception during a write request. The rest of the ERROR message body will be + CASS_ERR_READ_TIMEOUT = 0x1200, ///< Timeout exception during a read request. The rest of the ERROR message body will be + CASS_ERR_READ_FAILURE = 0x1300, ///< A non-timeout exception during a read request. The rest of the ERROR message body will be + CASS_ERR_FUNCTION_FAILURE = 0x1400, ///< A (user defined) function failed during execution. The rest of the ERROR message body will be + CASS_ERR_WRITE_FAILURE = 0x1500, ///< A non-timeout exception during a write request. The rest of the ERROR message body will be + CASS_ERR_SYNTAX = 0x2000, ///< The submitted query has a syntax error. + CASS_ERR_UNAUTHORIZED = 0x2100, ///< The logged user doesn't have the right to perform the query. + CASS_ERR_INVALID = 0x2200, ///< The query is syntactically correct but invalid. + CASS_ERR_CONFIG = 0x2300, ///< The query is invalid because of some configuration issue + CASS_ERR_ALREADY_EXIST = 0x2400, ///< The query attempted to create a keyspace or a table that was already existing. The rest of the ERROR message body will be + CASS_ERR_UNPREPARED = 0x2500, ///< Can be thrown while a prepared statement tries to be executed if the provided prepared statement ID is not known by this host. The rest of the ERROR message body will be [short bytes] representing the unknown ID. +}; + +/* + * @brief A consistency level specification. + */ +enum E_CASS_CONSISTENCY +{ + CASS_CONSIST_ANY = 0x0000, + CASS_CONSIST_ONE = 0x0001, + CASS_CONSIST_TWO = 0x0002, + CASS_CONSIST_THREE = 0x0003, + CASS_CONSIST_QUORUM = 0x0004, + CASS_CONSIST_ALL = 0x0005, + CASS_CONSIST_LOCAL_QUORUM = 0x0006, + CASS_CONSIST_EACH_QUORUM = 0x0007, + CASS_CONSIST_SERIAL = 0x0008, + CASS_CONSIST_LOCAL_SERIAL = 0x0009, + CASS_CONSIST_LOCAL_ONE = 0x000A, +}; + +/* + * @brief The CQL binary protocol is a frame based protocol. + * @note The protocol is big-endian (network byte order). Each frame contains + * a fixed size header (9 bytes) followed by a variable size. + body. + */ +struct CassFrameHead +{ + uint8 ucVersion = 0; ///< The version is a single byte that indicates both the direction of the message(request or response) and the version of the protocol in use. + uint8 ucFlags = 0; ///< Flags applying to this frame. The flags have the meaning: E_CASS_FLAGS + uint8 ucOpCode = 0; ///< An integer byte that distinguishes the actual message: E_CASS_OP_CODE + uint16 uiStream = 0; ///< A frame has a stream id (a [short] value). When sending request messages, this stream id must be set by the client to a non-negative value (negative stream id are reserved for streams initiated by the server; currently all EVENT messages(section 4.2.6) have a streamId of -1). If a client sends a request message with the stream id X, it is guaranteed that the stream id of the response to that message will be X. + uint32 uiLength = 0; ///< A 4 byte integer representing the length of the body of the frame (note: currently a frame is limited to 256MB in length). + CassFrameHead& operator=(const CassFrameHead& stFrameHead) + { + ucVersion = stFrameHead.ucVersion; + ucFlags = stFrameHead.ucFlags; + ucOpCode = stFrameHead.ucOpCode; + uiStream = stFrameHead.uiStream; + uiLength = stFrameHead.uiLength; + return(*this); + } +}; + +} + +#endif /* SRC_CODEC_CASSANDRA_CASSCOMM_HPP_ */ + diff --git a/src/codec/cass/CassMessage.cpp b/src/codec/cass/CassMessage.cpp new file mode 100644 index 00000000..b578c1db --- /dev/null +++ b/src/codec/cass/CassMessage.cpp @@ -0,0 +1,25 @@ +/******************************************************************************* + * Project: Nebula + * @file CassMessage.cpp + * @brief + * @author Bwar + * @date: 2021-11-28 + * @note + * Modify history: + ******************************************************************************/ +#include "CassMessage.hpp" + +namespace neb +{ + +CassMessage::CassMessage(uint16 uiMsgType) + : m_unMsgType(uiMsgType), m_unStreamId(0) +{ +} + +CassMessage::~CassMessage() +{ +} + +} /* namespace neb */ + diff --git a/src/codec/cass/CassMessage.hpp b/src/codec/cass/CassMessage.hpp new file mode 100644 index 00000000..d938d513 --- /dev/null +++ b/src/codec/cass/CassMessage.hpp @@ -0,0 +1,70 @@ +/******************************************************************************* + * Project: Nebula + * @file CassMessage.hpp + * @brief + * @author Bwar + * @date: 2021-11-27 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_CASSANDRA_CASSMESSAGE_HPP_ +#define SRC_CODEC_CASSANDRA_CASSMESSAGE_HPP_ + +#include "CassComm.hpp" +#include "type/Notations.hpp" + +namespace neb +{ + +class CodecCass; + +/** + * @note Dependant on the flags specified in the header, the layout of the message body must be: + * [][][] + * where: + * - is a UUID tracing ID, present if this is a request message and the Tracing flag is set. + * - is a string list of warnings (if this is a request message and the Warning flag is set. + * - is bytes map for the serialised custom payload present if this is one of the message types + * which support custom payloads (QUERY, PREPARE, EXECUTE and BATCH) and the Custom payload flag is set. + * - as defined below through sections 4 and 5. + */ +class CassMessage +{ +public: + CassMessage(uint16 uiMsgType = CASS_MSG_REQUEST); + CassMessage(const CassMessage&) = delete; + virtual ~CassMessage(); + + CassMessage& operator=(const CassMessage&) = delete; + + uint16 GetMsgType() const + { + return(m_unMsgType); + } + + uint16 GetStreamId() const + { + return(m_unStreamId); + } + + uint8 GetOpcode() const + { + return(m_ucOpcode); + } + + void SetOpcode(uint8 ucOpcode) + { + m_ucOpcode = ucOpcode; + } + +private: + friend class CodecCass; + uint8 m_ucOpcode; + uint16 m_unMsgType; + uint16 m_unStreamId; +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_CASSANDRA_CASSMESSAGE_HPP_ */ + diff --git a/src/codec/cass/CassRequest.cpp b/src/codec/cass/CassRequest.cpp new file mode 100644 index 00000000..ce7dd293 --- /dev/null +++ b/src/codec/cass/CassRequest.cpp @@ -0,0 +1,147 @@ +/******************************************************************************* + * Project: Nebula + * @file CassRequest.cpp + * @brief + * @author Bwar + * @date: 2021-11-28 + * @note + * Modify history: + ******************************************************************************/ +#include "CassRequest.hpp" +#include + +namespace neb +{ + +CassRequest::CassRequest() + : CassMessage(CASS_MSG_REQUEST) +{ +} + +CassRequest::~CassRequest() +{ +} + +void CassRequest::SetQuery(const std::string& strQuery) +{ + SetOpcode(CASS_OP_QUERY); + m_strQuery = strQuery; +} + +void CassRequest::SetQuery(const std::string& strQuery, QueryParameter& stQueryParam) +{ + SetOpcode(CASS_OP_QUERY); + m_strQuery = strQuery; + m_stQueryParam = std::move(stQueryParam); +} + +bool CassRequest::EncodeStarup(CBuffer* pBuff, const std::string& strCqlVersion, const std::string& strCompression) const +{ + std::unordered_map mapOption; + mapOption.insert(std::make_pair("CQL_VERSION", strCqlVersion)); + if (strCompression.size() > 0) + { + mapOption.insert(std::make_pair("COMPRESSION", strCompression)); + } + return(Notations::EncodeStringMap(mapOption, pBuff)); +} + +bool CassRequest::EncodeAuthResponse(const std::string& strToken, CBuffer* pBuff) const +{ + Bytes stValue; + stValue.Assign(strToken.data(), strToken.length()); + return(Notations::EncodeBytes(stValue, pBuff)); +} + +bool CassRequest::EncodeQuery(CBuffer* pBuff) const +{ + if (!Notations::EncodeLongString(m_strQuery, pBuff)) + { + return(false); + } + Notations::EncodeShort(m_stQueryParam.unConsistency, pBuff); + uint8 ucFlags = 0; + if (m_stQueryParam.vecValue.size() > 0) + { + ucFlags |= QUERY_FLAG_VALUES; + for (uint32 i = 0; i < m_stQueryParam.vecValue.size(); ++i) + { + if (m_stQueryParam.vecValue[i].first.size() > 0) + { + ucFlags |= QUERY_FLAG_WITH_NAMES_FOR_VALUE; + break; + } + } + } + if (m_stQueryParam.bSkipMetadata) + { + ucFlags |= QUERY_FLAG_SKIP_METADATA; + } + if (m_stQueryParam.iPageSize > 0) + { + ucFlags |= QUERY_FLAG_PAGE_SIZE; + } + if (m_stQueryParam.stPagingState.iLength > 0) + { + ucFlags |= QUERY_FLAG_WITH_PAGING_STATE; + } + if (m_stQueryParam.unSerialConsistency > 0) + { + ucFlags |= QUERY_FLAG_WITH_SERIAL_CONSISTENCY; + } + if (m_stQueryParam.llTimestamp > 0) + { + ucFlags |= QUERY_FLAG_WITH_TIMESTAMP; + } + Notations::EncodeByte(ucFlags, pBuff); + if (ucFlags & QUERY_FLAG_VALUES) + { + Notations::EncodeShort(m_stQueryParam.vecValue.size(), pBuff); + if (ucFlags & QUERY_FLAG_WITH_NAMES_FOR_VALUE) + { + for (uint32 i = 0; i < m_stQueryParam.vecValue.size(); ++i) + { + if (!Notations::EncodeString(m_stQueryParam.vecValue[i].first, pBuff) + || !Notations::EncodeValue(m_stQueryParam.vecValue[i].second, pBuff)) + { + return(false); + } + } + } + else + { + for (uint32 i = 0; i < m_stQueryParam.vecValue.size(); ++i) + { + if (!Notations::EncodeValue(m_stQueryParam.vecValue[i].second, pBuff)) + { + return(false); + } + } + } + } + if (ucFlags & QUERY_FLAG_PAGE_SIZE) + { + Notations::EncodeInt(m_stQueryParam.iPageSize, pBuff); + } + if (ucFlags & QUERY_FLAG_WITH_PAGING_STATE) + { + Notations::EncodeBytes(m_stQueryParam.stPagingState, pBuff); + } + if (ucFlags & QUERY_FLAG_WITH_SERIAL_CONSISTENCY) + { + Notations::EncodeShort(m_stQueryParam.unSerialConsistency, pBuff); + } + if (ucFlags & QUERY_FLAG_WITH_TIMESTAMP) + { + Notations::EncodeLong(m_stQueryParam.llTimestamp, pBuff); + } + return(true); +} + +bool CassRequest::EncodePrepare(const std::string& strQuery, CBuffer* pBuff) const +{ + return(Notations::EncodeLongString(strQuery, pBuff)); +} + +} /* namespace neb */ + diff --git a/src/codec/cass/CassRequest.hpp b/src/codec/cass/CassRequest.hpp new file mode 100644 index 00000000..5f7f9d0f --- /dev/null +++ b/src/codec/cass/CassRequest.hpp @@ -0,0 +1,102 @@ +/******************************************************************************* + * Project: Nebula + * @file CassRequest.hpp + * @brief + * @author Bwar + * @date: 2021-11-28 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_CASSANDRA_CASSREQUEST_HPP_ +#define SRC_CODEC_CASSANDRA_CASSREQUEST_HPP_ + +#include "CassMessage.hpp" + +namespace neb +{ + +class CodecCass; + +class CassRequest: public CassMessage +{ +public: + enum QUERY_FLAG + { + QUERY_FLAG_VALUES = 0x01, + QUERY_FLAG_SKIP_METADATA = 0x02, + QUERY_FLAG_PAGE_SIZE = 0x04, + QUERY_FLAG_WITH_PAGING_STATE = 0x08, + QUERY_FLAG_WITH_SERIAL_CONSISTENCY = 0x10, + QUERY_FLAG_WITH_TIMESTAMP = 0x20, + QUERY_FLAG_WITH_NAMES_FOR_VALUE = 0x40, + }; + + struct QueryParameter + { + uint16 unConsistency = 0; + uint16 unSerialConsistency = 0; + bool bSkipMetadata = false; + int32 iPageSize = 0; + Bytes stPagingState; + int64 llTimestamp = 0; + std::vector> vecValue; + + QueryParameter(){} + QueryParameter(const QueryParameter&) = delete; + QueryParameter(QueryParameter&& stParameter) + { + unConsistency = stParameter.unConsistency; + unSerialConsistency = stParameter.unSerialConsistency; + bSkipMetadata = stParameter.bSkipMetadata; + iPageSize = stParameter.iPageSize; + stPagingState = std::move(stParameter.stPagingState); + llTimestamp = stParameter.llTimestamp; + vecValue = std::move(stParameter.vecValue); + } + QueryParameter& operator=(const QueryParameter&) = delete; + QueryParameter& operator=(QueryParameter&& stParameter) + { + unConsistency = stParameter.unConsistency; + unSerialConsistency = stParameter.unSerialConsistency; + bSkipMetadata = stParameter.bSkipMetadata; + iPageSize = stParameter.iPageSize; + stPagingState = std::move(stParameter.stPagingState); + llTimestamp = stParameter.llTimestamp; + vecValue = std::move(stParameter.vecValue); + return(*this); + } + }; + +public: + CassRequest(); + CassRequest(const CassRequest&) = delete; + virtual ~CassRequest(); + + CassRequest& operator=(const CassRequest&) = delete; + + void SetQuery(const std::string& strQuery); + void SetQuery(const std::string& strQuery, QueryParameter& stQueryParam); + +private: + bool EncodeStarup(CBuffer* pBuff, const std::string& strCqlVersion = "3.0.0", const std::string& strCompression = "") const; + bool EncodeAuthResponse(const std::string& strToken, CBuffer* pBuff) const; + bool EncodeQuery(CBuffer* pBuff) const; // TODO: query_parameters + bool EncodePrepare(const std::string& strQuery, CBuffer* pBuff) const; + // TODO + // bool EncodeExcute() + // bool EncodeBatch() + // bool EncodeRegister() + +private: + friend class CodecCass; + std::string m_strQuery; + std::string m_strTracingId; + std::vector m_vecWarnings; + std::unordered_map m_mapCustomPayload; + QueryParameter m_stQueryParam; +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_CASSANDRA_CASSREQUEST_HPP_ */ + diff --git a/src/codec/cass/CassResponse.cpp b/src/codec/cass/CassResponse.cpp new file mode 100644 index 00000000..46549582 --- /dev/null +++ b/src/codec/cass/CassResponse.cpp @@ -0,0 +1,280 @@ +/******************************************************************************* + * Project: Nebula + * @file CassResponse.cpp + * @brief + * @author Bwar + * @date: 2021-11-27 + * @note + * Modify history: + ******************************************************************************/ +#include "CassResponse.hpp" +#include "result/CassResult.hpp" + +namespace neb +{ + +CassResponse::CassResponse() + : CassMessage(CASS_MSG_RESPONSE) +{ +} + +CassResponse::~CassResponse() +{ +} + +bool CassResponse::DecodeError(CBuffer* pBuff) +{ + m_bCassOk = false; + return(Notations::DecodeInt(pBuff, m_iErrCode) && Notations::DecodeString(pBuff, m_strErrMsg)); +} + +bool CassResponse::DecodeReady(CBuffer* pBuff) +{ + return(true); +} + +bool CassResponse::DecodeAuthenticate(CBuffer* pBuff, std::string& strAuthenticator) +{ + return(Notations::DecodeString(pBuff, strAuthenticator)); +} + +bool CassResponse::DecodeSupported(CBuffer* pBuff, std::unordered_map>& mapSupportedOptions) +{ + return(Notations::DecodeStringMultimap(pBuff, mapSupportedOptions)); +} + +bool CassResponse::DecodeResult(CBuffer* pBuff) +{ + if (!Notations::DecodeInt(pBuff, m_oResult.m_iKind)) + { + return(false); + } + switch (m_oResult.m_iKind) + { + case CASS_RESULT_KIND_ROWS: + return(DecodeRows(pBuff, m_oResult)); + break; + case CASS_RESULT_KIND_VOID: + break; + case CASS_RESULT_KIND_SCHEMA_CHANGE: + // TODO schema change + break; + case CASS_RESULT_KIND_SET_KEYSPACE: + return(Notations::DecodeString(pBuff, m_oResult.m_strKeySpace)); + break; + case CASS_RESULT_KIND_PREPARED: + return(DecodePrepared(pBuff, m_oResult)); + break; + default: + return(false); + } + return(true); +} + +bool CassResponse::DecodeRows(CBuffer* pBuff, CassResult& oResult) +{ + int32 iFlags = 0; + int32 iColumnCount = 0; + if (Notations::DecodeInt(pBuff, iFlags) + && Notations::DecodeInt(pBuff, iColumnCount)) + { + std::string strKeySpace; + std::string strTableName; + std::string strColumnName; + uint16 unColumnType = 0; + Value stValue; + if (CASS_RESULT_FLAG_HAS_MORE_PAGES & iFlags) + { + if (!Notations::DecodeBytes(pBuff, oResult.m_stPagingState)) + { + return(false); + } + } + if (!(CASS_RESULT_FLAG_NO_METADATA & iFlags)) + { + if (CASS_RESULT_FLAG_GLOBAL_TABLES_SPEC & iFlags) + { + if (!Notations::DecodeString(pBuff, strKeySpace) + || !Notations::DecodeString(pBuff, strTableName)) + { + return(false); + } + for (int i = 0; i < iColumnCount; ++i) + { + if (!Notations::DecodeString(pBuff, strColumnName) + || !Notations::DecodeOption(pBuff, CassResult::WithOptionValue, unColumnType, stValue)) + { + return(false); + } + CassResultMetaData stMetaData; + stMetaData.strColumnName = strColumnName; + stMetaData.strKeySpace = strKeySpace; + stMetaData.strTableName = strTableName; + stMetaData.unColumnType = unColumnType; + oResult.m_vecMetaData.emplace_back(std::move(stMetaData)); + } + } + else + { + for (int i = 0; i < iColumnCount; ++i) + { + if (!Notations::DecodeString(pBuff, strKeySpace) + || !Notations::DecodeString(pBuff, strTableName) + || !Notations::DecodeString(pBuff, strColumnName) + || !Notations::DecodeOption(pBuff, CassResult::WithOptionValue, unColumnType, stValue)) + { + return(false); + } + CassResultMetaData stMetaData; + stMetaData.strColumnName = strColumnName; + stMetaData.strKeySpace = strKeySpace; + stMetaData.strTableName = strTableName; + stMetaData.unColumnType = unColumnType; + oResult.m_vecMetaData.emplace_back(std::move(stMetaData)); + } + } + } + + int32 iRowCount = 0; + if (Notations::DecodeInt(pBuff, iRowCount)) + { + for (int j = 0; j < iRowCount; ++j) + { + CassRow oRow; + for (int k = 0; k < iColumnCount; ++k) + { + Bytes stByte; + if (!Notations::DecodeBytes(pBuff, stByte)) + { + return(false); + } + oRow.m_vecColumnValue.emplace_back(std::move(stByte)); + } + oResult.m_vecRow.emplace_back(std::move(oRow)); + } + return(true); + } + } + return(false); +} + +bool CassResponse::DecodePrepared(CBuffer* pBuff, CassResult& oResult) +{ + if (Notations::DecodeShortBytes(pBuff, oResult.m_stQueryId)) + { + int32 iFlags = 0; + int32 iColumnCount = 0; + int32 iPkCount = 0; + if (Notations::DecodeInt(pBuff, iFlags) + && Notations::DecodeInt(pBuff, iColumnCount) + && Notations::DecodeInt(pBuff, iPkCount)) + { + uint16 unColumnType = 0; + uint16 unPkIndex = 0; + std::string strKeySpace; + std::string strTableName; + std::string strColumnName; + Value stValue; + for (int i = 0; i < iPkCount; ++i) + { + if (!Notations::DecodeShort(pBuff, unPkIndex)) + { + return(false); + } + oResult.m_vecPkIndex.push_back(unPkIndex); + } + if (CASS_RESULT_FLAG_GLOBAL_TABLES_SPEC & iFlags) + { + if (!Notations::DecodeString(pBuff, strKeySpace) + || !Notations::DecodeString(pBuff, strTableName)) + { + return(false); + } + for (int i = 0; i < iColumnCount; ++i) + { + if (!Notations::DecodeString(pBuff, strColumnName) + || !Notations::DecodeOption(pBuff, CassResult::WithOptionValue, unColumnType, stValue)) + { + return(false); + } + CassResultMetaData stMetaData; + stMetaData.strColumnName = strColumnName; + stMetaData.strKeySpace = strKeySpace; + stMetaData.strTableName = strTableName; + stMetaData.unColumnType = unColumnType; + oResult.m_vecMetaData.emplace_back(std::move(stMetaData)); + } + } + else + { + for (int i = 0; i < iColumnCount; ++i) + { + if (!Notations::DecodeString(pBuff, strKeySpace) + || !Notations::DecodeString(pBuff, strTableName) + || !Notations::DecodeString(pBuff, strColumnName) + || !Notations::DecodeOption(pBuff, CassResult::WithOptionValue, unColumnType, stValue)) + { + return(false); + } + CassResultMetaData stMetaData; + stMetaData.strColumnName = strColumnName; + stMetaData.strKeySpace = strKeySpace; + stMetaData.strTableName = strTableName; + stMetaData.unColumnType = unColumnType; + oResult.m_vecMetaData.emplace_back(std::move(stMetaData)); + } + } + + // TODO result-metadata + if (!(CASS_RESULT_FLAG_NO_METADATA & iFlags)) + { + if (CASS_RESULT_FLAG_GLOBAL_TABLES_SPEC & iFlags) + { + if (!Notations::DecodeString(pBuff, strKeySpace) + || !Notations::DecodeString(pBuff, strTableName)) + { + return(false); + } + for (int i = 0; i < iColumnCount; ++i) + { + if (!Notations::DecodeString(pBuff, strColumnName) + || !Notations::DecodeOption(pBuff, CassResult::WithOptionValue, unColumnType, stValue)) + { + return(false); + } + CassResultMetaData stMetaData; + stMetaData.strColumnName = strColumnName; + stMetaData.strKeySpace = strKeySpace; + stMetaData.strTableName = strTableName; + stMetaData.unColumnType = unColumnType; + oResult.m_vecMetaData.emplace_back(std::move(stMetaData)); + } + } + else + { + for (int i = 0; i < iColumnCount; ++i) + { + if (!Notations::DecodeString(pBuff, strKeySpace) + || !Notations::DecodeString(pBuff, strTableName) + || !Notations::DecodeString(pBuff, strColumnName) + || !Notations::DecodeOption(pBuff, CassResult::WithOptionValue, unColumnType, stValue)) + { + return(false); + } + CassResultMetaData stMetaData; + stMetaData.strColumnName = strColumnName; + stMetaData.strKeySpace = strKeySpace; + stMetaData.strTableName = strTableName; + stMetaData.unColumnType = unColumnType; + oResult.m_vecMetaData.emplace_back(std::move(stMetaData)); + } + } + } + return(true); + } + } + return(false); +} + +} /* namespace neb */ + diff --git a/src/codec/cass/CassResponse.hpp b/src/codec/cass/CassResponse.hpp new file mode 100644 index 00000000..82c9ea3c --- /dev/null +++ b/src/codec/cass/CassResponse.hpp @@ -0,0 +1,74 @@ +/******************************************************************************* + * Project: Nebula + * @file CassResponse.hpp + * @brief + * @author Bwar + * @date: 2021-11-27 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_CASSANDRA_CASSRESPONSE_HPP_ +#define SRC_CODEC_CASSANDRA_CASSRESPONSE_HPP_ + +#include "CassMessage.hpp" +#include "result/CassResult.hpp" + +namespace neb +{ + +class CodecCass; + +class CassResponse: public CassMessage +{ +public: + CassResponse(); + CassResponse(const CassResponse&) = delete; + virtual ~CassResponse(); + CassResponse& operator=(const CassResponse&) = delete; + + bool IsSuccess() const + { + return(m_bCassOk); + } + + const CassResult& GetResult() const + { + return(m_oResult); + } + + int32 GetErrCode() const + { + return(m_iErrCode); + } + + const std::string& GetErrMsg() const + { + return(m_strErrMsg); + } + +protected: + bool DecodeError(CBuffer* pBuffg); + bool DecodeReady(CBuffer* pBuff); + bool DecodeAuthenticate(CBuffer* pBuff, std::string& strAuthenticator); + bool DecodeSupported(CBuffer* pBuff, std::unordered_map>& mapSupportedOptions); + bool DecodeResult(CBuffer* pBuff); + // TODO bool DecodeEvent(); + // TODO bool DecodeAuthChallenge(); + // TODO bool DecodeAuthSuccess(); + +protected: + bool DecodeRows(CBuffer* pBuff, CassResult& oResult); + bool DecodePrepared(CBuffer* pBuff, CassResult& oResult); + +private: + friend class CodecCass; + bool m_bCassOk = true; + int32 m_iErrCode = 0; + std::string m_strErrMsg; + CassResult m_oResult; +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_CASSANDRA_CASSRESPONSE_HPP_ */ + diff --git a/src/codec/cass/CodecCass.cpp b/src/codec/cass/CodecCass.cpp new file mode 100644 index 00000000..a19e96b7 --- /dev/null +++ b/src/codec/cass/CodecCass.cpp @@ -0,0 +1,244 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecCass.cpp + * @brief + * @author Bwar + * @date: 2021-11-28 + * @note + * Modify history: + ******************************************************************************/ +#include "CodecCass.hpp" +#include "type/Notations.hpp" +#include "CassRequest.hpp" +#include "CassResponse.hpp" + +namespace neb +{ + +CodecCass::CodecCass(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel) + : Codec(pLogger, eCodecType, pBindChannel) +{ +} + +CodecCass::~CodecCass() +{ +} + +E_CODEC_STATUS CodecCass::Encode(CBuffer* pBuff) +{ + return(CODEC_STATUS_OK); +} + +E_CODEC_STATUS CodecCass::Encode(const CassMessage& oCassMsg, CBuffer* pBuff) +{ + CassFrameHead stFrameHead; + if (CASS_MSG_RESPONSE == oCassMsg.GetMsgType()) + { + LOG4_WARNING("cass client only"); + return(CODEC_STATUS_ERR); + } + else + { + stFrameHead.ucVersion = m_ucVersion; + stFrameHead.ucVersion |= CASS_MSG_REQUEST; + stFrameHead.uiStream = GenerateStreamId(); + auto& oRequest = static_cast(oCassMsg); + CBuffer oBuff; + if (!m_bStartup) + { + stFrameHead.ucOpCode = CASS_OP_STARTUP; + oRequest.EncodeStarup(&oBuff, "3.0.0"); + stFrameHead.uiLength = oBuff.ReadableBytes(); + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(oBuff.GetRawReadBuffer(), oBuff.ReadableBytes()); + oBuff.AdvanceReadIndex(oBuff.ReadableBytes()); + m_bStartup = true; + } + if (stFrameHead.uiStream == 0) + { + LOG4_ERROR("unable to assign stream id."); + return(CODEC_STATUS_PART_ERR); + } + stFrameHead.ucOpCode = oRequest.GetOpcode(); + switch (stFrameHead.ucOpCode) + { + case CASS_OP_QUERY: + oRequest.EncodeQuery(&oBuff); + break; + default: + LOG4_WARNING("TODO opcode %d", (int)stFrameHead.ucOpCode); + } + stFrameHead.uiLength = oBuff.ReadableBytes(); + if (m_bIsReady) + { + EncodeFrameHeader(stFrameHead, pBuff); + pBuff->Write(oBuff.GetRawReadBuffer(), oBuff.ReadableBytes()); + oBuff.AdvanceReadIndex(oBuff.ReadableBytes()); + } + else + { + EncodeFrameHeader(stFrameHead, &m_oReqBuff); + m_oReqBuff.Write(oBuff.GetRawReadBuffer(), oBuff.ReadableBytes()); + oBuff.AdvanceReadIndex(oBuff.ReadableBytes()); + } + return(CODEC_STATUS_OK); + } +} + +E_CODEC_STATUS CodecCass::Decode(CBuffer* pBuff, CassMessage& oCassMsg) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + +E_CODEC_STATUS CodecCass::Decode(CBuffer* pBuff, CassMessage& oCassMsg, CBuffer* pReactBuff) +{ + if (pBuff->ReadableBytes() < 9) + { + return(CODEC_STATUS_PAUSE); + } + uint32 uiReadIndex = pBuff->GetReadIndex(); + CassFrameHead stFrameHead; + DecodeFrameHeader(pBuff, stFrameHead); + if (pBuff->ReadableBytes() < stFrameHead.uiLength) + { + pBuff->SetReadIndex(uiReadIndex); + return(CODEC_STATUS_PAUSE); + } + LOG4_TRACE("op code %d, stream %u, body len %u", + (int)stFrameHead.ucOpCode, stFrameHead.uiStream, stFrameHead.uiLength); + oCassMsg.m_ucOpcode = stFrameHead.ucOpCode; + oCassMsg.m_unStreamId = stFrameHead.uiStream; + auto iter = m_setReqStreamId.find(stFrameHead.uiStream); + if (iter != m_setReqStreamId.end()) + { + m_setReqStreamId.erase(iter); // TODO: 需确认stream是否接收完不同page的响应 + } + if (stFrameHead.ucVersion & CASS_MSG_RESPONSE) // response + { + oCassMsg.m_unMsgType = CASS_MSG_RESPONSE; + auto& oResponse = static_cast(oCassMsg); + CBuffer oBuff; + oBuff.Write(pBuff->GetRawReadBuffer(), stFrameHead.uiLength); + pBuff->SkipBytes(stFrameHead.uiLength); + switch (stFrameHead.ucOpCode) + { + case CASS_OP_RESULT: + if (!oResponse.DecodeResult(&oBuff)) + { + LOG4_ERROR("cass response decode failed."); + return(CODEC_STATUS_PART_ERR); + } + break; + case CASS_OP_ERROR: + if (!oResponse.DecodeError(&oBuff)) + { + return(CODEC_STATUS_PART_ERR); + } + LOG4_ERROR("%d: %s", oResponse.GetErrCode(), oResponse.GetErrMsg().c_str()); + break; + case CASS_OP_AUTHENTICATE: + break; + case CASS_OP_SUPPORTED: + break; + case CASS_OP_EVENT: + break; + case CASS_OP_READY: + m_bIsReady = true; + if (m_oReqBuff.ReadableBytes() > 0) + { + pReactBuff->Write(m_oReqBuff.GetRawReadBuffer(), m_oReqBuff.ReadableBytes()); + } + LOG4_INFO("cass connection ready."); + break; + case CASS_OP_AUTH_CHALLENGE: + break; + case CASS_OP_AUTH_SUCCESS: + break; + default: + LOG4_ERROR("invalid op code %d in response message.", stFrameHead.ucOpCode); + return(CODEC_STATUS_ERR); + } + return(CODEC_STATUS_OK); + } + else // request + { + LOG4_WARNING("cass client only"); + oCassMsg.m_unMsgType = CASS_MSG_REQUEST; + auto& oRequest = static_cast(oCassMsg); + if (CASS_FLAG_TRACING & stFrameHead.ucFlags) + { + if (!Notations::DecodeUuid(pBuff, oRequest.m_strTracingId)) + { + LOG4_ERROR("failed to decode uuid"); + return(CODEC_STATUS_PART_ERR); + } + } + if (CASS_FLAG_WARNING & stFrameHead.ucFlags) + { + if (!Notations::DecodeStringList(pBuff, oRequest.m_vecWarnings)) + { + LOG4_ERROR("failed to decode warnings"); + return(CODEC_STATUS_PART_ERR); + } + } + if (CASS_FLAG_CUSTOM_PAYLOAD & stFrameHead.ucFlags) + { + if (!Notations::DecodeBytesMap(pBuff, oRequest.m_mapCustomPayload)) + { + LOG4_ERROR("failed to decode warnings"); + return(CODEC_STATUS_PART_ERR); + } + } + return(CODEC_STATUS_ERR); // TODO + } +} + +uint16 CodecCass::GenerateStreamId() +{ + int iLoopNum = 0; + ++m_unLastReqStreamId; + m_unLastReqStreamId = (m_unLastReqStreamId > 0) ? m_unLastReqStreamId : 1; + auto iter = m_setReqStreamId.find(m_unLastReqStreamId); + while (iter != m_setReqStreamId.end()) + { + ++m_unLastReqStreamId; + if (m_unLastReqStreamId > 32768) + { + if (iLoopNum > 0) + { + return(0); + } + ++iLoopNum; + m_unLastReqStreamId = 1; + } + iter = m_setReqStreamId.find(m_unLastReqStreamId); + } + return(m_unLastReqStreamId); +} + +void CodecCass::DecodeFrameHeader(CBuffer* pBuff, CassFrameHead& stFrameHead) +{ + pBuff->Read(&stFrameHead.ucVersion, 1); + pBuff->Read(&stFrameHead.ucFlags, 1); + pBuff->Read(&stFrameHead.uiStream, 2); + pBuff->Read(&stFrameHead.ucOpCode, 1); + pBuff->Read(&stFrameHead.uiLength, 4); + stFrameHead.uiStream = CodecUtil::N2H(stFrameHead.uiStream); + stFrameHead.uiLength = CodecUtil::N2H(stFrameHead.uiLength); +} + +void CodecCass::EncodeFrameHeader(const CassFrameHead& stFrameHead, CBuffer* pBuff) +{ + uint16 uiStream = CodecUtil::H2N(stFrameHead.uiStream); + uint32 uiLength = CodecUtil::H2N(stFrameHead.uiLength); + pBuff->Write(&stFrameHead.ucVersion, 1); + pBuff->Write(&stFrameHead.ucFlags, 1); + pBuff->Write(&uiStream, 2); + pBuff->Write(&stFrameHead.ucOpCode, 1); + pBuff->Write(&uiLength, 4); +} + +} /* namespace neb */ + diff --git a/src/codec/cass/CodecCass.hpp b/src/codec/cass/CodecCass.hpp new file mode 100644 index 00000000..2da9b52a --- /dev/null +++ b/src/codec/cass/CodecCass.hpp @@ -0,0 +1,85 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecCass.hpp + * @brief + * @author Bwar + * @date: 2021-11-28 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_CASSANDRA_CODECCASS_HPP_ +#define SRC_CODEC_CASSANDRA_CODECCASS_HPP_ + +#include +#include "codec/Codec.hpp" +#include "CassMessage.hpp" + +namespace neb +{ + +/** + * @brief + * @note + * The CQL binary protocol is a frame based protocol. Frames are defined as: + * + * 0 8 16 24 32 40 + * +---------+---------+---------+---------+---------+ + * | version | flags | stream | opcode | + * +---------+---------+---------+---------+---------+ + * | length | + * +---------+---------+---------+---------+ + * | | + * . ... body ... . + * . . + * . . + * +---------------------------------------- + * + * The protocol is big-endian (network byte order). + */ +class CodecCass: public Codec +{ +public: + CodecCass(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel); + virtual ~CodecCass(); + + static E_CODEC_TYPE Type() + { + return(CODEC_CASS); + } + + virtual bool DecodeWithReactor() const + { + return(true); + } + + E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(const CassMessage& oCassMsg, CBuffer* pBuff); + E_CODEC_STATUS Decode(CBuffer* pBuff, CassMessage& oCassMsg); + E_CODEC_STATUS Decode(CBuffer* pBuff, CassMessage& oCassMsg, CBuffer* pReactBuff); + + uint16 GetLastStreamId() const + { + return(m_unLastReqStreamId); + } + + static void DecodeFrameHeader(CBuffer* pBuff, CassFrameHead& stFrameHead); + static void EncodeFrameHeader(const CassFrameHead& stFrameHead, CBuffer* pBuff); + +protected: + uint16 GenerateStreamId(); + void Ready(); + +private: + uint8 m_ucVersion = 4; + uint16 m_unLastReqStreamId = 0; + bool m_bIsReady = false; + bool m_bStartup = false; + std::unordered_set m_setReqStreamId; + CBuffer m_oReqBuff; +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_CASSANDRA_CODECCASS_HPP_ */ + diff --git a/src/codec/cass/result/CassResult.cpp b/src/codec/cass/result/CassResult.cpp new file mode 100644 index 00000000..109cad04 --- /dev/null +++ b/src/codec/cass/result/CassResult.cpp @@ -0,0 +1,66 @@ +/******************************************************************************* + * Project: Nebula + * @file CassResult.cpp + * @brief + * @author Bwar + * @date: 2021-11-28 + * @note + * Modify history: + ******************************************************************************/ +#include "CassResult.hpp" + +namespace neb +{ + +CassResult::CassResult() +{ +} + +CassResult::~CassResult() +{ +} + +CassResult::CassResult(CassResult&& oResult) +{ + m_iKind = oResult.m_iKind; + m_vecMetaData = std::move(oResult.m_vecMetaData); + m_vecRow = std::move(oResult.m_vecRow); + m_oEmptyRow = std::move(oResult.m_oEmptyRow); +} + +CassResult& CassResult::operator=(CassResult&& oResult) +{ + m_iKind = oResult.m_iKind; + m_vecMetaData = std::move(oResult.m_vecMetaData); + m_vecRow = std::move(oResult.m_vecRow); + m_oEmptyRow = std::move(oResult.m_oEmptyRow); + return(*this); +} + +const CassRow& CassResult::operator[](uint16 unIndex) const +{ + if (unIndex < m_vecRow.size()) + { + return(m_vecRow[unIndex]); + } + return(m_oEmptyRow); +} + +bool CassResult::WithOptionValue(uint16 unOptionId) +{ + switch (unOptionId) + { + case CASS_RESULT_COL_CUSTOM: + case CASS_RESULT_COL_LIST: + case CASS_RESULT_COL_MAP: + case CASS_RESULT_COL_SET: + case CASS_RESULT_COL_UDT: + case CASS_RESULT_COL_TUPLE: + return(true); + default: + return(false); + } +} + +} /* namespace neb */ + diff --git a/src/codec/cass/result/CassResult.hpp b/src/codec/cass/result/CassResult.hpp new file mode 100644 index 00000000..94cd3b18 --- /dev/null +++ b/src/codec/cass/result/CassResult.hpp @@ -0,0 +1,164 @@ +/******************************************************************************* + * Project: Nebula + * @file CassResult.hpp + * @brief + * @author Bwar + * @date: 2021-11-28 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_CASSANDRA_RESULT_CASSRESULT_HPP_ +#define SRC_CODEC_CASSANDRA_RESULT_CASSRESULT_HPP_ + +#include +#include +#include "Definition.hpp" +#include "CassRow.hpp" + +namespace neb +{ + +enum E_CASS_RESULT_KIND +{ + CASS_RESULT_KIND_VOID = 0x0001, ///< Void: for results carrying no information. + CASS_RESULT_KIND_ROWS = 0x0002, ///< Rows: for results to select queries, returning a set of rows. + CASS_RESULT_KIND_SET_KEYSPACE = 0x0003, ///< Set_keyspace: the result to a `use` query. + CASS_RESULT_KIND_PREPARED = 0x0004, ///< Prepared: result to a PREPARE message. + CASS_RESULT_KIND_SCHEMA_CHANGE = 0x0005, ///< Schema_change: the result to a schema altering query. +}; + +enum E_CASS_RESULT_FLAG +{ + CASS_RESULT_FLAG_GLOBAL_TABLES_SPEC = 0x0001, ///< if set, only one table spec (keyspace and table name) is provided as . If not set, is not present. + CASS_RESULT_FLAG_HAS_MORE_PAGES = 0x0002, ///< indicates whether this is not the last page of results and more should be retrieved. + CASS_RESULT_FLAG_NO_METADATA = 0x0004, ///< No_metadata +}; + +enum E_CASS_RESULT_COL_TYPE +{ + CASS_RESULT_COL_CUSTOM = 0x0000, ///< the value is a [string]. + CASS_RESULT_COL_ASCII = 0x0001, + CASS_RESULT_COL_BIGINT = 0x0002, + CASS_RESULT_COL_BLOB = 0x0003, + CASS_RESULT_COL_BOOLEAN = 0x0004, + CASS_RESULT_COL_COUNTER = 0x0005, + CASS_RESULT_COL_DECIMAL = 0x0006, + CASS_RESULT_COL_DOUBLE = 0x0007, + CASS_RESULT_COL_FLOAT = 0x0008, + CASS_RESULT_COL_INT = 0x0009, + CASS_RESULT_COL_TIMESTAMP = 0x000B, + CASS_RESULT_COL_UUID = 0x000C, + CASS_RESULT_COL_VARCHAR = 0x000D, + CASS_RESULT_COL_VARINT = 0x000E, + CASS_RESULT_COL_TIMEUUID = 0x000F, + CASS_RESULT_COL_INET = 0x0010, + CASS_RESULT_COL_DATE = 0x0011, + CASS_RESULT_COL_TIME = 0x0012, + CASS_RESULT_COL_SMALLINT = 0x0013, + CASS_RESULT_COL_TINYINT = 0x0014, + CASS_RESULT_COL_LIST = 0x0020, ///< the value is an [option], representing the type of the elements of the list. + CASS_RESULT_COL_MAP = 0x0021, ///< the value is two [option], representing the types of the keys and values of the map + CASS_RESULT_COL_SET = 0x0022, ///< the value is an [option], representing the type of the elements of the set + CASS_RESULT_COL_UDT = 0x0030, ///< the value is ... + CASS_RESULT_COL_TUPLE = 0x0031, ///< the value is ... where is a [short] representing the number of values in the type, and are [option] representing the type of the i_th component of the tuple +}; + +/** + * @brief is composed of: [][?...] + * @note - is an [int]. The bits of provides information on the + * formatting of the remaining information. A flag is set if the bit + * corresponding to its `mask` is set. Supported flags are, given their CassResult::FLAG + * - is an [int] representing the number of columns selected + * by the query that produced this result. It defines the number of + * elements in and the number of elements for each row in . + * - is present if the Global_tables_spec is set in + * . It is composed of two [string] representing the + * (unique) keyspace name and table name the columns belong to. + * - specifies the columns returned in the query. There are + * such column specifications that are composed of: + */ +struct CassResultMetaData +{ + uint16 unColumnType = 0; + std::string strColumnName; + std::string strTableName; + std::string strKeySpace; + + CassResultMetaData(){} + ~CassResultMetaData() + { + } + CassResultMetaData(const CassResultMetaData&) = delete; + CassResultMetaData(CassResultMetaData&& stMeta) + { + unColumnType = stMeta.unColumnType; + strColumnName = std::move(stMeta.strColumnName); + strTableName = std::move(stMeta.strTableName); + strKeySpace = std::move(stMeta.strKeySpace); + } + CassResultMetaData& operator=(const CassResultMetaData& stMeta) + { + unColumnType = stMeta.unColumnType; + strColumnName = std::move(stMeta.strColumnName); + strTableName = std::move(stMeta.strTableName); + strKeySpace = std::move(stMeta.strKeySpace); + return(*this); + } + CassResultMetaData& operator=(CassResultMetaData&& stMeta) + { + unColumnType = stMeta.unColumnType; + strColumnName = std::move(stMeta.strColumnName); + strTableName = std::move(stMeta.strTableName); + strKeySpace = std::move(stMeta.strKeySpace); + return(*this); + } +}; + +class CassResponse; + +class CassResult +{ +public: + CassResult(); + CassResult(const CassResult&) = delete; + CassResult(CassResult&& oResult); + virtual ~CassResult(); + + CassResult& operator=(const CassResult&) = delete; + CassResult& operator=(CassResult&& oResult); + const CassRow& operator[](uint16 unIndex) const; + + uint32 Size() const + { + return(m_vecRow.size()); + } + + const Bytes& GetQueryId() const + { + return(m_stQueryId); + } + + const std::vector& GetPkIndex() const + { + return(m_vecPkIndex); + } + + static bool WithOptionValue(uint16 unOptionId); + +private: + friend class CassResponse; + + int32 m_iKind = 0; + std::string m_strKeySpace; // for SET_KEYSPACE or SCHEMA_CHANGE + Bytes m_stQueryId; // for PREPARED + Bytes m_stPagingState; + std::vector m_vecPkIndex; // for PREPARED + std::vector m_vecMetaData; + std::vector m_vecRow; + CassRow m_oEmptyRow; +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_CASSANDRA_RESULT_CASSRESULT_HPP_ */ + diff --git a/src/codec/cass/result/CassRow.cpp b/src/codec/cass/result/CassRow.cpp new file mode 100644 index 00000000..dec057f7 --- /dev/null +++ b/src/codec/cass/result/CassRow.cpp @@ -0,0 +1,44 @@ +/******************************************************************************* + * Project: Nebula + * @file CassRow.cpp + * @brief + * @author Bwar + * @date: 2021-11-28 + * @note + * Modify history: + ******************************************************************************/ +#include "CassRow.hpp" + +namespace neb +{ + +CassRow::CassRow() +{ +} + +CassRow::~CassRow() +{ +} + +CassRow::CassRow(CassRow&& oRow) +{ + m_vecColumnValue = std::move(oRow.m_vecColumnValue); +} + +CassRow& CassRow::operator=(CassRow&& oRow) +{ + m_vecColumnValue = std::move(oRow.m_vecColumnValue); + return(*this); +} + +const Bytes* CassRow::operator[](uint16 unIndex) const +{ + if (unIndex >= m_vecColumnValue.size()) + { + return(nullptr); + } + return(&m_vecColumnValue[unIndex]); +} + +} /* namespace neb */ + diff --git a/src/codec/cass/result/CassRow.hpp b/src/codec/cass/result/CassRow.hpp new file mode 100644 index 00000000..b21c7d26 --- /dev/null +++ b/src/codec/cass/result/CassRow.hpp @@ -0,0 +1,47 @@ +/******************************************************************************* + * Project: Nebula + * @file CassRow.hpp + * @brief + * @author Bwar + * @date: 2021-11-28 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_CASSANDRA_RESULT_CASSROW_HPP_ +#define SRC_CODEC_CASSANDRA_RESULT_CASSROW_HPP_ + +#include +#include +#include "Definition.hpp" +#include "type/Notations.hpp" + +namespace neb +{ + +class CassResponse; + +class CassRow +{ +public: + CassRow(); + CassRow(const CassRow&) = delete; + CassRow(CassRow&& oRow); + virtual ~CassRow(); + + CassRow& operator=(const CassRow&) = delete; + CassRow& operator=(CassRow&& oRow); + const Bytes* operator[](uint16 unIndex) const; + uint32 Size() const + { + return(m_vecColumnValue.size()); + } + +private: + friend class CassResponse; + std::vector m_vecColumnValue; +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_CASSANDRA_RESULT_CASSROW_HPP_ */ + diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index b0ec107c..e17b1b93 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -14,18 +14,18 @@ #include "Http2Header.hpp" #include "codec/CodecUtil.hpp" #include "util/StringConverter.hpp" +#include "channel/SocketChannel.hpp" namespace neb { CodecHttp2::CodecHttp2(std::shared_ptr pLogger, - E_CODEC_TYPE eCodecType, bool bChannelIsClient) - : Codec(pLogger, eCodecType), - m_bChannelIsClient(bChannelIsClient) + E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel) + : Codec(pLogger, eCodecType, pBindChannel) { try { - m_pFrame = new Http2Frame(pLogger, eCodecType); + m_pFrame = new Http2Frame(pLogger, eCodecType, pBindChannel); m_pStreamWeightRoot = new TreeNode(); m_pStreamWeightRoot->pData = new tagStreamWeight(); m_pStreamWeightRoot->pData->uiStreamId = 0; @@ -64,7 +64,7 @@ void CodecHttp2::ConnectionSetting(CBuffer* pBuff) { std::vector vecSetting; tagSetting stSetting; - if (m_bChannelIsClient) + if (GetBindChannel()->IsClient()) { pBuff->Write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24); m_bWantMagic = false; @@ -106,9 +106,14 @@ void CodecHttp2::ConnectionSetting(CBuffer* pBuff) } } +E_CODEC_STATUS CodecHttp2::Encode(CBuffer* pBuff) +{ + return(CODEC_STATUS_OK); +} + E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { - if (m_bWantMagic && m_bChannelIsClient) + if (m_bWantMagic && GetBindChannel()->IsClient()) { pBuff->Write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24); m_bWantMagic = false; @@ -177,8 +182,8 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) if (CODEC_STATUS_PART_ERR == eCodecStatus || CODEC_STATUS_OK == eCodecStatus) { - LOG4_TRACE("m_bChannelIsClient = %d, m_pCodingStream->GetStreamId() = %u", - m_bChannelIsClient, m_pCodingStream->GetStreamId()); + LOG4_TRACE("is_client = %d, m_pCodingStream->GetStreamId() = %u", + GetBindChannel()->IsClient(), m_pCodingStream->GetStreamId()); if (m_pCodingStream->GetStreamState() == H2_STREAM_CLOSE) { CloseStream(m_stDecodeFrameHead.uiStreamIdentifier); @@ -187,10 +192,16 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) return(eCodecStatus); } +E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) { LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); - if (m_bWantMagic && !m_bChannelIsClient) + if (m_bWantMagic && !GetBindChannel()->IsClient()) { if (pBuff->ReadableBytes() >= 24) { @@ -220,7 +231,7 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR oHttpMsg.set_stream_id(1); try { - m_pCodingStream = new Http2Stream(m_pLogger, GetCodecType(), oHttpMsg.stream_id()); + m_pCodingStream = new Http2Stream(m_pLogger, GetCodecType(), GetBindChannel(), oHttpMsg.stream_id()); m_pCodingStream->SetState(H2_STREAM_HALF_CLOSE_REMOTE); m_mapStream.insert(std::make_pair((uint32)1, m_pCodingStream)); } @@ -775,7 +786,7 @@ E_CODEC_STATUS CodecHttp2::PromiseStream(uint32 uiStreamId, CBuffer* pReactBuff) Http2Stream* pPromiseStream = nullptr; try { - pPromiseStream = new Http2Stream(m_pLogger, GetCodecType(), uiStreamId); + pPromiseStream = new Http2Stream(m_pLogger, GetCodecType(), GetBindChannel(), uiStreamId); pPromiseStream->SetState(H2_STREAM_RESERVED_REMOTE); m_mapStream.insert(std::make_pair(uiStreamId, pPromiseStream)); } @@ -839,7 +850,7 @@ void CodecHttp2::TransferHoldingMsg(HttpMsg* pHoldingHttpMsg) uint32 CodecHttp2::StreamIdGenerate() { - if (m_bChannelIsClient) + if (GetBindChannel()->IsClient()) { if (m_uiStreamIdGenerate & 0x01) // odd number { @@ -869,7 +880,7 @@ Http2Stream* CodecHttp2::NewCodingStream(uint32 uiStreamId) uint32 uiNewWindowSize = m_uiRecvWindowSize + DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; try { - m_pCodingStream = new Http2Stream(m_pLogger, GetCodecType(), uiStreamId); + m_pCodingStream = new Http2Stream(m_pLogger, GetCodecType(), GetBindChannel(), uiStreamId); m_pCodingStream->WindowInit(m_uiSettingsMaxWindowSize); m_uiRecvWindowSize = (uiNewWindowSize < SETTINGS_MAX_INITIAL_WINDOW_SIZE) ? uiNewWindowSize : SETTINGS_MAX_INITIAL_WINDOW_SIZE; @@ -1038,7 +1049,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF void CodecHttp2::ClassifyHeader(const std::string& strHeaderName, const std::string& strHeaderValue, HttpMsg& oHttpMsg) { LOG4_TRACE("strHeaderName = %s, strHeaderValue = %s", strHeaderName.c_str(), strHeaderValue.c_str()); - if (IsClient()) + if (GetBindChannel()->IsClient()) { if (oHttpMsg.stream_id() & 0x01) { diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index 780ebea2..52a635e3 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -36,20 +36,23 @@ class Http2Stream; class CodecHttp2: public Codec { public: - CodecHttp2(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, bool m_bChannelIsClient); + CodecHttp2(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel); virtual ~CodecHttp2(); - virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) + static E_CODEC_TYPE Type() { - return(CODEC_STATUS_INVALID); + return(CODEC_HTTP2); } - virtual E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) + + virtual bool DecodeWithReator() const { - return(CODEC_STATUS_INVALID); + return(true); } - virtual E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff); - virtual E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff); + E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff); + E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg); + E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff); /** * @brief HTTP2 连接建立 @@ -57,10 +60,6 @@ class CodecHttp2: public Codec virtual void ConnectionSetting(CBuffer* pBuff); public: - bool IsClient() const - { - return(m_bChannelIsClient); - } void SetPriority(uint32 uiStreamId, const tagPriority& stPriority); void RstStream(uint32 uiStreamId); E_H2_ERR_CODE Setting(const std::vector& vecSetting, bool bRecvSetting = false); @@ -127,7 +126,6 @@ class CodecHttp2: public Codec void CloseStream(uint32 uiStreamId); private: - bool m_bChannelIsClient = false; // 当前编解码器所在channel是作为http客户端还是作为http服务端 bool m_bWantMagic = true; bool m_bHasWaittingFrame = false; uint32 m_uiStreamIdGenerate = 0; diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index fb5e5739..6812cdd5 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -28,8 +28,9 @@ std::unordered_set Http2Frame::s_setFrameType = { H2_FRAME_CONTINUATION }; -Http2Frame::Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, Http2Stream* pStream) - : Codec(pLogger, eCodecType), m_pStream(pStream) +Http2Frame::Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel, Http2Stream* pStream) + : Codec(pLogger, eCodecType, pBindChannel), m_pStream(pStream) { } diff --git a/src/codec/http2/Http2Frame.hpp b/src/codec/http2/Http2Frame.hpp index 25a16bbe..affe8514 100644 --- a/src/codec/http2/Http2Frame.hpp +++ b/src/codec/http2/Http2Frame.hpp @@ -46,11 +46,13 @@ enum E_H2_FRAME_FLAG class CodecHttp2; class Http2Stream; +class SocketChannel; class Http2Frame: public Codec { public: - Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, Http2Stream* pStream = nullptr); + Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel, Http2Stream* pStream = nullptr); virtual ~Http2Frame(); static void WriteMediumInt(uint32 uiValue, CBuffer* pBuff); diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index aa0b5a96..8c87cbbc 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -10,18 +10,20 @@ #include "Http2Stream.hpp" #include "Http2Frame.hpp" #include "CodecHttp2.hpp" +#include "channel/SocketChannel.hpp" namespace neb { -Http2Stream::Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, uint32 uiStreamId) - : Codec(pLogger, eCodecType), +Http2Stream::Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel, uint32 uiStreamId) + : Codec(pLogger, eCodecType, pBindChannel), m_eStreamState(H2_STREAM_IDLE), m_uiStreamId(uiStreamId), m_bEndHeaders(false), m_pFrame(nullptr) { #if __cplusplus >= 201401L - m_pFrame = std::make_unique(pLogger, eCodecType, this); + m_pFrame = std::make_unique(pLogger, eCodecType, pBindChannel, this); #else - m_pFrame = std::unique_ptr(new Http2Frame(pLogger, eCodecType, this)); + m_pFrame = std::unique_ptr(new Http2Frame(pLogger, eCodecType, pBindChannel, this)); #endif } @@ -247,7 +249,7 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, { if (CODEC_STATUS_OK == eStatus || CODEC_STATUS_PART_OK == eStatus) { - if (pCodecH2->IsClient()) + if (pCodecH2->GetBindChannel()->IsClient()) { if (m_oHttpMsg.stream_id() & 0x01) { diff --git a/src/codec/http2/Http2Stream.hpp b/src/codec/http2/Http2Stream.hpp index 8587c3fa..237525ce 100644 --- a/src/codec/http2/Http2Stream.hpp +++ b/src/codec/http2/Http2Stream.hpp @@ -38,7 +38,8 @@ class CodecHttp2; class Http2Stream: public Codec { public: - Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, uint32 uiStreamId); + Http2Stream(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel, uint32 uiStreamId); virtual ~Http2Stream(); virtual E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff) diff --git a/src/ios/ActorWatcher.cpp b/src/ios/ActorWatcher.cpp new file mode 100644 index 00000000..a24b2222 --- /dev/null +++ b/src/ios/ActorWatcher.cpp @@ -0,0 +1,63 @@ +/******************************************************************************* + * Project: Nebula + * @file ActorWatcher.cpp + * @brief + * @author Bwar + * @date: 2021-10-03 + * @note + * Modify history: + ******************************************************************************/ +#include "ActorWatcher.hpp" + +namespace neb +{ + +ActorWatcher::ActorWatcher() + : m_pTimerWatcher(nullptr), m_pActor(nullptr) +{ +} + +ActorWatcher::ActorWatcher(std::shared_ptr pActor) + : m_pTimerWatcher(nullptr), m_pActor(pActor) +{ +} + +ActorWatcher::~ActorWatcher() +{ + Reset(); +} + +ev_timer* ActorWatcher::MutableTimerWatcher() +{ + if (nullptr == m_pTimerWatcher) + { + m_pTimerWatcher = new ev_timer(); + if (nullptr != m_pTimerWatcher) + { + memset(m_pTimerWatcher, 0, sizeof(ev_timer)); + m_pTimerWatcher->data = this; + } + } + return(m_pTimerWatcher); +} + +void ActorWatcher::Set(std::shared_ptr pActor) +{ + if (m_pActor == nullptr) + { + m_pActor = pActor; + } +} + +void ActorWatcher::Reset() +{ + m_pActor = nullptr; + if (nullptr != m_pTimerWatcher) + { + delete m_pTimerWatcher; + m_pTimerWatcher = nullptr; + } +} + +} /* namespace neb */ + diff --git a/src/ios/ActorWatcher.hpp b/src/ios/ActorWatcher.hpp new file mode 100644 index 00000000..25bb205f --- /dev/null +++ b/src/ios/ActorWatcher.hpp @@ -0,0 +1,54 @@ +/******************************************************************************* + * Project: Nebula + * @file ActorWatcher.hpp + * @brief + * @author Bwar + * @date: 2021-10-03 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_IOS_ACTORWATCHER_HPP_ +#define SRC_IOS_ACTORWATCHER_HPP_ + +#include + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif +#include "ev.h" +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +namespace neb +{ + +class Actor; + +class ActorWatcher +{ +public: + ActorWatcher(); + ActorWatcher(std::shared_ptr pActor); + virtual ~ActorWatcher(); + + ev_timer* MutableTimerWatcher(); + + inline std::shared_ptr GetActor() const + { + return(m_pActor); + } + + void Set(std::shared_ptr pActor); + void Reset(); + +private: + ev_timer* m_pTimerWatcher; + std::shared_ptr m_pActor; +}; + +} /* namespace neb */ + +#endif /* SRC_IOS_ACTORWATCHER_HPP_ */ + diff --git a/src/ios/ChannelWatcher.cpp b/src/ios/ChannelWatcher.cpp new file mode 100644 index 00000000..4b2bc209 --- /dev/null +++ b/src/ios/ChannelWatcher.cpp @@ -0,0 +1,90 @@ +/******************************************************************************* + * Project: Nebula + * @file ChannelWatcher.cpp + * @brief + * @author Bwar + * @date: 2021-10-03 + * @note + * Modify history: + ******************************************************************************/ +#include "ChannelWatcher.hpp" +#include "channel/SocketChannel.hpp" + +namespace neb +{ + +ChannelWatcher::ChannelWatcher() + : m_pTimerWatcher(nullptr), m_pIoWatcher(nullptr), + m_pSocketChannel(nullptr) +{ +} + +ChannelWatcher::ChannelWatcher(std::shared_ptr pChannel) + : m_pTimerWatcher(nullptr), m_pIoWatcher(nullptr), + m_pSocketChannel(pChannel) +{ +} + +ChannelWatcher::~ChannelWatcher() +{ + Reset(); +} + +ev_io* ChannelWatcher::MutableIoWatcher() +{ + if (nullptr == m_pSocketChannel) + { + return(nullptr); + } + if (nullptr == m_pIoWatcher) + { + m_pIoWatcher = new ev_io(); + if (nullptr != m_pIoWatcher) + { + memset(m_pIoWatcher, 0, sizeof(ev_io)); + m_pIoWatcher->data = this; + m_pIoWatcher->fd = m_pSocketChannel->GetFd(); + } + } + return(m_pIoWatcher); +} + +ev_timer* ChannelWatcher::MutableTimerWatcher() +{ + if (nullptr == m_pTimerWatcher) + { + m_pTimerWatcher = new ev_timer(); + if (nullptr != m_pTimerWatcher) + { + memset(m_pTimerWatcher, 0, sizeof(ev_timer)); + m_pTimerWatcher->data = this; + } + } + return(m_pTimerWatcher); +} + +void ChannelWatcher::Set(std::shared_ptr pChannel) +{ + if (m_pSocketChannel == nullptr) + { + m_pSocketChannel = pChannel; + } +} + +void ChannelWatcher::Reset() +{ + m_pSocketChannel = nullptr; + if (nullptr != m_pTimerWatcher) + { + delete m_pTimerWatcher; + m_pTimerWatcher = nullptr; + } + if (nullptr != m_pIoWatcher) + { + delete m_pIoWatcher; + m_pIoWatcher = nullptr; + } +} + +} /* namespace neb */ + diff --git a/src/ios/ChannelWatcher.hpp b/src/ios/ChannelWatcher.hpp new file mode 100644 index 00000000..5722af6b --- /dev/null +++ b/src/ios/ChannelWatcher.hpp @@ -0,0 +1,64 @@ +/******************************************************************************* + * Project: Nebula + * @file ChannelWatcher.hpp + * @brief + * @author Bwar + * @date: 2021-10-03 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_IOS_CHANNELWATCHER_HPP_ +#define SRC_IOS_CHANNELWATCHER_HPP_ + +#include + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif +#include "ev.h" +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +namespace neb +{ + +class SocketChannel; + +enum class ACTOR_CB_TYPE +{ + ACTOR_CB_NONE = 0x00, ///< 未定义回调 + ACTOR_CB_TIMER = 0x01, ///< 定时器回调 + ACTOR_CB_IO_SEQ = 0x02, ///< IO seq回调 + ACTOR_CB_IO_POINTER = 0x04, ///< IO 指针回调 +}; + +class ChannelWatcher +{ +public: + ChannelWatcher(); + ChannelWatcher(std::shared_ptr pChannel); + virtual ~ChannelWatcher(); + + ev_io* MutableIoWatcher(); + ev_timer* MutableTimerWatcher(); + + inline std::shared_ptr GetSocketChannel() const + { + return(m_pSocketChannel); + } + + void Set(std::shared_ptr pChannel); + void Reset(); + +private: + ev_timer* m_pTimerWatcher; + ev_io* m_pIoWatcher; + std::shared_ptr m_pSocketChannel; +}; + +} /* namespace neb */ + +#endif /* SRC_IOS_CHANNELWATCHER_HPP_ */ + diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 830a69a7..622ec702 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -13,10 +13,14 @@ #include "Definition.hpp" #include "labor/Manager.hpp" #include "labor/Worker.hpp" +#include "IO.hpp" +#include "ChannelWatcher.hpp" #include "actor/Actor.hpp" #include "actor/step/Step.hpp" #include "actor/step/RedisStep.hpp" #include "actor/session/sys_session/manager/SessionManager.hpp" +#include "codec/CodecFactory.hpp" +#include "channel/SocketChannelImpl.hpp" namespace neb { @@ -39,20 +43,20 @@ void Dispatcher::IoCallback(struct ev_loop* loop, struct ev_io* watcher, int rev { if (watcher->data != NULL) { - SocketChannel* pChannel = static_cast(watcher->data); + auto pWatcher = static_cast(watcher->data); + auto pChannel = pWatcher->GetSocketChannel(); Dispatcher* pDispatcher = pChannel->m_pImpl->GetLabor()->GetDispatcher(); - std::shared_ptr pSharedChannel = pChannel->shared_from_this(); if (revents & EV_READ) { - pDispatcher->OnIoRead(pSharedChannel); + pDispatcher->OnIoRead(pChannel); } - if ((revents & EV_WRITE) && (CHANNEL_STATUS_CLOSED != pChannel->m_pImpl->GetChannelStatus())) // the channel maybe closed by OnIoRead() + if ((revents & EV_WRITE) && (CHANNEL_STATUS_CLOSED != pChannel->GetChannelStatus())) // the channel maybe closed by OnIoRead() { - pDispatcher->OnIoWrite(pSharedChannel); + pDispatcher->OnIoWrite(pChannel); } if (revents & EV_ERROR) { - pDispatcher->OnIoError(pSharedChannel); + pDispatcher->OnIoError(pChannel); } } } @@ -61,13 +65,14 @@ void Dispatcher::IoTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int { if (watcher->data != NULL) { - SocketChannel* pChannel = static_cast(watcher->data); + auto pWatcher = static_cast(watcher->data); + auto pChannel = pWatcher->GetSocketChannel(); Dispatcher* pDispatcher = pChannel->m_pImpl->GetLabor()->GetDispatcher(); - if (pChannel->m_pImpl->GetFd() < 3) // TODO 这个判断是不得已的做法,需查找fd为0回调到这里的原因 + if (pChannel->GetFd() < 3) // TODO 查找fd为0回调到这里的原因 { return; } - pDispatcher->OnIoTimeout(pChannel->shared_from_this()); + pDispatcher->OnIoTimeout(pChannel); } } @@ -119,16 +124,16 @@ void Dispatcher::ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_tim bool Dispatcher::OnIoRead(std::shared_ptr pChannel) { - LOG4_TRACE("fd[%d]", pChannel->m_pImpl->GetFd()); + LOG4_TRACE("fd[%d]", pChannel->GetFd()); m_pLastActivityChannel = pChannel; if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) { - if (pChannel->m_pImpl->GetFd() == ((Manager*)m_pLabor)->GetManagerInfo().iS2SListenFd) + if (pChannel->GetFd() == ((Manager*)m_pLabor)->GetManagerInfo().iS2SListenFd) { - return(AcceptServerConn(pChannel->m_pImpl->GetFd())); + return(AcceptServerConn(pChannel->GetFd())); } else if (((Manager*)m_pLabor)->GetManagerInfo().iC2SListenFd > 2 - && pChannel->m_pImpl->GetFd() == ((Manager*)m_pLabor)->GetManagerInfo().iC2SListenFd) + && pChannel->GetFd() == ((Manager*)m_pLabor)->GetManagerInfo().iC2SListenFd) { return(AcceptFdAndTransfer(((Manager*)m_pLabor)->GetManagerInfo().iC2SListenFd, ((Manager*)m_pLabor)->GetManagerInfo().iC2SFamily)); @@ -140,9 +145,9 @@ bool Dispatcher::OnIoRead(std::shared_ptr pChannel) } else if (Labor::LABOR_WORKER == m_pLabor->GetLaborType() || Labor::LABOR_LOADER == m_pLabor->GetLaborType()) { - if (pChannel->m_pImpl->GetFd() == ((Worker*)m_pLabor)->GetWorkerInfo().iDataFd) + if (pChannel->GetFd() == ((Worker*)m_pLabor)->GetWorkerInfo().iDataFd) { - return(FdTransfer(pChannel->m_pImpl->GetFd())); + return(FdTransfer(pChannel->GetFd())); } else { @@ -157,328 +162,40 @@ bool Dispatcher::OnIoRead(std::shared_ptr pChannel) bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) { - LOG4_TRACE(" "); - E_CODEC_STATUS eCodecStatus; - switch(pChannel->GetCodecType()) - { - case CODEC_HTTP: - case CODEC_HTTP2: - for (int i = 0; ; ++i) - { - HttpMsg oHttpMsg; - if (0 == i) - { - eCodecStatus = pChannel->m_pImpl->Recv(oHttpMsg); - } - else - { - eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); - } - - if (CODEC_STATUS_OK == eCodecStatus - || CODEC_STATUS_PART_OK == eCodecStatus) - { - if (oHttpMsg.http_major() > 1) - { - if (oHttpMsg.stream_id() > 0) - { - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg, eCodecStatus); - } - } - else - { - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); - } - } - else if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close - { - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); - } - else - { - break; - } - } - break; - case CODEC_RESP: - for (int i = 0; ; ++i) - { - RedisMsg oRedisMsg; - if (0 == i) - { - eCodecStatus = pChannel->m_pImpl->Recv(oRedisMsg); - } - else - { - eCodecStatus = pChannel->m_pImpl->Fetch(oRedisMsg); - } - - if (CODEC_STATUS_OK == eCodecStatus) - { - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oRedisMsg); - } - else - { - break; - } - } - break; - case CODEC_UNKNOW: - { - CBuffer oBuff; - for (int i = 0; ; ++i) - { - RedisMsg oRedisMsg; - if (0 == i) - { - eCodecStatus = pChannel->m_pImpl->Recv(oBuff); - } - else - { - eCodecStatus = pChannel->m_pImpl->Fetch(oBuff); - } - - if (CODEC_STATUS_OK == eCodecStatus) - { - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oBuff); - } - else - { - break; - } - } - } - break; - default: - for (int i = 0; ; ++i) - { - MsgHead oMsgHead; - MsgBody oMsgBody; - if (0 == i) - { - eCodecStatus = pChannel->m_pImpl->Recv(oMsgHead, oMsgBody); - } - else - { - eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); - } - - if (CODEC_STATUS_OK == eCodecStatus) - { - /* - if (m_pLabor->GetNodeInfo().bChannelVerify && !pChannel->m_pImpl->IsChannelVerify()) - { - if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() - && CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType() - && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 - { - LOG4_TRACE("invalid request, please login first!"); - DiscardSocketChannel(pChannel); - return(false); - } - } - */ - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); - } - else - { - break; - } - } - } + LOG4_TRACE("codec type %d", pChannel->GetCodecType()); + E_CODEC_STATUS eCodecStatus = CodecFactory::OnEvent(this, pChannel); switch (eCodecStatus) { - case CODEC_STATUS_PAUSE: + case CODEC_STATUS_PAUSE: case CODEC_STATUS_PART_OK: case CODEC_STATUS_PART_ERR: return(true); - case CODEC_STATUS_WANT_WRITE: - return(SendTo(pChannel)); - case CODEC_STATUS_WANT_READ: - RemoveIoWriteEvent(pChannel); + case CODEC_STATUS_WANT_WRITE: return(true); - default: // 编解码出错或连接关闭或连接中断 - if (CODEC_STATUS_INVALID == eCodecStatus && !pChannel->IsClient()) - { - if (pChannel->m_pImpl->AutoSwitchCodec()) - { - return(DataFetchAndHandle(pChannel)); - } - } - if (CHANNEL_STATUS_ESTABLISHED != pChannel->m_pImpl->GetChannelStatus()) - { - if (pChannel->IsClient()) - { - m_pSessionNode->NodeFailed(pChannel->GetIdentify()); - } - auto& listUncompletedStep = pChannel->m_pImpl->GetPipelineStepSeq(); - for (auto it = listUncompletedStep.begin(); - it != listUncompletedStep.end(); ++it) - { - m_pLabor->GetActorBuilder()->OnError(pChannel, *it, - pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); - } - auto& mapUncompletedStep = pChannel->m_pImpl->GetStreamStepSeq(); - for (auto it = mapUncompletedStep.begin(); - it != mapUncompletedStep.end(); ++it) - { - m_pLabor->GetActorBuilder()->OnError(pChannel, it->second, - pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); - } - } - LOG4_INFO("eCodecStatus = %d", eCodecStatus); - DiscardSocketChannel(pChannel); - return(false); - } -} - -bool Dispatcher::DataFetchAndHandle(std::shared_ptr pChannel) -{ - LOG4_TRACE(" "); - E_CODEC_STATUS eCodecStatus; - switch(pChannel->GetCodecType()) - { - case CODEC_HTTP: - case CODEC_HTTP2: - { - HttpMsg oHttpMsg; - eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); - while (CODEC_STATUS_OK == eCodecStatus - || CODEC_STATUS_PART_OK == eCodecStatus) - { - if (oHttpMsg.http_major() > 1) - { - if (oHttpMsg.stream_id() > 0) - { - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg, eCodecStatus); - } - } - else - { - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); - } - eCodecStatus = pChannel->m_pImpl->Fetch(oHttpMsg); - } - if (CODEC_STATUS_EOF == eCodecStatus && oHttpMsg.ByteSize() > 10) // http1.0 client close - { - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oHttpMsg); - } - } - break; - case CODEC_RESP: - for (int i = 0; ; ++i) - { - RedisMsg oRedisMsg; - eCodecStatus = pChannel->m_pImpl->Fetch(oRedisMsg); - if (CODEC_STATUS_OK == eCodecStatus) - { - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oRedisMsg); - } - else - { - break; - } - } - break; - case CODEC_UNKNOW: - { - CBuffer oBuff; - for (int i = 0; ; ++i) - { - RedisMsg oRedisMsg; - eCodecStatus = pChannel->m_pImpl->Fetch(oBuff); - if (CODEC_STATUS_OK == eCodecStatus) - { - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oBuff); - } - else - { - break; - } - } - } - break; - default: - for (int i = 0; ; ++i) - { - MsgHead oMsgHead; - MsgBody oMsgBody; - eCodecStatus = pChannel->m_pImpl->Fetch(oMsgHead, oMsgBody); - if (CODEC_STATUS_OK == eCodecStatus) - { - /* - if (m_pLabor->GetNodeInfo().bChannelVerify && !pChannel->m_pImpl->IsChannelVerify()) - { - if (CODEC_NEBULA != pChannel->m_pImpl->GetCodecType() - && CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType() - && pChannel->m_pImpl->GetMsgNum() > 1) // 未经账号验证的客户端连接发送数据过来,直接断开 - { - LOG4_TRACE("invalid request, please login first!"); - DiscardSocketChannel(pChannel); - return(false); - } - } - */ - m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - m_pLabor->GetActorBuilder()->OnMessage(pChannel, oMsgHead, oMsgBody); - } - else - { - break; - } - } - } - - switch (eCodecStatus) - { - case CODEC_STATUS_PAUSE: - case CODEC_STATUS_PART_OK: - case CODEC_STATUS_PART_ERR: - return(true); - case CODEC_STATUS_WANT_WRITE: - return(SendTo(pChannel)); - case CODEC_STATUS_WANT_READ: + case CODEC_STATUS_WANT_READ: RemoveIoWriteEvent(pChannel); return(true); default: // 编解码出错或连接关闭或连接中断 - if (CODEC_STATUS_INVALID == eCodecStatus && !pChannel->IsClient()) - { - if (pChannel->m_pImpl->AutoSwitchCodec()) - { - return(DataFetchAndHandle(pChannel)); - } - } - if (CHANNEL_STATUS_ESTABLISHED != pChannel->m_pImpl->GetChannelStatus()) + if (CHANNEL_STATUS_ESTABLISHED != pChannel->GetChannelStatus()) { if (pChannel->IsClient()) { m_pSessionNode->NodeFailed(pChannel->GetIdentify()); } - auto& listUncompletedStep = pChannel->m_pImpl->GetPipelineStepSeq(); + auto& listUncompletedStep = std::static_pointer_cast>(pChannel->m_pImpl)->GetPipelineStepSeq(); for (auto it = listUncompletedStep.begin(); it != listUncompletedStep.end(); ++it) { m_pLabor->GetActorBuilder()->OnError(pChannel, *it, - pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); + pChannel->GetErrno(), pChannel->GetErrMsg()); } - auto& mapUncompletedStep = pChannel->m_pImpl->GetStreamStepSeq(); + auto& mapUncompletedStep = std::static_pointer_cast>(pChannel->m_pImpl)->GetStreamStepSeq(); for (auto it = mapUncompletedStep.begin(); it != mapUncompletedStep.end(); ++it) { m_pLabor->GetActorBuilder()->OnError(pChannel, it->second, - pChannel->m_pImpl->GetErrno(), pChannel->m_pImpl->GetErrMsg()); + pChannel->GetErrno(), pChannel->GetErrMsg()); } } LOG4_INFO("eCodecStatus = %d", eCodecStatus); @@ -557,7 +274,7 @@ bool Dispatcher::FdTransfer(int iFd) { inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientAddr, sizeof(szClientAddr)); LOG4_TRACE("set fd %d's remote addr \"%s\"", iAcceptFd, szClientAddr); - pChannel->m_pImpl->SetRemoteAddr(std::string(szClientAddr)); + std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteAddr(std::string(szClientAddr)); } else { @@ -575,7 +292,7 @@ bool Dispatcher::FdTransfer(int iFd) { inet_ntop(AF_INET6, &stClientAddr.sin6_addr, szClientAddr, sizeof(szClientAddr)); LOG4_TRACE("set fd %d's remote addr \"%s\"", iAcceptFd, szClientAddr); - pChannel->m_pImpl->SetRemoteAddr(std::string(szClientAddr)); + std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteAddr(std::string(szClientAddr)); } else { @@ -596,13 +313,13 @@ bool Dispatcher::FdTransfer(int iFd) } else if (CODEC_NEBULA_IN_NODE == iCodec) { - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); m_mapLoaderAndWorkerChannel.insert(std::make_pair(pChannel->GetFd(), pChannel)); m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); } else { - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); AddIoTimeout(pChannel, 1.0); // 为了防止大量连接攻击,初始化连接只有一秒即超时,在正常发送第一个数据包之后才采用正常配置的网络IO超时检查 } return(true); @@ -617,9 +334,9 @@ bool Dispatcher::FdTransfer(int iFd) bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) { - if (CODEC_NEBULA == pChannel->m_pImpl->GetCodecType()) // 系统内部Server间通信 + if (CODEC_NEBULA == pChannel->GetCodecType()) // 系统内部Server间通信 { - if (pChannel->m_pImpl->GetRemoteWorkerIndex() < 0) // connect to Manager + if (std::static_pointer_cast>(pChannel->m_pImpl)->GetRemoteWorkerIndex() < 0) // connect to Manager { std::shared_ptr pStepTellWorker = m_pLabor->GetActorBuilder()->MakeSharedStep(nullptr, "neb::StepTellWorker", pChannel); @@ -628,14 +345,14 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) return(false); } pStepTellWorker->Emit(ERR_OK); - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); } else { - if (CHANNEL_STATUS_TRY_CONNECT == pChannel->m_pImpl->GetChannelStatus()) // connect之后的第一个写事件 + if (CHANNEL_STATUS_TRY_CONNECT == pChannel->GetChannelStatus()) // connect之后的第一个写事件 { std::shared_ptr pStepConnectWorker = m_pLabor->GetActorBuilder()->MakeSharedStep( - nullptr, "neb::StepConnectWorker", pChannel, pChannel->m_pImpl->GetRemoteWorkerIndex()); + nullptr, "neb::StepConnectWorker", pChannel, std::static_pointer_cast>(pChannel->m_pImpl)->GetRemoteWorkerIndex()); if (nullptr == pStepConnectWorker) { LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); @@ -651,17 +368,17 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) } else { - if (CHANNEL_STATUS_CLOSED != pChannel->m_pImpl->GetChannelStatus()) + if (CHANNEL_STATUS_CLOSED != pChannel->GetChannelStatus()) { - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); } } - if (pChannel->IsClient() && pChannel->m_pImpl->GetMsgNum() == 0) + if (pChannel->IsClient() && std::static_pointer_cast>(pChannel->m_pImpl)->GetMsgNum() == 0) { m_pSessionNode->NodeRecover(pChannel->GetIdentify()); } - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(); + E_CODEC_STATUS eCodecStatus = std::static_pointer_cast>(pChannel->m_pImpl)->Send(); if (CODEC_STATUS_OK == eCodecStatus) { RemoveIoWriteEvent(pChannel); @@ -676,7 +393,7 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) } else { - LOG4_INFO("%s fd[%d]", pChannel->GetIdentify().c_str(), pChannel->GetFd()); + LOG4_INFO("%s channel[%d]", pChannel->GetIdentify().c_str(), pChannel->GetFd()); DiscardSocketChannel(pChannel); } return(true); @@ -691,17 +408,18 @@ bool Dispatcher::OnIoError(std::shared_ptr pChannel) bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) { //ev_tstamp after = pChannel->m_pImpl->GetActiveTime() - ev_now(m_loop) + m_pLabor->GetNodeInfo().dIoTimeout; - ev_tstamp after = pChannel->m_pImpl->GetActiveTime() - ev_now(m_loop) + pChannel->m_pImpl->GetKeepAlive(); + ev_tstamp after = std::static_pointer_cast>(pChannel->m_pImpl)->GetActiveTime() + - ev_now(m_loop) + std::static_pointer_cast>(pChannel->m_pImpl)->GetKeepAlive(); if (after > 0) // IO在定时时间内被重新刷新过,重新设置定时器 { - ev_timer_stop (m_loop, pChannel->m_pImpl->MutableTimerWatcher()); - ev_timer_set (pChannel->m_pImpl->MutableTimerWatcher(), after + ev_time() - ev_now(m_loop), 0); - ev_timer_start (m_loop, pChannel->m_pImpl->MutableTimerWatcher()); + ev_timer_stop (m_loop, pChannel->MutableWatcher()->MutableTimerWatcher()); + ev_timer_set (pChannel->MutableWatcher()->MutableTimerWatcher(), after + ev_time() - ev_now(m_loop), 0); + ev_timer_start (m_loop, pChannel->MutableWatcher()->MutableTimerWatcher()); return(true); } - LOG4_TRACE("fd %d, seq %u:", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - if (pChannel->m_pImpl->NeedAliveCheck()) // 需要发送心跳检查 + LOG4_TRACE("fd %d, seq %u:", pChannel->GetFd(), pChannel->GetSequence()); + if (std::static_pointer_cast>(pChannel->m_pImpl)->NeedAliveCheck()) // 需要发送心跳检查 { std::shared_ptr pStepIoTimeout = m_pLabor->GetActorBuilder()->MakeSharedStep( nullptr, "neb::StepIoTimeout", pChannel); @@ -720,8 +438,8 @@ bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) } else // 关闭文件描述符并清理相关资源 { - if ((CODEC_NEBULA != pChannel->m_pImpl->GetCodecType()) - && (CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType())) // 非内部服务器间的连接才会在超时中关闭 + if ((CODEC_NEBULA != pChannel->GetCodecType()) + && (CODEC_NEBULA_IN_NODE != pChannel->GetCodecType())) // 非内部服务器间的连接才会在超时中关闭 { LOG4_TRACE("io timeout!"); DiscardSocketChannel(pChannel); @@ -760,8 +478,10 @@ void Dispatcher::EventRun() bool Dispatcher::AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout) { - LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_timer* timer_watcher = pChannel->m_pImpl->MutableTimerWatcher(); + LOG4_TRACE("%d, %u", pChannel->GetFd(), pChannel->GetSequence()); + auto pWatcher = pChannel->MutableWatcher(); + pWatcher->Set(pChannel); + ev_timer* timer_watcher = pWatcher->MutableTimerWatcher(); if (NULL == timer_watcher) { return(false); @@ -787,7 +507,7 @@ bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBod { if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) { - return(Broadcast("BEACON", SOCK_STREAM, CODEC_NEBULA, false, true, iCmd, uiSeq, oMsgBody)); + return(IO::Broadcast(this, 0, "BEACON", false, true, iCmd, uiSeq, oMsgBody)); } else { @@ -797,24 +517,7 @@ bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBod LOG4_ERROR("no connected channel to manager!"); return(false); } - E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - m_pLabor->IoStatAddSendNum(pChannel->GetFd()); - if (CODEC_STATUS_OK == eStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) - { - AddIoWriteEvent(pChannel); - return(true); - } - else if (CODEC_STATUS_WANT_READ == eStatus) - { - RemoveIoWriteEvent(pChannel); - return(true); - } - return(false); + return(IO::SendRequest(this, 0, pChannel, iCmd, uiSeq, oMsgBody)); } } @@ -892,18 +595,8 @@ std::shared_ptr Dispatcher::StressSend(const std::string& strIden AddIoTimeout(pChannel, 1.5); AddIoReadEvent(pChannel); AddIoWriteEvent(pChannel); - pChannel->m_pImpl->SetRemoteAddr(strHost); - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - m_pLabor->IoStatAddSendNum(pChannel->GetFd()); - if (CODEC_STATUS_OK != eCodecStatus - && CODEC_STATUS_PAUSE != eCodecStatus - && CODEC_STATUS_WANT_WRITE != eCodecStatus - && CODEC_STATUS_WANT_READ != eCodecStatus) - { - LOG4_TRACE("eStatus = %d, %s", eCodecStatus, pChannel->GetIdentify().c_str()); - DiscardSocketChannel(pChannel); - } - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); + std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteAddr(strHost); + IO::SendRequest(this, 0, pChannel, iCmd, uiSeq, oMsgBody); return(pChannel); } else @@ -930,29 +623,17 @@ bool Dispatcher::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) return(false); } } - E_CODEC_STATUS eStatus = m_iterLoaderAndWorkerChannel->second->m_pImpl->Send(iCmd, uiSeq, oMsgBody); - //m_pLabor->IoStatAddSendNum(pChannel->GetFd()); - m_pLastActivityChannel = m_iterLoaderAndWorkerChannel->second; - if (CODEC_STATUS_OK == eStatus) - { - RemoveIoWriteEvent(m_iterLoaderAndWorkerChannel->second); - m_iterLoaderAndWorkerChannel++; - return(true); - } - else if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) + bool bRes = false; + if (gc_uiCmdReq & iCmd) { - AddIoWriteEvent(m_iterLoaderAndWorkerChannel->second); - m_iterLoaderAndWorkerChannel++; - return(true); + bRes = IO::SendRequest(this, 0, m_iterLoaderAndWorkerChannel->second, iCmd, uiSeq, oMsgBody); } - else if (CODEC_STATUS_WANT_READ == eStatus) + else { - RemoveIoWriteEvent(m_iterLoaderAndWorkerChannel->second); - m_iterLoaderAndWorkerChannel++; - return(true); + bRes = IO::SendResponse(this, m_iterLoaderAndWorkerChannel->second, iCmd, uiSeq, oMsgBody); } m_iterLoaderAndWorkerChannel++; - return(false); + return(bRes); } bool Dispatcher::SendTo(int iFd, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) @@ -960,7 +641,16 @@ bool Dispatcher::SendTo(int iFd, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBo auto iter = m_mapSocketChannel.find(iFd); if (iter != m_mapSocketChannel.end()) { - return(SendTo(iter->second, iCmd, uiSeq, oMsgBody)); + bool bRes = false; + if (gc_uiCmdReq & iCmd) + { + bRes = IO::SendRequest(this, 0, iter->second, iCmd, uiSeq, oMsgBody); + } + else + { + bRes = IO::SendResponse(this, iter->second, iCmd, uiSeq, oMsgBody); + } + return(bRes); } return(false); } @@ -1009,8 +699,8 @@ bool Dispatcher::DiscardNamedChannel(const std::string& strIdentify) for (auto channel_iter = named_iter->second.begin(); channel_iter != named_iter->second.end(); ++channel_iter) { - (*channel_iter)->m_pImpl->SetIdentify(""); - (*channel_iter)->m_pImpl->SetClientData(""); + std::static_pointer_cast>((*channel_iter)->m_pImpl)->SetIdentify(""); + std::static_pointer_cast>((*channel_iter)->m_pImpl)->SetClientData(""); } named_iter->second.clear(); m_mapNamedSocketChannel.erase(named_iter); @@ -1018,11 +708,6 @@ bool Dispatcher::DiscardNamedChannel(const std::string& strIdentify) } } -Codec* Dispatcher::SwitchCodec(std::shared_ptr pChannel, E_CODEC_TYPE eCodecType, bool bIsUpgrade) -{ - return(pChannel->m_pImpl->SwitchCodec(eCodecType, m_pLabor->GetNodeInfo().dIoTimeout, bIsUpgrade)); -} - bool Dispatcher::AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel) { LOG4_TRACE("%s", strIdentify.c_str()); @@ -1037,7 +722,7 @@ bool Dispatcher::AddNamedSocketChannel(const std::string& strIdentify, std::shar { named_iter->second.insert(pChannel); } - pChannel->m_pImpl->SetIdentify(strIdentify); + std::static_pointer_cast>(pChannel->m_pImpl)->SetIdentify(strIdentify); return(true); } @@ -1056,7 +741,7 @@ void Dispatcher::DelNamedSocketChannel(const std::string& strIdentify) void Dispatcher::SetChannelIdentify(std::shared_ptr pChannel, const std::string& strIdentify) { - pChannel->m_pImpl->SetIdentify(strIdentify); + std::static_pointer_cast>(pChannel->m_pImpl)->SetIdentify(strIdentify); } void Dispatcher::AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify) @@ -1095,7 +780,7 @@ void Dispatcher::CircuitBreak(const std::string& strIdentify) void Dispatcher::SetClientData(std::shared_ptr pChannel, const std::string& strClientData) { - pChannel->m_pImpl->SetClientData(strClientData); + std::static_pointer_cast>(pChannel->m_pImpl)->SetClientData(strClientData); } bool Dispatcher::IsNodeType(const std::string& strNodeIdentify, const std::string& strNodeType) @@ -1103,6 +788,16 @@ bool Dispatcher::IsNodeType(const std::string& strNodeIdentify, const std::strin return(m_pSessionNode->IsNodeType(strNodeIdentify, strNodeType)); } +bool Dispatcher::GetAuth(const std::string& strNodeType, std::string& strAuth, std::string& strPassword) +{ + return(m_pSessionNode->GetAuth(strNodeType, strAuth, strPassword)); +} + +void Dispatcher::SetAuth(const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword) +{ + m_pSessionNode->SetAuth(strNodeType, strAuth, strPassword); +} + bool Dispatcher::AddEvent(ev_signal* signal_watcher, signal_callback pFunc, int iSignum) { if (NULL == signal_watcher) @@ -1276,11 +971,6 @@ bool Dispatcher::Init() #else m_pSessionNode = std::unique_ptr(new Nodes()); #endif - Codec::AddAutoSwitchCodecType(CODEC_HTTP); - Codec::AddAutoSwitchCodecType(CODEC_PROTO); - Codec::AddAutoSwitchCodecType(CODEC_RESP); - Codec::AddAutoSwitchCodecType(CODEC_HTTP2); - Codec::AddAutoSwitchCodecType(CODEC_PRIVATE); return(true); } @@ -1313,13 +1003,13 @@ std::shared_ptr Dispatcher::CreateSocketChannel(int iFd, E_CODEC_ { if (m_pLabor->GetNodeInfo().dConnectionProtection > 0) { - pChannel = std::make_shared(m_pLogger, iFd, - m_pLabor->GetSequence(), bWithSsl, m_pLabor->GetNodeInfo().dConnectionProtection); + pChannel = std::make_shared(m_pLabor, m_pLogger, iFd, + m_pLabor->GetSequence(), bWithSsl, bIsClient, m_pLabor->GetNodeInfo().dConnectionProtection); } else { - pChannel = std::make_shared(m_pLogger, iFd, - m_pLabor->GetSequence(), bWithSsl, m_pLabor->GetNodeInfo().dIoTimeout); + pChannel = std::make_shared(m_pLabor, m_pLogger, iFd, + m_pLabor->GetSequence(), bWithSsl, bIsClient, m_pLabor->GetNodeInfo().dIoTimeout); } } catch(std::bad_alloc& e) @@ -1327,21 +1017,20 @@ std::shared_ptr Dispatcher::CreateSocketChannel(int iFd, E_CODEC_ LOG4_ERROR("new channel for fd %d error: %s", e.what()); return(nullptr); } - pChannel->m_pImpl->SetLabor(m_pLabor); - bool bInitResult = pChannel->Init(eCodecType, bIsClient); - if (bInitResult) + Codec* pCodec = CodecFactory::Create(m_pLogger, eCodecType, pChannel); + if (pCodec == nullptr) { - m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); - if ((CODEC_NEBULA != eCodecType) && (CODEC_NEBULA_IN_NODE != eCodecType)) - { - ++m_iClientNum; - } - return(pChannel); + LOG4_ERROR("failed to new codec with codec type %d", eCodecType); + return(nullptr); } - else + std::static_pointer_cast>(pChannel->m_pImpl)->SetCodec(pCodec); + m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); + if ((CODEC_NEBULA != eCodecType) && (CODEC_NEBULA_IN_NODE != eCodecType)) { - return(nullptr); + ++m_iClientNum; } + LOG4_TRACE("new channel[%d] with codec type %d", pChannel->GetFd(), pChannel->GetCodecType()); + return(pChannel); } else { @@ -1367,7 +1056,7 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b if ((*it)->m_pImpl->GetSequence() == pChannel->m_pImpl->GetSequence()) { named_iter->second.erase(it); - LOG4_TRACE("erase channel %d from m_mapNamedSocketChannel.", pChannel->m_pImpl->GetFd()); + LOG4_TRACE("erase channel %d from m_mapNamedSocketChannel.", pChannel->GetFd()); break; } } @@ -1377,24 +1066,37 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b } } - bool bCloseResult = pChannel->m_pImpl->Close(); + bool bCloseResult = false; + if (pChannel->WithSsl()) + { +#ifdef WITH_OPENSSL + bCloseResult = std::static_pointer_cast>(pChannel->m_pImpl)->Close(); +#else + bCloseResult = std::static_pointer_cast>(pChannel->m_pImpl)->Close(); +#endif + } + else + { + bCloseResult = std::static_pointer_cast>(pChannel->m_pImpl)->Close(); + } if (bCloseResult) { LOG4_INFO("%s disconnect, fd %d, channel_seq %u, identify %s", - pChannel->m_pImpl->GetRemoteAddr().c_str(), - pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence(), - pChannel->m_pImpl->GetIdentify().c_str()); + pChannel->GetRemoteAddr().c_str(), + pChannel->GetFd(), pChannel->GetSequence(), + pChannel->GetIdentify().c_str()); if (bChannelNotice) { - m_pLabor->GetActorBuilder()->ChannelNotice(pChannel, pChannel->m_pImpl->GetIdentify(), pChannel->m_pImpl->GetClientData()); + m_pLabor->GetActorBuilder()->ChannelNotice(pChannel, pChannel->GetIdentify(), pChannel->GetClientData()); } - ev_io_stop (m_loop, pChannel->m_pImpl->MutableIoWatcher()); - if (nullptr != pChannel->m_pImpl->MutableTimerWatcher()) + ev_io_stop (m_loop, pChannel->MutableWatcher()->MutableIoWatcher()); + if (nullptr != pChannel->MutableWatcher()->MutableTimerWatcher()) { - ev_timer_stop (m_loop, pChannel->m_pImpl->MutableTimerWatcher()); + ev_timer_stop (m_loop, pChannel->MutableWatcher()->MutableTimerWatcher()); } + pChannel->MutableWatcher()->Reset(); - if (CODEC_NEBULA_IN_NODE == pChannel->m_pImpl->GetCodecType()) + if (CODEC_NEBULA_IN_NODE == pChannel->GetCodecType()) { auto inner_channel_iter = m_mapLoaderAndWorkerChannel.find(pChannel->GetFd()); if (inner_channel_iter != m_mapLoaderAndWorkerChannel.end()) @@ -1404,17 +1106,17 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); } - auto channel_iter = m_mapSocketChannel.find(pChannel->m_pImpl->GetFd()); + auto channel_iter = m_mapSocketChannel.find(pChannel->GetFd()); if (channel_iter != m_mapSocketChannel.end()) { m_mapSocketChannel.erase(channel_iter); - if ((CODEC_NEBULA != pChannel->m_pImpl->GetCodecType()) - && (CODEC_NEBULA_IN_NODE != pChannel->m_pImpl->GetCodecType())) + if ((CODEC_NEBULA != pChannel->GetCodecType()) + && (CODEC_NEBULA_IN_NODE != pChannel->GetCodecType())) { --m_iClientNum; } LOG4_TRACE("erase channel %d channel_seq %u from m_mapSocketChannel.", - pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); + pChannel->GetFd(), pChannel->GetSequence()); } return(true); } @@ -1426,8 +1128,10 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b bool Dispatcher::AddIoReadEvent(std::shared_ptr pChannel) { - LOG4_TRACE("fd[%d], seq[%u]", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); + LOG4_TRACE("fd[%d], seq[%u]", pChannel->GetFd(), pChannel->GetSequence()); + auto pWatcher = pChannel->MutableWatcher(); + pWatcher->Set(pChannel); + ev_io* io_watcher = pWatcher->MutableIoWatcher(); if (NULL == io_watcher || pChannel->GetFd() < 0) { return(false); @@ -1442,7 +1146,7 @@ bool Dispatcher::AddIoReadEvent(std::shared_ptr pChannel) } else { - ev_io_init (io_watcher, IoCallback, pChannel->m_pImpl->GetFd(), EV_READ); + ev_io_init (io_watcher, IoCallback, pChannel->GetFd(), EV_READ); ev_io_start (m_loop, io_watcher); } return(true); @@ -1451,8 +1155,10 @@ bool Dispatcher::AddIoReadEvent(std::shared_ptr pChannel) bool Dispatcher::AddIoWriteEvent(std::shared_ptr pChannel) { - LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); + LOG4_TRACE("%d, %u", pChannel->GetFd(), pChannel->GetSequence()); + auto pWatcher = pChannel->MutableWatcher(); + pWatcher->Set(pChannel); + ev_io* io_watcher = pWatcher->MutableIoWatcher(); if (NULL == io_watcher || pChannel->GetFd() < 0) { return(false); @@ -1467,7 +1173,7 @@ bool Dispatcher::AddIoWriteEvent(std::shared_ptr pChannel) } else { - ev_io_init (io_watcher, IoCallback, pChannel->m_pImpl->GetFd(), EV_WRITE); + ev_io_init (io_watcher, IoCallback, pChannel->GetFd(), EV_WRITE); ev_io_start (m_loop, io_watcher); } return(true); @@ -1475,8 +1181,10 @@ bool Dispatcher::AddIoWriteEvent(std::shared_ptr pChannel) } bool Dispatcher::RemoveIoWriteEvent(std::shared_ptr pChannel) { - LOG4_TRACE("%d, %u", pChannel->m_pImpl->GetFd(), pChannel->m_pImpl->GetSequence()); - ev_io* io_watcher = pChannel->m_pImpl->MutableIoWatcher(); + LOG4_TRACE("%d, %u", pChannel->GetFd(), pChannel->GetSequence()); + auto pWatcher = pChannel->MutableWatcher(); + pWatcher->Set(pChannel); + ev_io* io_watcher = pWatcher->MutableIoWatcher(); if (NULL == io_watcher || pChannel->GetFd() < 0) { return(false); @@ -1492,7 +1200,7 @@ bool Dispatcher::RemoveIoWriteEvent(std::shared_ptr pChannel) void Dispatcher::SetChannelStatus(std::shared_ptr pChannel, E_CHANNEL_STATUS eStatus) { - pChannel->m_pImpl->SetChannelStatus(eStatus); + std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(eStatus); } bool Dispatcher::AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout) @@ -1705,75 +1413,5 @@ void Dispatcher::EvBreak() ev_break (m_loop, EVBREAK_ALL); } -bool Dispatcher::Deliver(std::shared_ptr pSelfChannel) -{ - return(false); -} - -bool Dispatcher::Deliver(std::shared_ptr pSelfChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, uint32 uiStepSeq) -{ - if (uiStepSeq > 0) - { - pSelfChannel->SetStepSeq(uiStepSeq); - } - else - { - pSelfChannel->SetResponse(); - } - auto pChannel = std::dynamic_pointer_cast(pSelfChannel); - m_pLastActivityChannel = pChannel; - MsgHead oMsgHead; - oMsgHead.set_cmd(iCmd); - oMsgHead.set_seq(uiSeq); - oMsgHead.set_len(oMsgBody.ByteSize()); - return(m_pLabor->GetActorBuilder()->OnSelfMessage(pChannel, oMsgHead, oMsgBody)); -} - -bool Dispatcher::Deliver(std::shared_ptr pSelfChannel, const HttpMsg& oHttpMsg, uint32 uiStepSeq) -{ - if (uiStepSeq > 0) - { - pSelfChannel->SetStepSeq(uiStepSeq); - } - else - { - pSelfChannel->SetResponse(); - } - auto pChannel = std::dynamic_pointer_cast(pSelfChannel); - m_pLastActivityChannel = pChannel; - return(m_pLabor->GetActorBuilder()->OnSelfMessage(pChannel, oHttpMsg)); } -bool Dispatcher::Deliver(std::shared_ptr pSelfChannel, const RedisMsg& oRedisMsg, uint32 uiStepSeq) -{ - if (uiStepSeq > 0) - { - pSelfChannel->SetStepSeq(uiStepSeq); - } - else - { - pSelfChannel->SetResponse(); - } - auto pChannel = std::dynamic_pointer_cast(pSelfChannel); - m_pLastActivityChannel = pChannel; - return(m_pLabor->GetActorBuilder()->OnSelfMessage(pChannel, oRedisMsg)); -} - -bool Dispatcher::Deliver(std::shared_ptr pSelfChannel, const char* pRaw, uint32 uiRawSize, uint32 uiStepSeq) -{ - if (uiStepSeq > 0) - { - pSelfChannel->SetStepSeq(uiStepSeq); - } - else - { - pSelfChannel->SetResponse(); - } - auto pChannel = std::dynamic_pointer_cast(pSelfChannel); - m_pLastActivityChannel = pChannel; - CBuffer oBuffer; - oBuffer.Write(pRaw, uiRawSize); - return(m_pLabor->GetActorBuilder()->OnSelfMessage(pChannel, oBuffer)); -} - -} diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 29c98412..0d0a35e1 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -59,10 +59,12 @@ namespace neb class Labor; class Manager; class Worker; +class CodecFactory; class LoadStress; // not in Nebula project class Actor; class ActorBuilder; struct tagClientConnWatcherData; +template class IO; typedef void (*signal_callback)(struct ev_loop*,ev_signal*,int); typedef void (*timer_callback)(struct ev_loop*,ev_timer*,int); @@ -101,7 +103,6 @@ class Dispatcher bool OnIoRead(std::shared_ptr pChannel); bool DataRecvAndHandle(std::shared_ptr pChannel); - bool DataFetchAndHandle(std::shared_ptr pChannel); bool FdTransfer(int iFd); bool OnIoWrite(std::shared_ptr pChannel); bool OnIoError(std::shared_ptr pChannel); @@ -115,27 +116,6 @@ class Dispatcher public: bool AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout = 1.0); - template - bool SendToSelf(Targs&&... args); - template - bool SendTo(std::shared_ptr pChannel, Targs&&... args); - template - bool SendTo(const std::string& strIdentify, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); - template - bool SendTo(const std::string& strHost, int iPort, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); - template - bool AutoSend(const std::string& strIdentify, const std::string& strHost, - int iPort, int iRemoteWorkerIndex, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); - template - bool SendRoundRobin(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); - template - bool SendOriented(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, - bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args); - template - bool SendOriented(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, - bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args); - template - bool Broadcast(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args); bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); std::shared_ptr StressSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); @@ -147,7 +127,6 @@ class Dispatcher bool Disconnect(std::shared_ptr pChannel, bool bChannelNotice = true); bool Disconnect(const std::string& strIdentify, bool bChannelNotice = true); bool DiscardNamedChannel(const std::string& strIdentify); - Codec* SwitchCodec(std::shared_ptr pChannel, E_CODEC_TYPE eCodecType, bool bIsUpgrade = false); public: void SetChannelIdentify(std::shared_ptr pChannel, const std::string& strIdentify); @@ -158,6 +137,8 @@ class Dispatcher void CircuitBreak(const std::string& strIdentify); void SetClientData(std::shared_ptr pChannel, const std::string& strClientData); bool IsNodeType(const std::string& strNodeIdentify, const std::string& strNodeType); + bool GetAuth(const std::string& strNodeType, std::string& strAuth, std::string& strPassword); + void SetAuth(const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword); time_t GetNowTime() const { @@ -167,6 +148,10 @@ class Dispatcher { return((long)ev_now(m_loop) * 1000); } + std::shared_ptr GetLogger() const + { + return(m_pLogger); + } std::shared_ptr CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient = false, bool bWithSsl = false); bool DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice = true); bool CreateListenFd(const std::string& strHost, int32 iPort, int& iFd, int& iFamily); @@ -192,11 +177,6 @@ class Dispatcher bool AcceptServerConn(int iFd); void CheckFailedNode(); void EvBreak(); - bool Deliver(std::shared_ptr pSelfChannel); - bool Deliver(std::shared_ptr pSelfChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, uint32 uiStepSeq = 0); - bool Deliver(std::shared_ptr pSelfChannel, const HttpMsg& oHttpMsg, uint32 uiStepSeq = 0); - bool Deliver(std::shared_ptr pSelfChannel, const RedisMsg& oRedisMsg, uint32 uiStepSeq = 0); - bool Deliver(std::shared_ptr pSelfChannel, const char* pRaw, uint32 uiRawSize, uint32 uiStepSeq = 0); private: char* m_pErrBuff; @@ -222,6 +202,8 @@ class Dispatcher friend class Worker; friend class ActorBuilder; friend class LoadStress; + friend class CodecFactory; + template friend class IO; }; template @@ -230,381 +212,6 @@ void Dispatcher::Logger(int iLogLevel, const char* szFileName, unsigned int uiFi m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } -template -bool Dispatcher::SendToSelf(Targs&&... args) -{ - auto pSelfChannel = std::make_shared(); - return(Deliver(pSelfChannel, std::forward(args)...)); -} - -template -bool Dispatcher::SendTo(std::shared_ptr pChannel, Targs&&... args) -{ - if (pChannel->GetCodecType() == CODEC_DIRECT) - { - auto pSelfChannel = std::dynamic_pointer_cast(pChannel); - if (pSelfChannel == nullptr) - { - LOG4_ERROR("channel is not a self channel."); - return(false); - } - return(Deliver(pSelfChannel, std::forward(args)...)); - } - E_CODEC_STATUS eStatus = pChannel->m_pImpl->Send(std::forward(args)...); - m_pLabor->IoStatAddSendNum(pChannel->GetFd()); - m_pLastActivityChannel = pChannel; - switch (eStatus) - { - case CODEC_STATUS_OK: - return(true); - case CODEC_STATUS_PAUSE: - case CODEC_STATUS_WANT_WRITE: - case CODEC_STATUS_PART_OK: - AddIoWriteEvent(pChannel); - return(true); - case CODEC_STATUS_WANT_READ: - RemoveIoWriteEvent(pChannel); - return(true); - case CODEC_STATUS_EOF: // a case: http1.0 respone and close - LOG4_TRACE("eStatus = %d, %s", eStatus, pChannel->GetIdentify().c_str()); - DiscardSocketChannel(pChannel); - return(true); - default: - LOG4_TRACE("eStatus = %d, %s", eStatus, pChannel->GetIdentify().c_str()); - DiscardSocketChannel(pChannel); - return(false); - } -} - -template -bool Dispatcher::SendTo(const std::string& strIdentify, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) -{ - LOG4_TRACE("identify: %s", strIdentify.c_str()); - // 将strIdentify分割的功能只在此SendTo()函数内两处调用,定义为Dispatcher的成员函数语义上不太合适,故定义lambda表达式 - auto split = [](const std::string& strIdentify, std::string& strHost, int& iPort, int& iWorkerIndex, std::string& strError)->bool - { - size_t iPosIpPortSeparator = strIdentify.rfind(':'); - size_t iPosPortWorkerIndexSeparator = strIdentify.rfind('.'); - if (iPosIpPortSeparator == std::string::npos) - { - return(false); - } - strHost = strIdentify.substr(0, iPosIpPortSeparator); - std::string strPort; - if (iPosPortWorkerIndexSeparator != std::string::npos && iPosPortWorkerIndexSeparator > iPosIpPortSeparator) - { - strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); - iPort = atoi(strPort.c_str()); - if (iPort == 0) - { - return(false); - } - std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); - if (strWorkerIndex.size() > 0) - { - iWorkerIndex = atoi(strWorkerIndex.c_str()); - if (iWorkerIndex > 200) - { - strError = "worker index must smaller than 200"; - return(false); - } - } - } - else - { - strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); - iPort = atoi(strPort.c_str()); - if (iPort == 0) - { - return(false); - } - } - return(true); - }; - - auto named_iter = m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == m_mapNamedSocketChannel.end()) - { - LOG4_TRACE("no channel match %s.", strIdentify.c_str()); - std::string strError; - std::string strHost; - int iPort = 0; - int iWorkerIndex = -1; - if (!split(strIdentify, strHost, iPort, iWorkerIndex, strError)) - { - LOG4_ERROR("%s", strError.c_str()); - return(false); - } - return(AutoSend(strIdentify, strHost, iPort, iWorkerIndex, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); - } - else - { - if (named_iter->second.empty()) - { - std::string strError; - std::string strHost; - int iPort = 0; - int iWorkerIndex = -1; - if (!split(strIdentify, strHost, iPort, iWorkerIndex, strError)) - { - LOG4_ERROR("%s", strError.c_str()); - return(false); - } - return(AutoSend(strIdentify, strHost, iPort, iWorkerIndex, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); - } - else - { - auto channel_iter = named_iter->second.begin(); - bool bResult = SendTo((*channel_iter), std::forward(args)...); - if (!bPipeline && bResult) - { - named_iter->second.erase(channel_iter); - } - return(bResult); - } - } -} - -template -bool Dispatcher::SendTo(const std::string& strHost, int iPort, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) -{ - LOG4_TRACE("host %s port %d", strHost.c_str(), iPort); - std::ostringstream ossIdentify; - ossIdentify << strHost << ":" << iPort; - auto named_iter = m_mapNamedSocketChannel.find(ossIdentify.str()); - if (named_iter == m_mapNamedSocketChannel.end()) - { - LOG4_TRACE("no channel match %s.", ossIdentify.str().c_str()); - return(AutoSend(ossIdentify.str(), strHost, iPort, 0, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); - } - else - { - auto channel_iter = named_iter->second.begin(); - if (channel_iter == named_iter->second.end()) - { - return(AutoSend(ossIdentify.str(), strHost, iPort, 0, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); - } - bool bResult = SendTo((*channel_iter), std::forward(args)...); - if (!bPipeline && bResult) - { - named_iter->second.erase(channel_iter); - } - return(bResult); - } -} - -template -bool Dispatcher::AutoSend( - const std::string& strIdentify, const std::string& strHost, int iPort, - int iRemoteWorkerIndex, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) -{ - LOG4_TRACE("%s", strIdentify.c_str()); - struct addrinfo stAddrHints; - struct addrinfo* pAddrResult; - struct addrinfo* pAddrCurrent; - memset(&stAddrHints, 0, sizeof(struct addrinfo)); - stAddrHints.ai_family = AF_UNSPEC; - stAddrHints.ai_socktype = iSocketType; - stAddrHints.ai_protocol = IPPROTO_IP; - int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); - if (0 != iCode) - { - LOG4_ERROR("getaddrinfo(\"%s\", \"%d\") error %d: %s", - strHost.c_str(), iPort, iCode, gai_strerror(iCode)); - return(false); - } - int iFd = -1; - for (pAddrCurrent = pAddrResult; - pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) - { - iFd = socket(pAddrCurrent->ai_family, - pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); - if (iFd == -1) - { - continue; - } - - break; - } - - /* No address succeeded */ - if (pAddrCurrent == NULL) - { - LOG4_ERROR("Could not connect to \"%s:%d\"", strHost.c_str(), iPort); - freeaddrinfo(pAddrResult); /* No longer needed */ - return(false); - } - - x_sock_set_block(iFd, 0); - int nREUSEADDR = 1; - int iKeepAlive = 1; - int iKeepIdle = 60; - int iKeepInterval = 5; - int iKeepCount = 3; - int iTcpNoDelay = 1; - int iTcpQuickAck = 1; - setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); - setsockopt(iFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)); - setsockopt(iFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)); - setsockopt(iFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)); - setsockopt(iFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)); - setsockopt(iFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)); - setsockopt(iFd, IPPROTO_TCP, TCP_QUICKACK, (void*)&iTcpQuickAck, sizeof(iTcpQuickAck)); - std::shared_ptr pChannel = CreateSocketChannel(iFd, eCodecType, true, bWithSsl); - if (nullptr != pChannel) - { - connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); - freeaddrinfo(pAddrResult); /* No longer needed */ - AddIoTimeout(pChannel, 1.5); - AddIoReadEvent(pChannel); - AddIoWriteEvent(pChannel); - pChannel->m_pImpl->SetIdentify(strIdentify); - pChannel->m_pImpl->SetRemoteAddr(strHost); - pChannel->m_pImpl->SetPipeline(bPipeline); - E_CODEC_STATUS eCodecStatus = pChannel->m_pImpl->Send(std::forward(args)...); - m_pLabor->IoStatAddSendNum(pChannel->GetFd()); - m_pLastActivityChannel = pChannel; - if (CODEC_STATUS_OK != eCodecStatus - && CODEC_STATUS_PAUSE != eCodecStatus - && CODEC_STATUS_WANT_WRITE != eCodecStatus - && CODEC_STATUS_WANT_READ != eCodecStatus) - { - DiscardSocketChannel(pChannel); - } - - pChannel->m_pImpl->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); - pChannel->m_pImpl->SetRemoteWorkerIndex(iRemoteWorkerIndex); - if (bPipeline) - { - AddNamedSocketChannel(strIdentify, pChannel); - } - return(true); - } - else // 没有足够资源分配给新连接,直接close掉 - { - freeaddrinfo(pAddrResult); /* No longer needed */ - close(iFd); - return(false); - } -} - -template -bool Dispatcher::SendRoundRobin(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) -{ - LOG4_TRACE("node_type: %s", strNodeType.c_str()); - std::string strOnlineNode; - if (m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) - { - SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline); - } - if (m_pSessionNode->GetNode(strNodeType, strOnlineNode)) - { - return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); - } - else - { - LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); - if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) - { - return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); - } - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } -} - -template -bool Dispatcher::SendOriented(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args) -{ - LOG4_TRACE("node_type: %s", strNodeType.c_str()); - std::string strOnlineNode; - if (m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) - { - SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline); - } - if (m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) - { - return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); - } - else - { - LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); - if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) - { - return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); - } - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } -} - -template -bool Dispatcher::SendOriented(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args) -{ - LOG4_TRACE("node_type: %s", strNodeType.c_str()); - std::string strOnlineNode; - if (m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) - { - SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline); - } - if (m_pSessionNode->GetNode(strNodeType, strFactor, strOnlineNode)) - { - return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); - } - else - { - LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); - if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) - { - return(SendTo(strOnlineNode, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...)); - } - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); - } -} - -template -bool Dispatcher::Broadcast(const std::string& strNodeType, int iSocketType, E_CODEC_TYPE eCodecType, bool bWithSsl, bool bPipeline, Targs&&... args) -{ - LOG4_TRACE("node_type: %s", strNodeType.c_str()); - std::unordered_set setOnlineNodes; - if (m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) - { - bool bSendResult = false; - for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) - { - bSendResult |= SendTo(*node_iter, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...); - } - return(bSendResult); - } - else - { - if ("BEACON" == strNodeType) - { - LOG4_TRACE("no beacon config."); - } - else - { - LOG4_TRACE("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); - std::string strOnlineNode; - if (m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) - { - if (m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) - { - bool bSendResult = false; - for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) - { - bSendResult |= SendTo(*node_iter, iSocketType, eCodecType, bWithSsl, bPipeline, std::forward(args)...); - } - return(bSendResult); - } - } - LOG4_ERROR("no online node match node_type \"%s\"", strNodeType.c_str()); - } - return(false); - } -} - } /* namespace neb */ #endif /* SRC_IOS_DISPATCHER_HPP_ */ diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp new file mode 100644 index 00000000..449897ca --- /dev/null +++ b/src/ios/IO.hpp @@ -0,0 +1,1170 @@ +/******************************************************************************* + * Project: Nebula + * @file IO.hpp + * @brief in and out + * @author Bwar + * @date: 2021-10-01 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_IOS_IO_HPP_ +#define SRC_IOS_IO_HPP_ + +#include +#include "channel/SocketChannel.hpp" +#include "channel/SocketChannelImpl.hpp" +#include "channel/SocketChannelSslImpl.hpp" +#include "ios/ActorWatcher.hpp" +#include "ios/Dispatcher.hpp" +#include "labor/Labor.hpp" +#include "actor/Actor.hpp" +#include "actor/ActorBuilder.hpp" +#include "actor/chain/Chain.hpp" +#include "codec/CodecFactory.hpp" + +namespace neb +{ + +class Actor; +class Dispatcher; + +template +class IO +{ +public: + IO(){} + virtual ~IO(){} + + static bool Send(std::shared_ptr pChannel); + + template + static bool SendResponse(Actor* pActor, std::shared_ptr pChannel, Targs&&... args); + + template + static bool SendResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args); + + template + static bool SendRequest(Actor* pActor, std::shared_ptr pChannel, Targs&&... args); + + template + static bool SendRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, Targs&&... args); + + template + static bool SendTo(Actor* pActor, const std::string& strIdentify, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); + + template + static bool SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); + + template + static bool SendTo(Actor* pActor, const std::string& strHost, int iPort, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); + + template + static bool SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strHost, int iPort, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); + + template + static bool SendRoundRobin(Actor* pActor, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args); + + template + static bool SendRoundRobin(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args); + + template + static bool SendOriented(Actor* pActor, const std::string& strNodeType, + bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args); + + template + static bool SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, + bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args); + + template + static bool SendOriented(Actor* pActor, const std::string& strNodeType, + bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args); + + template + static bool SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, + bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args); + + template + static bool Broadcast(Actor* pActor, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args); + + template + static bool Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args); + + template + static bool SendToSelf(Actor* pActor, Targs&&... args); + + template + static E_CODEC_STATUS Recv(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args); + + template + static E_CODEC_STATUS Fetch(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args); + + template + static bool OnMessage(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args); // default + + template + static bool OnRequest(Dispatcher* pDispatcher, std::shared_ptr pChannel, int32 iCmd, Targs&&... args); + + template + static bool OnRequest(Dispatcher* pDispatcher, std::shared_ptr pChannel, const std::string& strPath, Targs&&... args); + + template + static bool OnResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, uint32 uiStreamId, E_CODEC_STATUS eCodecStatus, Targs&&... args); + + // SelfChannel response + template + static bool OnResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, uint32 uiStepSeq, Targs&&... args); + + template + static bool OnMessage(ActorBuilder* pBuilder, std::shared_ptr pChannel, Targs&&... args); + + template + static bool OnRequest(ActorBuilder* pBuilder, std::shared_ptr pChannel, int32 iCmd, Targs&&... args); + + template + static bool OnRequest(ActorBuilder* pBuilder, std::shared_ptr pChannel, const std::string& strPath, Targs&&... args); + + template + static bool OnResponse(ActorBuilder* pBuilder, std::shared_ptr pChannel, uint32 uiStreamId, E_CODEC_STATUS eCodecStatus, Targs&&... args); + + // SelfChannel response + template + static bool OnResponse(ActorBuilder* pBuilder, std::shared_ptr pChannel, uint32 uiStepSeq, Targs&&... args); + + template + static std::shared_ptr CreateSocketChannel(Dispatcher* pDispatcher, int iFd, bool bIsClient, bool bWithSsl); + +protected: + template + static bool AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, const std::string& strHost, + int iPort, int iRemoteWorkerIndex, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); +}; + +template +bool IO::Send(std::shared_ptr pChannel) +{ + if (pChannel->m_pImpl == nullptr) + { + return(false); + } + if (pChannel->WithSsl()) + { +#ifdef WITH_OPENSSL + std::static_pointer_cast>(pChannel->m_pImpl)->Send(); +#else + std::static_pointer_cast>(pChannel->m_pImpl)->Send(); +#endif + } + else + { + std::static_pointer_cast>(pChannel->m_pImpl)->Send(); + } + return(true); +} + +template +template +bool IO::SendResponse(Actor* pActor, std::shared_ptr pChannel, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + return(SendResponse(pActor->m_pLabor->GetDispatcher(), pChannel, std::forward(args)...)); + } +} + +template +template +bool IO::SendResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args) +{ + if (CODEC_UNKNOW == pChannel->GetCodecType()) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "CODEC_UNKNOW is invalid, channel had not been init?"); + return(false); + } + if (pChannel->GetCodecType() == CODEC_DIRECT) + { + auto pSelfChannel = std::dynamic_pointer_cast(pChannel); + if (pSelfChannel == nullptr) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "channel is not a self channel."); + return(false); + } + pSelfChannel->SetResponse(); + return(CodecFactory::OnSelfResponse(pDispatcher, pSelfChannel, std::forward(args)...)); + } + if (T::Type() != pChannel->GetCodecType()) + { + E_CODEC_TYPE eOriginCodecType = pChannel->GetCodecType(); + if (SocketChannelImpl::NewCodec(pChannel, pDispatcher->m_pLabor, pDispatcher->m_pLogger, T::Type())) + { + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "channel %s[%d] change codec type from %d to %d", + pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); + } + else + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "failed to change codec type of channel %s[%d] from %d to %d", + pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); + return(false); + } + } + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + if (pChannel->WithSsl()) + { +#ifdef WITH_OPENSSL + eStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendResponse(std::forward(args)...); +#else + eStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendResponse(std::forward(args)...); +#endif + } + else + { + eStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendResponse(std::forward(args)...); + } + pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd()); + pDispatcher->m_pLastActivityChannel = pChannel; + switch (eStatus) + { + case CODEC_STATUS_OK: + return(true); + case CODEC_STATUS_PAUSE: + case CODEC_STATUS_WANT_WRITE: + case CODEC_STATUS_PART_OK: + pDispatcher->AddIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_WANT_READ: + pDispatcher->RemoveIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_EOF: // a case: http1.0 respone and close + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "eStatus = %d, %s", eStatus, pChannel->GetIdentify().c_str()); + pDispatcher->DiscardSocketChannel(pChannel); + return(true); + default: + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "eStatus = %d, %s", eStatus, pChannel->GetIdentify().c_str()); + pDispatcher->DiscardSocketChannel(pChannel); + return(false); + } +} + +template +template +bool IO::SendRequest(Actor* pActor, std::shared_ptr pChannel, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendRequest(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + pChannel, std::forward(args)...)); + } + else + { + return(SendRequest(pActor->m_pLabor->GetDispatcher(), 0, + pChannel, std::forward(args)...)); + } + } +} + +template +template +bool IO::SendRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, Targs&&... args) +{ + if (CODEC_UNKNOW == pChannel->GetCodecType()) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "CODEC_UNKNOW is invalid, channel had not been init?"); + return(false); + } + if (T::Type() != pChannel->GetCodecType()) + { + E_CODEC_TYPE eOriginCodecType = pChannel->GetCodecType(); + if (SocketChannelImpl::NewCodec(pChannel, pDispatcher->m_pLabor, pDispatcher->m_pLogger, T::Type())) + { + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "channel %s[%d] change codec type from %d to %d", + pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); + } + else + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "failed to change codec type of channel %s[%d] from %d to %d", + pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); + return(false); + } + } + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + if (pChannel->WithSsl()) + { +#ifdef WITH_OPENSSL + eStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); +#else + eStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); +#endif + } + else + { + eStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); + } + pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd()); + pDispatcher->m_pLastActivityChannel = pChannel; + switch (eStatus) + { + case CODEC_STATUS_OK: + return(true); + case CODEC_STATUS_PAUSE: + case CODEC_STATUS_WANT_WRITE: + case CODEC_STATUS_PART_OK: + pDispatcher->AddIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_WANT_READ: + pDispatcher->RemoveIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_EOF: // a case: http1.0 respone and close + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "eStatus = %d, %s", eStatus, pChannel->GetIdentify().c_str()); + pDispatcher->DiscardSocketChannel(pChannel); + return(true); + default: + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "eStatus = %d, %s", eStatus, pChannel->GetIdentify().c_str()); + pDispatcher->DiscardSocketChannel(pChannel); + return(false); + } +} + +template +template +bool IO::SendTo(Actor* pActor, const std::string& strIdentify, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendTo(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strIdentify, iSocketType, bWithSsl, bPipeline, + std::forward(args)...)); + } + else + { + return(SendTo(pActor->m_pLabor->GetDispatcher(), 0, + strIdentify, iSocketType, bWithSsl, bPipeline, + std::forward(args)...)); + } + } +} + +template +template +bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "identify: %s", strIdentify.c_str()); + // 将strIdentify分割的功能只在此SendTo()函数内两处调用,定义为Dispatcher的成员函数语义上不太合适,故定义lambda表达式 + auto split = [](const std::string& strIdentify, std::string& strHost, int& iPort, int& iWorkerIndex, std::string& strError)->bool + { + size_t iPosIpPortSeparator = strIdentify.rfind(':'); + size_t iPosPortWorkerIndexSeparator = strIdentify.rfind('.'); + if (iPosIpPortSeparator == std::string::npos) + { + return(false); + } + strHost = strIdentify.substr(0, iPosIpPortSeparator); + std::string strPort; + if (iPosPortWorkerIndexSeparator != std::string::npos && iPosPortWorkerIndexSeparator > iPosIpPortSeparator) + { + strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); + iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + return(false); + } + std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); + if (strWorkerIndex.size() > 0) + { + iWorkerIndex = atoi(strWorkerIndex.c_str()); + if (iWorkerIndex > 200) + { + strError = "worker index must smaller than 200"; + return(false); + } + } + } + else + { + strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); + iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + return(false); + } + } + return(true); + }; + + auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(strIdentify); + if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + { + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "no channel match %s.", strIdentify.c_str()); + std::string strError; + std::string strHost; + int iPort = 0; + int iWorkerIndex = -1; + if (!split(strIdentify, strHost, iPort, iWorkerIndex, strError)) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "%s", strError.c_str()); + return(false); + } + return(AutoSend(pDispatcher, uiStepSeq, strIdentify, strHost, iPort, iWorkerIndex, iSocketType, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + if (named_iter->second.empty()) + { + std::string strError; + std::string strHost; + int iPort = 0; + int iWorkerIndex = -1; + if (!split(strIdentify, strHost, iPort, iWorkerIndex, strError)) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "%s", strError.c_str()); + return(false); + } + return(AutoSend(pDispatcher, uiStepSeq, strIdentify, strHost, iPort, iWorkerIndex, iSocketType, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + auto channel_iter = named_iter->second.begin(); + bool bResult = SendRequest(pDispatcher, uiStepSeq, (*channel_iter), std::forward(args)...); + if (!bPipeline && bResult) + { + named_iter->second.erase(channel_iter); + } + return(bResult); + } + } +} + +template +template +bool IO::SendTo(Actor* pActor, const std::string& strHost, int iPort, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendTo(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strHost, iPort, iSocketType, bWithSsl, bPipeline, + std::forward(args)...)); + } + else + { + return(SendTo(pActor->m_pLabor->GetDispatcher(), 0, + strHost, iPort, iSocketType, bWithSsl, bPipeline, + std::forward(args)...)); + } + } +} + +template +template +bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strHost, int iPort, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "host %s port %d", strHost.c_str(), iPort); + std::ostringstream ossIdentify; + ossIdentify << strHost << ":" << iPort; + auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(ossIdentify.str()); + if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + { + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "no channel match %s.", ossIdentify.str().c_str()); + return(AutoSend(pDispatcher, uiStepSeq, ossIdentify.str(), strHost, iPort, 0, iSocketType, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + auto channel_iter = named_iter->second.begin(); + if (channel_iter == named_iter->second.end()) + { + return(AutoSend(pDispatcher, uiStepSeq, ossIdentify.str(), strHost, iPort, 0, iSocketType, bWithSsl, bPipeline, std::forward(args)...)); + } + bool bResult = SendRequest(pDispatcher, uiStepSeq, (*channel_iter), std::forward(args)...); + if (!bPipeline && bResult) + { + named_iter->second.erase(channel_iter); + } + return(bResult); + } +} + +template +template +bool IO::SendRoundRobin(Actor* pActor, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendRoundRobin(pActor->m_pLabor->GetDispatcher(), + pActor->GetSequence(), strNodeType, bWithSsl, bPipeline, + std::forward(args)...)); + } + else + { + return(SendRoundRobin(pActor->m_pLabor->GetDispatcher(), + 0, strNodeType, bWithSsl, bPipeline, + std::forward(args)...)); + } + } +} + +template +template +bool IO::SendRoundRobin(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "node_type: %s", strNodeType.c_str()); + std::string strOnlineNode; + if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) + { + SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); + } + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, strOnlineNode)) + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + } + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } +} + +template +template +bool IO::SendOriented(Actor* pActor, const std::string& strNodeType, + bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendOriented(pActor->m_pLabor->GetDispatcher(), + pActor->GetSequence(), strNodeType, bWithSsl, bPipeline, uiFactor, + std::forward(args)...)); + } + else + { + return(SendOriented(pActor->m_pLabor->GetDispatcher(), + 0, strNodeType, bWithSsl, bPipeline, uiFactor, + std::forward(args)...)); + } + } +} + +template +template +bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, + bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args) +{ + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "node_type: %s", strNodeType.c_str()); + std::string strOnlineNode; + if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) + { + SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); + } + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + } + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } +} + +template +template +bool IO::SendOriented(Actor* pActor, const std::string& strNodeType, + bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendOriented(pActor->m_pLabor->GetDispatcher(), + pActor->GetSequence(), strNodeType, bWithSsl, bPipeline, strFactor, + std::forward(args)...)); + } + else + { + return(SendOriented(pActor->m_pLabor->GetDispatcher(), + 0, strNodeType, bWithSsl, bPipeline, strFactor, + std::forward(args)...)); + } + } +} + +template +template +bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, + bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args) +{ + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "node_type: %s", strNodeType.c_str()); + std::string strOnlineNode; + if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) + { + SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); + } + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, strFactor, strOnlineNode)) + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + } + else + { + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + } + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } +} + +template +template +bool IO::Broadcast(Actor* pActor, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(Broadcast(pActor->m_pLabor->GetDispatcher(), + pActor->GetSequence(), strNodeType, bWithSsl, bPipeline, + std::forward(args)...)); + } + else + { + return(Broadcast(pActor->m_pLabor->GetDispatcher(), + 0, strNodeType, bWithSsl, bPipeline, + std::forward(args)...)); + } + } +} + +template +template +bool IO::Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "node_type: %s", strNodeType.c_str()); + std::unordered_set setOnlineNodes; + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) + { + bool bSendResult = false; + for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + { + bSendResult |= SendTo(pDispatcher, uiStepSeq, *node_iter, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...); + } + return(bSendResult); + } + else + { + if ("BEACON" == strNodeType) + { + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "no beacon config."); + } + else + { + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + std::string strOnlineNode; + if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) + { + bool bSendResult = false; + for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + { + bSendResult |= SendTo(pDispatcher, uiStepSeq, *node_iter, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...); + } + return(bSendResult); + } + } + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "no online node match node_type \"%s\"", strNodeType.c_str()); + } + return(false); + } +} + +template +template +bool IO::SendToSelf(Actor* pActor, Targs&&... args) +{ + auto pSelfChannel = std::make_shared(); + return(CodecFactory::OnSelfRequest(pActor->m_pLabor->GetDispatcher(), + pActor->GetSequence(), pSelfChannel, std::forward(args)...)); +} + +template +template +bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, const std::string& strHost, + int iPort, int iRemoteWorkerIndex, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) +{ + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "%s", strIdentify.c_str()); + struct addrinfo stAddrHints; + struct addrinfo* pAddrResult; + struct addrinfo* pAddrCurrent; + memset(&stAddrHints, 0, sizeof(struct addrinfo)); + stAddrHints.ai_family = AF_UNSPEC; + stAddrHints.ai_socktype = iSocketType; + stAddrHints.ai_protocol = IPPROTO_IP; + int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); + if (0 != iCode) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "getaddrinfo(\"%s\", \"%d\") error %d: %s", + strHost.c_str(), iPort, iCode, gai_strerror(iCode)); + return(false); + } + int iFd = -1; + for (pAddrCurrent = pAddrResult; + pAddrCurrent != NULL; pAddrCurrent = pAddrCurrent->ai_next) + { + iFd = socket(pAddrCurrent->ai_family, + pAddrCurrent->ai_socktype, pAddrCurrent->ai_protocol); + if (iFd == -1) + { + continue; + } + + break; + } + + /* No address succeeded */ + if (pAddrCurrent == NULL) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "Could not connect to \"%s:%d\"", strHost.c_str(), iPort); + freeaddrinfo(pAddrResult); /* No longer needed */ + return(false); + } + + x_sock_set_block(iFd, 0); + int nREUSEADDR = 1; + int iKeepAlive = 1; + int iKeepIdle = 60; + int iKeepInterval = 5; + int iKeepCount = 3; + int iTcpNoDelay = 1; + int iTcpQuickAck = 1; + setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&nREUSEADDR, sizeof(int)); + setsockopt(iFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)); + setsockopt(iFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)); + setsockopt(iFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)); + setsockopt(iFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)); + setsockopt(iFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)); + setsockopt(iFd, IPPROTO_TCP, TCP_QUICKACK, (void*)&iTcpQuickAck, sizeof(iTcpQuickAck)); + std::shared_ptr pChannel = CreateSocketChannel(pDispatcher, iFd, true, bWithSsl); + if (nullptr != pChannel) + { + connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); + freeaddrinfo(pAddrResult); /* No longer needed */ + pDispatcher->AddIoTimeout(pChannel, 1.5); + pDispatcher->AddIoReadEvent(pChannel); + pDispatcher->AddIoWriteEvent(pChannel); + std::static_pointer_cast>(pChannel->m_pImpl)->SetIdentify(strIdentify); + std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteAddr(strHost); + std::static_pointer_cast>(pChannel->m_pImpl)->SetPipeline(bPipeline); + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + if (pChannel->WithSsl()) + { +#ifdef WITH_OPENSSL + eCodecStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); +#else + eCodecStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); +#endif + } + else + { + eCodecStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); + } + pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd()); + pDispatcher->m_pLastActivityChannel = pChannel; + if (CODEC_STATUS_OK != eCodecStatus + && CODEC_STATUS_PAUSE != eCodecStatus + && CODEC_STATUS_WANT_WRITE != eCodecStatus + && CODEC_STATUS_WANT_READ != eCodecStatus) + { + pDispatcher->DiscardSocketChannel(pChannel); + } + + std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); + std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteWorkerIndex(iRemoteWorkerIndex); + if (bPipeline) + { + pDispatcher->AddNamedSocketChannel(strIdentify, pChannel); + } + return(true); + } + else // 没有足够资源分配给新连接,直接close掉 + { + freeaddrinfo(pAddrResult); /* No longer needed */ + close(iFd); + return(false); + } +} + +template +template +E_CODEC_STATUS IO::Recv(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args) +{ + if (CODEC_UNKNOW == pChannel->GetCodecType()) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "CODEC_UNKNOW is invalid, channel had not been init?"); + return(CODEC_STATUS_ERR); + } + if (T::Type() != pChannel->GetCodecType()) + { + E_CODEC_TYPE eOriginCodecType = pChannel->GetCodecType(); + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "failed to change codec type of channel %s[%d] from %d to %d", + pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); + return(CODEC_STATUS_ERR); + } + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + if (pChannel->WithSsl()) + { +#ifdef WITH_OPENSSL + eStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->Recv(std::forward(args)...); +#else + eStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->Recv(std::forward(args)...); +#endif + } + else + { + eStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->Recv(std::forward(args)...); + } + pDispatcher->m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); + pDispatcher->m_pLastActivityChannel = pChannel; + return(eStatus); +} + +template +template +E_CODEC_STATUS IO::Fetch(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args) +{ + if (CODEC_UNKNOW == pChannel->GetCodecType()) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "CODEC_UNKNOW is invalid, channel had not been init?"); + return(CODEC_STATUS_ERR); + } + if (T::Type() != pChannel->GetCodecType()) + { + E_CODEC_TYPE eOriginCodecType = pChannel->GetCodecType(); + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "failed to change codec type of channel %s[%d] from %d to %d", + pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); + return(CODEC_STATUS_ERR); + } + E_CODEC_STATUS eStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->Fetch(std::forward(args)...); + pDispatcher->m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); + pDispatcher->m_pLastActivityChannel = pChannel; + return(eStatus); +} + +template +template +bool IO::OnMessage(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args) +{ + return(OnMessage(pDispatcher->m_pLabor->GetActorBuilder(), pChannel, std::forward(args)...)); +} + +template +template +bool IO::OnMessage(ActorBuilder* pBuilder, std::shared_ptr pChannel, Targs&&... args) +{ + return(pBuilder->OnMessage(pChannel, std::forward(args)...)); +} + +template +template +bool IO::OnRequest(Dispatcher* pDispatcher, std::shared_ptr pChannel, int32 iCmd, Targs&&... args) +{ + return(OnRequest(pDispatcher->m_pLabor->GetActorBuilder(), pChannel, iCmd, std::forward(args)...)); +} + +template +template +bool IO::OnRequest(ActorBuilder* pBuilder, std::shared_ptr pChannel, int32 iCmd, Targs&&... args) +{ + auto cmd_iter = pBuilder->m_mapCmd.find(CMD_REQ_REDIS_PROXY); + if (cmd_iter != pBuilder->m_mapCmd.end() && cmd_iter->second != nullptr) + { + auto pCmd = std::static_pointer_cast(cmd_iter->second); + return(pCmd->AnyMessage(pChannel, std::forward(args)...)); + } + pBuilder->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "no cmd handler found for cmd %d", iCmd); + return(false); +} + +template +template +bool IO::OnRequest(Dispatcher* pDispatcher, std::shared_ptr pChannel, const std::string& strPath, Targs&&... args) +{ + return(OnRequest(pDispatcher->m_pLabor->GetActorBuilder(), pChannel, strPath, std::forward(args)...)); +} + +template +template +bool IO::OnRequest(ActorBuilder* pBuilder, std::shared_ptr pChannel, const std::string& strPath, Targs&&... args) +{ + auto module_iter = pBuilder->m_mapModule.find(strPath); + if (module_iter == pBuilder->m_mapModule.end()) + { + module_iter = pBuilder->m_mapModule.find("/switch"); + if (module_iter == pBuilder->m_mapModule.end()) + { + module_iter = pBuilder->m_mapModule.find("/route"); + if (module_iter == pBuilder->m_mapModule.end()) + { + pBuilder->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "no module to dispose %s!", strPath.c_str()); + return(false); + } + else + { + std::static_pointer_cast(module_iter->second)->AnyMessage(pChannel, std::forward(args)...); + } + } + else + { + std::static_pointer_cast(module_iter->second)->AnyMessage(pChannel, std::forward(args)...); + } + } + else + { + std::static_pointer_cast(module_iter->second)->AnyMessage(pChannel, std::forward(args)...); + } + return(true); +} + +template +template +bool IO::OnResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, uint32 uiStreamId, E_CODEC_STATUS eCodecStatus, Targs&&... args) +{ + return(OnResponse(pDispatcher->m_pLabor->GetActorBuilder(), pChannel, uiStreamId, eCodecStatus, std::forward(args)...)); +} + +template +template +bool IO::OnResponse(ActorBuilder* pBuilder, std::shared_ptr pChannel, uint32 uiStreamId, E_CODEC_STATUS eCodecStatus, Targs&&... args) +{ + auto step_iter = pBuilder->m_mapCallbackStep.find(pChannel->PopStepSeq(uiStreamId, eCodecStatus)); + if (!pChannel->IsPipeline() && pChannel->PipelineIsEmpty()) + { + pBuilder->m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. + } + if (step_iter == pBuilder->m_mapCallbackStep.end()) + { + pBuilder->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "no callback for reply from %s!", pChannel->GetIdentify().c_str()); + return(false); + } + else + { + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(pBuilder->m_pLabor->GetNowTime()); + eResult = std::static_pointer_cast(step_iter->second)->Callback(pChannel, std::forward(args)...); + if (CMD_STATUS_RUNNING != eResult) + { + uint32 uiChainId = std::static_pointer_cast(step_iter->second)->GetChainId(); + pBuilder->m_pLabor->GetDispatcher()->DelEvent(step_iter->second->MutableWatcher()->MutableTimerWatcher()); + step_iter->second->MutableWatcher()->Reset(); + pBuilder->m_mapCallbackStep.erase(step_iter); + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) + { + auto chain_iter = pBuilder->m_mapChain.find(uiChainId); + if (chain_iter != pBuilder->m_mapChain.end()) + { + chain_iter->second->SetActiveTime(pBuilder->m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + pBuilder->RemoveChain(uiChainId); + } + } + } + } + return(eResult); + } +} + +template +template +bool IO::OnResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, uint32 uiStepSeq, Targs&&... args) +{ + return(OnResponse(pDispatcher->m_pLabor->GetActorBuilder(), pChannel, uiStepSeq, std::forward(args)...)); +} + +template +template +bool IO::OnResponse(ActorBuilder* pBuilder, std::shared_ptr pChannel, uint32 uiStepSeq, Targs&&... args) +{ + auto step_iter = pBuilder->m_mapCallbackStep.find(uiStepSeq); + if (step_iter == pBuilder->m_mapCallbackStep.end()) + { + pBuilder->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "no callback for reply from %s!", pChannel->GetIdentify().c_str()); + return(false); + } + else + { + E_CMD_STATUS eResult; + step_iter->second->SetActiveTime(pBuilder->m_pLabor->GetNowTime()); + eResult = std::static_pointer_cast(step_iter->second)->Callback(pChannel, std::forward(args)...); + if (CMD_STATUS_RUNNING != eResult) + { + uint32 uiChainId = std::static_pointer_cast(step_iter->second)->GetChainId(); + pBuilder->m_pLabor->GetDispatcher()->DelEvent(step_iter->second->MutableWatcher()->MutableTimerWatcher()); + step_iter->second->MutableWatcher()->Reset(); + pBuilder->m_mapCallbackStep.erase(step_iter); + if (CMD_STATUS_FAULT != eResult && 0 != uiChainId) + { + auto chain_iter = pBuilder->m_mapChain.find(uiChainId); + if (chain_iter != pBuilder->m_mapChain.end()) + { + chain_iter->second->SetActiveTime(pBuilder->m_pLabor->GetNowTime()); + eResult = chain_iter->second->Next(); + if (CMD_STATUS_RUNNING != eResult) + { + pBuilder->RemoveChain(uiChainId); + } + } + } + } + return(eResult); + } +} + +template +template +std::shared_ptr IO::CreateSocketChannel(Dispatcher* pDispatcher, int iFd, bool bIsClient, bool bWithSsl) +{ + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "iFd %d, codec_type %d, with_ssl = %d", iFd, T::Type(), bWithSsl); + auto iter = pDispatcher->m_mapSocketChannel.find(iFd); + if (iter == pDispatcher->m_mapSocketChannel.end()) + { + std::shared_ptr pChannel = nullptr; + ev_tstamp dKeepAlive = pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection; + (dKeepAlive > 0) ? dKeepAlive : pDispatcher->m_pLabor->GetNodeInfo().dIoTimeout; + try + { + pChannel = std::make_shared( + pDispatcher->m_pLabor, pDispatcher->m_pLogger, iFd, + pDispatcher->m_pLabor->GetSequence(), bWithSsl, bIsClient, dKeepAlive); + } + catch(std::bad_alloc& e) + { + pDispatcher->Logger(Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "new channel for fd %d error: %s", e.what()); + return(nullptr); + } + if (!SocketChannelImpl::NewCodec(pChannel, pDispatcher->m_pLabor, pDispatcher->m_pLogger, T::Type())) + { + pDispatcher->Logger(Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "failed to new codec"); + return(nullptr); + } + pDispatcher->m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); + if ((CODEC_NEBULA != T::Type()) && (CODEC_NEBULA_IN_NODE != T::Type())) + { + ++pDispatcher->m_iClientNum; + } + pDispatcher->Logger(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + "new channel for fd %d with codec type %d", pChannel->GetFd(), pChannel->GetCodecType()); + return(pChannel); + } + else + { + pDispatcher->Logger(Logger::WARNING, __FILE__, __LINE__, __FUNCTION__,"fd %d is exist!", iFd); + return(iter->second); + } +} + +} /* namespace neb */ + +#endif /* SRC_IOS_IO_HPP_ */ + diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index 4f1bfe42..94c6daca 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -404,6 +404,39 @@ bool Nodes::SplitAddAndGetNode(const std::string& strNodeType, std::string& strN return(GetNode(strNodeType, strNodeIdentify)); } +bool Nodes::GetAuth(const std::string& strNodeType, std::string& strAuth, std::string& strPassword) +{ + auto node_type_iter = m_mapNode.find(strNodeType); + if (node_type_iter == m_mapNode.end()) + { + return(false); + } + else + { + strAuth = node_type_iter->second->strAuth; + strPassword = node_type_iter->second->strPassword; + return(true); + } +} + +void Nodes::SetAuth(const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword) +{ + auto node_type_iter = m_mapNode.find(strNodeType); + if (node_type_iter == m_mapNode.end()) + { + std::shared_ptr pNode = std::make_shared(); + m_mapNode.insert(std::make_pair(strNodeType, pNode)); + pNode->strNodeType = strNodeType; + pNode->strAuth = strAuth; + pNode->strPassword = strPassword; + } + else + { + node_type_iter->second->strAuth = strAuth; + node_type_iter->second->strPassword = strPassword; + } +} + void Nodes::DelNode(const std::string& strNodeType, const std::string& strNodeIdentify) { auto node_type_iter = m_mapNode.find(strNodeType); diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index 29d2fcbc..e4a85540 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -57,6 +57,8 @@ class Nodes { bool bCheckFailedNode = false; std::string strNodeType; + std::string strAuth; + std::string strPassword; T_NODE2HASH_MAP mapNode2Hash; T_NODE2HASH_MAP::iterator itPollingNode; std::map mapHash2Node; @@ -111,6 +113,10 @@ class Nodes */ bool SplitAddAndGetNode(const std::string& strNodeType, std::string& strNodeIdentify); + bool GetAuth(const std::string& strNodeType, std::string& strAuth, std::string& strPassword); + + void SetAuth(const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword); + /** * @brief 删除节点 * @note 删除节点信息,每个节点均有一个主节点一个被节点构成。 diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index ad770e59..5e968330 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -421,7 +421,8 @@ bool Manager::CreateEvents() bool bDirectToLoader = false; m_oCurrentConf.Get("new_client_to_loader", bDirectToLoader); m_pSessionManager = std::dynamic_pointer_cast( - m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", (bool)bDirectToLoader)); + m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", + bDirectToLoader, m_stNodeInfo.dDataReportInterval)); AddPeriodicTaskEvent(); return(true); diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 68e4d519..cc56b832 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -20,8 +20,10 @@ extern "C" { #endif #include "Worker.hpp" #include "ios/Dispatcher.hpp" +#include "ios/IO.hpp" #include "actor/ActorBuilder.hpp" #include "actor/session/sys_session/manager/SessionManager.hpp" +#include "actor/session/sys_session/SessionDataReport.hpp" #include "pb/report.pb.h" namespace neb @@ -104,27 +106,15 @@ bool Worker::CheckParent() exit(0); } } + return(true); +} + +void Worker::DataReport() +{ m_stWorkerInfo.uiConnect = m_pDispatcher->GetConnectionNum(); m_stWorkerInfo.uiClientNum = m_pDispatcher->GetClientNum(); MsgBody oMsgBody; CJsonObject oJsonLoad; - neb::Report oReport; - auto pRecord = oReport.add_records(); - pRecord->set_key("recv_num"); - pRecord->set_item("nebula"); - pRecord->add_value(m_stWorkerInfo.uiRecvNum); - pRecord = oReport.add_records(); - pRecord->set_key("recv_byte"); - pRecord->set_item("nebula"); - pRecord->add_value(m_stWorkerInfo.uiRecvByte); - pRecord = oReport.add_records(); - pRecord->set_key("send_num"); - pRecord->set_item("nebula"); - pRecord->add_value(m_stWorkerInfo.uiSendNum); - pRecord = oReport.add_records(); - pRecord->set_key("send_byte"); - pRecord->set_item("nebula"); - pRecord->add_value(m_stWorkerInfo.uiSendByte); oJsonLoad.Add("load", int32(m_stWorkerInfo.uiConnect + m_pActorBuilder->GetStepNum())); oJsonLoad.Add("connect", m_stWorkerInfo.uiConnect); oJsonLoad.Add("recv_num", m_stWorkerInfo.uiRecvNum); @@ -134,16 +124,33 @@ bool Worker::CheckParent() oJsonLoad.Add("client", m_stWorkerInfo.uiClientNum); oMsgBody.set_data(oJsonLoad.ToString()); LOG4_TRACE("%s", oJsonLoad.ToString().c_str()); - m_pDispatcher->SendTo(m_pManagerControlChannel, CMD_REQ_UPDATE_WORKER_LOAD, GetSequence(), oMsgBody); - std::string strReport; - oReport.SerializeToString(&strReport); - oMsgBody.set_data(strReport); - m_pDispatcher->SendDataReport(CMD_REQ_DATA_REPORT, GetSequence(), oMsgBody); + IO::SendRequest(m_pDispatcher, 0, m_pManagerControlChannel, CMD_REQ_UPDATE_WORKER_LOAD, GetSequence(), oMsgBody); + auto pSessionDataReport = std::dynamic_pointer_cast(m_pActorBuilder->GetSession("neb::SessionManager")); + if (pSessionDataReport != nullptr) + { + auto pReport = std::make_shared(); + auto pRecord = pReport->add_records(); + pRecord->set_key("recv_num"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiRecvNum); + pRecord = pReport->add_records(); + pRecord->set_key("recv_byte"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiRecvByte); + pRecord = pReport->add_records(); + pRecord->set_key("send_num"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiSendNum); + pRecord = pReport->add_records(); + pRecord->set_key("send_byte"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiSendByte); + pSessionDataReport->AddReport(pReport); + } m_stWorkerInfo.uiRecvNum = 0; m_stWorkerInfo.uiRecvByte = 0; m_stWorkerInfo.uiSendNum = 0; m_stWorkerInfo.uiSendByte = 0; - return(true); } bool Worker::Init(CJsonObject& oJsonConf) @@ -207,19 +214,24 @@ bool Worker::Init(CJsonObject& oJsonConf) if (oJsonConf["with_ssl"]("config_path").length() > 0) { #ifdef WITH_OPENSSL - if (ERR_OK != SocketChannelSslImpl::SslInit(m_pLogger)) + if (ERR_OK != SslContext::SslInit(m_pLogger)) { LOG4_FATAL("SslInit() failed!"); return(false); } - if (ERR_OK != SocketChannelSslImpl::SslServerCtxCreate(m_pLogger)) + if (ERR_OK != SslContext::SslClientCtxCreate(m_pLogger)) + { + LOG4_FATAL("SslClientCtxCreate() failed!"); + return(false); + } + if (ERR_OK != SslContext::SslServerCtxCreate(m_pLogger)) { LOG4_FATAL("SslServerCtxCreate() failed!"); return(false); } std::string strCertFile = m_stNodeInfo.strWorkPath + "/" + oJsonConf["with_ssl"]("config_path") + "/" + oJsonConf["with_ssl"]("cert_file"); std::string strKeyFile = m_stNodeInfo.strWorkPath + "/" + oJsonConf["with_ssl"]("config_path") + "/" + oJsonConf["with_ssl"]("key_file"); - if (ERR_OK != SocketChannelSslImpl::SslServerCertificate(m_pLogger, strCertFile, strKeyFile)) + if (ERR_OK != SslContext::SslServerCertificate(m_pLogger, strCertFile, strKeyFile)) { LOG4_FATAL("SslServerCertificate() failed!"); return(false); @@ -383,7 +395,7 @@ void Worker::StartService() { MsgBody oMsgBody; oMsgBody.set_data(std::to_string(m_stWorkerInfo.iWorkerIndex)); - m_pDispatcher->SendTo(m_pManagerControlChannel, CMD_REQ_START_SERVICE, GetSequence(), oMsgBody); + IO::SendRequest(m_pDispatcher, 0, m_pManagerControlChannel, CMD_REQ_START_SERVICE, GetSequence(), oMsgBody); } void Worker::Destroy() @@ -391,7 +403,7 @@ void Worker::Destroy() LOG4_TRACE(" "); #ifdef WITH_OPENSSL - SocketChannelSslImpl::SslFree(); + SslContext::SslFree(); #endif if (m_pDispatcher != nullptr) { diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index ed8d88c4..e0c80f28 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -29,6 +29,7 @@ extern "C" { #include "util/CBuffer.hpp" #include "labor/Labor.hpp" +#include "util/json/CJsonObject.hpp" #include "channel/SocketChannel.hpp" #include "codec/Codec.hpp" #include "logger/NetLogger.hpp" @@ -52,6 +53,7 @@ class Worker: public Labor // timeout,worker进程无响应或与Manager通信通道异常,被manager进程终止时返回 void OnTerminated(struct ev_signal* watcher); bool CheckParent(); + void DataReport(); virtual bool Init(CJsonObject& oJsonConf); void Run(); diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp index a8ef3bb0..fd193226 100644 --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -53,6 +53,10 @@ int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLi return 0; } + if(!m_fout.good()) + { + ReOpen(); + } if(!m_fout.good()) { std::cerr << "Write log error: no log file handle." << std::endl; @@ -80,6 +84,10 @@ int FileLogger::WriteLog(const std::string& strTraceId, int iLev, const char* sz return 0; } + if(!m_fout.good()) + { + ReOpen(); + } if(!m_fout.good()) { std::cerr << "Write log error: no log file handle." << std::endl; @@ -112,10 +120,7 @@ bool FileLogger::OpenLogFile(const std::string strLogFile) void FileLogger::ReOpen() { - if (m_fout.good()) - { - m_fout.close(); - } + m_fout.close(); m_fout.open(m_strLogFileBase.c_str(), std::ios::app); } diff --git a/src/logger/FileLogger.hpp b/src/logger/FileLogger.hpp index e36eac37..363087a7 100644 --- a/src/logger/FileLogger.hpp +++ b/src/logger/FileLogger.hpp @@ -100,6 +100,10 @@ int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLi return 0; } + if(!m_fout.good()) + { + ReOpen(); + } if(!m_fout.good()) { std::cerr << "Write log error: no log file handle." << std::endl; @@ -129,6 +133,10 @@ int FileLogger::WriteLog(const std::string& strTraceId, int iLev, return 0; } + if(!m_fout.good()) + { + ReOpen(); + } if(!m_fout.good()) { std::cerr << "Write log error: no log file handle." << std::endl; @@ -157,6 +165,10 @@ int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLi return 0; } + if(!m_fout.good()) + { + ReOpen(); + } if(!m_fout.good()) { std::cerr << "Write log error: no log file handle." << std::endl; @@ -186,6 +198,10 @@ int FileLogger::WriteLog(const std::string& strTraceId, int iLev, return 0; } + if(!m_fout.good()) + { + ReOpen(); + } if(!m_fout.good()) { std::cerr << "Write log error: no log file handle." << std::endl; diff --git a/src/type/Notations.cpp b/src/type/Notations.cpp new file mode 100644 index 00000000..e294851f --- /dev/null +++ b/src/type/Notations.cpp @@ -0,0 +1,578 @@ +/******************************************************************************* + * Project: Nebula + * @file Notations.hpp + * @brief + * @author Bwar + * @date: 2021-10-05 + * @note + * Modify history: + ******************************************************************************/ +#include "Notations.hpp" + +namespace neb +{ + +Notations::Notations() +{ +} + +Notations::~Notations() +{ +} + +void Notations::EncodeInt(int32 iValue, CBuffer* pBuff) +{ + uint32 uiData = CodecUtil::H2N(uint32(iValue)); + pBuff->Write(&uiData, 4); +} + +void Notations::EncodeLong(int64 llValue, CBuffer* pBuff) +{ + uint64 ullData = CodecUtil::H2N(uint64(llValue)); + pBuff->Write(&ullData, 8); +} + +void Notations::EncodeByte(uint8 ucValue, CBuffer* pBuff) +{ + pBuff->WriteByte((char)ucValue); +} + +void Notations::EncodeShort(uint16 unValue, CBuffer* pBuff) +{ + uint16 unData = CodecUtil::H2N(unValue); + pBuff->Write(&unData, 2); +} + +bool Notations::EncodeString(const std::string& strValue, CBuffer* pBuff) +{ + if (strValue.size() > 65535) + { + return(false); + } + else + { + EncodeShort((uint16)strValue.size(), pBuff); + pBuff->Write(strValue.data(), strValue.size()); + return(true); + } +} + +bool Notations::EncodeLongString(const std::string& strValue, CBuffer* pBuff) +{ + if (strValue.size() > 2147483647) + { + return(false); + } + else + { + EncodeInt((int32)strValue.size(), pBuff); + pBuff->Write(strValue.data(), strValue.size()); + return(true); + } +} + +bool Notations::EncodeUuid(const std::string& strValue, CBuffer* pBuff) +{ + if (strValue.size() != 16) + { + return(false); + } + pBuff->Write(strValue.data(), strValue.size()); + return(true); +} + +bool Notations::EncodeStringList(const std::vector& vecValue, CBuffer* pBuff) +{ + if (vecValue.size() > 65535) + { + return(false); + } + EncodeShort(vecValue.size(), pBuff); + for (uint16 i = 0; i < vecValue.size(); ++i) + { + if (!EncodeString(vecValue[i], pBuff)) + { + return(false); + } + } + return(true); +} + +bool Notations::EncodeBytes(const Bytes& stValue, CBuffer* pBuff) +{ + if (stValue.iLength < 0) + { + EncodeInt(stValue.iLength, pBuff); + return(true); + } + else if (stValue.iLength > 2147483647) + { + return(false); + } + else + { + EncodeInt(stValue.iLength, pBuff); + pBuff->Write(stValue.pData, stValue.iLength); + return(true); + } +} + +bool Notations::EncodeValue(const Value& stValue, CBuffer* pBuff) +{ + return(EncodeBytes(stValue, pBuff)); +} + +bool Notations::EncodeShortBytes(const Bytes& stValue, CBuffer* pBuff) +{ + if (stValue.iLength > 65535) + { + return(false); + } + else + { + EncodeShort((uint16)stValue.iLength, pBuff); + pBuff->Write(stValue.pData, stValue.iLength); + return(true); + } +} + +bool Notations::EncodeOption(uint16 unId, const Value& stValue, std::function WithOptionValue, CBuffer* pBuff) +{ + EncodeShort(unId, pBuff); + if (WithOptionValue(unId)) + { + return(EncodeValue(stValue, pBuff)); + } + return(true); +} + +bool Notations::EncodeOptionList(const std::vector>& vecValue, std::function WithOptionValue, CBuffer* pBuff) +{ + if (vecValue.size() > 65535) + { + return(false); + } + EncodeShort(vecValue.size(), pBuff); + for (uint16 i = 0; i < vecValue.size(); ++i) + { + if (!EncodeOption(vecValue[i].first, vecValue[i].second, WithOptionValue, pBuff)) + { + return(false); + } + } + return(true); +} + +bool Notations::EncodeInet(const InetAddr& stAddr, int iPort, CBuffer* pBuff) +{ + if (EncodeInetaddr(stAddr, pBuff)) + { + EncodeInt(iPort, pBuff); + return(true); + } + return(false); +} + +bool Notations::EncodeInetaddr(const InetAddr& stAddr, CBuffer* pBuff) +{ + if (stAddr.type == 4) + { + EncodeByte(stAddr.type, pBuff); + uint32 uiAddr = CodecUtil::H2N(stAddr.addr[0]); + pBuff->Write(&uiAddr, 4); + return(true); + } + else if (stAddr.type == 16) + { + EncodeByte(stAddr.type, pBuff); + uint32 uiAddr = CodecUtil::H2N(stAddr.addr[0]); + pBuff->Write(&uiAddr, 4); + uiAddr = CodecUtil::H2N(stAddr.addr[1]); + pBuff->Write(&uiAddr, 4); + uiAddr = CodecUtil::H2N(stAddr.addr[2]); + pBuff->Write(&uiAddr, 4); + uiAddr = CodecUtil::H2N(stAddr.addr[3]); + pBuff->Write(&uiAddr, 4); + return(true); + } + else + { + return(false); + } +} + +void Notations::EncodeConsistency(uint16 unValue, CBuffer* pBuff) +{ + EncodeShort(unValue, pBuff); +} + +bool Notations::EncodeStringMap(const std::unordered_map& mapValue, CBuffer* pBuff) +{ + if (mapValue.size() > 65535) + { + return(false); + } + EncodeShort(mapValue.size(), pBuff); + for (auto iter = mapValue.begin(); iter != mapValue.end(); ++iter) + { + if (!EncodeString(iter->first, pBuff) + || !EncodeString(iter->second, pBuff)) + { + return(false); + } + } + return(true); +} + +bool Notations::EncodeStringMultimap(const std::unordered_map>& mapValue, CBuffer* pBuff) +{ + if (mapValue.size() > 65535) + { + return(false); + } + EncodeShort(mapValue.size(), pBuff); + for (auto iter = mapValue.begin(); iter != mapValue.end(); ++iter) + { + if (!EncodeString(iter->first, pBuff) + || !EncodeStringList(iter->second, pBuff)) + { + return(false); + } + } + return(true); +} + +bool Notations::EncodeBytesMap(const std::unordered_map& mapValue, CBuffer* pBuff) +{ + if (mapValue.size() > 65535) + { + return(false); + } + EncodeShort(mapValue.size(), pBuff); + for (auto iter = mapValue.begin(); iter != mapValue.end(); ++iter) + { + if (!EncodeString(iter->first, pBuff) + || !EncodeBytes(iter->second, pBuff)) + { + return(false); + } + } + return(true); +} + +bool Notations::DecodeInt(CBuffer* pBuff, int32& iValue) +{ + if (pBuff->ReadableBytes() < 4) + { + return(false); + } + uint32 uiData; + pBuff->Read(&uiData, 4); + iValue = CodecUtil::N2H(uiData); + return(true); +} + +bool Notations::DecodeLong(CBuffer* pBuff, int64& llValue) +{ + if (pBuff->ReadableBytes() < 8) + { + return(false); + } + uint64 ullData; + pBuff->Read(&ullData, 8); + llValue = CodecUtil::N2H(ullData); + return(true); +} + +bool Notations::DecodeByte(CBuffer* pBuff, uint8& ucValue) +{ + if (pBuff->ReadableBytes() < 1) + { + return(false); + } + pBuff->Read(&ucValue, 1); + return(true); +} + +bool Notations::DecodeShort(CBuffer* pBuff, uint16& unValue) +{ + if (pBuff->ReadableBytes() < 2) + { + return(false); + } + uint16 unData; + pBuff->Read(&unData, 2); + unValue = CodecUtil::N2H(unData); + return(true); +} + +bool Notations::DecodeString(CBuffer* pBuff, std::string& strValue) +{ + uint16 unLength; + if (DecodeShort(pBuff, unLength)) + { + if (pBuff->ReadableBytes() < unLength) + { + return(false); + } + strValue.assign(pBuff->GetRawReadBuffer(), unLength); + pBuff->AdvanceReadIndex(unLength); + return(true); + } + return(false); +} + +bool Notations::DecodeLongString(CBuffer* pBuff, std::string& strValue) +{ + int32 iLength; + if (DecodeInt(pBuff, iLength)) + { + if ((int32)pBuff->ReadableBytes() < iLength) + { + return(false); + } + strValue.assign(pBuff->GetRawReadBuffer(), iLength); + pBuff->AdvanceReadIndex(iLength); + return(true); + } + return(false); +} + +bool Notations::DecodeUuid(CBuffer* pBuff, std::string& strValue) +{ + if (pBuff->ReadableBytes() < 16) + { + return(false); + } + strValue.assign(pBuff->GetRawReadBuffer(), 16); + pBuff->AdvanceReadIndex(16); + return(true); +} + +bool Notations::DecodeStringList(CBuffer* pBuff, std::vector& vecValue) +{ + uint16 unNum; + if (DecodeShort(pBuff, unNum)) + { + vecValue.clear(); + std::string strValue; + for (uint16 i = 0; i < unNum; ++i) + { + if (!DecodeString(pBuff, strValue)) + { + return(false); + } + vecValue.push_back(strValue); + } + return(true); + } + return(false); +} + +bool Notations::DecodeBytes(CBuffer* pBuff, Bytes& stValue) +{ + if (DecodeInt(pBuff, stValue.iLength)) + { + if (stValue.iLength < 0) + { + stValue.Clear(); + return(true); + } + if ((int32)pBuff->ReadableBytes() < stValue.iLength) + { + stValue.Clear(); + return(false); + } + stValue.Assign(pBuff->GetRawReadBuffer(), stValue.iLength); + pBuff->AdvanceReadIndex(stValue.iLength); + return(true); + } + return(false); +} + +bool Notations::DecodeValue(CBuffer* pBuff, Value& stValue) +{ + if (DecodeInt(pBuff, stValue.iLength)) + { + if (stValue.iLength < 0) + { + return(true); + } + if ((int32)pBuff->ReadableBytes() < stValue.iLength) + { + stValue.Clear(); + return(false); + } + stValue.Assign(pBuff->GetRawReadBuffer(), stValue.iLength); + pBuff->AdvanceReadIndex(stValue.iLength); + return(true); + } + return(false); +} + +bool Notations::DecodeShortBytes(CBuffer* pBuff, Bytes& stValue) +{ + uint16 unLength; + if (DecodeShort(pBuff, unLength)) + { + if (pBuff->ReadableBytes() < unLength) + { + return(false); + } + stValue.iLength = (int32)unLength; + stValue.Assign(pBuff->GetRawReadBuffer(), stValue.iLength); + pBuff->AdvanceReadIndex(unLength); + return(true); + } + return(false); +} + +bool Notations::DecodeOption(CBuffer* pBuff, std::function WithOptionValue, uint16& unId, Value& stValue) +{ + if (DecodeShort(pBuff, unId)) + { + if (WithOptionValue(unId)) + { + return(DecodeValue(pBuff, stValue)); + } + return(true); + } + return(false); +} + +bool Notations::DecodeOptionList(CBuffer* pBuff, std::function WithOptionValue, std::vector>& vecValue) +{ + uint16 unNum; + if (DecodeShort(pBuff, unNum)) + { + vecValue.clear(); + uint16 unId; + Value stValue; + for (uint16 i = 0; i < unNum; ++i) + { + stValue.Clear(); + if (!DecodeOption(pBuff, WithOptionValue, unId, stValue)) + { + return(false); + } + vecValue.push_back(std::move(std::make_pair(unId, std::move(stValue)))); + } + return(true); + } + return(false); +} + +bool Notations::DecodeInet(CBuffer* pBuff, InetAddr& stAddr, int& iPort) +{ + return(DecodeInetaddr(pBuff, stAddr) && DecodeInt(pBuff, iPort)); +} + +bool Notations::DecodeInetaddr(CBuffer* pBuff, InetAddr& stAddr) +{ + char cLen = 0; + if (!pBuff->ReadByte(cLen)) + { + return(false); + } + if (cLen == 4) // ipv4 + { + stAddr.type = cLen; + if (pBuff->ReadableBytes() < 4) + { + return(false); + } + pBuff->Read(&stAddr.addr[0], 4); + stAddr.addr[0] = CodecUtil::N2H(stAddr.addr[0]); + return(true); + } + else if (cLen == 16) // ipv6 + { + stAddr.type = cLen; + if (pBuff->ReadableBytes() < 16) + { + return(false); + } + pBuff->Read(&stAddr.addr[0], 4); + stAddr.addr[0] = CodecUtil::N2H(stAddr.addr[0]); + pBuff->Read(&stAddr.addr[1], 4); + stAddr.addr[1] = CodecUtil::N2H(stAddr.addr[1]); + pBuff->Read(&stAddr.addr[2], 4); + stAddr.addr[2] = CodecUtil::N2H(stAddr.addr[2]); + pBuff->Read(&stAddr.addr[3], 4); + stAddr.addr[3] = CodecUtil::N2H(stAddr.addr[3]); + return(true); + } + else + { + return(false); + } +} + +bool Notations::DecodeConsistency(CBuffer* pBuff, uint16& unValue) +{ + return(DecodeShort(pBuff, unValue)); +} + +bool Notations::DecodeStringMap(CBuffer* pBuff, std::unordered_map& mapValue) +{ + uint16 unNum; + if (DecodeShort(pBuff, unNum)) + { + std::string strKey; + std::string strValue; + for (uint16 i = 0; i < unNum; ++i) + { + if (!DecodeString(pBuff, strKey) || !DecodeString(pBuff, strValue)) + { + return(false); + } + mapValue.insert(std::make_pair(strKey, strValue)); + } + return(true); + } + return(false); +} + +bool Notations::DecodeStringMultimap(CBuffer* pBuff, std::unordered_map>& mapValue) +{ + uint16 unNum; + if (DecodeShort(pBuff, unNum)) + { + std::string strKey; + std::vector vecValue; + for (uint16 i = 0; i < unNum; ++i) + { + vecValue.clear(); + if (!DecodeString(pBuff, strKey) || !DecodeStringList(pBuff, vecValue)) + { + return(false); + } + mapValue.insert(std::move(std::make_pair(strKey, std::move(vecValue)))); + } + return(true); + } + return(false); +} + +bool Notations::DecodeBytesMap(CBuffer* pBuff, std::unordered_map& mapValue) +{ + uint16 unNum; + if (DecodeShort(pBuff, unNum)) + { + std::string strKey; + Bytes stValue; + for (uint16 i = 0; i < unNum; ++i) + { + if (!DecodeString(pBuff, strKey) || !DecodeBytes(pBuff, stValue)) + { + return(false); + } + mapValue.insert(std::make_pair(strKey, std::move(stValue))); + } + return(true); + } + return(false); +} + +} /* namespace neb */ + diff --git a/src/type/Notations.hpp b/src/type/Notations.hpp new file mode 100644 index 00000000..4ff2a113 --- /dev/null +++ b/src/type/Notations.hpp @@ -0,0 +1,187 @@ +/******************************************************************************* + * Project: Nebula + * @file Notations.hpp + * @brief + * @author Bwar + * @date: 2021-10-05 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_TYPE_NOTATIONS_HPP_ +#define SRC_TYPE_NOTATIONS_HPP_ + +#include +#include +#include +#include +#include +#include "Definition.hpp" +#include "util/CBuffer.hpp" +#include "codec/Codec.hpp" + +namespace neb +{ + +struct InetAddr +{ + char type = 4; // 4: ipv4, 6: ipv6 + uint32 addr[4] = {0}; +}; + + +/** +* @brief Bytes define +* @note if (iLength >= 0) strValue length == iLength; +* if (iBytesCase < 0) the value represented is `null`. +*/ +struct Bytes +{ + uint32 uiCapacity = 0; + int32 iLength = -1; + char* pData = nullptr; + Bytes(){} + ~Bytes() + { + if (pData != nullptr) + { + free(pData); + pData = nullptr; + } + uiCapacity = 0; + iLength = -1; + } + Bytes(const Bytes&) = delete; + Bytes(Bytes&& stBytes) + { + uiCapacity = stBytes.uiCapacity; + iLength = stBytes.iLength; + pData = stBytes.pData; + stBytes.uiCapacity = 0; + stBytes.iLength = -1; + stBytes.pData = nullptr; + } + Bytes& operator=(const Bytes& stBytes) + { + if (stBytes.pData != nullptr && stBytes.iLength > 0) + { + pData = (char*)malloc(stBytes.uiCapacity); + if (pData == nullptr) + { + iLength = -1; + } + else + { + uiCapacity = stBytes.uiCapacity; + iLength = stBytes.iLength; + memcpy(pData, stBytes.pData, stBytes.iLength); + } + } + return(*this); + } + Bytes& operator=(Bytes&& stBytes) + { + uiCapacity = stBytes.uiCapacity; + iLength = stBytes.iLength; + pData = stBytes.pData; + stBytes.uiCapacity = 0; + stBytes.iLength = -1; + stBytes.pData = nullptr; + return(*this); + } + bool Assign(const char* pSrc, int32 iSrcLen) + { + if ((int)uiCapacity >= iSrcLen) + { + iLength = iSrcLen; + memcpy(pData, pSrc, iSrcLen); + return(true); + } + else + { + if (pData != nullptr) + { + free(pData); + } + pData = (char*)malloc(iSrcLen); + if (pData == nullptr) + { + uiCapacity = 0; + iLength = -1; + return(false); + } + else + { + uiCapacity = iSrcLen; + iLength = iSrcLen; + memcpy(pData, pSrc, iSrcLen); + return(true); + } + } + } + void Clear() + { + iLength = -1; + } +}; + + +/** + * @brief Value define + * @note if (iLength >= 0) strValue length == iLength; + * if (iLength == -1) no byte should follow and the value represented is `null`. + * if (iLength == -2) no byte should follow and the value represented is `not set` not resulting in any change to the existing value. + * if (iLength < -2) is an invalid value and results in an error. + */ +typedef Bytes Value; + +class Notations +{ +public: + Notations(); + virtual ~Notations(); + + static void EncodeInt(int32 iValue, CBuffer* pBuff); + static void EncodeLong(int64 llValue, CBuffer* pBuff); + static void EncodeByte(uint8 ucValue, CBuffer* pBuff); + static void EncodeShort(uint16 unValue, CBuffer* pBuff); + static bool EncodeString(const std::string& strValue, CBuffer* pBuff); + static bool EncodeLongString(const std::string& strValue, CBuffer* pBuff); + static bool EncodeUuid(const std::string& strValue, CBuffer* pBuff); + static bool EncodeStringList(const std::vector& vecValue, CBuffer* pBuff); + static bool EncodeBytes(const Bytes& stValue, CBuffer* pBuff); + static bool EncodeValue(const Value& stValue, CBuffer* pBuff); + static bool EncodeShortBytes(const Bytes& stValue, CBuffer* pBuff); + static bool EncodeOption(uint16 unId, const Value& stValue, std::function WithOptionValue, CBuffer* pBuff); + static bool EncodeOptionList(const std::vector>& vecValue, std::function WithOptionValue, CBuffer* pBuff); + static bool EncodeInet(const InetAddr& stAddr, int iPort, CBuffer* pBuff); + static bool EncodeInetaddr(const InetAddr& stAddr, CBuffer* pBuff); + static void EncodeConsistency(uint16 unValue, CBuffer* pBuff); + static bool EncodeStringMap(const std::unordered_map& mapValue, CBuffer* pBuff); + static bool EncodeStringMultimap(const std::unordered_map>& mapValue, CBuffer* pBuff); + static bool EncodeBytesMap(const std::unordered_map& mapValue, CBuffer* pBuff); + + static bool DecodeInt(CBuffer* pBuff, int32& iValue); + static bool DecodeLong(CBuffer* pBuff, int64& llValue); + static bool DecodeByte(CBuffer* pBuff, uint8& ucValue); + static bool DecodeShort(CBuffer* pBuff, uint16& unValue); + static bool DecodeString(CBuffer* pBuff, std::string& strValue); + static bool DecodeLongString(CBuffer* pBuff, std::string& strValue); + static bool DecodeUuid(CBuffer* pBuff, std::string& strValue); + static bool DecodeStringList(CBuffer* pBuff, std::vector& vecValue); + static bool DecodeBytes(CBuffer* pBuff, Bytes& stValue); + static bool DecodeValue(CBuffer* pBuff, Value& stValue); + static bool DecodeShortBytes(CBuffer* pBuff, Bytes& stValue); + static bool DecodeOption(CBuffer* pBuff, std::function WithOptionValue, uint16& unId, Value& stValue); + static bool DecodeOptionList(CBuffer* pBuff, std::function WithOptionValue, std::vector>& vecValue); + static bool DecodeInet(CBuffer* pBuff, InetAddr& stAddr, int& iPort); + static bool DecodeInetaddr(CBuffer* pBuff, InetAddr& stAddr); + static bool DecodeConsistency(CBuffer* pBuff, uint16& unValue); + static bool DecodeStringMap(CBuffer* pBuff, std::unordered_map& mapValue); + static bool DecodeStringMultimap(CBuffer* pBuff, std::unordered_map>& mapValue); + static bool DecodeBytesMap(CBuffer* pBuff, std::unordered_map& mapValue); +}; + +} /* namespace neb */ + +#endif /* SRC_TYPE_NOTATIONS_HPP_ */ + From 19969787451ff8f2c7aceab36954ed78d7d6175e Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 21 Jan 2022 11:17:38 +0800 Subject: [PATCH 149/176] Update CodecHttp.cpp --- src/codec/CodecHttp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index f33445e9..6e3571a5 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -822,6 +822,7 @@ int CodecHttp::OnMessageComplete(http_parser *parser) } pCodec->MutableParsingHttpMsg()->set_http_major(parser->http_major); pCodec->MutableParsingHttpMsg()->set_http_minor(parser->http_minor); + pCodec->m_bIsDecoding = false; return(0); } From f6642e1ad4414397d80e9e26c6f5d96e0e2a4ee2 Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 7 Feb 2022 23:00:56 +0800 Subject: [PATCH 150/176] logs optimization. v1.7.0 release --- README.md | 10 +- README_cn.md | 8 + src/Definition.hpp | 6 + src/Makefile | 4 +- src/Makefile_ssl | 4 +- src/actor/Actor.cpp | 10 + src/actor/ActorSender.cpp | 5 + src/actor/ActorSender.hpp | 4 + .../sys_session/manager/SessionManager.cpp | 4 +- src/actor/step/sys_step/StepConnectWorker.hpp | 4 +- src/channel/SocketChannel.cpp | 15 +- src/channel/SocketChannel.hpp | 11 + src/channel/SocketChannelImpl.hpp | 16 +- src/codec/CodecFactory.cpp | 3 +- src/codec/CodecHttp.cpp | 1 + src/codec/CodecProto.cpp | 12 +- src/codec/CodecProto.hpp | 2 +- src/ios/Dispatcher.cpp | 4 +- src/ios/IO.hpp | 78 ++--- src/labor/Manager.cpp | 20 +- src/labor/Manager.hpp | 1 - src/labor/NodeInfo.hpp | 3 +- src/labor/Worker.cpp | 15 +- src/logger/AsyncLogger.cpp | 281 ++++++++++++++++++ src/logger/AsyncLogger.hpp | 88 ++++++ src/logger/FileLogger.cpp | 36 ++- src/logger/FileLogger.hpp | 38 ++- src/logger/Logger.hpp | 16 +- src/logger/NetLogger.cpp | 63 +++- src/logger/NetLogger.hpp | 67 +++-- 30 files changed, 668 insertions(+), 161 deletions(-) create mode 100644 src/logger/AsyncLogger.cpp create mode 100644 src/logger/AsyncLogger.hpp diff --git a/README.md b/README.md index 759e6aa0..028f081f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ English | [中文](/README_cn.md)         ## Overview -Nebula is a flexible, high-performance business-oriented IoC distributed network framework developed in C++ language, designed for production environments. It supports multiple application layer communication protocols including proto3, http, https, http2, grpc, and websocket. Nebula makes it easy to deploy a fast and high-performance distributed service with C++, while keeping the same server architecture and APIs. NebulaBootstrap provides out-of-the-box integration with Nebula service, but can be easily extended to serve other types of application. +Nebula is a flexible, high-performance business-oriented IoC distributed network framework developed in C++ language, designed for production environments. It supports multiple application layer communication protocols including proto3, http, https, http2, grpc, cassandra, and websocket. Nebula makes it easy to deploy a fast and high-performance distributed service with C++, while keeping the same server architecture and APIs. NebulaBootstrap provides out-of-the-box integration with Nebula service, but can be easily extended to serve other types of application. Nebula is a production level framework and distributed solution project for instant messaging, data collection, real-time computing, message push and other applications, as well as web api services. There were production applications for instant messaging, data acquisition and real-time analysis on line now, and a recommendation engine application for a large user base will be born soon. By the way, using Nebula for toy-level projects is also good for learning network communication. Bwar welcomes more developers to join the Nebula project. @@ -128,6 +128,14 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v1.7.0 + - IO optimization, codec plug-in. + - remove shared_from_this of Channel and Actor and use ChannelWatcher and ActorWatcher instead to improve performance. + - removed the unload feature of dynamically loaded plugins (which reduces performance). + - raw data codec is independent into codec. + - add cassandra client codec. + - add support for password verification of redis cluster. + - improve log performance; add asynchronous file logs. #### v1.6.2 - circuit breaker optimization - http chunk decode bug fixed; resp bulk string bug fixed diff --git a/README_cn.md b/README_cn.md index 2daf714a..27f84cad 100644 --- a/README_cn.md +++ b/README_cn.md @@ -133,6 +133,14 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 +#### v1.7.0 + - 优化IO通信,编解码器插件化。 + - 移除Channel和Actor的shared_from_this,改用ChannelWatcher和ActorWatcher替代,以提高性能。 + - 移除动态加载插件的卸载功能(会降低性能)。 + - 裸数据编解码独立成编解码器。 + - 增加cassandra客户端编解码器。 + - 增加redis cluster密码校验支持。 + - 优化文件日志,提升写日志性能;增加异步文件日志。 #### v1.6.2 - 断路器优化 - http chunk解码、resp字符串解码、redis集群无可用节点bug修复 diff --git a/src/Definition.hpp b/src/Definition.hpp index fbbeea0d..71e4fdea 100644 --- a/src/Definition.hpp +++ b/src/Definition.hpp @@ -104,6 +104,9 @@ #ifdef _TRACE #define LOG4_DEBUG(args...) Logger(neb::Logger::DEBUG, __FILE__, __LINE__, __FUNCTION__, ##args) #define LOG4_TRACE(args...) Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ##args) +#define LOG4_TRACE_DISPATCH(args...) pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ##args) +#define LOG4_TRACE_BUILDER(args...) pBuilder->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ##args) +#define LOG4_TRACE_LOGGER(args...) pLogger->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ##args) #else #ifdef _DEBUG #define LOG4_DEBUG(args...) Logger(neb::Logger::DEBUG, __FILE__, __LINE__, __FUNCTION__, ##args) @@ -111,6 +114,9 @@ #define LOG4_DEBUG(args...) /**/ #endif #define LOG4_TRACE(args...) /**/ +#define LOG4_TRACE_DISPATCH(args...) /**/ +#define LOG4_TRACE_BUILDER(args...) /**/ +#define LOG4_TRACE_LOGGER(args...) /**/ #endif typedef int8_t int8; diff --git a/src/Makefile b/src/Makefile index 316adc3b..909b9700 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,7 +2,7 @@ CC = gcc CXX = g++ CFLAGS = -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic -D__GUNC__ -fPIC cplusplus_version=$(shell g++ -dumpversion | awk '{if ($$NF > 5.0) print "c++14"; else print "c++11";}') -CXXFLAG = -std=$(cplusplus_version) -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic --no-gnu-unique -D_GNU_SOURCE=1 -D_REENTRANT -D__GUNC__ -fPIC -DNODE_BEAT=10.0 +CXXFLAG = -std=$(cplusplus_version) -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic --no-gnu-unique -D_GNU_SOURCE=1 -D_REENTRANT -D__GUNC__ -fPIC -DNODE_BEAT=10.0 -D_TRACE ifeq ($(unit_test),y) CXXFLAG += -DUNIT_TEST @@ -38,7 +38,7 @@ LDFLAGS := $(LDFLAGS) -D_LINUX_OS_ \ -L$(LIB3RD_PATH)/lib -lprotobuf \ -L$(SYSTEM_LIB_PATH) -lc -lrt -ldl -SUB_INCLUDE = channel ios labor pb mydis logger +SUB_INCLUDE = channel ios labor pb mydis logger type DEEP_SUB_INCLUDE = actor util codec CPP_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cpp)) CC_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cc)) diff --git a/src/Makefile_ssl b/src/Makefile_ssl index fee7f1ce..d21874dd 100644 --- a/src/Makefile_ssl +++ b/src/Makefile_ssl @@ -2,7 +2,7 @@ CC = gcc CXX = g++ CFLAGS = -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic -D__GUNC__ -fPIC cplusplus_version=$(shell g++ -dumpversion | awk '{if ($$NF > 5.0) print "c++14"; else print "c++11";}') -CXXFLAG = -std=$(cplusplus_version) -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic --no-gnu-unique -D_GNU_SOURCE=1 -D_REENTRANT -D__GUNC__ -fPIC -DNODE_BEAT=10.0 -DWITH_OPENSSL -D_DEBUG +CXXFLAG = -std=$(cplusplus_version) -g -O3 -Wall -Wno-unused-function -m64 -Wl,--export-dynamic --no-gnu-unique -D_GNU_SOURCE=1 -D_REENTRANT -D__GUNC__ -fPIC -DNODE_BEAT=10.0 -DWITH_OPENSSL -D_DEBUG -D_TRACE ifeq ($(unit_test),y) CXXFLAG += -DUNIT_TEST @@ -39,7 +39,7 @@ LDFLAGS := $(LDFLAGS) -D_LINUX_OS_ \ -L$(LIB3RD_PATH)/lib -lssl \ -L$(SYSTEM_LIB_PATH) -lc -lrt -ldl -SUB_INCLUDE = channel ios labor pb mydis logger +SUB_INCLUDE = channel ios labor pb mydis logger type DEEP_SUB_INCLUDE = actor util codec CPP_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cpp)) CC_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.cc)) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 42c1a145..9f5db47b 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -362,4 +362,14 @@ void Actor::SetTraceId(const std::string& strTraceId) m_strTraceId = strTraceId; } +void Actor::SetActorStatus(uint32 uiActorStatus) +{ + m_uiActorStatus |= uiActorStatus; +} + +void Actor::UnsetActorStatus(uint32 uiActorStatus) +{ + m_uiActorStatus &= (~uiActorStatus); +} + } /* namespace neb */ diff --git a/src/actor/ActorSender.cpp b/src/actor/ActorSender.cpp index 3822fe4b..08fab6f4 100644 --- a/src/actor/ActorSender.cpp +++ b/src/actor/ActorSender.cpp @@ -330,5 +330,10 @@ bool ActorSender::SendTo(Actor* pActor, const std::string& strUrl, const std::st } } +bool ActorSender::SendRoundRobin(Actor* pActor, const std::string& strIdentify, const CassMessage& oCassMsg, bool bWithSsl, bool bPipeline) +{ + return(IO::SendRoundRobin(pActor, strIdentify, bWithSsl, bPipeline, oCassMsg)); +} + } /* namespace neb */ diff --git a/src/actor/ActorSender.hpp b/src/actor/ActorSender.hpp index cafbbd50..f8f0bbe5 100644 --- a/src/actor/ActorSender.hpp +++ b/src/actor/ActorSender.hpp @@ -24,6 +24,7 @@ namespace neb { typedef RedisReply RedisMsg; +class CassMessage; class Actor; class SocketChannel; @@ -71,6 +72,9 @@ class ActorSender E_GRPC_STATUS_CODE eStatus, const std::string& strStatusMessage, E_COMPRESSION eCompression = COMPRESS_NA); static bool SendTo(Actor* pActor, const std::string& strUrl, const std::string& strGrpcRequest, E_COMPRESSION eCompression = COMPRESS_NA); + + // send cass message + static bool SendRoundRobin(Actor* pActor, const std::string& strIdentify, const CassMessage& oCassMsg, bool bWithSsl = false, bool bPipeline = true); }; } /* namespace neb */ diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index abbcd409..44853172 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -391,8 +391,8 @@ bool SessionManager::CheckWorker() if (worker_iter->second->bStartBeatCheck && !GetLabor(this)->GetNodeInfo().bThreadMode) { LOG4_TRACE("now %lf, worker_beat_time %lf, worker_beat %d", - GetNowTime(), worker_iter->second->dBeatTime, ((Manager*)GetLabor(this))->GetManagerInfo().iWorkerBeat); - if (GetNowTime() - worker_iter->second->dBeatTime > ((Manager*)GetLabor(this))->GetManagerInfo().iWorkerBeat) + GetNowTime(), worker_iter->second->dBeatTime, GetLabor(this)->GetNodeInfo().dDataReportInterval); + if (GetNowTime() - worker_iter->second->dBeatTime > GetLabor(this)->GetNodeInfo().dDataReportInterval) { LOG4_ERROR("worker_%d pid %d is unresponsive, " "terminate it.", worker_iter->second->iWorkerIndex, worker_iter->first); diff --git a/src/actor/step/sys_step/StepConnectWorker.hpp b/src/actor/step/sys_step/StepConnectWorker.hpp index 084ece09..1929abbd 100644 --- a/src/actor/step/sys_step/StepConnectWorker.hpp +++ b/src/actor/step/sys_step/StepConnectWorker.hpp @@ -10,13 +10,15 @@ #ifndef SRC_ACTOR_STEP_SYS_STEP_STEPCONNECTWORKER_HPP_ #define SRC_ACTOR_STEP_SYS_STEP_STEPCONNECTWORKER_HPP_ +#include #include #include namespace neb { -class StepConnectWorker: public PbStep, public DynamicCreator, int16> +class StepConnectWorker: public PbStep, + public DynamicCreator&, int16> { public: StepConnectWorker(std::shared_ptr pChannel, int16 iRemoteWorkerIndex); diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index 93ceab6b..2c6afe09 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -30,24 +30,24 @@ SocketChannel::SocketChannel() } SocketChannel::SocketChannel(Labor* pLabor, std::shared_ptr pLogger, int iFd, uint32 ulSeq, bool bWithSsl, bool bIsClient, ev_tstamp dKeepAlive) - : m_bIsClient(bIsClient), m_bWithSsl(bWithSsl), m_pImpl(nullptr), m_pLogger(nullptr), m_pWatcher(nullptr) + : m_bIsClient(bIsClient), m_bWithSsl(bWithSsl), m_pImpl(nullptr), m_pLogger(pLogger), m_pWatcher(nullptr) { if (bWithSsl) { #ifdef WITH_OPENSSL - pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "with openssl, create SocekChannelSslImpl."); + LOG4_TRACE("with openssl, create SocekChannelSslImpl."); auto pImpl = std::make_shared>(pLabor, pLogger, iFd, ulSeq, dKeepAlive); pImpl->Init(bIsClient); m_pImpl = std::static_pointer_cast(pImpl); #else - pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "without openssl, SocekChannelImpl instead."); + LOG4_TRACE("without openssl, SocekChannelImpl instead."); auto pImpl = std::make_shared>(pLabor, pLogger, iFd, ulSeq, dKeepAlive); m_pImpl = std::static_pointer_cast(pImpl); #endif } else { - pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "create SocekChannelImpl."); + LOG4_TRACE("create SocekChannelImpl."); auto pImpl = std::make_shared>(pLabor, pLogger, iFd, ulSeq, dKeepAlive); m_pImpl = std::static_pointer_cast(pImpl); } @@ -55,10 +55,7 @@ SocketChannel::SocketChannel(Labor* pLabor, std::shared_ptr pLogger, SocketChannel::~SocketChannel() { - if (m_pLogger != nullptr) - { - m_pLogger->WriteLog(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, ""); - } + LOG4_TRACE(""); if (nullptr != m_pWatcher) { delete m_pWatcher; @@ -353,7 +350,7 @@ ChannelWatcher* SocketChannel::MutableWatcher() } catch(std::bad_alloc& e) { - m_pLogger->WriteLog(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "new ChannelWatcher error %s", e.what()); + LOG4_TRACE("new ChannelWatcher error %s", e.what()); } } return(m_pWatcher); diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index 52679947..eb0b5237 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -61,6 +61,8 @@ class SocketChannel: public Channel virtual Codec* GetCodec() const; ChannelWatcher* MutableWatcher(); + template + void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) const; protected: virtual Labor* GetLabor(); bool InitImpl(std::shared_ptr pImpl); @@ -82,6 +84,15 @@ class SocketChannel: public Channel template friend class SocketChannelImpl; }; +template +void SocketChannel::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) const +{ + if (m_pLogger != nullptr) + { + m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); + } +} + } /* namespace neb */ #endif /* SRC_CHANNEL_SOCKETCHANNEL_HPP_ */ diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index fcfdc67e..132db890 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -418,14 +418,14 @@ E_CODEC_STATUS SocketChannelImpl::Send() m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); return(CODEC_STATUS_ERR); } - else if (CHANNEL_STATUS_ESTABLISHED != m_ucChannelStatus) - { - return(CODEC_STATUS_PAUSE); - } int iNeedWriteLen = 0; iNeedWriteLen = m_pSendBuff->ReadableBytes(); if (0 == iNeedWriteLen) { + if (CHANNEL_STATUS_ESTABLISHED != m_ucChannelStatus) + { + return(CODEC_STATUS_OK); + } iNeedWriteLen = m_pWaitForSendBuff->ReadableBytes(); if (0 == iNeedWriteLen) { @@ -756,6 +756,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) m_pRecvBuff->Compact(m_pRecvBuff->ReadableBytes() * 2); } m_dActiveTime = m_pLabor->GetNowTime(); + auto uiReadIndex = m_pRecvBuff->GetReadIndex(); E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; if (m_pCodec->DecodeWithReactor()) { @@ -771,7 +772,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) { eCodecStatus = (static_cast(m_pCodec))->Decode(m_pRecvBuff, std::forward(args)...); } - if (CODEC_STATUS_OK == eCodecStatus) + if (CODEC_STATUS_OK == eCodecStatus || CODEC_STATUS_PART_OK == eCodecStatus) { ++m_uiUnitTimeMsgNum; ++m_uiMsgNum; @@ -783,6 +784,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) } else { + m_pRecvBuff->SetReadIndex(uiReadIndex); if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) { if (!IsClient()) @@ -808,6 +810,7 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(Targs&&... args) m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); return(CODEC_STATUS_ERR); } + auto uiReadIndex = m_pRecvBuff->GetReadIndex(); E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; if (m_pCodec->DecodeWithReactor()) { @@ -823,7 +826,7 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(Targs&&... args) { eCodecStatus = (static_cast(m_pCodec))->Decode(m_pRecvBuff, std::forward(args)...); } - if (CODEC_STATUS_OK == eCodecStatus) + if (CODEC_STATUS_OK == eCodecStatus || CODEC_STATUS_PART_OK == eCodecStatus) { ++m_uiUnitTimeMsgNum; ++m_uiMsgNum; @@ -835,6 +838,7 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(Targs&&... args) } else { + m_pRecvBuff->SetReadIndex(uiReadIndex); if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) { return(CODEC_STATUS_INVALID); diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp index 8c7c306a..f358fb7c 100644 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -388,8 +388,7 @@ bool CodecFactory::AutoSwitchCodec(Dispatcher* pDispatcher, Codec* pCodec = Create(pDispatcher->GetLogger(), s_vecAutoSwitchCodec[i], pChannel); if (pCodec == nullptr) { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "failed to new codec with codec type %d", s_vecAutoSwitchCodec[i]); + LOG4_TRACE_DISPATCH("failed to new codec with codec type %d", s_vecAutoSwitchCodec[i]); continue; } uiLastCodecPos = i; diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index f8e3a373..7e2e7cd5 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -773,6 +773,7 @@ int CodecHttp::OnMessageComplete(http_parser *parser) } pCodec->MutableParsingHttpMsg()->set_http_major(parser->http_major); pCodec->MutableParsingHttpMsg()->set_http_minor(parser->http_minor); + pCodec->m_bIsDecoding = false; return(0); } diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp index 45b9f44e..e1469c4c 100644 --- a/src/codec/CodecProto.cpp +++ b/src/codec/CodecProto.cpp @@ -33,7 +33,6 @@ E_CODEC_STATUS CodecProto::Encode(CBuffer* pBuff) E_CODEC_STATUS CodecProto::Encode(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, CBuffer* pBuff) { - LOG4_TRACE("pBuff->ReadableBytes()=%u, oMsgHead.ByteSize() = %d", pBuff->ReadableBytes(), oMsgHead.ByteSize()); int iHadWriteLen = 0; int iWriteLen = 0; int iNeedWriteLen = gc_uiMsgHeadSize; @@ -53,14 +52,14 @@ E_CODEC_STATUS CodecProto::Encode(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgB iHadWriteLen += iWriteLen; if (oMsgHead.len() <= 0) // 无包体(心跳包等),nebula在proto3的使用上以-1表示包体长度为0 { - return(CODEC_STATUS_OK); + return(ChannelSticky(oMsgHead, oMsgBody)); } iNeedWriteLen = oMsgBody.ByteSize(); oMsgBody.SerializeToString(&strTmpData); iWriteLen = pBuff->Write(strTmpData.c_str(), oMsgBody.ByteSize()); if (iWriteLen == iNeedWriteLen) { - return(CODEC_STATUS_OK); + return(ChannelSticky(oMsgHead, oMsgBody)); } else { @@ -94,6 +93,7 @@ E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oM if (bResult) { pBuff->SkipBytes(gc_uiMsgHeadSize + oMsgHead.len()); + oMsgBody.set_add_on(GetBindChannel()->GetClientData()); return(ChannelSticky(oMsgHead, oMsgBody)); } else @@ -125,7 +125,7 @@ E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oM return(CODEC_STATUS_ERR); } -E_CODEC_STATUS CodecProto::ChannelSticky(MsgHead& oMsgHead, MsgBody& oMsgBody) +E_CODEC_STATUS CodecProto::ChannelSticky(const MsgHead& oMsgHead, const MsgBody& oMsgBody) { switch (GetBindChannel()->GetChannelStatus()) { @@ -133,7 +133,6 @@ E_CODEC_STATUS CodecProto::ChannelSticky(MsgHead& oMsgHead, MsgBody& oMsgBody) if ((gc_uiCmdReq & oMsgHead.cmd()) && (GetBindChannel()->GetClientData().size() > 0)) { m_uiForeignSeq = oMsgHead.seq(); - oMsgBody.set_add_on(GetBindChannel()->GetClientData()); } break; case CHANNEL_STATUS_CLOSED: @@ -166,7 +165,8 @@ E_CODEC_STATUS CodecProto::ChannelSticky(MsgHead& oMsgHead, MsgBody& oMsgBody) break; default: LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[from %d to %d] may be a fault.", - m_iFd, m_uiSeq, (int)m_ucChannelStatus, CHANNEL_STATUS_ESTABLISHED); + pChannelImpl->GetFd(), pChannelImpl->GetSequence(), + (int)pChannelImpl->GetChannelStatus(), CHANNEL_STATUS_ESTABLISHED); pChannelImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); break; } diff --git a/src/codec/CodecProto.hpp b/src/codec/CodecProto.hpp index a8fd14f9..dae36cab 100644 --- a/src/codec/CodecProto.hpp +++ b/src/codec/CodecProto.hpp @@ -33,7 +33,7 @@ class CodecProto: public Codec E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff); protected: - E_CODEC_STATUS ChannelSticky(MsgHead& oMsgHead, MsgBody& oMsgBody); + E_CODEC_STATUS ChannelSticky(const MsgHead& oMsgHead, const MsgBody& oMsgBody); private: uint32 m_uiForeignSeq = 0; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 622ec702..85193e6a 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -352,7 +352,8 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) if (CHANNEL_STATUS_TRY_CONNECT == pChannel->GetChannelStatus()) // connect之后的第一个写事件 { std::shared_ptr pStepConnectWorker = m_pLabor->GetActorBuilder()->MakeSharedStep( - nullptr, "neb::StepConnectWorker", pChannel, std::static_pointer_cast>(pChannel->m_pImpl)->GetRemoteWorkerIndex()); + nullptr, "neb::StepConnectWorker", pChannel, + std::static_pointer_cast>(pChannel->m_pImpl)->GetRemoteWorkerIndex()); if (nullptr == pStepConnectWorker) { LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); @@ -378,6 +379,7 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) { m_pSessionNode->NodeRecover(pChannel->GetIdentify()); } + LOG4_TRACE(""); E_CODEC_STATUS eCodecStatus = std::static_pointer_cast>(pChannel->m_pImpl)->Send(); if (CODEC_STATUS_OK == eCodecStatus) { diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index 449897ca..f567e33f 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -181,8 +181,7 @@ bool IO::SendResponse(Dispatcher* pDispatcher, std::shared_ptr { if (CODEC_UNKNOW == pChannel->GetCodecType()) { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "CODEC_UNKNOW is invalid, channel had not been init?"); + LOG4_TRACE_DISPATCH("CODEC_UNKNOW is invalid, channel had not been init?"); return(false); } if (pChannel->GetCodecType() == CODEC_DIRECT) @@ -190,8 +189,7 @@ bool IO::SendResponse(Dispatcher* pDispatcher, std::shared_ptr auto pSelfChannel = std::dynamic_pointer_cast(pChannel); if (pSelfChannel == nullptr) { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "channel is not a self channel."); + LOG4_TRACE_DISPATCH("channel is not a self channel."); return(false); } pSelfChannel->SetResponse(); @@ -208,8 +206,7 @@ bool IO::SendResponse(Dispatcher* pDispatcher, std::shared_ptr } else { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "failed to change codec type of channel %s[%d] from %d to %d", + LOG4_TRACE_DISPATCH("failed to change codec type of channel %s[%d] from %d to %d", pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); return(false); } @@ -286,8 +283,7 @@ bool IO::SendRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_p { if (CODEC_UNKNOW == pChannel->GetCodecType()) { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "CODEC_UNKNOW is invalid, channel had not been init?"); + LOG4_TRACE_DISPATCH("CODEC_UNKNOW is invalid, channel had not been init?"); return(false); } if (T::Type() != pChannel->GetCodecType()) @@ -301,8 +297,7 @@ bool IO::SendRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_p } else { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "failed to change codec type of channel %s[%d] from %d to %d", + LOG4_TRACE_DISPATCH("failed to change codec type of channel %s[%d] from %d to %d", pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); return(false); } @@ -379,8 +374,7 @@ template template bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "identify: %s", strIdentify.c_str()); + LOG4_TRACE_DISPATCH("identify: %s", strIdentify.c_str()); // 将strIdentify分割的功能只在此SendTo()函数内两处调用,定义为Dispatcher的成员函数语义上不太合适,故定义lambda表达式 auto split = [](const std::string& strIdentify, std::string& strHost, int& iPort, int& iWorkerIndex, std::string& strError)->bool { @@ -426,8 +420,7 @@ bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(strIdentify); if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "no channel match %s.", strIdentify.c_str()); + LOG4_TRACE_DISPATCH("no channel match %s.", strIdentify.c_str()); std::string strError; std::string strHost; int iPort = 0; @@ -502,8 +495,7 @@ bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(ossIdentify.str()); if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "no channel match %s.", ossIdentify.str().c_str()); + LOG4_TRACE_DISPATCH("no channel match %s.", ossIdentify.str().c_str()); return(AutoSend(pDispatcher, uiStepSeq, ossIdentify.str(), strHost, iPort, 0, iSocketType, bWithSsl, bPipeline, std::forward(args)...)); } else @@ -563,14 +555,12 @@ bool IO::SendRoundRobin(Dispatcher* pDispatcher, uint32 uiStepSeq, const std: } else { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) { return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); } - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "no online node match node_type \"%s\"", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); return(false); } } @@ -606,8 +596,7 @@ template bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args) { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "node_type: %s", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) { @@ -619,14 +608,12 @@ bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::s } else { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) { return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); } - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "no online node match node_type \"%s\"", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); return(false); } } @@ -662,8 +649,7 @@ template bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args) { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "node_type: %s", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) { @@ -675,14 +661,12 @@ bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::s } else { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) { return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); } - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "no online node match node_type \"%s\"", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); return(false); } } @@ -716,8 +700,7 @@ template template bool IO::Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args) { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "node_type: %s", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); std::unordered_set setOnlineNodes; if (pDispatcher->m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) { @@ -736,8 +719,7 @@ bool IO::Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::stri } else { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); std::string strOnlineNode; if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) { @@ -751,8 +733,7 @@ bool IO::Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::stri return(bSendResult); } } - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "no online node match node_type \"%s\"", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); } return(false); } @@ -783,8 +764,7 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::strin int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); if (0 != iCode) { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "getaddrinfo(\"%s\", \"%d\") error %d: %s", + LOG4_TRACE_DISPATCH("getaddrinfo(\"%s\", \"%d\") error %d: %s", strHost.c_str(), iPort, iCode, gai_strerror(iCode)); return(false); } @@ -805,8 +785,7 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::strin /* No address succeeded */ if (pAddrCurrent == NULL) { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "Could not connect to \"%s:%d\"", strHost.c_str(), iPort); + LOG4_TRACE_DISPATCH("Could not connect to \"%s:%d\"", strHost.c_str(), iPort); freeaddrinfo(pAddrResult); /* No longer needed */ return(false); } @@ -885,15 +864,13 @@ E_CODEC_STATUS IO::Recv(Dispatcher* pDispatcher, std::shared_ptrGetCodecType()) { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "CODEC_UNKNOW is invalid, channel had not been init?"); + LOG4_TRACE_DISPATCH("CODEC_UNKNOW is invalid, channel had not been init?"); return(CODEC_STATUS_ERR); } if (T::Type() != pChannel->GetCodecType()) { E_CODEC_TYPE eOriginCodecType = pChannel->GetCodecType(); - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "failed to change codec type of channel %s[%d] from %d to %d", + LOG4_TRACE_DISPATCH("failed to change codec type of channel %s[%d] from %d to %d", pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); return(CODEC_STATUS_ERR); } @@ -924,15 +901,13 @@ E_CODEC_STATUS IO::Fetch(Dispatcher* pDispatcher, std::shared_ptrGetCodecType()) { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "CODEC_UNKNOW is invalid, channel had not been init?"); + LOG4_TRACE_DISPATCH("CODEC_UNKNOW is invalid, channel had not been init?"); return(CODEC_STATUS_ERR); } if (T::Type() != pChannel->GetCodecType()) { E_CODEC_TYPE eOriginCodecType = pChannel->GetCodecType(); - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "failed to change codec type of channel %s[%d] from %d to %d", + LOG4_TRACE_DISPATCH("failed to change codec type of channel %s[%d] from %d to %d", pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); return(CODEC_STATUS_ERR); } @@ -1122,8 +1097,7 @@ template template std::shared_ptr IO::CreateSocketChannel(Dispatcher* pDispatcher, int iFd, bool bIsClient, bool bWithSsl) { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "iFd %d, codec_type %d, with_ssl = %d", iFd, T::Type(), bWithSsl); + LOG4_TRACE_DISPATCH("iFd %d, codec_type %d, with_ssl = %d", iFd, T::Type(), bWithSsl); auto iter = pDispatcher->m_mapSocketChannel.find(iFd); if (iter == pDispatcher->m_mapSocketChannel.end()) { diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 5e968330..21818d89 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -54,7 +54,6 @@ Manager::Manager(const std::string& strConfFile) { exit(3); } - m_stManagerInfo.iWorkerBeat = (gc_iBeatInterval * 2) + 1; CreateEvents(); if (m_stNodeInfo.bThreadMode) { @@ -163,7 +162,20 @@ bool Manager::InitLogger(const CJsonObject& oJsonConf) oJsonConf.Get("log_max_line_len", iMaxLogLineLen); oJsonConf.Get("log_level", iLogLevel); oJsonConf.Get("always_flush_log", bAlwaysFlushLog); - m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, iMaxLogLineLen, bAlwaysFlushLog, this); + if (m_stNodeInfo.bAsyncLogger) + { + uint32 uiLogThreadNum = m_stNodeInfo.uiWorkerNum + m_stNodeInfo.uiLoaderNum + 1; + if (m_stNodeInfo.uiLoaderNum == 0) + { + uiLogThreadNum++; + } + AsyncLogger::Instance(uiLogThreadNum, iLogLevel, iMaxLogFileSize, iMaxLogFileNum); + m_pLogger = std::make_shared(-1, strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, this); + } + else + { + m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, bAlwaysFlushLog, this); + } m_pLogger->SetNetLogLevel(iNetLogLevel); LOG4_NOTICE("%s program begin, and work path %s...", oJsonConf("server_name").c_str(), m_stNodeInfo.strWorkPath.c_str()); return(true); @@ -365,6 +377,10 @@ bool Manager::GetConf() bool Manager::Init() { m_oCurrentConf.Get("thread_mode", m_stNodeInfo.bThreadMode); + if (m_stNodeInfo.bThreadMode) + { + m_oCurrentConf.Get("async_logger", m_stNodeInfo.bAsyncLogger); + } if (!InitLogger(m_oCurrentConf) || !InitDispatcher() || !InitActorBuilder()) { return(false); diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index 523afb07..a5848b60 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -47,7 +47,6 @@ class Manager: public Labor public: struct tagManagerInfo { - int iWorkerBeat = 7; ///< worker进程心跳,若大于此心跳未收到worker进程上报,则重启worker进程 int iS2SListenFd = -1; ///< Server to Server监听文件描述符(Server与Server之间的连接较少,但每个Server的每个Worker均与其他Server的每个Worker相连) int iS2SFamily = 0; ///< int iC2SListenFd = -1; ///< Client to Server监听文件描述符(Client与Server之间的连接较多,但每个Client只需连接某个Server的某个Worker) diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index 3a51940f..8a8dafb5 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -32,7 +32,8 @@ struct NodeInfo int32 iPortForClient = 0; ///< 对Client通信监听端口,对应 iC2SListenFd int32 iForClientSocketType = 0; ///< 对Client通信的socket类型 int32 iGatewayPort = 0; ///< 对Client服务的真实端口 - bool bThreadMode = 0; ///< 是否线程模型 + bool bThreadMode = false; ///< 是否线程模型 + bool bAsyncLogger = false; ///< 是否启用异步文件日志 bool bIsAccess = false; ///< 是否接入Server bool bChannelVerify = false; ///< 是否需要连接验证 ev_tstamp dConnectionProtection = 0.0; ///< >0时为连接保护时间,新建连接会设置成这个时间,接收到第一个数据包之后改设成dIoTimeout diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index cc56b832..78bfb43b 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -158,7 +158,11 @@ bool Worker::Init(CJsonObject& oJsonConf) char szProcessName[64] = {0}; snprintf(szProcessName, sizeof(szProcessName), "%s_W%d", oJsonConf("server_name").c_str(), m_stWorkerInfo.iWorkerIndex); oJsonConf.Get("thread_mode", m_stNodeInfo.bThreadMode); - if (!m_stNodeInfo.bThreadMode) + if (m_stNodeInfo.bThreadMode) + { + oJsonConf.Get("async_logger", m_stNodeInfo.bAsyncLogger); + } + else { ngx_setproctitle(szProcessName); } @@ -314,7 +318,14 @@ bool Worker::InitLogger(const CJsonObject& oJsonConf, const std::string& strLogN oJsonConf.Get("net_log_level", iNetLogLevel); oJsonConf.Get("log_level", iLogLevel); oJsonConf.Get("always_flush_log", bAlwaysFlushLog); - m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, iMaxLogLineLen, bAlwaysFlushLog, this); + if (m_stNodeInfo.bAsyncLogger) + { + m_pLogger = std::make_shared(m_stWorkerInfo.iWorkerIndex, strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, this); + } + else + { + m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, bAlwaysFlushLog, this); + } m_pLogger->SetNetLogLevel(iNetLogLevel); LOG4_NOTICE("%s program begin...", getproctitle()); return(true); diff --git a/src/logger/AsyncLogger.cpp b/src/logger/AsyncLogger.cpp new file mode 100644 index 00000000..e366d8c8 --- /dev/null +++ b/src/logger/AsyncLogger.cpp @@ -0,0 +1,281 @@ +/******************************************************************************* + * Project: Nebula + * @file AsyncLogger.cpp + * @brief + * @author Bwar + * @date: 2022-01-29 + * @note + * Modify history: + ******************************************************************************/ +#include "AsyncLogger.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace neb +{ + +AsyncLogger* AsyncLogger::s_pInstance = nullptr; + +AsyncLogger::AsyncLogger(uint32 uiLogFileNum, int iLogLev, + unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex) + : m_iLogLevel(iLogLev), m_uiMaxFileSize(uiMaxFileSize), + m_uiMaxRollFileIndex(uiMaxRollFileIndex), + m_uiWrittingIndex(0), m_uiLastLogSize(0) +{ + m_vecTimeSec.resize(uiLogFileNum, 0); + m_vecLogFileName.resize(uiLogFileNum, ""); + m_vecLogFout.resize(uiLogFileNum, nullptr); + m_vecLogFormatTime.resize(uiLogFileNum, ""); + m_vecLogContent[0].resize(uiLogFileNum, ""); + m_vecLogContent[1].resize(uiLogFileNum, ""); + char szFormat[32] = {0}; + for (uint32 i = 0; i < 1000; ++i) + { + snprintf(szFormat, 32, "%u]", i); + m_vecLogFormatMs.emplace_back(std::move(std::string(szFormat))); + } + for (uint32 i = 0; i < 15000; ++i) // a single code file lines should not be larger than 15000 + { + snprintf(szFormat, 32, ":%u ", i); + m_vecLogFormatCodeLine.emplace_back(std::move(std::string(szFormat))); + } + std::thread t(&AsyncLogger::LogToFile, this); + t.detach(); +} + +AsyncLogger::~AsyncLogger() +{ +} + +AsyncLogger* AsyncLogger::Instance(uint32 uiLogFileNum, int iLogLev, + unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex) +{ + if (s_pInstance == nullptr) + { + s_pInstance = new AsyncLogger(uiLogFileNum, iLogLev, uiMaxFileSize, uiMaxRollFileIndex); + } + return(s_pInstance); +} + +int AsyncLogger::WriteLog(int iWorkerIndex, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, const std::string& strContent) +{ + if (iLev > m_iLogLevel) + { + return(0); + } + uint32 uiContentIndex = 0; + if (iWorkerIndex < 0 || iWorkerIndex >= (int)m_vecLogFout.size()) + { + uiContentIndex = m_vecLogFout.size() - 1; + } + else + { + uiContentIndex = (uint32)iWorkerIndex; + } + AppendLogPattern(uiContentIndex, iLev, szFileName, uiFileLine, szFunction); + m_vecLogContent[m_uiWrittingIndex][uiContentIndex].append(strContent); + m_vecLogContent[m_uiWrittingIndex][uiContentIndex].append("\n"); + return(0); +} + +int AsyncLogger::WriteLog(int iWorkerIndex, const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, const std::string& strContent) +{ + if (iLev > m_iLogLevel) + { + return(0); + } + uint32 uiContentIndex = 0; + if (iWorkerIndex < 0 || iWorkerIndex >= (int)m_vecLogFout.size()) + { + uiContentIndex = m_vecLogFout.size() - 1; + } + else + { + uiContentIndex = (uint32)iWorkerIndex; + } + AppendLogPattern(uiContentIndex, strTraceId, iLev, szFileName, uiFileLine, szFunction); + m_vecLogContent[m_uiWrittingIndex][uiContentIndex].append(strContent); + m_vecLogContent[m_uiWrittingIndex][uiContentIndex].append("\n"); + return(0); +} + +void AsyncLogger::AddLogFile(int iWorkerIndex, const std::string& strLogFile) +{ + uint32 uiContentIndex = 0; + if (iWorkerIndex < 0 || iWorkerIndex >= (int)m_vecLogFileName.size()) + { + uiContentIndex = m_vecLogFileName.size() - 1; + } + else + { + uiContentIndex = (uint32)iWorkerIndex; + } + m_vecLogFileName[uiContentIndex] = strLogFile; +} + +void AsyncLogger::AppendLogPattern(uint32 uiIndex, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction) +{ + struct timeval timeval; + gettimeofday(&timeval, NULL); + if (timeval.tv_sec > m_vecTimeSec[uiIndex]) + { + char szTime[32]; + m_vecTimeSec[uiIndex] = (time_t)timeval.tv_sec; + strftime(szTime, 32, "[%Y-%m-%d %H:%M:%S.", std::localtime(&timeval.tv_sec)); + m_vecLogFormatTime[uiIndex] = szTime; + } + auto& strLogBuff = m_vecLogContent[m_uiWrittingIndex][uiIndex]; + strLogBuff.append(m_vecLogFormatTime[uiIndex]); + strLogBuff.append(m_vecLogFormatMs[timeval.tv_usec / 1000]); + strLogBuff.append(LogLevMsg[iLev]); + strLogBuff.append(szFileName); + strLogBuff.append(m_vecLogFormatCodeLine[uiFileLine]); + strLogBuff.append(szFunction); + strLogBuff.append(" "); +} + +void AsyncLogger::AppendLogPattern(uint32 uiIndex, const std::string& strTraceId, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction) +{ + struct timeval timeval; + gettimeofday(&timeval, NULL); + if (timeval.tv_sec > m_vecTimeSec[uiIndex]) + { + char szTime[32]; + m_vecTimeSec[uiIndex] = (time_t)timeval.tv_sec; + strftime(szTime, 32, "[%Y-%m-%d %H:%M:%S.", std::localtime(&timeval.tv_sec)); + m_vecLogFormatTime[uiIndex] = szTime; + } + auto& strLogBuff = m_vecLogContent[m_uiWrittingIndex][uiIndex]; + strLogBuff.append(m_vecLogFormatTime[uiIndex]); + strLogBuff.append(m_vecLogFormatMs[timeval.tv_usec / 1000]); + strLogBuff.append(LogLevMsg[iLev]); + strLogBuff.append(szFileName); + strLogBuff.append(m_vecLogFormatCodeLine[uiFileLine]); + strLogBuff.append(szFunction); + strLogBuff.append(" "); + strLogBuff.append(strTraceId); + strLogBuff.append(" "); +} + +void AsyncLogger::LogToFile() +{ + char szLogCost[200] = {0}; + uint32 uiReadIndex = 0; + auto time_now = std::chrono::system_clock::now(); + auto t = std::chrono::system_clock::to_time_t(time_now); + auto time_from_ms = std::chrono::duration_cast(time_now.time_since_epoch()); + auto time_to_ms = time_from_ms; + int64 lCostTimeMs = 0; + std::ostringstream oss; + while(true) + { + if (m_uiLastLogSize < 1048576) // When the log volume of a single write is less than 1MB + { + sleep(1); + } + uiReadIndex = m_uiWrittingIndex; + m_uiWrittingIndex = 1 - uiReadIndex; + m_uiLastLogSize = 0; + uint32 uiLogSize = 0; + time_now = std::chrono::system_clock::now(); + time_from_ms = std::chrono::duration_cast(time_now.time_since_epoch()); + for (uint32 i = 0; i < m_vecLogFout.size(); ++i) + { + if (m_vecLogFileName[i].size() == 0) + { + continue; + } + if (m_vecLogFout[i] == nullptr) + { + m_vecLogFout[i] = std::make_shared(); + if (m_vecLogFout[i] == nullptr) + { + continue; + } + m_vecLogFout[i]->open(m_vecLogFileName[i].c_str(), std::ios::app); + } + if (m_vecLogContent[uiReadIndex][i].empty()) + { + continue; + } + RollOver(m_vecLogFileName[i], m_vecLogFout[i]); + if (!m_vecLogFout[i]->good()) + { + std::cerr << "Can not open file: " << m_vecLogFileName[i] << std::endl; + continue; + } + uiLogSize = m_vecLogContent[uiReadIndex][i].size(); + m_vecLogFout[i]->write(m_vecLogContent[uiReadIndex][i].data(), uiLogSize); + m_vecLogFout[i]->flush(); + m_vecLogContent[uiReadIndex][i].clear(); + m_uiLastLogSize += uiLogSize; + } + time_now = std::chrono::system_clock::now(); + time_to_ms = std::chrono::duration_cast(time_now.time_since_epoch()); + lCostTimeMs = time_to_ms.count() - time_from_ms.count(); + t = std::chrono::system_clock::to_time_t(time_now); + oss.str(""); + // There is a bug: The std::get_time and std::put_time manipulators are still missing in gcc 4.9. + // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54354 + #if __GNUC__ >= 5 + oss << "[" << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S") << "." + << time_to_ms.count() % 1000 << "]" << LogLevMsg[Logger::INFO] << " " + << __FILE__ << ":" << __LINE__ << " " << __FUNCTION__ << " "; + #else + char szTime[32]; + strftime(szTime, 32, "%Y-%m-%d %H:%M:%S", std::localtime(&t)); + oss << "[" << szTime << "." + << time_to_ms.count() % 1000 << "]" << LogLevMsg[Logger::INFO] << " " + << __FILE__ << ":" << __LINE__ << " " << __FUNCTION__ << " "; + #endif + snprintf(szLogCost, 200, "%s it takes %ldms to write %u logs to log file.\n", oss.str().c_str(), lCostTimeMs, m_uiLastLogSize); + m_vecLogFout[m_vecLogFout.size() - 1]->write(szLogCost, strlen(szLogCost)); + m_vecLogFout[m_vecLogFout.size() - 1]->flush(); + } +} + +void AsyncLogger::RollOver(const std::string& strFileBase, std::shared_ptr pFout) +{ + long lFileSize = -1; + lFileSize = pFout->tellp(); + if (lFileSize < 0) + { + pFout->open(strFileBase.c_str(), std::ios::app); + } + else if (lFileSize >= m_uiMaxFileSize) + { + pFout->close(); + std::stringstream ssOldestFile( + std::stringstream::in | std::stringstream::out); + ssOldestFile << strFileBase << "." << m_uiMaxRollFileIndex; + remove(ssOldestFile.str().c_str()); + + for (int i = m_uiMaxRollFileIndex - 1; i >= 1; --i) + { + std::stringstream ssSrcFileName( + std::stringstream::in | std::stringstream::out); + std::stringstream ssDestFileName( + std::stringstream::in | std::stringstream::out); + + ssSrcFileName << strFileBase << "." << i; + ssDestFileName << strFileBase << "." << (i + 1); + remove(ssDestFileName.str().c_str()); + rename(ssSrcFileName.str().c_str(), ssDestFileName.str().c_str()); + } + std::stringstream ss(std::stringstream::in | std::stringstream::out); + ss << strFileBase << ".1"; + std::string strBackupFile = ss.str(); + rename(strFileBase.c_str(), strBackupFile.c_str()); + pFout->open(strFileBase.c_str(), std::ios::app); + } +} + +} /* namespace neb */ + diff --git a/src/logger/AsyncLogger.hpp b/src/logger/AsyncLogger.hpp new file mode 100644 index 00000000..3a5525f0 --- /dev/null +++ b/src/logger/AsyncLogger.hpp @@ -0,0 +1,88 @@ +/******************************************************************************* + * Project: Nebula + * @file AsyncLogger.hpp + * @brief + * @author Bwar + * @date: 2022-01-29 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_LOGGER_ASYNCLOGGER_HPP_ +#define SRC_LOGGER_ASYNCLOGGER_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include "Definition.hpp" +#include "Logger.hpp" + +namespace neb +{ + +class AsyncLogger +{ +public: + virtual ~AsyncLogger(); + + int WriteLog(int iWorkerIndex, int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, const std::string& strContent); + int WriteLog(int iWorkerIndex, const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, const std::string& strContent); + + void AddLogFile(int iWorkerIndex, const std::string& strLogFile); + + void SetLogLevel(int iLev) + { + m_iLogLevel = iLev; + } + + static AsyncLogger* Instance( + uint32 uiLogFileNum, + int iLogLev = Logger::INFO, + unsigned int uiMaxFileSize = neb::gc_uiMaxLogFileSize, + unsigned int uiMaxRollFileIndex = neb::gc_uiMaxRollLogFileIndex); + static inline AsyncLogger* Instance() + { + return(s_pInstance); + } + +protected: + void AppendLogPattern(uint32 uiIndex, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction); + void AppendLogPattern(uint32 uiIndex, const std::string& strTraceId, + int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction); + void LogToFile(); + void RollOver(const std::string& strFileBase, std::shared_ptr pFout); + +private: + explicit AsyncLogger( + uint32 uiLogFileNum, + int iLogLev = Logger::INFO, + unsigned int uiMaxFileSize = neb::gc_uiMaxLogFileSize, + unsigned int uiMaxRollFileIndex = neb::gc_uiMaxRollLogFileIndex); + + static AsyncLogger* s_pInstance; + +private: + int m_iLogLevel; + uint32 m_uiMaxFileSize; // 日志文件大小 + uint32 m_uiMaxRollFileIndex; // 滚动日志文件数量 + uint32 m_uiWrittingIndex; + uint32 m_uiLastLogSize; + std::vector m_vecTimeSec; + std::vector m_vecLogFileName; + std::vector> m_vecLogFout; + std::vector m_vecLogFormatTime; + std::vector m_vecLogFormatMs; + std::vector m_vecLogFormatCodeLine; + std::vector m_vecLogContent[2]; +}; + +} /* namespace neb */ + +#endif /* SRC_LOGGER_ASYNCLOGGER_HPP_ */ + diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp index fd193226..955be3d9 100644 --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -212,21 +212,27 @@ void FileLogger::AppendLogPattern(const std::string& strTraceId, int iLev, const return; } } - auto time_now = std::chrono::system_clock::now(); - auto duration_in_ms = std::chrono::duration_cast(time_now.time_since_epoch()); - auto t = std::chrono::system_clock::to_time_t(time_now); - std::ostringstream oss; -#if __GNUC__ >= 5 - oss << "[" << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S") << "." - << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" - << szFileName << ":" << uiFileLine << "][" << szFunction << "][" << strTraceId << "] "; -#else - strftime(m_szTime, 20, "%Y-%m-%d %H:%M:%S", std::localtime(&t)); - oss << "[" << m_szTime << "." - << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" - << szFileName << ":" << uiFileLine << "][" << szFunction << "][" << strTraceId << "] "; -#endif - Append(oss.str()); + struct timeval timeval; + gettimeofday(&timeval, NULL); + if (timeval.tv_sec > m_lTimeSec) + { + char szTime[32]; + m_lTimeSec = (time_t)timeval.tv_sec; + strftime(szTime, 32, "[%Y-%m-%d %H:%M:%S.", std::localtime(&m_lTimeSec)); + m_strLogFormatTime = szTime; + } + m_strLogLine.clear(); + m_strLogLine.append(m_strLogFormatTime); + m_strLogLine.append(std::to_string(timeval.tv_usec / 1000)); + m_strLogLine.append("]"); + m_strLogLine.append(LogLevMsg[iLev]); + m_strLogLine.append(szFileName); + m_strLogLine.append(":"); + m_strLogLine.append(std::to_string(uiFileLine)); + m_strLogLine.append(" "); + m_strLogLine.append(szFunction); + m_strLogLine.append(" "); + Append(m_strLogLine); } } /* namespace neb */ diff --git a/src/logger/FileLogger.hpp b/src/logger/FileLogger.hpp index 363087a7..4951a58f 100644 --- a/src/logger/FileLogger.hpp +++ b/src/logger/FileLogger.hpp @@ -11,6 +11,8 @@ #define LOGGER_FILELOGGER_HPP_ #include +#include +#include #include #include #include @@ -88,7 +90,11 @@ class FileLogger: public Logger unsigned int m_uiMaxFileSize; // 日志文件大小 unsigned int m_uiMaxRollFileIndex; // 滚动日志文件数量 bool m_bAlwaysFlush; + time_t m_lTimeSec; std::string m_strLogFileBase; // 日志文件基本名(如 log/program_name.log) + std::string m_strLogFormatTime; + std::string m_strLogLine; + std::string m_strOutStr; }; template @@ -262,7 +268,7 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) } const char* pPos = szFormat; bool bPlaceholder = false; - std::string strOutStr; + m_strOutStr.clear(); int i = 0; for (size_t i = 0; ; ++i) { @@ -271,22 +277,22 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) case '\0': if (i > 0) { - strOutStr.assign(szFormat, i); - m_fout << strOutStr; + m_strOutStr.assign(szFormat, i); + m_fout << m_strOutStr; } m_fout << arg; return(nullptr); case '%': if (bPlaceholder) { - strOutStr.append("%"); + m_strOutStr.append("%"); pPos = szFormat + i + 1; - m_fout << strOutStr; + m_fout << m_strOutStr; return(PrintfAppend(pPos, std::forward(arg))); } else { - strOutStr.assign(szFormat, i); + m_strOutStr.assign(szFormat, i); bPlaceholder = true; } break; @@ -294,7 +300,7 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_fout << strOutStr; + m_fout << m_strOutStr; m_fout << std::fixed << arg; return(pPos); } @@ -309,7 +315,7 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_fout << strOutStr; + m_fout << m_strOutStr; m_fout << arg; return(pPos); } @@ -320,7 +326,7 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_fout << strOutStr; + m_fout << m_strOutStr; m_fout << std::dec << arg; return(pPos); } @@ -329,7 +335,7 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_fout << strOutStr; + m_fout << m_strOutStr; m_fout << std::hex << std::nouppercase << arg; return(pPos); } @@ -338,7 +344,7 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_fout << strOutStr; + m_fout << m_strOutStr; m_fout << std::hex << std::uppercase << arg; return(pPos); } @@ -347,7 +353,7 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_fout << strOutStr; + m_fout << m_strOutStr; m_fout << std::oct << arg; return(pPos); } @@ -357,7 +363,7 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_fout << strOutStr; + m_fout << m_strOutStr; m_fout << std::scientific << std::nouppercase << arg; return(pPos); } @@ -367,7 +373,7 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_fout << strOutStr; + m_fout << m_strOutStr; m_fout << std::scientific << std::uppercase << arg; return(pPos); } @@ -376,8 +382,8 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) ; } } - strOutStr.assign(szFormat, i); - m_fout << strOutStr; + m_strOutStr.assign(szFormat, i); + m_fout << m_strOutStr; return(nullptr); } diff --git a/src/logger/Logger.hpp b/src/logger/Logger.hpp index 5401290b..326f5cdd 100644 --- a/src/logger/Logger.hpp +++ b/src/logger/Logger.hpp @@ -44,14 +44,14 @@ class Logger const std::string LogLevMsg[Logger::LEV_MAX] = { - "FATAL", //致命错误 - "CRITICAL", //严重错误 - "ERROR", //一般错误 - "NOTICE", //关键提示消息 - "WARNING", //警告 - "INFO", //一般提示消息 - "DEBUG", //调试消息 - "TRACE" + "[FATAL]", //致命错误 + "[CRITICAL]", //严重错误 + "[ERROR]", //一般错误 + "[NOTICE]", //关键提示消息 + "[WARN]", //警告 + "[INFO]", //一般提示消息 + "[DEBUG]", //调试消息 + "[TRACE]" }; } /* namespace neb */ diff --git a/src/logger/NetLogger.cpp b/src/logger/NetLogger.cpp index b891758e..80117d92 100644 --- a/src/logger/NetLogger.cpp +++ b/src/logger/NetLogger.cpp @@ -8,21 +8,22 @@ * Modify history: ******************************************************************************/ +#include "NetLogger.hpp" #include #include #include "pb/msg.pb.h" #include "pb/neb_sys.pb.h" #include "labor/NodeInfo.hpp" #include "labor/Labor.hpp" +#include "labor/Worker.hpp" #include "actor/cmd/CW.hpp" -#include "NetLogger.hpp" namespace neb { -NetLogger::NetLogger(const std::string strLogFile, int iLogLev, unsigned int uiMaxFileSize, - unsigned int uiMaxRollFileIndex, unsigned int uiMaxLogLineLen, bool bAlwaysFlush, Labor* pLabor) - : m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), m_uiMaxLogLineLen(uiMaxFileSize), +NetLogger::NetLogger(const std::string& strLogFile, int iLogLev, unsigned int uiMaxFileSize, + unsigned int uiMaxRollFileIndex, bool bAlwaysFlush, Labor* pLabor) + : m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), m_bEnableNetLogger(false), m_pLabor(pLabor), m_pLog(nullptr) { #if __cplusplus >= 201401L @@ -32,10 +33,60 @@ NetLogger::NetLogger(const std::string strLogFile, int iLogLev, unsigned int uiM #endif } +NetLogger::NetLogger(int iWorkerIndex, const std::string& strLogFile, int iLogLev, unsigned int uiMaxFileSize, + unsigned int uiMaxRollFileIndex, Labor* pLabor) + : m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), + m_bEnableNetLogger(false), m_pLabor(pLabor), m_pLog(nullptr) +{ + AsyncLogger::Instance()->AddLogFile(iWorkerIndex, strLogFile); +} + NetLogger::~NetLogger() { } +void NetLogger::WriteFileLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, const std::string& strLogContent) +{ + if (m_pLog == nullptr) // m_pLabor->GetNodeInfo().bAsyncLogger + { + if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) + { + AsyncLogger::Instance()->WriteLog(-1, iLev, szFileName, uiFileLine, szFunction, strLogContent); + } + else + { + AsyncLogger::Instance()->WriteLog(((Worker*)m_pLabor)->GetWorkerInfo().iWorkerIndex, + iLev, szFileName, uiFileLine, szFunction, strLogContent); + } + } + else + { + m_pLog->WriteLog(iLev, szFileName, uiFileLine, szFunction, strLogContent); + } +} + +void NetLogger::WriteFileLog(const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, const std::string& strLogContent) +{ + if (m_pLog == nullptr) // m_pLabor->GetNodeInfo().bAsyncLogger + { + if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) + { + AsyncLogger::Instance()->WriteLog(-1, strTraceId, iLev, szFileName, uiFileLine, szFunction, strLogContent); + } + else + { + AsyncLogger::Instance()->WriteLog(((Worker*)m_pLabor)->GetWorkerInfo().iWorkerIndex, + strTraceId, iLev, szFileName, uiFileLine, szFunction, strLogContent); + } + } + else + { + m_pLog->WriteLog(strTraceId, iLev, szFileName, uiFileLine, szFunction, strLogContent); + } +} + void NetLogger::SinkLog(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const std::string& strLogContent, const std::string& strTraceId) { @@ -59,8 +110,8 @@ void NetLogger::SinkLog(int iLev, const char* szFileName, unsigned int uiFileLin { oMsgBody.mutable_req_target()->set_route(m_pLabor->GetNodeInfo().strNodeIdentify); } - oTraceLog.SerializeToString(&m_strLogData); - oMsgBody.set_data(m_strLogData); + oTraceLog.SerializeToString(&m_strOutStr); + oMsgBody.set_data(m_strOutStr); m_pLabor->AddNetLogMsg(oMsgBody); } } diff --git a/src/logger/NetLogger.hpp b/src/logger/NetLogger.hpp index cbfd893a..ca198fbc 100644 --- a/src/logger/NetLogger.hpp +++ b/src/logger/NetLogger.hpp @@ -15,6 +15,7 @@ #include #include "Logger.hpp" #include "FileLogger.hpp" +#include "AsyncLogger.hpp" namespace neb { @@ -25,13 +26,19 @@ class NetLogger: public Logger { public: NetLogger( - const std::string strLogFile, + const std::string& strLogFile, int iLogLev = Logger::INFO, unsigned int uiMaxFileSize = gc_uiMaxLogFileSize, unsigned int uiMaxRollFileIndex = gc_uiMaxRollLogFileIndex, - unsigned int uiMaxLogLineLen = gc_uiMaxLogLineLen, bool bAlwaysFlush = true, Labor* pLabor = nullptr); + NetLogger( + int iWorkerIndex, + const std::string& strLogFile, + int iLogLev = Logger::INFO, + unsigned int uiMaxFileSize = gc_uiMaxLogFileSize, + unsigned int uiMaxRollFileIndex = gc_uiMaxRollLogFileIndex, + Labor* pLabor = nullptr); virtual ~NetLogger(); template @@ -50,7 +57,14 @@ class NetLogger: public Logger virtual void SetLogLevel(int iLev) { m_iLogLevel = iLev; - m_pLog->SetLogLevel(iLev); + if (m_pLog == nullptr) + { + AsyncLogger::Instance()->SetLogLevel(iLev); + } + else + { + m_pLog->SetLogLevel(iLev); + } } virtual void SetNetLogLevel(int iLev) @@ -69,17 +83,20 @@ class NetLogger: public Logger template void Append(T&& arg); template void Append(const char* szFormat, T&& arg, Targs&&... args); template const char* PrintfAppend(const char* szFormat, T&& arg); + void WriteFileLog(int iLev, const char* szFileName, unsigned int uiFileLine, + const char* szFunction, const std::string& strLogContent); + void WriteFileLog(const std::string& strTraceId, int iLev, const char* szFileName, + unsigned int uiFileLine, const char* szFunction, const std::string& strLogContent); void SinkLog(int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction, const std::string& strLogContent, const std::string& strTraceId = ""); private: int m_iLogLevel; int m_iNetLogLevel; - unsigned int m_uiMaxLogLineLen; bool m_bEnableNetLogger; std::ostringstream m_ossLogContent; Labor* m_pLabor; - std::string m_strLogData; ///< 用于提高序列化效率 + std::string m_strOutStr; std::unique_ptr m_pLog; }; @@ -94,7 +111,7 @@ int NetLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLin m_ossLogContent.str(""); Append(szLogFormat, std::forward(args)...); - m_pLog->WriteLog(iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); + WriteFileLog(iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); if (iLev > m_iNetLogLevel) { @@ -115,7 +132,7 @@ int NetLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szF m_ossLogContent.str(""); Append(szLogFormat, std::forward(args)...); - m_pLog->WriteLog(strTraceId, iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); + WriteFileLog(strTraceId, iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); if (iLev > m_iNetLogLevel) { @@ -137,7 +154,7 @@ int NetLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLin m_ossLogContent.str(""); Append(std::forward(args)...); - m_pLog->WriteLog(iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); + WriteFileLog(iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); if (iLev > m_iNetLogLevel) { @@ -158,7 +175,7 @@ int NetLogger::WriteLog(const std::string& strTraceId, int iLev, const char* szF m_ossLogContent.str(""); Append(std::forward(args)...); - m_pLog->WriteLog(strTraceId, iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); + WriteFileLog(strTraceId, iLev, szFileName, uiFileLine, szFunction, m_ossLogContent.str()); if (iLev > m_iNetLogLevel) { @@ -210,7 +227,7 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) } const char* pPos = szFormat; bool bPlaceholder = false; - std::string strOutStr; + m_strOutStr.clear(); int i = 0; for (size_t i = 0; ; ++i) { @@ -219,22 +236,22 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) case '\0': if (i > 0) { - strOutStr.assign(szFormat, i); - m_ossLogContent << strOutStr; + m_strOutStr.assign(szFormat, i); + m_ossLogContent << m_strOutStr; } m_ossLogContent << arg; return(nullptr); case '%': if (bPlaceholder) { - strOutStr.append("%"); + m_strOutStr.append("%"); pPos = szFormat + i + 1; - m_ossLogContent << strOutStr; + m_ossLogContent << m_strOutStr; return(PrintfAppend(pPos, std::forward(arg))); } else { - strOutStr.assign(szFormat, i); + m_strOutStr.assign(szFormat, i); bPlaceholder = true; } break; @@ -242,7 +259,7 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_ossLogContent << strOutStr; + m_ossLogContent << m_strOutStr; m_ossLogContent << std::fixed << arg; return(pPos); } @@ -257,7 +274,7 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_ossLogContent << strOutStr; + m_ossLogContent << m_strOutStr; m_ossLogContent << arg; return(pPos); } @@ -268,7 +285,7 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_ossLogContent << strOutStr; + m_ossLogContent << m_strOutStr; m_ossLogContent << std::dec << arg; return(pPos); } @@ -277,7 +294,7 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_ossLogContent << strOutStr; + m_ossLogContent << m_strOutStr; m_ossLogContent << std::hex << std::nouppercase << arg; return(pPos); } @@ -286,7 +303,7 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_ossLogContent << strOutStr; + m_ossLogContent << m_strOutStr; m_ossLogContent << std::hex << std::uppercase << arg; return(pPos); } @@ -295,7 +312,7 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_ossLogContent << strOutStr; + m_ossLogContent << m_strOutStr; m_ossLogContent << std::oct << arg; return(pPos); } @@ -305,7 +322,7 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_ossLogContent << strOutStr; + m_ossLogContent << m_strOutStr; m_ossLogContent << std::scientific << std::nouppercase << arg; return(pPos); } @@ -315,7 +332,7 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) if (bPlaceholder) { pPos = szFormat + i + 1; - m_ossLogContent << strOutStr; + m_ossLogContent << m_strOutStr; m_ossLogContent << std::scientific << std::uppercase << arg; return(pPos); } @@ -324,8 +341,8 @@ const char* NetLogger::PrintfAppend(const char* szFormat, T&& arg) ; } } - strOutStr.assign(szFormat, i); - m_ossLogContent << strOutStr; + m_strOutStr.assign(szFormat, i); + m_ossLogContent << m_strOutStr; return(nullptr); } From 2e2b94217a6f945b9f441fbc7514c2e54c28c1a1 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 19 Feb 2022 21:51:31 +0800 Subject: [PATCH 151/176] async logger optimization --- src/logger/NetLogger.cpp | 24 ++++-------------------- src/logger/NetLogger.hpp | 1 + 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/logger/NetLogger.cpp b/src/logger/NetLogger.cpp index 80117d92..1849e5b7 100644 --- a/src/logger/NetLogger.cpp +++ b/src/logger/NetLogger.cpp @@ -23,7 +23,7 @@ namespace neb NetLogger::NetLogger(const std::string& strLogFile, int iLogLev, unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex, bool bAlwaysFlush, Labor* pLabor) - : m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), + : m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), m_iWorkerIndex(0), m_bEnableNetLogger(false), m_pLabor(pLabor), m_pLog(nullptr) { #if __cplusplus >= 201401L @@ -35,7 +35,7 @@ NetLogger::NetLogger(const std::string& strLogFile, int iLogLev, unsigned int ui NetLogger::NetLogger(int iWorkerIndex, const std::string& strLogFile, int iLogLev, unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex, Labor* pLabor) - : m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), + : m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), m_iWorkerIndex(iWorkerIndex), m_bEnableNetLogger(false), m_pLabor(pLabor), m_pLog(nullptr) { AsyncLogger::Instance()->AddLogFile(iWorkerIndex, strLogFile); @@ -50,15 +50,7 @@ void NetLogger::WriteFileLog(int iLev, const char* szFileName, unsigned int uiFi { if (m_pLog == nullptr) // m_pLabor->GetNodeInfo().bAsyncLogger { - if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) - { - AsyncLogger::Instance()->WriteLog(-1, iLev, szFileName, uiFileLine, szFunction, strLogContent); - } - else - { - AsyncLogger::Instance()->WriteLog(((Worker*)m_pLabor)->GetWorkerInfo().iWorkerIndex, - iLev, szFileName, uiFileLine, szFunction, strLogContent); - } + AsyncLogger::Instance()->WriteLog(m_iWorkerIndex, iLev, szFileName, uiFileLine, szFunction, strLogContent); } else { @@ -71,15 +63,7 @@ void NetLogger::WriteFileLog(const std::string& strTraceId, int iLev, const char { if (m_pLog == nullptr) // m_pLabor->GetNodeInfo().bAsyncLogger { - if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) - { - AsyncLogger::Instance()->WriteLog(-1, strTraceId, iLev, szFileName, uiFileLine, szFunction, strLogContent); - } - else - { - AsyncLogger::Instance()->WriteLog(((Worker*)m_pLabor)->GetWorkerInfo().iWorkerIndex, - strTraceId, iLev, szFileName, uiFileLine, szFunction, strLogContent); - } + AsyncLogger::Instance()->WriteLog(m_iWorkerIndex, strTraceId, iLev, szFileName, uiFileLine, szFunction, strLogContent); } else { diff --git a/src/logger/NetLogger.hpp b/src/logger/NetLogger.hpp index ca198fbc..4c69075c 100644 --- a/src/logger/NetLogger.hpp +++ b/src/logger/NetLogger.hpp @@ -93,6 +93,7 @@ class NetLogger: public Logger private: int m_iLogLevel; int m_iNetLogLevel; + int m_iWorkerIndex; bool m_bEnableNetLogger; std::ostringstream m_ossLogContent; Labor* m_pLabor; From 345ae850c9aa9348992b3af779c7cf5ec7e4ec17 Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 23 Feb 2022 21:19:40 +0800 Subject: [PATCH 152/176] add sequence to SelfChannel --- src/channel/SelfChannel.cpp | 3 ++- src/channel/SelfChannel.hpp | 12 +++++++++--- src/ios/IO.hpp | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/channel/SelfChannel.cpp b/src/channel/SelfChannel.cpp index 3d70d024..953823dc 100644 --- a/src/channel/SelfChannel.cpp +++ b/src/channel/SelfChannel.cpp @@ -12,7 +12,8 @@ namespace neb { -SelfChannel::SelfChannel() +SelfChannel::SelfChannel(uint32 uiSeq) + : m_bIsResponse(false), m_uiChannelSeq(uiSeq), m_uiStepSeq(0) { } diff --git a/src/channel/SelfChannel.hpp b/src/channel/SelfChannel.hpp index 14cee536..2bf54e77 100644 --- a/src/channel/SelfChannel.hpp +++ b/src/channel/SelfChannel.hpp @@ -18,7 +18,7 @@ namespace neb class SelfChannel: public SocketChannel { public: - SelfChannel(); + SelfChannel(uint32 uiSeq); virtual ~SelfChannel(); virtual int GetFd() const override @@ -26,6 +26,11 @@ class SelfChannel: public SocketChannel return(0); } + virtual uint32 GetSequence() const override + { + return(m_uiChannelSeq); + } + virtual bool IsClient() const override { return(false); @@ -77,8 +82,9 @@ class SelfChannel: public SocketChannel } private: - bool m_bIsResponse = false; - uint32 m_uiStepSeq = 0; + bool m_bIsResponse; + uint32 m_uiChannelSeq; + uint32 m_uiStepSeq; std::string strEmpty; }; diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index f567e33f..544af110 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -743,7 +743,7 @@ template template bool IO::SendToSelf(Actor* pActor, Targs&&... args) { - auto pSelfChannel = std::make_shared(); + auto pSelfChannel = std::make_shared(pActor->m_pLabor->GetSequence()); return(CodecFactory::OnSelfRequest(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), pSelfChannel, std::forward(args)...)); } From 74b60946c7d22fec1f6e4c7f2c331e22d0fea940 Mon Sep 17 00:00:00 2001 From: Bwar Date: Thu, 3 Mar 2022 22:38:27 +0800 Subject: [PATCH 153/176] file logger init --- src/logger/FileLogger.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp index 955be3d9..631be5d5 100644 --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -28,7 +28,8 @@ FileLogger* FileLogger::s_pInstance = nullptr; FileLogger::FileLogger(const std::string& strLogFile, int iLogLev, unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex, bool bAlwaysFlush) : m_iLogLevel(iLogLev), m_uiLogNum(0), m_uiMaxFileSize(uiMaxFileSize), - m_uiMaxRollFileIndex(uiMaxRollFileIndex), m_bAlwaysFlush(bAlwaysFlush), m_strLogFileBase(strLogFile) + m_uiMaxRollFileIndex(uiMaxRollFileIndex), m_bAlwaysFlush(bAlwaysFlush), + m_lTimeSec(0), m_strLogFileBase(strLogFile) { #if __GNUC__ < 5 m_szTime = (char*)malloc(20); From b9b9409941f489ffc668a3e663ab046d9a221a43 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 13 Mar 2022 17:10:57 +0800 Subject: [PATCH 154/176] add selfchannel sequence --- src/actor/Actor.cpp | 6 +++--- src/actor/Actor.hpp | 7 ++++--- src/codec/CodecFactory.cpp | 36 +++++++++++++++++++++++++++--------- src/ios/IO.hpp | 12 ++++++++---- 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 9f5db47b..5bf0050b 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -283,17 +283,17 @@ void Actor::CircuitBreak(const std::string& strIdentify) m_pLabor->GetDispatcher()->CircuitBreak(strIdentify); } -bool Actor::SendToSelf(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) +uint32 Actor::SendToSelf(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { return(IO::SendToSelf(this, iCmd, uiSeq, oMsgBody)); } -bool Actor::SendToSelf(const HttpMsg& oHttpMsg) +uint32 Actor::SendToSelf(const HttpMsg& oHttpMsg) { return(IO::SendToSelf(this, oHttpMsg)); } -bool Actor::SendToSelf(const RedisMsg& oRedisMsg) +uint32 Actor::SendToSelf(const RedisMsg& oRedisMsg) { return(IO::SendToSelf(this, oRedisMsg)); } diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index e082f11d..44aefff9 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -301,10 +301,11 @@ class Actor: public std::enable_shared_from_this /** * @brief 发送请求到当前worker + * @return SelfChannel的seq */ - bool SendToSelf(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - bool SendToSelf(const HttpMsg& oHttpMsg); - bool SendToSelf(const RedisMsg& oRedisMsg); + uint32 SendToSelf(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); + uint32 SendToSelf(const HttpMsg& oHttpMsg); + uint32 SendToSelf(const RedisMsg& oRedisMsg); std::shared_ptr GetLastActivityChannel(); diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp index f358fb7c..f07d3e22 100644 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -118,7 +118,9 @@ bool CodecFactory::OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std: oMsgHead.set_seq(uiSeq); oMsgHead.set_len(oMsgBody.ByteSize()); pChannel->SetStepSeq(uiStepSeq); - return(IO::OnRequest(pDispatcher, std::static_pointer_cast(pChannel), iCmd, oMsgHead, oMsgBody)); + auto pSocketChannel = std::static_pointer_cast(pChannel); + pDispatcher->m_pLastActivityChannel = pSocketChannel; + return(IO::OnRequest(pDispatcher, pSocketChannel, iCmd, oMsgHead, oMsgBody)); } bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) @@ -127,45 +129,61 @@ bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr::OnResponse(pDispatcher, std::static_pointer_cast(pChannel), uiSeq, oMsgHead, oMsgBody)); + auto pSocketChannel = std::static_pointer_cast(pChannel); + pDispatcher->m_pLastActivityChannel = pSocketChannel; + return(IO::OnResponse(pDispatcher, pSocketChannel, uiSeq, oMsgHead, oMsgBody)); } bool CodecFactory::OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const HttpMsg& oHttpMsg) { pChannel->SetStepSeq(uiStepSeq); - return(IO::OnRequest(pDispatcher, std::static_pointer_cast(pChannel), oHttpMsg.path(), oHttpMsg)); + auto pSocketChannel = std::static_pointer_cast(pChannel); + pDispatcher->m_pLastActivityChannel = pSocketChannel; + return(IO::OnRequest(pDispatcher, pSocketChannel, oHttpMsg.path(), oHttpMsg)); } bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const HttpMsg& oHttpMsg) { - return(IO::OnResponse(pDispatcher, std::static_pointer_cast(pChannel), pChannel->GetStepSeq(), oHttpMsg)); + auto pSocketChannel = std::static_pointer_cast(pChannel); + pDispatcher->m_pLastActivityChannel = pSocketChannel; + return(IO::OnResponse(pDispatcher, pSocketChannel, pChannel->GetStepSeq(), oHttpMsg)); } bool CodecFactory::OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const RedisMsg& oRedisMsg) { pChannel->SetStepSeq(uiStepSeq); - return(IO::OnRequest(pDispatcher, std::static_pointer_cast(pChannel), (int32)CMD_REQ_REDIS_PROXY, oRedisMsg)); + auto pSocketChannel = std::static_pointer_cast(pChannel); + pDispatcher->m_pLastActivityChannel = pSocketChannel; + return(IO::OnRequest(pDispatcher, pSocketChannel, (int32)CMD_REQ_REDIS_PROXY, oRedisMsg)); } bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const RedisMsg& oRedisMsg) { - return(IO::OnResponse(pDispatcher, std::static_pointer_cast(pChannel), pChannel->GetStepSeq(), oRedisMsg)); + auto pSocketChannel = std::static_pointer_cast(pChannel); + pDispatcher->m_pLastActivityChannel = pSocketChannel; + return(IO::OnResponse(pDispatcher, pSocketChannel, pChannel->GetStepSeq(), oRedisMsg)); } bool CodecFactory::OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const char* pRaw, uint32 uiRawSize) { pChannel->SetStepSeq(uiStepSeq); - return(IO::OnRequest(pDispatcher, std::static_pointer_cast(pChannel), (int32)CMD_REQ_RAW_DATA, pRaw, uiRawSize)); + auto pSocketChannel = std::static_pointer_cast(pChannel); + pDispatcher->m_pLastActivityChannel = pSocketChannel; + return(IO::OnRequest(pDispatcher, pSocketChannel, (int32)CMD_REQ_RAW_DATA, pRaw, uiRawSize)); } bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const char* pRaw, uint32 uiRawSize) { - return(IO::OnResponse(pDispatcher, std::static_pointer_cast(pChannel), pChannel->GetStepSeq(), pRaw, uiRawSize)); + auto pSocketChannel = std::static_pointer_cast(pChannel); + pDispatcher->m_pLastActivityChannel = pSocketChannel; + return(IO::OnResponse(pDispatcher, pSocketChannel, pChannel->GetStepSeq(), pRaw, uiRawSize)); } bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const CassMessage& oCassMsg) { - return(IO::OnResponse(pDispatcher, std::static_pointer_cast(pChannel), pChannel->GetStepSeq(), oCassMsg)); + auto pSocketChannel = std::static_pointer_cast(pChannel); + pDispatcher->m_pLastActivityChannel = pSocketChannel; + return(IO::OnResponse(pDispatcher, pSocketChannel, pChannel->GetStepSeq(), oCassMsg)); } E_CODEC_STATUS CodecFactory::OnNebulaEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart) diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index 544af110..c303bd86 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -90,7 +90,7 @@ class IO static bool Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args); template - static bool SendToSelf(Actor* pActor, Targs&&... args); + static uint32 SendToSelf(Actor* pActor, Targs&&... args); template static E_CODEC_STATUS Recv(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args); @@ -741,11 +741,15 @@ bool IO::Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::stri template template -bool IO::SendToSelf(Actor* pActor, Targs&&... args) +uint32 IO::SendToSelf(Actor* pActor, Targs&&... args) { auto pSelfChannel = std::make_shared(pActor->m_pLabor->GetSequence()); - return(CodecFactory::OnSelfRequest(pActor->m_pLabor->GetDispatcher(), - pActor->GetSequence(), pSelfChannel, std::forward(args)...)); + if (CodecFactory::OnSelfRequest(pActor->m_pLabor->GetDispatcher(), + pActor->GetSequence(), pSelfChannel, std::forward(args)...)) + { + return(pSelfChannel->GetSequence()); + } + return(0); // failed } template From cb7132b0793e3e415e9894ac31ec9edefd8e6afe Mon Sep 17 00:00:00 2001 From: Bwar Date: Wed, 23 Mar 2022 21:46:02 +0800 Subject: [PATCH 155/176] StepRedisCluster circuit break and recovery. Nodes recovery bug fixed. --- src/actor/step/sys_step/StepRedisCluster.cpp | 52 +++++++++++++++++--- src/actor/step/sys_step/StepRedisCluster.hpp | 3 ++ src/ios/Dispatcher.hpp | 1 + src/ios/IO.hpp | 2 +- src/ios/Nodes.cpp | 12 +++-- src/ios/Nodes.hpp | 10 ++-- src/logger/FileLogger.cpp | 42 +++++++++------- 7 files changed, 90 insertions(+), 32 deletions(-) diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index c38c7e61..83585418 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -139,6 +139,10 @@ E_CMD_STATUS StepRedisCluster::Callback( { CmdAskingCallback(pChannel, pChannel->GetIdentify(), oRedisReply); } + else if (pRedisRequest->element(0).str() == "PING") + { + CmdPingCallback(pChannel, pChannel->GetIdentify(), oRedisReply); + } else if (pRedisRequest->element(0).str() == "READONLY") { CmdReadOnlyCallback(pChannel, pChannel->GetIdentify(), oRedisReply); @@ -342,6 +346,10 @@ E_CMD_STATUS StepRedisCluster::Callback( E_CMD_STATUS StepRedisCluster::Timeout() { SendCmdClusterSlots(); + for (auto iter = m_setFailedNode.begin(); iter != m_setFailedNode.end(); ++iter) + { + SendCmdPing(*iter); + } for (auto timeout_iter = m_mapTimeoutStep.begin(); timeout_iter != m_mapTimeoutStep.end(); ) { if (GetNowTime() - timeout_iter->first >= GetTimeout()) @@ -375,6 +383,7 @@ E_CMD_STATUS StepRedisCluster::ErrBack( std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) { LOG4_ERROR("error %d: %s", iErrno, strErrMsg.c_str()); + m_setFailedNode.insert(pChannel->GetIdentify()); SendCmdClusterSlots(); CmdErrBack(pChannel, iErrno, strErrMsg); return(CMD_STATUS_RUNNING); @@ -397,7 +406,7 @@ void StepRedisCluster::CmdErrBack( step_iter->second.pop(); if (uiRealStepSeq == GetSequence()) { - SendCmdClusterSlots(); + ; // SendCmdClusterSlots(); } else { @@ -667,13 +676,23 @@ bool StepRedisCluster::GetRedisNode(int iSlotId, int iReadOrWrite, std::string& } else { - slot_iter->second->iterFllower++; - if (slot_iter->second->iterFllower == slot_iter->second->setFllower.end()) + for (uint32 i = 0; i < slot_iter->second->setFllower.size(); ++i) { - slot_iter->second->iterFllower = slot_iter->second->setFllower.begin(); + slot_iter->second->iterFllower++; + if (slot_iter->second->iterFllower == slot_iter->second->setFllower.end()) + { + slot_iter->second->iterFllower = slot_iter->second->setFllower.begin(); + } + strNodeIdentify = *(slot_iter->second->iterFllower); + bIsMaster = false; + auto node_iter = m_setFailedNode.find(strNodeIdentify); + if (node_iter == m_setFailedNode.end()) + { + return(true); + } } - strNodeIdentify = *(slot_iter->second->iterFllower); - bIsMaster = false; + strNodeIdentify = slot_iter->second->strMaster; + bIsMaster = true; } return(true); } @@ -790,6 +809,14 @@ bool StepRedisCluster::SendCmdAsking(const std::string& strIdentify) return(SendTo(strIdentify, pRedisRequest)); } +bool StepRedisCluster::SendCmdPing(const std::string& strIdentify) +{ + SetCmd("PING"); + auto pRedisRequest = MutableRedisRequest(); + pRedisRequest->set_integer(GetSequence()); // 借用integer暂存seq + return(SendTo(strIdentify, pRedisRequest)); +} + void StepRedisCluster::CmdAskingCallback(std::shared_ptr pChannel, const std::string& strIdentify, const RedisReply& oRedisReply) { @@ -832,6 +859,19 @@ void StepRedisCluster::CmdAskingCallback(std::shared_ptr pChannel } } +void StepRedisCluster::CmdPingCallback(std::shared_ptr pChannel, + const std::string& strIdentify, const RedisReply& oRedisReply) +{ + if (REDIS_REPLY_STATUS == oRedisReply.type() || REDIS_REPLY_STRING == oRedisReply.type()) + { + auto iter = m_setFailedNode.find(strIdentify); + if (iter != m_setFailedNode.end()) + { + m_setFailedNode.erase(iter); + } + } +} + bool StepRedisCluster::SendCmdReadOnly(const std::string& strIdentify) { SetCmd("READONLY"); diff --git a/src/actor/step/sys_step/StepRedisCluster.hpp b/src/actor/step/sys_step/StepRedisCluster.hpp index 1b87dda0..67abfae7 100644 --- a/src/actor/step/sys_step/StepRedisCluster.hpp +++ b/src/actor/step/sys_step/StepRedisCluster.hpp @@ -88,7 +88,9 @@ class StepRedisCluster: public RedisStep, bool SendCmdClusterSlots(); bool CmdClusterSlotsCallback(const RedisReply& oRedisReply); bool SendCmdAsking(const std::string& strIdentify); + bool SendCmdPing(const std::string& strIdentify); void CmdAskingCallback(std::shared_ptr pChannel, const std::string& strIdentify, const RedisReply& oRedisReply); + void CmdPingCallback(std::shared_ptr pChannel, const std::string& strIdentify, const RedisReply& oRedisReply); bool SendCmdReadOnly(const std::string& strIdentify); void CmdReadOnlyCallback(std::shared_ptr pChannel, const std::string& strIdentify, const RedisReply& oRedisReply); void AskingQueueErrBack(std::shared_ptr pChannel, @@ -115,6 +117,7 @@ class StepRedisCluster: public RedisStep, std::unordered_map> m_mapReply; std::map> m_mapTimeoutStep; std::vector> m_vecWaittingRequest; + std::set m_setFailedNode; static const uint16 sc_unClusterSlots; ///< redis cluster槽位数 static const std::unordered_set s_setSupportExtractCmd; diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 0d0a35e1..ca78aa92 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -42,6 +42,7 @@ extern "C" { #include #include +#include #include #include diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index c303bd86..1a969d94 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -701,7 +701,7 @@ template bool IO::Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args) { LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); - std::unordered_set setOnlineNodes; + std::set setOnlineNodes; if (pDispatcher->m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) { bool bSendResult = false; diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index 94c6daca..78977d19 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -177,7 +177,7 @@ bool Nodes::GetNode(const std::string& strNodeType, std::string& strNodeIdentify } } -bool Nodes::GetNode(const std::string& strNodeType, std::unordered_set& setNodeIdentify) +bool Nodes::GetNode(const std::string& strNodeType, std::set& setNodeIdentify) { auto node_type_iter = m_mapNode.find(strNodeType); if (node_type_iter == m_mapNode.end()) @@ -296,7 +296,7 @@ void Nodes::AddNode(const std::string& strNodeType, const std::string& strNodeId auto node_id_iter = m_mapNodeType.find(strNodeIdentify); if (node_id_iter == m_mapNodeType.end()) { - std::unordered_set setNodeType; + std::set setNodeType; setNodeType.insert(strNodeType); m_mapNodeType.insert(std::make_pair(strNodeIdentify, std::move(setNodeType))); } @@ -375,7 +375,7 @@ void Nodes::AddNodeKetama(const std::string& strNodeType, const std::string& str auto node_id_iter = m_mapNodeType.find(strNodeIdentify); if (node_id_iter == m_mapNodeType.end()) { - std::unordered_set setNodeType; + std::set setNodeType; setNodeType.insert(strNodeType); m_mapNodeType.insert(std::make_pair(strNodeIdentify, std::move(setNodeType))); } @@ -465,6 +465,7 @@ void Nodes::DelNode(const std::string& strNodeType, const std::string& strNodeId if (it != node_type_iter->second->setFailedNode.end()) { node_type_iter->second->setFailedNode.erase(it); + node_type_iter->second->itPollingFailed = node_type_iter->second->setFailedNode.begin(); } } @@ -494,6 +495,10 @@ void Nodes::NodeFailed(const std::string& strNodeIdentify) auto node_type_iter = m_mapNode.find(*type_it); if (node_type_iter != m_mapNode.end()) { + if (node_type_iter->second->mapNode2Hash.size() <= 1) + { + continue; // only one node, should not be circuit break. + } auto node_iter = node_type_iter->second->mapNode2Hash.find(strNodeIdentify); if (node_iter != node_type_iter->second->mapNode2Hash.end()) { @@ -537,6 +542,7 @@ void Nodes::NodeRecover(const std::string& strNodeIdentify) if (it != node_type_iter->second->setFailedNode.end()) { node_type_iter->second->setFailedNode.erase(it); + node_type_iter->second->itPollingFailed = node_type_iter->second->setFailedNode.begin(); } } AddNode(*type_it, strNodeIdentify); diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index e4a85540..bc5881b8 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include "Definition.hpp" //#define ROT32(x, y) ((x << y) | (x >> (32 - y))) // avoid effort @@ -63,8 +63,8 @@ class Nodes T_NODE2HASH_MAP::iterator itPollingNode; std::map mapHash2Node; std::map::const_iterator itHashRing; - std::unordered_set setFailedNode; - std::unordered_set::const_iterator itPollingFailed; + std::set setFailedNode; + std::set::const_iterator itPollingFailed; tagNode(){} ~tagNode(){} @@ -89,7 +89,7 @@ class Nodes bool GetNode(const std::string& strNodeType, std::string& strNodeIdentify); - bool GetNode(const std::string& strNodeType, std::unordered_set& setNodeIdentify); + bool GetNode(const std::string& strNodeType, std::set& setNodeIdentify); bool NodeDetect(const std::string& strNodeType, std::string& strNodeIdentify); @@ -152,7 +152,7 @@ class Nodes const int m_iVirtualNodeNum; std::unordered_map > m_mapNode; - std::unordered_map> m_mapNodeType; // key为节点标识 + std::unordered_map> m_mapNodeType; // key为节点标识 }; } /* namespace neb */ diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp index 631be5d5..5b4021b5 100644 --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -174,23 +174,28 @@ void FileLogger::AppendLogPattern(int iLev, const char* szFileName, unsigned int return; } } - auto time_now = std::chrono::system_clock::now(); - auto duration_in_ms = std::chrono::duration_cast(time_now.time_since_epoch()); - auto t = std::chrono::system_clock::to_time_t(time_now); - std::ostringstream oss; -// There is a bug: The std::get_time and std::put_time manipulators are still missing in gcc 4.9. -// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54354 -#if __GNUC__ >= 5 - oss << "[" << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S") << "." - << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" - << szFileName << ":" << uiFileLine << "][" << szFunction << "] "; -#else - strftime(m_szTime, 20, "%Y-%m-%d %H:%M:%S", std::localtime(&t)); - oss << "[" << m_szTime << "." - << duration_in_ms.count() % 1000 << "][" << LogLevMsg[iLev] << "][" - << szFileName << ":" << uiFileLine << "][" << szFunction << "] "; -#endif - Append(oss.str()); + struct timeval timeval; + gettimeofday(&timeval, NULL); + if (timeval.tv_sec > m_lTimeSec) + { + char szTime[32]; + m_lTimeSec = (time_t)timeval.tv_sec; + strftime(szTime, 32, "[%Y-%m-%d %H:%M:%S.", std::localtime(&m_lTimeSec)); + m_strLogFormatTime = szTime; + } + m_strLogLine.clear(); + m_strLogLine.append(m_strLogFormatTime); + m_strLogLine.append(std::to_string(timeval.tv_usec / 1000)); + m_strLogLine.append("]"); + m_strLogLine.append(LogLevMsg[iLev]); + m_strLogLine.append(" "); + m_strLogLine.append(szFileName); + m_strLogLine.append(":"); + m_strLogLine.append(std::to_string(uiFileLine)); + m_strLogLine.append(" "); + m_strLogLine.append(szFunction); + m_strLogLine.append(" "); + Append(m_strLogLine); } void FileLogger::AppendLogPattern(const std::string& strTraceId, int iLev, const char* szFileName, unsigned int uiFileLine, const char* szFunction) @@ -227,12 +232,15 @@ void FileLogger::AppendLogPattern(const std::string& strTraceId, int iLev, const m_strLogLine.append(std::to_string(timeval.tv_usec / 1000)); m_strLogLine.append("]"); m_strLogLine.append(LogLevMsg[iLev]); + m_strLogLine.append(" "); m_strLogLine.append(szFileName); m_strLogLine.append(":"); m_strLogLine.append(std::to_string(uiFileLine)); m_strLogLine.append(" "); m_strLogLine.append(szFunction); m_strLogLine.append(" "); + m_strLogLine.append(strTraceId); + m_strLogLine.append(" "); Append(m_strLogLine); } From 65eb93d7af502c257b117bbf1dc4bf06288f5258 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 27 Mar 2022 12:21:30 +0800 Subject: [PATCH 156/176] v1.7.1 release --- README.md | 5 +++++ README_cn.md | 5 +++++ conf/nebula.json | 2 ++ 3 files changed, 12 insertions(+) diff --git a/README.md b/README.md index 028f081f..b3620208 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,11 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v1.7.1 + - async logger optimization + - add selfchannel sequence + - StepRedisCluster circuit break and recovery + - Nodes recovery bug fixed #### v1.7.0 - IO optimization, codec plug-in. - remove shared_from_this of Channel and Actor and use ChannelWatcher and ActorWatcher instead to improve performance. diff --git a/README_cn.md b/README_cn.md index 27f84cad..69029cc1 100644 --- a/README_cn.md +++ b/README_cn.md @@ -133,6 +133,11 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 +#### v1.7.1 + - 优化异步文件日志 + - SelfChannel增加seq + - redis cluster客户端增加熔断和熔断恢复 + - 节点熔断bug修复 #### v1.7.0 - 优化IO通信,编解码器插件化。 - 移除Channel和Actor的shared_from_this,改用ChannelWatcher和ActorWatcher替代,以提高性能。 diff --git a/conf/nebula.json b/conf/nebula.json index 98c35ba6..76f1e884 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -7,6 +7,7 @@ "access_port": 9988, "//access_codec": "接入端编解码器,见codec/Codec.hpp中E_CODEC_TYPE枚举定义" "access_codec": 4, + "access_socket_type":"TCP", "//need_channel_verify":"是否需要连接验证,如设置为true,第一个消息必须是验证消息,未经验证的连接收到第二个消息会被立即关闭", "need_channel_verify":false, "gateway": "113.102.157.188", @@ -47,6 +48,7 @@ "//max_log_file_size": "单个日志文件大小限制", "max_log_file_size": 20480000, "always_flush_log":true, + "async_logger":false, "//permission": "限制。addr_permit为连接限制,限制每个IP在统计时间内连接次数;uin_permit为消息数量限制,限制每个用户在单位统计时间内发送消息数量。", "permission": { "addr_permit": { "stat_interval": 60.0, "permit_num": 1000000000 }, From 7d54dde7709bfbd59c160019ddbc7490086ea458 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 27 Mar 2022 22:34:59 +0800 Subject: [PATCH 157/176] Modify some makefile if judgments that are not supported by make versions --- README.md | 3 ++- README_cn.md | 1 + src/Makefile | 16 ++++++++-------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b3620208..bb126471 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,12 @@ Nebula can be used as a single high-performance TCP server, but building a clust * Load balancing * Circuit Breakers * Leadership election and cluster state +* A convenient way to extend third-party protocols ## Getting Start Nebula was developed with C++11/C++14 standard, requires a compiler capable of the C++11-standard and at least gcc4.8 (some C++14 features are replaced by C++11 standard when encountering a lower version of the compiler). - We provides [NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap), which allows developers to build and deploy Nebula quickly. Nebula will be a framework that be widely used. A distributed solution based on NebulaBootstrap will make it easy to develop micro-service applications in C++. + [NebulaBootstrap](https://github.com/Bwar/NebulaBootstrap) provides a quick way to build and deploy Nebula. A distributed solution based on NebulaBootstrap will make it easy to develop micro-service applications in C++. All dependencies will be automatically resolved in the following build steps. first install gcc and auto tools. centos: diff --git a/README_cn.md b/README_cn.md index 69029cc1..61eda1a8 100644 --- a/README_cn.md +++ b/README_cn.md @@ -75,6 +75,7 @@ * 分层服务 * 认证和鉴权 * 分布式日志跟踪 +* 容易扩展第三方通信协议 ## 开始 diff --git a/src/Makefile b/src/Makefile index 909b9700..abf41813 100644 --- a/src/Makefile +++ b/src/Makefile @@ -55,18 +55,18 @@ libnebula.so: $(OBJS) @for dir in $(SUB_INCLUDE); \ do \ mkdir -p $(NEBULA_PATH)/include/$$dir; \ - for f in `ls $(NEBULA_PATH)/src/$$dir/*.*`; \ - do \ - if [[ $$f =~ ".hpp" || $$f =~ ".h" || $$f =~ ".inl" ]]; then cp $$f $(NEBULA_PATH)/include/$$dir/; fi; \ - done; \ + cp $(NEBULA_PATH)/src/$$dir/*.h $(NEBULA_PATH)/include/$$dir/ >> /dev/null 2>&1; \ + cp $(NEBULA_PATH)/src/$$dir/*.hpp $(NEBULA_PATH)/include/$$dir/ >> /dev/null 2>&1; \ + cp $(NEBULA_PATH)/src/$$dir/*.inl $(NEBULA_PATH)/include/$$dir/ >> /dev/null 2>&1; \ + echo "$(NEBULA_PATH)/src/$$dir done"; \ done @for d in `find $(DEEP_SUB_INCLUDE) -type d`; \ do \ mkdir -p $(NEBULA_PATH)/include/$$d; \ - for f in `ls $(NEBULA_PATH)/src/$$d/*.*`; \ - do \ - if [[ $$f =~ ".hpp" || $$f =~ ".h" || $$f =~ ".inl" ]]; then cp $$f $(NEBULA_PATH)/include/$$d/; fi; \ - done; \ + cp $(NEBULA_PATH)/src/$$d/*.h $(NEBULA_PATH)/include/$$d/ >> /dev/null 2>&1; \ + cp $(NEBULA_PATH)/src/$$d/*.hpp $(NEBULA_PATH)/include/$$d/ >> /dev/null 2>&1; \ + cp $(NEBULA_PATH)/src/$$d/*.inl $(NEBULA_PATH)/include/$$d/ >> /dev/null 2>&1; \ + echo "$(NEBULA_PATH)/src/$$d done"; \ done cp -f $(NEBULA_PATH)/src/*.hpp $(NEBULA_PATH)/include/ cp -f $@ $(NEBULA_PATH)/lib/ From 99cdf3bb9bcfaf99fbd9a965095d94ab03d94bc9 Mon Sep 17 00:00:00 2001 From: Bwar Date: Fri, 1 Apr 2022 22:47:08 +0800 Subject: [PATCH 158/176] reduce the number of sending cluster slots to redis cluster. --- src/actor/ActorBuilder.cpp | 2 +- src/actor/step/sys_step/StepRedisCluster.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index f6537932..ef22b5f8 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -587,7 +587,7 @@ bool ActorBuilder::TransformToSharedStep(Actor* pCreator, std::shared_ptr { m_pLabor->GetDispatcher()->AddEvent(timer_watcher, StepTimeoutCallback, pSharedStep->m_dTimeout); } - LOG4_TRACE("Step(seq %u, active_time %lf, lifetime %lf) register successful.", + LOG4_TRACE("%s(seq %u, active_time %lf, lifetime %lf) register successful.", pSharedStep->GetActorName().c_str(), pSharedStep->GetSequence(), pSharedStep->GetActiveTime(), pSharedStep->GetTimeout()); return(true); } diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index 83585418..91d8a7b9 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -345,7 +345,10 @@ E_CMD_STATUS StepRedisCluster::Callback( E_CMD_STATUS StepRedisCluster::Timeout() { - SendCmdClusterSlots(); + if (m_setFailedNode.size() > 0) + { + SendCmdClusterSlots(); + } for (auto iter = m_setFailedNode.begin(); iter != m_setFailedNode.end(); ++iter) { SendCmdPing(*iter); From 701f726a248abea75b76421be069ad676399943d Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 30 Apr 2022 10:30:52 +0800 Subject: [PATCH 159/176] =?UTF-8?q?1.=20=E4=BF=AE=E5=A4=8Dcodec=20bind=20c?= =?UTF-8?q?hannel=E5=BE=AA=E7=8E=AF=E5=BC=95=E7=94=A8=E9=97=AE=E9=A2=98=20?= =?UTF-8?q?2.=20=E4=BF=AE=E5=A4=8DCodecProto=E6=97=A0=E5=8C=85=E4=BD=93?= =?UTF-8?q?=E9=80=9A=E4=BF=A1=E9=97=AE=E9=A2=98=203.=20=E5=90=AF=E7=94=A8?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E4=BF=9D=E6=8A=A4=E9=85=8D=E7=BD=AE=204.=20?= =?UTF-8?q?=E5=8E=BB=E9=99=A4=E5=AE=A2=E6=88=B7=E7=AB=AF=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E6=95=B0=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actor/ActorBuilder.cpp | 2 +- .../sys_session/manager/SessionManager.cpp | 18 ++--------- src/actor/step/sys_step/StepRedisCluster.cpp | 12 ++++++- src/actor/step/sys_step/StepRedisCluster.hpp | 1 + src/channel/SocketChannel.cpp | 18 +++++++++++ src/channel/SocketChannel.hpp | 2 ++ src/channel/SocketChannelImpl.hpp | 22 +++++++++---- src/codec/Codec.hpp | 7 ++++ src/codec/CodecFactory.cpp | 1 + src/codec/CodecProto.cpp | 16 ++++++---- src/ios/Dispatcher.cpp | 32 ++++++++----------- src/ios/Dispatcher.hpp | 2 -- src/ios/IO.hpp | 29 ++++++++++------- src/labor/NodeInfo.hpp | 1 - src/labor/Worker.cpp | 4 +-- 15 files changed, 99 insertions(+), 68 deletions(-) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index ef22b5f8..fba9bc53 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -479,7 +479,7 @@ void ActorBuilder::LoadSysCmd() strModulePath = "/health"; MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); strModulePath = "/status"; - MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); + MakeSharedModule(nullptr, "neb::ModuleMetrics", strModulePath); strModulePath = "http_upgrade"; MakeSharedModule(nullptr, "neb::ModuleHttpUpgrade", strModulePath); } diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 44853172..c6f35e0d 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -66,12 +66,10 @@ E_CMD_STATUS SessionManager::Timeout() neb::ReportRecord* pRecord = nullptr; uint32 uiLoad = 0; uint32 uiConnect = 0; - uint32 uiClient = 0; for (auto iter = m_mapWorkerInfo.begin(); iter != m_mapWorkerInfo.end(); ++iter) { uiLoad += iter->second->uiLoad; uiConnect += iter->second->uiConnect; - uiClient += iter->second->uiClientNum; } pRecord = pReport->add_records(); pRecord->set_key("load"); @@ -83,11 +81,6 @@ E_CMD_STATUS SessionManager::Timeout() pRecord->set_item("nebula"); pRecord->add_value(uiConnect); pRecord->set_value_type(ReportRecord::VALUE_FIXED); - pRecord = pReport->add_records(); - pRecord->set_key("client"); - pRecord->set_item("nebula"); - pRecord->add_value(uiClient); - pRecord->set_value_type(ReportRecord::VALUE_FIXED); std::string strSessionId = "neb::SessionDataReport"; auto pSharedSession = GetSession(strSessionId); if (pSharedSession == nullptr) @@ -284,7 +277,6 @@ bool SessionManager::SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad) oJsonLoad.Get("recv_byte", it->second->uiRecvByte); oJsonLoad.Get("send_num", it->second->uiSendNum); oJsonLoad.Get("send_byte", it->second->uiSendByte); - oJsonLoad.Get("client", it->second->uiClientNum); it->second->dBeatTime = GetNowTime(); it->second->bStartBeatCheck = true; return(true); @@ -481,9 +473,9 @@ void SessionManager::SendOnlineNodesToWorker() * }, * "worker": * [ - * {"load":655666, "connect":495873, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":195870}}, - * {"load":655235, "connect":485872, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":195870}}, - * {"load":585696, "connect":415379, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":195870}} + * {"load":655666, "connect":495873, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222}}, + * {"load":655235, "connect":485872, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222}}, + * {"load":585696, "connect":415379, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222}} * ] * } */ @@ -495,7 +487,6 @@ void SessionManager::MakeReportData(CJsonObject& oReportData) int iRecvByte = 0; int iSendNum = 0; int iSendByte = 0; - int iClientNum = 0; CJsonObject oMember; oReportData.Add("node_type", GetLabor(this)->GetNodeInfo().strNodeType); oReportData.Add("node_id", GetLabor(this)->GetNodeInfo().uiNodeId); @@ -541,7 +532,6 @@ void SessionManager::MakeReportData(CJsonObject& oReportData) iRecvByte += worker_iter->second->uiRecvByte; iSendNum += worker_iter->second->uiSendNum; iSendByte += worker_iter->second->uiSendByte; - iClientNum += worker_iter->second->uiClientNum; oMember.Clear(); oMember.Add("load", worker_iter->second->uiLoad); oMember.Add("connect", worker_iter->second->uiConnect); @@ -549,7 +539,6 @@ void SessionManager::MakeReportData(CJsonObject& oReportData) oMember.Add("recv_byte", worker_iter->second->uiRecvByte); oMember.Add("send_num", worker_iter->second->uiSendNum); oMember.Add("send_byte", worker_iter->second->uiSendByte); - oMember.Add("client", worker_iter->second->uiClientNum); oReportData["worker"].Add(oMember); } oReportData["node"].Add("load", iLoad); @@ -558,7 +547,6 @@ void SessionManager::MakeReportData(CJsonObject& oReportData) oReportData["node"].Add("recv_byte", iRecvByte); oReportData["node"].Add("send_num", iSendNum); oReportData["node"].Add("send_byte", iSendByte); - oReportData["node"].Add("client", iClientNum); } int SessionManager::GetLoaderDataFd() const diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index 91d8a7b9..b9c8c331 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -351,7 +351,15 @@ E_CMD_STATUS StepRedisCluster::Timeout() } for (auto iter = m_setFailedNode.begin(); iter != m_setFailedNode.end(); ++iter) { - SendCmdPing(*iter); + if (m_setAllNode.find(*iter) == m_setAllNode.end()) + { + m_setFailedNode.erase(iter); + iter = m_setFailedNode.begin(); + } + else + { + SendCmdPing(*iter); + } } for (auto timeout_iter = m_mapTimeoutStep.begin(); timeout_iter != m_mapTimeoutStep.end(); ) { @@ -734,6 +742,7 @@ bool StepRedisCluster::CmdClusterSlotsCallback(const RedisReply& oRedisReply) { if (REDIS_REPLY_ARRAY == oRedisReply.type()) { + m_setAllNode.clear(); int iFromSlot = 0; int iToSlot = 0; for (int i = 0; i < oRedisReply.element_size(); ++i) @@ -778,6 +787,7 @@ bool StepRedisCluster::CmdClusterSlotsCallback(const RedisReply& oRedisReply) { pRedisNode->setFllower.insert(oss.str()); } + m_setAllNode.insert(oss.str()); } pRedisNode->iterFllower = pRedisNode->setFllower.begin(); for (int iSlotId = iFromSlot; iSlotId <= iToSlot; ++iSlotId) diff --git a/src/actor/step/sys_step/StepRedisCluster.hpp b/src/actor/step/sys_step/StepRedisCluster.hpp index 67abfae7..9ca54649 100644 --- a/src/actor/step/sys_step/StepRedisCluster.hpp +++ b/src/actor/step/sys_step/StepRedisCluster.hpp @@ -117,6 +117,7 @@ class StepRedisCluster: public RedisStep, std::unordered_map> m_mapReply; std::map> m_mapTimeoutStep; std::vector> m_vecWaittingRequest; + std::set m_setAllNode; std::set m_setFailedNode; static const uint16 sc_unClusterSlots; ///< redis cluster槽位数 diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index 2c6afe09..52dd25fb 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -162,6 +162,24 @@ bool SocketChannel::PipelineIsEmpty() const return(m_pImpl->PipelineIsEmpty()); } +ev_tstamp SocketChannel::GetActiveTime() const +{ + if (m_pImpl == nullptr) + { + return(0.0); + } + return(m_pImpl->GetActiveTime()); +} + +ev_tstamp SocketChannel::GetKeepAlive() +{ + if (m_pImpl == nullptr) + { + return(0.0); + } + return(m_pImpl->GetKeepAlive()); +} + int SocketChannel::GetErrno() const { if (m_pImpl == nullptr) diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index eb0b5237..2a42affb 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -56,6 +56,8 @@ class SocketChannel: public Channel virtual uint8 GetChannelStatus() const; virtual uint32 PopStepSeq(uint32 uiStreamId = 0, E_CODEC_STATUS eStatus = CODEC_STATUS_OK); virtual bool PipelineIsEmpty() const; + virtual ev_tstamp GetActiveTime() const; + virtual ev_tstamp GetKeepAlive(); virtual int GetErrno() const; virtual const std::string& GetErrMsg() const; virtual Codec* GetCodec() const; diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 132db890..94e7ba0b 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -114,6 +114,13 @@ class SocketChannelImpl: public SocketChannel return(m_listPipelineStepSeq.empty()); } + virtual ev_tstamp GetActiveTime() const + { + return(m_dActiveTime); + } + + virtual ev_tstamp GetKeepAlive(); + virtual int GetErrno() const { return(m_iErrno); @@ -134,13 +141,6 @@ class SocketChannelImpl: public SocketChannel return(m_pLabor); } - ev_tstamp GetActiveTime() const - { - return(m_dActiveTime); - } - - ev_tstamp GetKeepAlive(); - int16 GetRemoteWorkerIndex() const { return(m_iRemoteWorkerIdx); @@ -441,6 +441,10 @@ E_CODEC_STATUS SocketChannelImpl::Send() } } + if (m_uiMsgNum < 1) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + } m_dActiveTime = m_pLabor->GetNowTime(); int iHadWrittenLen = 0; int iWrittenLen = 0; @@ -870,6 +874,10 @@ template bool SocketChannelImpl::Close() { LOG4_TRACE("channel[%d] channel_status %d", m_iFd, (int)m_ucChannelStatus); + if (m_pCodec != nullptr) + { + m_pCodec->UnbindChannel(); + } if (CHANNEL_STATUS_CLOSED != m_ucChannelStatus) { m_pSendBuff->Compact(1); diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 1fdcc3dc..050ae2ec 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -71,6 +71,7 @@ enum E_CODEC_STATUS }; class SocketChannel; +template class SocketChannelImpl; class Codec { @@ -141,6 +142,11 @@ class Codec bool AesEncrypt(const std::string& strSrc, std::string& strDest); bool AesDecrypt(const std::string& strSrc, std::string& strDest); +private: + void UnbindChannel() + { + m_pBindChannel = nullptr; + } protected: std::shared_ptr m_pLogger; @@ -152,6 +158,7 @@ class Codec static std::vector m_vecAutoSwitchCodecType; // 自动转换有效的编解码类型 friend class SocketChannel; + template friend class SocketChannelImpl; }; template diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp index f07d3e22..aee2b858 100644 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -403,6 +403,7 @@ bool CodecFactory::AutoSwitchCodec(Dispatcher* pDispatcher, continue; } + LOG4_TRACE_DISPATCH("to codec type %d", s_vecAutoSwitchCodec[i]); Codec* pCodec = Create(pDispatcher->GetLogger(), s_vecAutoSwitchCodec[i], pChannel); if (pCodec == nullptr) { diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp index e1469c4c..6da3e002 100644 --- a/src/codec/CodecProto.cpp +++ b/src/codec/CodecProto.cpp @@ -35,15 +35,16 @@ E_CODEC_STATUS CodecProto::Encode(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgB { int iHadWriteLen = 0; int iWriteLen = 0; - int iNeedWriteLen = gc_uiMsgHeadSize; std::string strTmpData; MsgHead oMsgHead; + int32 iMsgBodyLen = oMsgBody.ByteSize(); oMsgHead.set_cmd(iCmd); oMsgHead.set_seq(uiSeq); - oMsgHead.set_len(oMsgBody.ByteSize()); + iMsgBodyLen = (iMsgBodyLen > 0) ? iMsgBodyLen : -1; + oMsgHead.set_len(iMsgBodyLen); oMsgHead.SerializeToString(&strTmpData); iWriteLen = pBuff->Write(strTmpData.c_str(), gc_uiMsgHeadSize); - if (iWriteLen != iNeedWriteLen) + if (iWriteLen != gc_uiMsgHeadSize) { LOG4_ERROR("buff write head iWriteLen != iNeedWriteLen!"); pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadWriteLen); @@ -54,10 +55,10 @@ E_CODEC_STATUS CodecProto::Encode(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgB { return(ChannelSticky(oMsgHead, oMsgBody)); } - iNeedWriteLen = oMsgBody.ByteSize(); oMsgBody.SerializeToString(&strTmpData); - iWriteLen = pBuff->Write(strTmpData.c_str(), oMsgBody.ByteSize()); - if (iWriteLen == iNeedWriteLen) + iWriteLen = pBuff->Write(strTmpData.c_str(), iMsgBodyLen); + iHadWriteLen += iWriteLen; + if (iWriteLen == iMsgBodyLen) { return(ChannelSticky(oMsgHead, oMsgBody)); } @@ -82,6 +83,7 @@ E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oM pBuff->ReadableBytes(), oMsgHead.len()); if (oMsgHead.len() <= 0) // 无包体(心跳包等),nebula在proto3的使用上以-1表示包体长度为0 { + oMsgHead.set_len(0); pBuff->SkipBytes(gc_uiMsgHeadSize); return(ChannelSticky(oMsgHead, oMsgBody)); } @@ -109,7 +111,7 @@ E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oM } else { - LOG4_TRACE("oMsgHead.ParseFromArray() error!"); // maybe port scan from operation and maintenance system. + LOG4_TRACE("oMsgHead.ParseFromArray() failed!"); // maybe port scan from operation and maintenance system. return(CODEC_STATUS_ERR); } } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 85193e6a..46af6a22 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -26,7 +26,7 @@ namespace neb { Dispatcher::Dispatcher(Labor* pLabor, std::shared_ptr pLogger) - : m_pErrBuff(NULL), m_pLabor(pLabor), m_loop(NULL), m_iClientNum(0), m_lLastCheckNodeTime(0), + : m_pErrBuff(NULL), m_pLabor(pLabor), m_loop(NULL), m_lLastCheckNodeTime(0), m_pLogger(pLogger), m_pSessionNode(nullptr) { m_pErrBuff = (char*)malloc(gc_iErrBuffLen); @@ -319,8 +319,10 @@ bool Dispatcher::FdTransfer(int iFd) } else { + ev_tstamp dIoTimeout = (m_pLabor->GetNodeInfo().dConnectionProtection > 0) + ? m_pLabor->GetNodeInfo().dConnectionProtection : m_pLabor->GetNodeInfo().dIoTimeout; std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - AddIoTimeout(pChannel, 1.0); // 为了防止大量连接攻击,初始化连接只有一秒即超时,在正常发送第一个数据包之后才采用正常配置的网络IO超时检查 + AddIoTimeout(pChannel, dIoTimeout); } return(true); } @@ -421,7 +423,8 @@ bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) } LOG4_TRACE("fd %d, seq %u:", pChannel->GetFd(), pChannel->GetSequence()); - if (std::static_pointer_cast>(pChannel->m_pImpl)->NeedAliveCheck()) // 需要发送心跳检查 + if (CODEC_PROTO == pChannel->GetCodecType() + && std::static_pointer_cast>(pChannel->m_pImpl)->NeedAliveCheck()) // 需要发送心跳检查 { std::shared_ptr pStepIoTimeout = m_pLabor->GetActorBuilder()->MakeSharedStep( nullptr, "neb::StepIoTimeout", pChannel); @@ -480,7 +483,7 @@ void Dispatcher::EventRun() bool Dispatcher::AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout) { - LOG4_TRACE("%d, %u", pChannel->GetFd(), pChannel->GetSequence()); + LOG4_TRACE("channel_fd %d, channel_seq %u, timeout %f", pChannel->GetFd(), pChannel->GetSequence(), dTimeout); auto pWatcher = pChannel->MutableWatcher(); pWatcher->Set(pChannel); ev_timer* timer_watcher = pWatcher->MutableTimerWatcher(); @@ -594,7 +597,9 @@ std::shared_ptr Dispatcher::StressSend(const std::string& strIden { connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); freeaddrinfo(pAddrResult); /* no longer needed */ - AddIoTimeout(pChannel, 1.5); + ev_tstamp dIoTimeout = (m_pLabor->GetNodeInfo().dConnectionProtection > 0) + ? m_pLabor->GetNodeInfo().dConnectionProtection : m_pLabor->GetNodeInfo().dIoTimeout; + AddIoTimeout(pChannel, dIoTimeout); AddIoReadEvent(pChannel); AddIoWriteEvent(pChannel); std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteAddr(strHost); @@ -961,10 +966,6 @@ int32 Dispatcher::GetConnectionNum() const { return((int32)m_mapSocketChannel.size()); } -int32 Dispatcher::GetClientNum() const -{ - return(m_iClientNum); -} bool Dispatcher::Init() { @@ -1027,10 +1028,6 @@ std::shared_ptr Dispatcher::CreateSocketChannel(int iFd, E_CODEC_ } std::static_pointer_cast>(pChannel->m_pImpl)->SetCodec(pCodec); m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); - if ((CODEC_NEBULA != eCodecType) && (CODEC_NEBULA_IN_NODE != eCodecType)) - { - ++m_iClientNum; - } LOG4_TRACE("new channel[%d] with codec type %d", pChannel->GetFd(), pChannel->GetCodecType()); return(pChannel); } @@ -1112,11 +1109,6 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b if (channel_iter != m_mapSocketChannel.end()) { m_mapSocketChannel.erase(channel_iter); - if ((CODEC_NEBULA != pChannel->GetCodecType()) - && (CODEC_NEBULA_IN_NODE != pChannel->GetCodecType())) - { - --m_iClientNum; - } LOG4_TRACE("erase channel %d channel_seq %u from m_mapSocketChannel.", pChannel->GetFd(), pChannel->GetSequence()); } @@ -1394,7 +1386,9 @@ bool Dispatcher::AcceptServerConn(int iFd) std::shared_ptr pChannel = CreateSocketChannel(iAcceptFd, CODEC_NEBULA); if (NULL != pChannel) { - AddIoTimeout(pChannel, 1.0); // 初始化连接只有一秒即超时,在正常发送第一个数据包之后才采用正常配置的网络IO超时检查 + ev_tstamp dIoTimeout = (m_pLabor->GetNodeInfo().dConnectionProtection > 0) + ? m_pLabor->GetNodeInfo().dConnectionProtection : m_pLabor->GetNodeInfo().dIoTimeout; + AddIoTimeout(pChannel, dIoTimeout); AddIoReadEvent(pChannel); } } diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index ca78aa92..858a8b74 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -171,7 +171,6 @@ class Dispatcher bool DelEvent(ev_io* io_watcher); bool DelEvent(ev_timer* timer_watcher); int32 GetConnectionNum() const; - int32 GetClientNum() const; void SetChannelStatus(std::shared_ptr pChannel, E_CHANNEL_STATUS eStatus); bool AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout = 60.0); bool AcceptFdAndTransfer(int iFd, int iFamily = AF_INET); @@ -183,7 +182,6 @@ class Dispatcher char* m_pErrBuff; Labor* m_pLabor; struct ev_loop* m_loop; - int32 m_iClientNum; time_t m_lLastCheckNodeTime; std::shared_ptr m_pLogger; std::unique_ptr m_pSessionNode; diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index 1a969d94..c4737a0a 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -202,12 +202,12 @@ bool IO::SendResponse(Dispatcher* pDispatcher, std::shared_ptr { pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, "channel %s[%d] change codec type from %d to %d", - pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); + pChannel->GetIdentify().c_str(), pChannel->GetFd(), eOriginCodecType, pChannel->GetCodecType()); } else { LOG4_TRACE_DISPATCH("failed to change codec type of channel %s[%d] from %d to %d", - pChannel->GetIdentify().c_str(), eOriginCodecType, pChannel->GetCodecType()); + pChannel->GetIdentify().c_str(), pChannel->GetFd(), eOriginCodecType, pChannel->GetCodecType()); return(false); } } @@ -814,7 +814,9 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::strin { connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); freeaddrinfo(pAddrResult); /* No longer needed */ - pDispatcher->AddIoTimeout(pChannel, 1.5); + ev_tstamp dIoTimeout = (pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection > 0) + ? pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection : pDispatcher->m_pLabor->GetNodeInfo().dIoTimeout; + pDispatcher->AddIoTimeout(pChannel, dIoTimeout); pDispatcher->AddIoReadEvent(pChannel); pDispatcher->AddIoWriteEvent(pChannel); std::static_pointer_cast>(pChannel->m_pImpl)->SetIdentify(strIdentify); @@ -894,8 +896,11 @@ E_CODEC_STATUS IO::Recv(Dispatcher* pDispatcher, std::shared_ptr>( pChannel->m_pImpl)->Recv(std::forward(args)...); } - pDispatcher->m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - pDispatcher->m_pLastActivityChannel = pChannel; + if (CODEC_STATUS_OK == eStatus) + { + pDispatcher->m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); + pDispatcher->m_pLastActivityChannel = pChannel; + } return(eStatus); } @@ -917,8 +922,11 @@ E_CODEC_STATUS IO::Fetch(Dispatcher* pDispatcher, std::shared_ptr>( pChannel->m_pImpl)->Fetch(std::forward(args)...); - pDispatcher->m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); - pDispatcher->m_pLastActivityChannel = pChannel; + if (CODEC_STATUS_OK == eStatus) + { + pDispatcher->m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); + pDispatcher->m_pLastActivityChannel = pChannel; + } return(eStatus); } @@ -1106,7 +1114,8 @@ std::shared_ptr IO::CreateSocketChannel(Dispatcher* pDispatche if (iter == pDispatcher->m_mapSocketChannel.end()) { std::shared_ptr pChannel = nullptr; - ev_tstamp dKeepAlive = pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection; + ev_tstamp dKeepAlive = (pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection > 0) + ? pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection : pDispatcher->m_pLabor->GetNodeInfo().dIoTimeout; (dKeepAlive > 0) ? dKeepAlive : pDispatcher->m_pLabor->GetNodeInfo().dIoTimeout; try { @@ -1127,10 +1136,6 @@ std::shared_ptr IO::CreateSocketChannel(Dispatcher* pDispatche return(nullptr); } pDispatcher->m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); - if ((CODEC_NEBULA != T::Type()) && (CODEC_NEBULA_IN_NODE != T::Type())) - { - ++pDispatcher->m_iClientNum; - } pDispatcher->Logger(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "new channel for fd %d with codec type %d", pChannel->GetFd(), pChannel->GetCodecType()); return(pChannel); diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index 8a8dafb5..849c2020 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -66,7 +66,6 @@ struct WorkerInfo uint32 uiRecvByte = 0; ///< 接收字节数 uint32 uiSendNum = 0; ///< 发送数据包数量 uint32 uiSendByte = 0; ///< 发送字节数 - uint32 uiClientNum = 0; ///< 客户端数量 ev_tstamp dBeatTime = 0.0; ///< 心跳时间 bool bStartBeatCheck = 0.0; ///< 是否需要心跳检查,worker或loader进程启动时可能需要加载数据而处于繁忙状态无法响应Manager的心跳,需等待其就绪之后才开始心跳检查。 diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 78bfb43b..619ce1db 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -112,7 +112,6 @@ bool Worker::CheckParent() void Worker::DataReport() { m_stWorkerInfo.uiConnect = m_pDispatcher->GetConnectionNum(); - m_stWorkerInfo.uiClientNum = m_pDispatcher->GetClientNum(); MsgBody oMsgBody; CJsonObject oJsonLoad; oJsonLoad.Add("load", int32(m_stWorkerInfo.uiConnect + m_pActorBuilder->GetStepNum())); @@ -121,11 +120,10 @@ void Worker::DataReport() oJsonLoad.Add("recv_byte", m_stWorkerInfo.uiRecvByte); oJsonLoad.Add("send_num", m_stWorkerInfo.uiSendNum); oJsonLoad.Add("send_byte", m_stWorkerInfo.uiSendByte); - oJsonLoad.Add("client", m_stWorkerInfo.uiClientNum); oMsgBody.set_data(oJsonLoad.ToString()); LOG4_TRACE("%s", oJsonLoad.ToString().c_str()); IO::SendRequest(m_pDispatcher, 0, m_pManagerControlChannel, CMD_REQ_UPDATE_WORKER_LOAD, GetSequence(), oMsgBody); - auto pSessionDataReport = std::dynamic_pointer_cast(m_pActorBuilder->GetSession("neb::SessionManager")); + auto pSessionDataReport = std::dynamic_pointer_cast(m_pActorBuilder->GetSession("neb::SessionDataReport")); if (pSessionDataReport != nullptr) { auto pReport = std::make_shared(); From af3c6e40fccca763238a0d2f2249a17fbfd192f0 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 25 Jun 2022 12:06:17 +0800 Subject: [PATCH 160/176] - error callback caused by fuse node detection bug fixed - redis cluster returns when executing batch write commands bug fixed - the channel creation method and use CodecFactory instead --- README.md | 8 + README_cn.md | 8 + src/actor/step/sys_step/StepRedisCluster.cpp | 147 ++++++++----------- src/actor/step/sys_step/StepRedisCluster.hpp | 3 +- src/channel/SocketChannel.cpp | 145 +++++++++++++++--- src/channel/SocketChannel.hpp | 15 +- src/channel/SocketChannelImpl.hpp | 96 ++++++------ src/codec/CodecFactory.cpp | 118 ++++++++++++++- src/codec/CodecFactory.hpp | 3 +- src/codec/cass/CodecCass.cpp | 8 +- src/ios/Dispatcher.cpp | 87 +++++------ src/ios/IO.hpp | 47 +++--- 12 files changed, 449 insertions(+), 236 deletions(-) diff --git a/README.md b/README.md index bb126471..45e0d550 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,14 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v1.7.3 + - error callback caused by fuse node detection bug fixed + - redis cluster returns when executing batch write commands bug fixed + - the channel creation method and use CodecFactory instead +#### v1.7.2 + - codec bind channel circular reference bug fixed + - CodecProto no packet communication bug fixed + - enable connection protection configuration #### v1.7.1 - async logger optimization - add selfchannel sequence diff --git a/README_cn.md b/README_cn.md index 61eda1a8..b8e2ce04 100644 --- a/README_cn.md +++ b/README_cn.md @@ -134,6 +134,14 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 +#### v1.7.3 + - 修复熔断节点探测导致错误回调问题 + - 修复redis cluster执行批量写命令返回问题 + - 修改channel创建方式,改由CodecFactory创建 +#### v1.7.2 + - 修复codec bind channel循环引用问题 + - 修复CodecProto无包体通信问题 + - 启用连接保护配置 #### v1.7.1 - 优化异步文件日志 - SelfChannel增加seq diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index b9c8c331..9abaa667 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -103,8 +103,9 @@ const std::unordered_set StepRedisCluster::s_setMultipleKeyValueCmd StepRedisCluster::StepRedisCluster( const std::string& strIdentify, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) - : m_bWithSsl(bWithSsl), m_bPipeline(bPipeline), m_bEnableReadOnly(bEnableReadOnly), - m_uiAddressIndex(0), + : RedisStep(30.0), + m_bWithSsl(bWithSsl), m_bPipeline(bPipeline), m_bEnableReadOnly(bEnableReadOnly), + m_uiAddressIndex(0), m_lLastCheckTime(0), m_strIdentify(strIdentify) { Split(strIdentify, ",", m_vecAddress); @@ -300,40 +301,11 @@ E_CMD_STATUS StepRedisCluster::Callback( LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); return(CMD_STATUS_RUNNING); } - std::vector vecElementIndex; - for (int i = 1; i < pRedisRequest->element_size(); ++i) // element(0).str() is cmd - { - if (pRedisRequest->element(i).integer() > 0 || i == 1) - { - vecElementIndex.push_back((uint32)pRedisRequest->element(i).integer()); - } - } - for (uint32 j = 0; j < vecElementIndex.size(); ++j) - { - if (vecElementIndex[j] < reply_iter->second.size()) - { - if (reply_iter->second[vecElementIndex[j]] == nullptr) - { - reply_iter->second[vecElementIndex[j]] = new RedisReply(); - } - reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply); - } - else - { - LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); - } - } + // error or write cmd callback num_iter->second--; if (num_iter->second == 0) { - RedisReply oFinalReply; - oFinalReply.set_type(oRedisReply.type()); - for (size_t k = 0; k < reply_iter->second.size(); ++k) - { - oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); - reply_iter->second[k] = nullptr; - } - IO::OnResponse(GetLabor(this)->GetActorBuilder(), pChannel, uiRealStepSeq, oFinalReply); + IO::OnResponse(GetLabor(this)->GetActorBuilder(), pChannel, uiRealStepSeq, oRedisReply); m_mapStepEmitNum.erase(num_iter); m_mapReply.erase(reply_iter); } @@ -345,57 +317,16 @@ E_CMD_STATUS StepRedisCluster::Callback( E_CMD_STATUS StepRedisCluster::Timeout() { - if (m_setFailedNode.size() > 0) - { - SendCmdClusterSlots(); - } - for (auto iter = m_setFailedNode.begin(); iter != m_setFailedNode.end(); ++iter) - { - if (m_setAllNode.find(*iter) == m_setAllNode.end()) - { - m_setFailedNode.erase(iter); - iter = m_setFailedNode.begin(); - } - else - { - SendCmdPing(*iter); - } - } - for (auto timeout_iter = m_mapTimeoutStep.begin(); timeout_iter != m_mapTimeoutStep.end(); ) - { - if (GetNowTime() - timeout_iter->first >= GetTimeout()) - { - for (uint32 i = 0; i < timeout_iter->second.size(); ++i) - { - auto reply_iter = m_mapReply.find(timeout_iter->second[i]); - if (reply_iter != m_mapReply.end()) - { - m_mapReply.erase(reply_iter); - } - auto num_iter = m_mapStepEmitNum.find(timeout_iter->second[i]); - if (num_iter != m_mapStepEmitNum.end()) - { - m_mapStepEmitNum.erase(num_iter); - } - } - timeout_iter->second.clear(); - m_mapTimeoutStep.erase(timeout_iter); - timeout_iter = m_mapTimeoutStep.begin(); - } - else - { - break; - } - } + HealthCheck(); return(CMD_STATUS_RUNNING); } E_CMD_STATUS StepRedisCluster::ErrBack( std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) { - LOG4_ERROR("error %d: %s", iErrno, strErrMsg.c_str()); + LOG4_ERROR("step %u: channel %s error %d: %s", GetSequence(), pChannel->GetIdentify().c_str(), iErrno, strErrMsg.c_str()); m_setFailedNode.insert(pChannel->GetIdentify()); - SendCmdClusterSlots(); + HealthCheck(); CmdErrBack(pChannel, iErrno, strErrMsg); return(CMD_STATUS_RUNNING); } @@ -928,16 +859,15 @@ void StepRedisCluster::AskingQueueErrBack( } else { + LOG4_WARNING("%s asking queue size %u", pChannel->GetIdentify().c_str(), step_iter->second.size()); + m_setFailedNode.insert(pChannel->GetIdentify()); while (step_iter->second.size() > 0) { auto pRedisRequest = step_iter->second.front(); uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); + LOG4_WARNING("real step seq %u", uiRealStepSeq); step_iter->second.pop(); - if (uiRealStepSeq == GetSequence()) - { - SendCmdClusterSlots(); - } - else + if (uiRealStepSeq != GetSequence()) { auto num_iter = m_mapStepEmitNum.find(uiRealStepSeq); if (num_iter == m_mapStepEmitNum.end()) // 单key请求的响应 @@ -1155,5 +1085,58 @@ bool StepRedisCluster::Auth(const std::string& strIdentify, std::shared_ptr 0) + { + SendCmdClusterSlots(); + } + for (auto iter = m_setFailedNode.begin(); iter != m_setFailedNode.end(); ++iter) + { + if (m_setAllNode.find(*iter) == m_setAllNode.end()) + { + m_setFailedNode.erase(iter); + iter = m_setFailedNode.begin(); + } + else + { + SendCmdPing(*iter); + } + } + for (auto timeout_iter = m_mapTimeoutStep.begin(); timeout_iter != m_mapTimeoutStep.end(); ) + { + if (GetNowTime() - timeout_iter->first >= GetTimeout()) + { + for (uint32 i = 0; i < timeout_iter->second.size(); ++i) + { + auto reply_iter = m_mapReply.find(timeout_iter->second[i]); + if (reply_iter != m_mapReply.end()) + { + m_mapReply.erase(reply_iter); + } + auto num_iter = m_mapStepEmitNum.find(timeout_iter->second[i]); + if (num_iter != m_mapStepEmitNum.end()) + { + m_mapStepEmitNum.erase(num_iter); + } + } + timeout_iter->second.clear(); + m_mapTimeoutStep.erase(timeout_iter); + timeout_iter = m_mapTimeoutStep.begin(); + } + else + { + break; + } + } +} + } /* namespace neb */ diff --git a/src/actor/step/sys_step/StepRedisCluster.hpp b/src/actor/step/sys_step/StepRedisCluster.hpp index 9ca54649..9390f270 100644 --- a/src/actor/step/sys_step/StepRedisCluster.hpp +++ b/src/actor/step/sys_step/StepRedisCluster.hpp @@ -78,7 +78,6 @@ class StepRedisCluster: public RedisStep, virtual bool SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) override; - protected: bool SendTo(const std::string& strIdentify, std::shared_ptr pRedisMsg); bool ExtractCmd(const RedisMsg& oRedisMsg, std::string& strCmd, @@ -101,12 +100,14 @@ class StepRedisCluster: public RedisStep, void RegisterStep(uint32 uiStepSeq); void AddToAskingQueue(const std::string& strIdentify, std::shared_ptr pRedisMsg); bool Auth(const std::string& strIdentify, std::shared_ptr pRedisMsg); + void HealthCheck(); private: bool m_bWithSsl; ///< 是否支持SSL bool m_bPipeline; ///< 是否支持pipeline bool m_bEnableReadOnly; ///< 是否对从节点启用只读 uint32 m_uiAddressIndex; + time_t m_lLastCheckTime; ///< last helth check time std::string m_strIdentify; ///< 地址标识,由m_vecAddress合并而成,形如 192.168.47.101:6379,198.168.47.102:6379,192.168.47.103:6379 std::vector m_vecAddress; ///< 集群地址 diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index 52dd25fb..7513f742 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -29,28 +29,9 @@ SocketChannel::SocketChannel() { } -SocketChannel::SocketChannel(Labor* pLabor, std::shared_ptr pLogger, int iFd, uint32 ulSeq, bool bWithSsl, bool bIsClient, ev_tstamp dKeepAlive) +SocketChannel::SocketChannel(std::shared_ptr pLogger, bool bIsClient, bool bWithSsl) : m_bIsClient(bIsClient), m_bWithSsl(bWithSsl), m_pImpl(nullptr), m_pLogger(pLogger), m_pWatcher(nullptr) { - if (bWithSsl) - { -#ifdef WITH_OPENSSL - LOG4_TRACE("with openssl, create SocekChannelSslImpl."); - auto pImpl = std::make_shared>(pLabor, pLogger, iFd, ulSeq, dKeepAlive); - pImpl->Init(bIsClient); - m_pImpl = std::static_pointer_cast(pImpl); -#else - LOG4_TRACE("without openssl, SocekChannelImpl instead."); - auto pImpl = std::make_shared>(pLabor, pLogger, iFd, ulSeq, dKeepAlive); - m_pImpl = std::static_pointer_cast(pImpl); -#endif - } - else - { - LOG4_TRACE("create SocekChannelImpl."); - auto pImpl = std::make_shared>(pLabor, pLogger, iFd, ulSeq, dKeepAlive); - m_pImpl = std::static_pointer_cast(pImpl); - } } SocketChannel::~SocketChannel() @@ -76,6 +57,7 @@ int SocketChannel::GetFd() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(-1); } return(m_pImpl->GetFd()); @@ -85,6 +67,7 @@ uint32 SocketChannel::GetSequence() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(0); } return(m_pImpl->GetSequence()); @@ -94,6 +77,7 @@ bool SocketChannel::IsPipeline() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(false); } return(m_pImpl->IsPipeline()); @@ -103,6 +87,7 @@ const std::string& SocketChannel::GetIdentify() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(m_strEmpty); } return(m_pImpl->GetIdentify()); @@ -112,6 +97,7 @@ const std::string& SocketChannel::GetRemoteAddr() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(m_strEmpty); } return(m_pImpl->GetRemoteAddr()); @@ -121,6 +107,7 @@ const std::string& SocketChannel::GetClientData() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(m_strEmpty); } return(m_pImpl->GetClientData()); @@ -130,6 +117,7 @@ E_CODEC_TYPE SocketChannel::GetCodecType() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(CODEC_UNKNOW); } return(m_pImpl->GetCodecType()); @@ -139,6 +127,7 @@ uint8 SocketChannel::GetChannelStatus() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(0); } return(m_pImpl->GetChannelStatus()); @@ -148,6 +137,7 @@ uint32 SocketChannel::PopStepSeq(uint32 uiStreamId, E_CODEC_STATUS eStatus) { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(0); } return(m_pImpl->PopStepSeq(uiStreamId, eStatus)); @@ -157,6 +147,7 @@ bool SocketChannel::PipelineIsEmpty() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(0); } return(m_pImpl->PipelineIsEmpty()); @@ -166,15 +157,17 @@ ev_tstamp SocketChannel::GetActiveTime() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(0.0); } return(m_pImpl->GetActiveTime()); } -ev_tstamp SocketChannel::GetKeepAlive() +ev_tstamp SocketChannel::GetKeepAlive() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(0.0); } return(m_pImpl->GetKeepAlive()); @@ -184,6 +177,7 @@ int SocketChannel::GetErrno() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(0); } return(m_pImpl->GetErrno()); @@ -193,6 +187,7 @@ const std::string& SocketChannel::GetErrMsg() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(m_strEmpty); } return(m_pImpl->GetErrMsg()); @@ -202,15 +197,123 @@ Codec* SocketChannel::GetCodec() const { if (m_pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(nullptr); } return(m_pImpl->GetCodec()); } +bool SocketChannel::IsChannelVerify() const +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + return(false); + } + return(m_pImpl->IsChannelVerify()); +} + +bool SocketChannel::NeedAliveCheck() const +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + return(false); + } + return(m_pImpl->NeedAliveCheck()); +} + +uint32 SocketChannel::GetMsgNum() const +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + return(false); + } + return(m_pImpl->GetMsgNum()); +} + +uint32 SocketChannel::GetUnitTimeMsgNum() const +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + return(false); + } + return(m_pImpl->GetUnitTimeMsgNum()); +} + +E_CODEC_STATUS SocketChannel::Send() +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + return(CODEC_STATUS_ERR); + } + return(m_pImpl->Send()); +} + +int16 SocketChannel::GetRemoteWorkerIndex() const +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + return(false); + } + return(m_pImpl->GetRemoteWorkerIndex()); +} + +void SocketChannel::SetChannelStatus(E_CHANNEL_STATUS eStatus) +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + } + return(m_pImpl->SetChannelStatus(eStatus)); +} + +void SocketChannel::SetClientData(const std::string& strClientData) +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + } + return(m_pImpl->SetClientData(strClientData)); +} + +void SocketChannel::SetIdentify(const std::string& strIdentify) +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + } + return(m_pImpl->SetIdentify(strIdentify)); +} + +void SocketChannel::SetRemoteAddr(const std::string& strRemoteAddr) +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + } + return(m_pImpl->SetRemoteAddr(strRemoteAddr)); +} + +bool SocketChannel::Close() +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + return(false); + } + return(m_pImpl->Close()); +} + bool SocketChannel::InitImpl(std::shared_ptr pImpl) { if (pImpl == nullptr) { + LOG4_TRACE("m_pImpl is nullptr"); return(false); } m_pImpl = pImpl; diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index 2a42affb..818246e7 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -38,7 +38,7 @@ class SocketChannel: public Channel }; SocketChannel(); // only for SelfChannel - SocketChannel(Labor* pLabor, std::shared_ptr pLogger, int iFd, uint32 ulSeq, bool bWithSsl, bool bIsClient, ev_tstamp dKeepAlive = 10.0); + SocketChannel(std::shared_ptr pLogger, bool bIsClient, bool bWithSsl); virtual ~SocketChannel(); static int SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, std::shared_ptr pLogger); @@ -57,16 +57,27 @@ class SocketChannel: public Channel virtual uint32 PopStepSeq(uint32 uiStreamId = 0, E_CODEC_STATUS eStatus = CODEC_STATUS_OK); virtual bool PipelineIsEmpty() const; virtual ev_tstamp GetActiveTime() const; - virtual ev_tstamp GetKeepAlive(); + virtual ev_tstamp GetKeepAlive() const; virtual int GetErrno() const; virtual const std::string& GetErrMsg() const; virtual Codec* GetCodec() const; + virtual bool IsChannelVerify() const; + virtual bool NeedAliveCheck() const; + virtual uint32 GetMsgNum() const; + virtual uint32 GetUnitTimeMsgNum() const; + virtual E_CODEC_STATUS Send(); ChannelWatcher* MutableWatcher(); template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) const; protected: virtual Labor* GetLabor(); + virtual int16 GetRemoteWorkerIndex() const; + virtual void SetChannelStatus(E_CHANNEL_STATUS eStatus); + virtual void SetClientData(const std::string& strClientData); + virtual void SetIdentify(const std::string& strIdentify); + virtual void SetRemoteAddr(const std::string& strRemoteAddr); + virtual bool Close(); bool InitImpl(std::shared_ptr pImpl); private: diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 94e7ba0b..8404d5fb 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -65,100 +65,100 @@ class SocketChannelImpl: public SocketChannel template E_CODEC_STATUS Fetch(Targs&&... args); - E_CODEC_STATUS Send(); + virtual E_CODEC_STATUS Send(); - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) const; public: - virtual int GetFd() const + virtual int GetFd() const override { return(m_iFd); } - virtual uint32 GetSequence() const + virtual uint32 GetSequence() const override { return(m_uiSeq); } - virtual bool IsPipeline() const + virtual bool IsPipeline() const override { return(m_bPipeline); } - virtual const std::string& GetIdentify() const + virtual const std::string& GetIdentify() const override { return(m_strIdentify); } - virtual const std::string& GetRemoteAddr() const + virtual const std::string& GetRemoteAddr() const override { return(m_strRemoteAddr); } - virtual const std::string& GetClientData() const + virtual const std::string& GetClientData() const override { return(m_strClientData); } - virtual E_CODEC_TYPE GetCodecType() const; + virtual E_CODEC_TYPE GetCodecType() const override; - virtual uint8 GetChannelStatus() const + virtual uint8 GetChannelStatus() const override { return(m_ucChannelStatus); } - virtual uint32 PopStepSeq(uint32 uiStreamId = 0, E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK); + virtual uint32 PopStepSeq(uint32 uiStreamId = 0, E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK) override; - virtual bool PipelineIsEmpty() const + virtual bool PipelineIsEmpty() const override { return(m_listPipelineStepSeq.empty()); } - virtual ev_tstamp GetActiveTime() const + virtual ev_tstamp GetActiveTime() const override { return(m_dActiveTime); } - virtual ev_tstamp GetKeepAlive(); + virtual ev_tstamp GetKeepAlive() const override; - virtual int GetErrno() const + virtual int GetErrno() const override { return(m_iErrno); } - virtual const std::string& GetErrMsg() const + virtual const std::string& GetErrMsg() const override { return(m_strErrMsg); } - virtual Codec* GetCodec() const + virtual Codec* GetCodec() const override { return(m_pCodec); } - virtual Labor* GetLabor() + virtual Labor* GetLabor() override { return(m_pLabor); } - int16 GetRemoteWorkerIndex() const + int16 GetRemoteWorkerIndex() const override { return(m_iRemoteWorkerIdx); } - bool IsChannelVerify() const + bool IsChannelVerify() const override { return(m_strClientData.size()); } - bool NeedAliveCheck() const; + bool NeedAliveCheck() const override; - uint32 GetMsgNum() const + uint32 GetMsgNum() const override { return(m_uiMsgNum); } - uint32 GetUnitTimeMsgNum() const + uint32 GetUnitTimeMsgNum() const override { return(m_uiUnitTimeMsgNum); } @@ -186,7 +186,7 @@ class SocketChannelImpl: public SocketChannel m_dKeepAlive = dTime; } - void SetChannelStatus(E_CHANNEL_STATUS eStatus) + void SetChannelStatus(E_CHANNEL_STATUS eStatus) override { m_ucChannelStatus = (uint8)eStatus; } @@ -196,17 +196,17 @@ class SocketChannelImpl: public SocketChannel m_bPipeline = bPipeline; } - void SetClientData(const std::string& strClientData) + void SetClientData(const std::string& strClientData) override { m_strClientData = strClientData; } - void SetIdentify(const std::string& strIdentify) + void SetIdentify(const std::string& strIdentify) override { m_strIdentify = strIdentify; } - void SetRemoteAddr(const std::string& strRemoteAddr) + void SetRemoteAddr(const std::string& strRemoteAddr) override { m_strRemoteAddr = strRemoteAddr; } @@ -215,7 +215,7 @@ class SocketChannelImpl: public SocketChannel void SetRemoteWorkerIndex(int16 iRemoteWorkerIndex); - virtual bool Close(); + virtual bool Close() override; protected: virtual int Write(CBuffer* pBuff, int& iErrno); @@ -313,7 +313,20 @@ E_CODEC_TYPE SocketChannelImpl::GetCodecType() const template uint32 SocketChannelImpl::PopStepSeq(uint32 uiStreamId, E_CODEC_STATUS eCodecStatus) { - if (m_listPipelineStepSeq.empty()) + if (m_mapStreamStepSeq.empty()) + { + if (m_listPipelineStepSeq.empty()) + { + return(0); + } + uint32 uiStepSeq = m_listPipelineStepSeq.front(); + if (CODEC_STATUS_OK == eCodecStatus) + { + m_listPipelineStepSeq.pop_front(); + } + return(uiStepSeq); + } + else { auto iter = m_mapStreamStepSeq.find(uiStreamId); if (iter != m_mapStreamStepSeq.end()) @@ -327,30 +340,23 @@ uint32 SocketChannelImpl::PopStepSeq(uint32 uiStreamId, E_CODEC_STATUS eCodec } return(0); } - else - { - uint32 uiStepSeq = m_listPipelineStepSeq.front(); - if (CODEC_STATUS_OK == eCodecStatus) - { - m_listPipelineStepSeq.pop_front(); - } - return(uiStepSeq); - } } template -ev_tstamp SocketChannelImpl::GetKeepAlive() +ev_tstamp SocketChannelImpl::GetKeepAlive() const { if (m_pCodec == nullptr) { LOG4_ERROR("no codec found, please check codec type is valid."); - return(0.0); + return(m_dKeepAlive); } if (CODEC_HTTP == m_pCodec->GetCodecType()) { - if ((static_cast(m_pCodec))->GetKeepAlive() >= 0.0) + ev_tstamp dKeepAlive = (static_cast(m_pCodec))->GetKeepAlive(); + if (dKeepAlive >= 0.0) { - return((static_cast(m_pCodec))->GetKeepAlive()); + LOG4_INFO("CodecHttp return keep alive %f", dKeepAlive); + return(dKeepAlive); } } return(m_dKeepAlive); @@ -370,7 +376,7 @@ bool SocketChannelImpl::NeedAliveCheck() const template template -void SocketChannelImpl::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) +void SocketChannelImpl::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) const { m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); } @@ -495,7 +501,8 @@ template template E_CODEC_STATUS SocketChannelImpl::SendRequest(uint32 uiStepSeq, Targs&&... args) { - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d]", m_iFd, m_uiSeq, (int)m_ucChannelStatus); + LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[%d], step_seq[%u]", + m_iFd, m_uiSeq, (int)m_ucChannelStatus, uiStepSeq); if (m_pCodec == nullptr) { LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); @@ -509,6 +516,7 @@ E_CODEC_STATUS SocketChannelImpl::SendRequest(uint32 uiStepSeq, Targs&&... ar if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) { uint32 uiStreamId = (static_cast(m_pCodec))->GetLastStreamId(); + LOG4_TRACE("uiStreamId = %u", uiStreamId); if (0 == uiStreamId) { m_listPipelineStepSeq.push_back(uiStepSeq); diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp index aee2b858..3394abb7 100644 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -9,6 +9,9 @@ ******************************************************************************/ #include "CodecFactory.hpp" #include "ios/IO.hpp" +#include "channel/SocketChannel.hpp" +#include "channel/SocketChannelImpl.hpp" +#include "channel/SocketChannelSslImpl.hpp" namespace neb { @@ -25,7 +28,118 @@ CodecFactory::~CodecFactory() { } -Codec* CodecFactory::Create(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel) +std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::shared_ptr pLogger, int iFd, E_CODEC_TYPE eCodecType, bool bIsClient, bool bWithSsl) +{ + Codec* pCodec = nullptr; + auto pChannel = std::make_shared(pLogger, bIsClient, bWithSsl); + ev_tstamp dKeepAlive = 10.0; + if (pLabor->GetNodeInfo().dConnectionProtection > 0) + { + dKeepAlive = pLabor->GetNodeInfo().dConnectionProtection; + } + else + { + dKeepAlive = pLabor->GetNodeInfo().dIoTimeout; + } + switch (eCodecType) + { + case CODEC_NEBULA: + case CODEC_PROTO: + case CODEC_NEBULA_IN_NODE: + pCodec = new CodecProto(pLogger, eCodecType, pChannel); + if (pChannel->WithSsl()) + { +#ifdef WITH_OPENSSL + auto pImpl = std::make_shared>( + pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pImpl->Init(bIsClient); + pImpl->SetCodec(pCodec); + pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); +#else + auto pImpl = std::make_shared>( + pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pImpl->SetCodec(pCodec); + pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); +#endif + } + else + { + auto pImpl = std::make_shared>( + pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pImpl->SetCodec(pCodec); + pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); + } + break; + case CODEC_HTTP: + pCodec = new CodecHttp(pLogger, eCodecType, pChannel); + if (pChannel->WithSsl()) + { +#ifdef WITH_OPENSSL + auto pImpl = std::make_shared>( + pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pImpl->Init(bIsClient); + pImpl->SetCodec(pCodec); + pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); +#else + auto pImpl = std::make_shared>( + pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pImpl->SetCodec(pCodec); + pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); +#endif + } + else + { + auto pImpl = std::make_shared>( + pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pImpl->SetCodec(pCodec); + pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); + } + break; + case CODEC_HTTP2: + { + pCodec = new CodecHttp2(pLogger, eCodecType, pChannel); + auto pImpl = std::make_shared>( + pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pImpl->SetCodec(pCodec); + pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); + } + break; + case CODEC_RESP: + { + pCodec = new CodecResp(pLogger, eCodecType, pChannel); + auto pImpl = std::make_shared>( + pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pImpl->SetCodec(pCodec); + pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); + } + break; + case CODEC_PRIVATE: + { + pCodec = new CodecPrivate(pLogger, eCodecType, pChannel); + auto pImpl = std::make_shared>( + pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pImpl->SetCodec(pCodec); + pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); + } + break; + case CODEC_CASS: + { + pCodec = new CodecCass(pLogger, eCodecType, pChannel); + auto pImpl = std::make_shared>( + pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pImpl->SetCodec(pCodec); + pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); + } + break; + case CODEC_UNKNOW: + return(nullptr); + default: + return(nullptr); + } + return(pChannel); +} + +Codec* CodecFactory::CreateCodec(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel) { Codec* pCodec = nullptr; switch (eCodecType) @@ -404,7 +518,7 @@ bool CodecFactory::AutoSwitchCodec(Dispatcher* pDispatcher, } LOG4_TRACE_DISPATCH("to codec type %d", s_vecAutoSwitchCodec[i]); - Codec* pCodec = Create(pDispatcher->GetLogger(), s_vecAutoSwitchCodec[i], pChannel); + Codec* pCodec = CreateCodec(pDispatcher->GetLogger(), s_vecAutoSwitchCodec[i], pChannel); if (pCodec == nullptr) { LOG4_TRACE_DISPATCH("failed to new codec with codec type %d", s_vecAutoSwitchCodec[i]); diff --git a/src/codec/CodecFactory.hpp b/src/codec/CodecFactory.hpp index ff2fe1e8..49704960 100644 --- a/src/codec/CodecFactory.hpp +++ b/src/codec/CodecFactory.hpp @@ -43,7 +43,8 @@ class CodecFactory CodecFactory(); virtual ~CodecFactory(); - static Codec* Create(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel); + static std::shared_ptr CreateChannel(Labor* pLabor, std::shared_ptr pLogger, int iFd, E_CODEC_TYPE eCodecType, bool bIsClient, bool bWithSsl); + static Codec* CreateCodec(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel); static E_CODEC_STATUS OnEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel); diff --git a/src/codec/cass/CodecCass.cpp b/src/codec/cass/CodecCass.cpp index a19e96b7..9b6eb8f1 100644 --- a/src/codec/cass/CodecCass.cpp +++ b/src/codec/cass/CodecCass.cpp @@ -11,6 +11,7 @@ #include "type/Notations.hpp" #include "CassRequest.hpp" #include "CassResponse.hpp" +#include "channel/SocketChannel.hpp" namespace neb { @@ -27,6 +28,7 @@ CodecCass::~CodecCass() E_CODEC_STATUS CodecCass::Encode(CBuffer* pBuff) { + GenerateStreamId(); return(CODEC_STATUS_OK); } @@ -127,16 +129,16 @@ E_CODEC_STATUS CodecCass::Decode(CBuffer* pBuff, CassMessage& oCassMsg, CBuffer* case CASS_OP_RESULT: if (!oResponse.DecodeResult(&oBuff)) { - LOG4_ERROR("cass response decode failed."); + LOG4_ERROR("cass response CASS_OP_RESULT decode failed."); return(CODEC_STATUS_PART_ERR); } break; case CASS_OP_ERROR: if (!oResponse.DecodeError(&oBuff)) { + LOG4_ERROR("cass response CASS_OP_ERROR decode failed."); return(CODEC_STATUS_PART_ERR); } - LOG4_ERROR("%d: %s", oResponse.GetErrCode(), oResponse.GetErrMsg().c_str()); break; case CASS_OP_AUTHENTICATE: break; @@ -150,7 +152,7 @@ E_CODEC_STATUS CodecCass::Decode(CBuffer* pBuff, CassMessage& oCassMsg, CBuffer* { pReactBuff->Write(m_oReqBuff.GetRawReadBuffer(), m_oReqBuff.ReadableBytes()); } - LOG4_INFO("cass connection ready."); + LOG4_INFO("%s cass connection ready.", GetBindChannel()->GetRemoteAddr().c_str()); break; case CASS_OP_AUTH_CHALLENGE: break; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 46af6a22..926acea2 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -181,6 +181,7 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) { if (pChannel->IsClient()) { + LOG4_INFO("NodeFailed(%s)", pChannel->GetIdentify().c_str()); m_pSessionNode->NodeFailed(pChannel->GetIdentify()); } auto& listUncompletedStep = std::static_pointer_cast>(pChannel->m_pImpl)->GetPipelineStepSeq(); @@ -274,7 +275,7 @@ bool Dispatcher::FdTransfer(int iFd) { inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientAddr, sizeof(szClientAddr)); LOG4_TRACE("set fd %d's remote addr \"%s\"", iAcceptFd, szClientAddr); - std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteAddr(std::string(szClientAddr)); + pChannel->SetRemoteAddr(std::string(szClientAddr)); } else { @@ -292,7 +293,7 @@ bool Dispatcher::FdTransfer(int iFd) { inet_ntop(AF_INET6, &stClientAddr.sin6_addr, szClientAddr, sizeof(szClientAddr)); LOG4_TRACE("set fd %d's remote addr \"%s\"", iAcceptFd, szClientAddr); - std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteAddr(std::string(szClientAddr)); + pChannel->SetRemoteAddr(std::string(szClientAddr)); } else { @@ -313,7 +314,7 @@ bool Dispatcher::FdTransfer(int iFd) } else if (CODEC_NEBULA_IN_NODE == iCodec) { - std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + pChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); m_mapLoaderAndWorkerChannel.insert(std::make_pair(pChannel->GetFd(), pChannel)); m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); } @@ -321,7 +322,7 @@ bool Dispatcher::FdTransfer(int iFd) { ev_tstamp dIoTimeout = (m_pLabor->GetNodeInfo().dConnectionProtection > 0) ? m_pLabor->GetNodeInfo().dConnectionProtection : m_pLabor->GetNodeInfo().dIoTimeout; - std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + pChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); AddIoTimeout(pChannel, dIoTimeout); } return(true); @@ -338,7 +339,7 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) { if (CODEC_NEBULA == pChannel->GetCodecType()) // 系统内部Server间通信 { - if (std::static_pointer_cast>(pChannel->m_pImpl)->GetRemoteWorkerIndex() < 0) // connect to Manager + if (pChannel->GetRemoteWorkerIndex() < 0) // connect to Manager { std::shared_ptr pStepTellWorker = m_pLabor->GetActorBuilder()->MakeSharedStep(nullptr, "neb::StepTellWorker", pChannel); @@ -347,7 +348,7 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) return(false); } pStepTellWorker->Emit(ERR_OK); - std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + pChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); } else { @@ -355,7 +356,7 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) { std::shared_ptr pStepConnectWorker = m_pLabor->GetActorBuilder()->MakeSharedStep( nullptr, "neb::StepConnectWorker", pChannel, - std::static_pointer_cast>(pChannel->m_pImpl)->GetRemoteWorkerIndex()); + pChannel->GetRemoteWorkerIndex()); if (nullptr == pStepConnectWorker) { LOG4_ERROR("error %d: new StepConnectWorker() error!", ERR_NEW); @@ -373,16 +374,16 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) { if (CHANNEL_STATUS_CLOSED != pChannel->GetChannelStatus()) { - std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + pChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); } } - if (pChannel->IsClient() && std::static_pointer_cast>(pChannel->m_pImpl)->GetMsgNum() == 0) + if (pChannel->IsClient() && pChannel->GetMsgNum() == 0) { m_pSessionNode->NodeRecover(pChannel->GetIdentify()); } LOG4_TRACE(""); - E_CODEC_STATUS eCodecStatus = std::static_pointer_cast>(pChannel->m_pImpl)->Send(); + auto eCodecStatus = pChannel->Send(); if (CODEC_STATUS_OK == eCodecStatus) { RemoveIoWriteEvent(pChannel); @@ -411,9 +412,7 @@ bool Dispatcher::OnIoError(std::shared_ptr pChannel) bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) { - //ev_tstamp after = pChannel->m_pImpl->GetActiveTime() - ev_now(m_loop) + m_pLabor->GetNodeInfo().dIoTimeout; - ev_tstamp after = std::static_pointer_cast>(pChannel->m_pImpl)->GetActiveTime() - - ev_now(m_loop) + std::static_pointer_cast>(pChannel->m_pImpl)->GetKeepAlive(); + ev_tstamp after = pChannel->GetActiveTime() - ev_now(m_loop) + pChannel->GetKeepAlive(); if (after > 0) // IO在定时时间内被重新刷新过,重新设置定时器 { ev_timer_stop (m_loop, pChannel->MutableWatcher()->MutableTimerWatcher()); @@ -422,9 +421,10 @@ bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) return(true); } - LOG4_TRACE("fd %d, seq %u:", pChannel->GetFd(), pChannel->GetSequence()); - if (CODEC_PROTO == pChannel->GetCodecType() - && std::static_pointer_cast>(pChannel->m_pImpl)->NeedAliveCheck()) // 需要发送心跳检查 + LOG4_TRACE("fd %d, seq %u, active time %f, now time %f, keep alive %f", + pChannel->GetFd(), pChannel->GetSequence(), pChannel->GetActiveTime(), + ev_now(m_loop), pChannel->GetKeepAlive()); + if (CODEC_PROTO == pChannel->GetCodecType() && pChannel->NeedAliveCheck()) // 需要发送心跳检查 { std::shared_ptr pStepIoTimeout = m_pLabor->GetActorBuilder()->MakeSharedStep( nullptr, "neb::StepIoTimeout", pChannel); @@ -484,6 +484,11 @@ void Dispatcher::EventRun() bool Dispatcher::AddIoTimeout(std::shared_ptr pChannel, ev_tstamp dTimeout) { LOG4_TRACE("channel_fd %d, channel_seq %u, timeout %f", pChannel->GetFd(), pChannel->GetSequence(), dTimeout); + if (dTimeout < 0) + { + LOG4_INFO("no io timeout"); + return(true); + } auto pWatcher = pChannel->MutableWatcher(); pWatcher->Set(pChannel); ev_timer* timer_watcher = pWatcher->MutableTimerWatcher(); @@ -602,7 +607,7 @@ std::shared_ptr Dispatcher::StressSend(const std::string& strIden AddIoTimeout(pChannel, dIoTimeout); AddIoReadEvent(pChannel); AddIoWriteEvent(pChannel); - std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteAddr(strHost); + pChannel->SetRemoteAddr(strHost); IO::SendRequest(this, 0, pChannel, iCmd, uiSeq, oMsgBody); return(pChannel); } @@ -706,8 +711,8 @@ bool Dispatcher::DiscardNamedChannel(const std::string& strIdentify) for (auto channel_iter = named_iter->second.begin(); channel_iter != named_iter->second.end(); ++channel_iter) { - std::static_pointer_cast>((*channel_iter)->m_pImpl)->SetIdentify(""); - std::static_pointer_cast>((*channel_iter)->m_pImpl)->SetClientData(""); + (*channel_iter)->SetIdentify(""); + (*channel_iter)->SetClientData(""); } named_iter->second.clear(); m_mapNamedSocketChannel.erase(named_iter); @@ -729,7 +734,7 @@ bool Dispatcher::AddNamedSocketChannel(const std::string& strIdentify, std::shar { named_iter->second.insert(pChannel); } - std::static_pointer_cast>(pChannel->m_pImpl)->SetIdentify(strIdentify); + pChannel->SetIdentify(strIdentify); return(true); } @@ -748,7 +753,7 @@ void Dispatcher::DelNamedSocketChannel(const std::string& strIdentify) void Dispatcher::SetChannelIdentify(std::shared_ptr pChannel, const std::string& strIdentify) { - std::static_pointer_cast>(pChannel->m_pImpl)->SetIdentify(strIdentify); + pChannel->SetIdentify(strIdentify); } void Dispatcher::AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify) @@ -787,7 +792,7 @@ void Dispatcher::CircuitBreak(const std::string& strIdentify) void Dispatcher::SetClientData(std::shared_ptr pChannel, const std::string& strClientData) { - std::static_pointer_cast>(pChannel->m_pImpl)->SetClientData(strClientData); + pChannel->SetClientData(strClientData); } bool Dispatcher::IsNodeType(const std::string& strNodeIdentify, const std::string& strNodeType) @@ -1001,34 +1006,12 @@ std::shared_ptr Dispatcher::CreateSocketChannel(int iFd, E_CODEC_ auto iter = m_mapSocketChannel.find(iFd); if (iter == m_mapSocketChannel.end()) { - std::shared_ptr pChannel = nullptr; - try - { - if (m_pLabor->GetNodeInfo().dConnectionProtection > 0) - { - pChannel = std::make_shared(m_pLabor, m_pLogger, iFd, - m_pLabor->GetSequence(), bWithSsl, bIsClient, m_pLabor->GetNodeInfo().dConnectionProtection); - } - else - { - pChannel = std::make_shared(m_pLabor, m_pLogger, iFd, - m_pLabor->GetSequence(), bWithSsl, bIsClient, m_pLabor->GetNodeInfo().dIoTimeout); - } - } - catch(std::bad_alloc& e) - { - LOG4_ERROR("new channel for fd %d error: %s", e.what()); - return(nullptr); - } - Codec* pCodec = CodecFactory::Create(m_pLogger, eCodecType, pChannel); - if (pCodec == nullptr) + auto pChannel = CodecFactory::CreateChannel(m_pLabor, m_pLogger, iFd, eCodecType, bIsClient, bWithSsl); + if (pChannel != nullptr) { - LOG4_ERROR("failed to new codec with codec type %d", eCodecType); - return(nullptr); + m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); + LOG4_TRACE("new channel[%d] with codec type %d", pChannel->GetFd(), pChannel->GetCodecType()); } - std::static_pointer_cast>(pChannel->m_pImpl)->SetCodec(pCodec); - m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); - LOG4_TRACE("new channel[%d] with codec type %d", pChannel->GetFd(), pChannel->GetCodecType()); return(pChannel); } else @@ -1069,14 +1052,14 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b if (pChannel->WithSsl()) { #ifdef WITH_OPENSSL - bCloseResult = std::static_pointer_cast>(pChannel->m_pImpl)->Close(); + bCloseResult = pChannel->Close(); #else - bCloseResult = std::static_pointer_cast>(pChannel->m_pImpl)->Close(); + bCloseResult = pChannel->Close(); #endif } else { - bCloseResult = std::static_pointer_cast>(pChannel->m_pImpl)->Close(); + bCloseResult = pChannel->Close(); } if (bCloseResult) { @@ -1194,7 +1177,7 @@ bool Dispatcher::RemoveIoWriteEvent(std::shared_ptr pChannel) void Dispatcher::SetChannelStatus(std::shared_ptr pChannel, E_CHANNEL_STATUS eStatus) { - std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(eStatus); + pChannel->SetChannelStatus(eStatus); } bool Dispatcher::AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout) diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index c4737a0a..bfaf7371 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -543,11 +543,13 @@ template template bool IO::SendRoundRobin(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args) { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "node_type: %s", strNodeType.c_str()); + LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) { - SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); + SendTo(pDispatcher, 0, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); } if (pDispatcher->m_pSessionNode->GetNode(strNodeType, strOnlineNode)) { @@ -600,7 +602,9 @@ bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::s std::string strOnlineNode; if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) { - SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); + SendTo(pDispatcher, 0, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); } if (pDispatcher->m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) { @@ -653,7 +657,9 @@ bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::s std::string strOnlineNode; if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) { - SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); + SendTo(pDispatcher, 0, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); } if (pDispatcher->m_pSessionNode->GetNode(strNodeType, strFactor, strOnlineNode)) { @@ -1018,7 +1024,8 @@ template template bool IO::OnResponse(ActorBuilder* pBuilder, std::shared_ptr pChannel, uint32 uiStreamId, E_CODEC_STATUS eCodecStatus, Targs&&... args) { - auto step_iter = pBuilder->m_mapCallbackStep.find(pChannel->PopStepSeq(uiStreamId, eCodecStatus)); + auto uiStepSeq = pChannel->PopStepSeq(uiStreamId, eCodecStatus); + auto step_iter = pBuilder->m_mapCallbackStep.find(uiStepSeq); if (!pChannel->IsPipeline() && pChannel->PipelineIsEmpty()) { pBuilder->m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. @@ -1027,6 +1034,8 @@ bool IO::OnResponse(ActorBuilder* pBuilder, std::shared_ptr pC { pBuilder->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "no callback for reply from %s!", pChannel->GetIdentify().c_str()); + LOG4_TRACE_BUILDER("no callback for reply from %s, stream id %u, step seq %u", + pChannel->GetIdentify().c_str(), uiStreamId, uiStepSeq); return(false); } else @@ -1113,31 +1122,13 @@ std::shared_ptr IO::CreateSocketChannel(Dispatcher* pDispatche auto iter = pDispatcher->m_mapSocketChannel.find(iFd); if (iter == pDispatcher->m_mapSocketChannel.end()) { - std::shared_ptr pChannel = nullptr; - ev_tstamp dKeepAlive = (pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection > 0) - ? pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection : pDispatcher->m_pLabor->GetNodeInfo().dIoTimeout; - (dKeepAlive > 0) ? dKeepAlive : pDispatcher->m_pLabor->GetNodeInfo().dIoTimeout; - try - { - pChannel = std::make_shared( - pDispatcher->m_pLabor, pDispatcher->m_pLogger, iFd, - pDispatcher->m_pLabor->GetSequence(), bWithSsl, bIsClient, dKeepAlive); - } - catch(std::bad_alloc& e) + auto pChannel = CodecFactory::CreateChannel(pDispatcher->m_pLabor, pDispatcher->m_pLogger, iFd, T::Type(), bIsClient, bWithSsl); + if (pChannel != nullptr) { - pDispatcher->Logger(Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "new channel for fd %d error: %s", e.what()); - return(nullptr); - } - if (!SocketChannelImpl::NewCodec(pChannel, pDispatcher->m_pLabor, pDispatcher->m_pLogger, T::Type())) - { - pDispatcher->Logger(Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "failed to new codec"); - return(nullptr); - } - pDispatcher->m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); - pDispatcher->Logger(Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, + pDispatcher->m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); + LOG4_TRACE_DISPATCH( "new channel for fd %d with codec type %d", pChannel->GetFd(), pChannel->GetCodecType()); + } return(pChannel); } else From 9034172208d884cc6fa9acba983097dd35755152 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 27 Aug 2022 10:18:23 +0800 Subject: [PATCH 161/176] redis cluster asking bug fixed --- README.md | 2 +- README_cn.md | 2 +- src/actor/step/sys_step/StepRedisCluster.cpp | 140 ++----------------- src/actor/step/sys_step/StepRedisCluster.hpp | 4 - 4 files changed, 12 insertions(+), 136 deletions(-) diff --git a/README.md b/README.md index 45e0d550..8ddcb1ab 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log #### v1.7.3 - error callback caused by fuse node detection bug fixed - - redis cluster returns when executing batch write commands bug fixed + - redis cluster returns when executing batch write commands and asking bug fixed - the channel creation method and use CodecFactory instead #### v1.7.2 - codec bind channel circular reference bug fixed diff --git a/README_cn.md b/README_cn.md index b8e2ce04..630fe4bf 100644 --- a/README_cn.md +++ b/README_cn.md @@ -136,7 +136,7 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 #### v1.7.3 - 修复熔断节点探测导致错误回调问题 - - 修复redis cluster执行批量写命令返回问题 + - 修复redis cluster执行批量写命令返回和asking命令bug - 修改channel创建方式,改由CodecFactory创建 #### v1.7.2 - 修复codec bind channel循环引用问题 diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index 9abaa667..267f76b9 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -87,7 +87,7 @@ const std::unordered_set StepRedisCluster::s_setWriteCmd = "ZADD","ZINCRBY","ZPOPMAX","ZPOPMIN", "ZREM","ZREMRANGEBYLEX","ZREMRANGEBYRANK","ZREMRANGEBYSCORE", // keys - "DEL","EXPIREAT","MOVE","PERSIST","PEXPIRE","PEXPIREAT", + "DEL","EXPIRE","EXPIREAT","MOVE","PERSIST","PEXPIRE","PEXPIREAT", "RESTORE","SORT","TOUCH","UNLINK" }; @@ -203,8 +203,8 @@ E_CMD_STATUS StepRedisCluster::Callback( } if (vecMsg[0] == "ASK") { - AddToAskingQueue(vecMsg[2], pRedisRequest); SendCmdAsking(vecMsg[2]); + SendTo(vecMsg[2], pRedisRequest); return(CMD_STATUS_RUNNING); } if (vecMsg[0] == "NOAUTH") @@ -285,8 +285,8 @@ E_CMD_STATUS StepRedisCluster::Callback( } if (vecMsg[0] == "ASK") { - AddToAskingQueue(vecMsg[2], pRedisRequest); SendCmdAsking(vecMsg[2]); + SendTo(vecMsg[2], pRedisRequest); return(CMD_STATUS_RUNNING); } if (vecMsg[0] == "CROSSSLOT") @@ -408,7 +408,6 @@ void StepRedisCluster::CmdErrBack( } } } - AskingQueueErrBack(pChannel, iErrno, strErrMsg); } } @@ -428,7 +427,7 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, const RedisMsg& oR bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptr pRedisMsg) { - LOG4_TRACE("%s", pRedisMsg->DebugString().c_str()); + LOG4_TRACE("%s: %s", strIdentify.c_str(), pRedisMsg->DebugString().c_str()); bool bResult = IO::SendTo(this, strIdentify, SOCKET_STREAM, m_bWithSsl, m_bPipeline, (*pRedisMsg.get())); if (bResult) @@ -665,7 +664,7 @@ bool StepRedisCluster::SendCmdClusterSlots() m_uiAddressIndex = 0; } bool bResult = SendTo(m_vecAddress[m_uiAddressIndex++], pRedisRequest); - m_uiAddressIndex++; + ++m_uiAddressIndex; return(bResult); } @@ -768,39 +767,14 @@ void StepRedisCluster::CmdAskingCallback(std::shared_ptr pChannel { if ("OK" == oRedisReply.str()) { - auto step_iter = m_mapAskingRequest.find(strIdentify); - if (step_iter == m_mapAskingRequest.end() || step_iter->second.size() == 0) - { - LOG4_INFO("no \"%s\" found in m_mapPipelineStep", strIdentify.c_str()); - } - while (step_iter->second.size() > 0) - { - auto& oRedisRequest = step_iter->second.front(); - uint32 uiRealStepSeq = (uint32)oRedisRequest->integer(); - step_iter->second.pop(); - if (uiRealStepSeq == GetSequence()) - { - ; - } - else - { - SendTo(strIdentify, oRedisRequest); - } - } - } - else - { - AskingQueueErrBack(pChannel, oRedisReply.type(), "unexpected asking error"); + ; } } - else if (REDIS_REPLY_ERROR == oRedisReply.type()) - { - AskingQueueErrBack(pChannel, oRedisReply.type(), oRedisReply.str()); - } else { - AskingQueueErrBack(pChannel, oRedisReply.type(), oRedisReply.str()); + LOG4_ERROR("%s unexpected asking errmsg: %s", strIdentify.c_str(), oRedisReply.str().c_str()); } + LOG4_ERROR("%s unexpected error type %d: %s", strIdentify.c_str(), oRedisReply.type(), oRedisReply.str().c_str()); } void StepRedisCluster::CmdPingCallback(std::shared_ptr pChannel, @@ -848,86 +822,6 @@ void StepRedisCluster::CmdReadOnlyCallback(std::shared_ptr pChann } } -void StepRedisCluster::AskingQueueErrBack( - std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) -{ - LOG4_ERROR("error %d: %s", iErrno, strErrMsg.c_str()); - auto step_iter = m_mapAskingRequest.find(pChannel->GetIdentify()); - if (step_iter == m_mapAskingRequest.end()) - { - LOG4_TRACE("no \"%s\" found in m_mapPipelineStep", pChannel->GetIdentify().c_str()); - } - else - { - LOG4_WARNING("%s asking queue size %u", pChannel->GetIdentify().c_str(), step_iter->second.size()); - m_setFailedNode.insert(pChannel->GetIdentify()); - while (step_iter->second.size() > 0) - { - auto pRedisRequest = step_iter->second.front(); - uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); - LOG4_WARNING("real step seq %u", uiRealStepSeq); - step_iter->second.pop(); - if (uiRealStepSeq != GetSequence()) - { - auto num_iter = m_mapStepEmitNum.find(uiRealStepSeq); - if (num_iter == m_mapStepEmitNum.end()) // 单key请求的响应 - { - GetLabor(this)->GetActorBuilder()->OnError(pChannel, uiRealStepSeq, iErrno, strErrMsg); - } - else - { - auto reply_iter = m_mapReply.find(uiRealStepSeq); - if (reply_iter == m_mapReply.end()) - { - LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); - continue; - } - std::vector vecElementIndex; - for (int i = 1; i < pRedisRequest->element_size(); ++i) // element(0).str() is cmd - { - if (pRedisRequest->element(i).integer() > 0 || i == 1) - { - vecElementIndex.push_back((uint32)pRedisRequest->element(i).integer()); - } - } - RedisReply oRedisReply; - oRedisReply.set_type(REDIS_REPLY_ERROR); - oRedisReply.set_str(strErrMsg); - for (uint32 j = 0; j < vecElementIndex.size(); ++j) - { - if (vecElementIndex[j] < reply_iter->second.size()) - { - if (reply_iter->second[vecElementIndex[j]] == nullptr) - { - reply_iter->second[vecElementIndex[j]] = new RedisReply(); - } - reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply); - } - else - { - LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); - } - } - num_iter->second--; - if (num_iter->second == 0) - { - RedisReply oFinalReply; - oFinalReply.set_type(REDIS_REPLY_ARRAY); - for (size_t k = 0; k < reply_iter->second.size(); ++k) - { - oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); - reply_iter->second[k] = nullptr; - } - IO::OnResponse(GetLabor(this)->GetActorBuilder(), pChannel, uiRealStepSeq, oFinalReply); - m_mapStepEmitNum.erase(num_iter); - m_mapReply.erase(reply_iter); - } - } - } - } - } -} - bool StepRedisCluster::Dispatch(const RedisMsg& oRedisMsg, uint32 uiStepSeq) { std::string strCmd; @@ -1050,21 +944,6 @@ void StepRedisCluster::RegisterStep(uint32 uiStepSeq) } } -void StepRedisCluster::AddToAskingQueue(const std::string& strIdentify, std::shared_ptr pRedisMsg) -{ - auto iter = m_mapAskingRequest.find(strIdentify); - if (iter == m_mapAskingRequest.end()) - { - std::queue> queStep; - queStep.push(pRedisMsg); - m_mapAskingRequest.insert(std::make_pair(strIdentify, std::move(queStep))); - } - else - { - iter->second.push(pRedisMsg); - } -} - bool StepRedisCluster::Auth(const std::string& strIdentify, std::shared_ptr pRedisMsg) { std::string strAuth; @@ -1098,7 +977,7 @@ void StepRedisCluster::HealthCheck() { SendCmdClusterSlots(); } - for (auto iter = m_setFailedNode.begin(); iter != m_setFailedNode.end(); ++iter) + for (auto iter = m_setFailedNode.begin(); iter != m_setFailedNode.end(); ) { if (m_setAllNode.find(*iter) == m_setAllNode.end()) { @@ -1108,6 +987,7 @@ void StepRedisCluster::HealthCheck() else { SendCmdPing(*iter); + ++iter; } } for (auto timeout_iter = m_mapTimeoutStep.begin(); timeout_iter != m_mapTimeoutStep.end(); ) diff --git a/src/actor/step/sys_step/StepRedisCluster.hpp b/src/actor/step/sys_step/StepRedisCluster.hpp index 9390f270..bf361996 100644 --- a/src/actor/step/sys_step/StepRedisCluster.hpp +++ b/src/actor/step/sys_step/StepRedisCluster.hpp @@ -92,13 +92,10 @@ class StepRedisCluster: public RedisStep, void CmdPingCallback(std::shared_ptr pChannel, const std::string& strIdentify, const RedisReply& oRedisReply); bool SendCmdReadOnly(const std::string& strIdentify); void CmdReadOnlyCallback(std::shared_ptr pChannel, const std::string& strIdentify, const RedisReply& oRedisReply); - void AskingQueueErrBack(std::shared_ptr pChannel, - int iErrno, const std::string& strErrMsg); void CmdErrBack(std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg); bool Dispatch(const RedisMsg& oRedisMsg, uint32 uiStepSeq); void SendWaittingRequest(); void RegisterStep(uint32 uiStepSeq); - void AddToAskingQueue(const std::string& strIdentify, std::shared_ptr pRedisMsg); bool Auth(const std::string& strIdentify, std::shared_ptr pRedisMsg); void HealthCheck(); @@ -113,7 +110,6 @@ class StepRedisCluster: public RedisStep, std::vector m_vecAddress; ///< 集群地址 std::unordered_map> m_mapSlot2Node; // redis cluster slot对应的节点 std::unordered_map>> m_mapPipelineRequest; ///< 等待回调的请求 - std::unordered_map>> m_mapAskingRequest; ///< 等待Asking的请求 std::unordered_map m_mapStepEmitNum; // 每个step发出请求(等待响应)数量 std::unordered_map> m_mapReply; std::map> m_mapTimeoutStep; From 86e4cc8178ff26e81f359871e491bab68966e9fd Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 4 Sep 2022 15:15:29 +0800 Subject: [PATCH 162/176] - Add connection backlog configuration - Optimize worker mini load forwarding - add dns cache - add upstream and downstren monitor --- README.md | 4 + README_cn.md | 6 +- conf/nebula.json | 1 + src/actor/cmd/sys_cmd/ModuleMetrics.cpp | 4 +- .../manager/CmdOnOrientationFdTransfer.cpp | 2 +- .../session/sys_session/SessionDataReport.cpp | 70 ++- .../session/sys_session/SessionDataReport.hpp | 4 +- .../session/sys_session/TimerAddrinfo.cpp | 49 ++ .../session/sys_session/TimerAddrinfo.hpp | 48 ++ .../sys_session/manager/SessionManager.cpp | 138 +++-- .../sys_session/manager/SessionManager.hpp | 4 +- src/channel/SocketChannel.cpp | 26 +- src/channel/SocketChannel.hpp | 6 +- src/channel/SocketChannelImpl.hpp | 46 +- src/codec/Codec.hpp | 5 + src/codec/CodecResp.cpp | 482 ++++++++++++++++-- src/codec/CodecResp.hpp | 48 ++ src/ios/Dispatcher.cpp | 75 +-- src/ios/Dispatcher.hpp | 4 +- src/ios/IO.hpp | 148 +++++- src/labor/Labor.hpp | 9 +- src/labor/Manager.cpp | 17 +- src/labor/NodeInfo.hpp | 57 ++- src/labor/Worker.cpp | 59 ++- src/labor/Worker.hpp | 60 ++- 25 files changed, 1147 insertions(+), 225 deletions(-) create mode 100644 src/actor/session/sys_session/TimerAddrinfo.cpp create mode 100644 src/actor/session/sys_session/TimerAddrinfo.hpp diff --git a/README.md b/README.md index 8ddcb1ab..d49d4b89 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,10 @@ A simple testing can be start with a NebulaInterface only, and also can be start - error callback caused by fuse node detection bug fixed - redis cluster returns when executing batch write commands and asking bug fixed - the channel creation method and use CodecFactory instead + - Add connection backlog configuration + - Optimize worker mini load forwarding + - add dns cache + - add upstream and downstren monitor #### v1.7.2 - codec bind channel circular reference bug fixed - CodecProto no packet communication bug fixed diff --git a/README_cn.md b/README_cn.md index 630fe4bf..2b0e6df9 100644 --- a/README_cn.md +++ b/README_cn.md @@ -136,8 +136,12 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 #### v1.7.3 - 修复熔断节点探测导致错误回调问题 - - 修复redis cluster执行批量写命令返回和asking命令bug + - 修复redis cluster执行批量写命令返回、集群主从节点切换和asking命令bug - 修改channel创建方式,改由CodecFactory创建 + - 增加连接队列配置 + - 优化worker最小负载转发 + - 增加dns cache + - 增加上下游连接和数据包监控 #### v1.7.2 - 修复codec bind channel循环引用问题 - 修复CodecProto无包体通信问题 diff --git a/conf/nebula.json b/conf/nebula.json index 76f1e884..d07af757 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -18,6 +18,7 @@ "port": 9987, "//bind_ip":"绑定IP地址。当有此项配置时bind()会绑定此ip地址,否则绑定host和access_host", "bind_ip":"0.0.0.0", + "backlog":128, "//server_name": "异步事件驱动Server", "server_name": "AsyncServer", "//worker_num": "进程数量", diff --git a/src/actor/cmd/sys_cmd/ModuleMetrics.cpp b/src/actor/cmd/sys_cmd/ModuleMetrics.cpp index a42a24c5..ad3857e6 100644 --- a/src/actor/cmd/sys_cmd/ModuleMetrics.cpp +++ b/src/actor/cmd/sys_cmd/ModuleMetrics.cpp @@ -64,13 +64,13 @@ bool ModuleMetrics::AnyMessage(std::shared_ptr pChannel, const Ht { if (m_strApp.empty()) { - oss << "nebula{key=\"}" << pReport->records(i).key() + oss << "nebula{key=\"" << pReport->records(i).key() << "\"}" << pReport->records(i).value(0) << "\n"; } else { oss << "nebula{app=\"" << m_strApp - << "\", key=\"}" << pReport->records(i).key() + << "\", key=\"" << pReport->records(i).key() << "\"}" << pReport->records(i).value(0) << "\n"; } } diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp index d91ba659..19ce163d 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp @@ -54,7 +54,7 @@ bool CmdOnOrientationFdTransfer::AnyMessage( int iErrno = GetLabor(this)->GetDispatcher()->SendFd( pWorkerInfo->iDataFd, pChannel->GetFd(), ((Manager*)GetLabor(this))->GetManagerInfo().iS2SFamily, - (int)pChannel->GetCodecType()); + (int)pChannel->GetCodecType(), pChannel->GetRemoteAddr()); if (iErrno != ERR_OK) { GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pChannel); diff --git a/src/actor/session/sys_session/SessionDataReport.cpp b/src/actor/session/sys_session/SessionDataReport.cpp index bffd3a79..546c0430 100644 --- a/src/actor/session/sys_session/SessionDataReport.cpp +++ b/src/actor/session/sys_session/SessionDataReport.cpp @@ -30,12 +30,15 @@ SessionDataReport::~SessionDataReport() delete m_pReport; m_pReport = nullptr; } - for (auto iter = m_mapDataCollect.begin(); iter != m_mapDataCollect.end(); ++iter) + for (auto data_iter = m_mapDataCollect.begin(); data_iter != m_mapDataCollect.end(); ++data_iter) { - if (iter->second != nullptr) + for (auto iter = data_iter->second.begin(); iter != data_iter->second.end(); ++iter) { - delete iter->second; - iter->second = nullptr; + if (iter->second != nullptr) + { + delete iter->second; + iter->second = nullptr; + } } } m_mapDataCollect.clear(); @@ -67,10 +70,13 @@ E_CMD_STATUS SessionDataReport::Timeout() LOG4_ERROR("failed to new Report!"); return(CMD_STATUS_FAULT); } - for (auto iter = m_mapDataCollect.begin(); iter != m_mapDataCollect.end(); ++iter) + for (auto data_iter = m_mapDataCollect.begin(); data_iter != m_mapDataCollect.end(); ++data_iter) { - m_pReport->mutable_records()->AddAllocated(iter->second); - iter->second = nullptr; + for (auto iter = data_iter->second.begin(); iter != data_iter->second.end(); ++iter) + { + m_pReport->mutable_records()->AddAllocated(iter->second); + iter->second = nullptr; + } } MsgBody oMsgBody; m_pReport->SerializeToString(&m_strReport); @@ -85,11 +91,12 @@ E_CMD_STATUS SessionDataReport::Timeout() void SessionDataReport::AddReport(const std::shared_ptr pReport) { LOG4_TRACE(""); + std::unordered_map>::iterator data_iter; std::unordered_map::iterator iter; for (int i = 0; i < pReport->records_size(); ++i) { - iter = m_mapDataCollect.find(pReport->records(i).key()); - if (iter == m_mapDataCollect.end()) + data_iter = m_mapDataCollect.find(pReport->records(i).item()); + if (data_iter == m_mapDataCollect.end()) { ReportRecord* pRecord = nullptr; try @@ -106,27 +113,52 @@ void SessionDataReport::AddReport(const std::shared_ptr pReport) { pRecord->add_value(pReport->records(i).value(j)); } - m_mapDataCollect.insert(std::make_pair(pReport->records(i).key(), pRecord)); + std::unordered_map mapKeyRecord; + mapKeyRecord.insert(std::make_pair(pReport->records(i).key(), pRecord)); + m_mapDataCollect.insert(std::make_pair(pReport->records(i).item(), std::move(mapKeyRecord))); } else { - for (int j = 0; j < pReport->records(i).value_size(); ++j) + iter = data_iter->second.find(pReport->records(i).key()); + if (iter == data_iter->second.end()) { - if (j < iter->second->value_size()) + ReportRecord* pRecord = nullptr; + try + { + pRecord = new ReportRecord(); + } + catch (std::bad_alloc& e) { - if (pReport->records(i).value_type() == ReportRecord::VALUE_ACC) + return; + } + pRecord->set_key(pReport->records(i).key()); + pRecord->set_item(pReport->records(i).item()); + for (int j = 0; j < pReport->records(i).value_size(); ++j) + { + pRecord->add_value(pReport->records(i).value(j)); + } + data_iter->second.insert(std::make_pair(pReport->records(i).key(), pRecord)); + } + else + { + for (int j = 0; j < pReport->records(i).value_size(); ++j) + { + if (j < iter->second->value_size()) { - iter->second->set_value(j, iter->second->value(j) + pReport->records(i).value(j)); + if (pReport->records(i).value_type() == ReportRecord::VALUE_ACC) + { + iter->second->set_value(j, iter->second->value(j) + pReport->records(i).value(j)); + } + else + { + iter->second->set_value(j, pReport->records(i).value(j)); + } } else { - iter->second->set_value(j, pReport->records(i).value(j)); + iter->second->add_value(pReport->records(i).value(j)); } } - else - { - iter->second->add_value(pReport->records(i).value(j)); - } } } } diff --git a/src/actor/session/sys_session/SessionDataReport.hpp b/src/actor/session/sys_session/SessionDataReport.hpp index bb114326..94ddd8f2 100644 --- a/src/actor/session/sys_session/SessionDataReport.hpp +++ b/src/actor/session/sys_session/SessionDataReport.hpp @@ -20,7 +20,7 @@ namespace neb { class SessionDataReport: public Timer, - DynamicCreator, + public DynamicCreator, public ActorSys { public: @@ -51,7 +51,7 @@ class SessionDataReport: public Timer, uint32 m_uiNodeReportUpdatingIndex; Report* m_pReport; ///< final report std::string m_strReport; ///< m_pReport SerializeToString - std::unordered_map m_mapDataCollect; ///< report collector + std::unordered_map> m_mapDataCollect; ///< report collector std::vector> > m_vecNodeReport; ///< report data from nebula node }; diff --git a/src/actor/session/sys_session/TimerAddrinfo.cpp b/src/actor/session/sys_session/TimerAddrinfo.cpp new file mode 100644 index 00000000..7b407eb0 --- /dev/null +++ b/src/actor/session/sys_session/TimerAddrinfo.cpp @@ -0,0 +1,49 @@ +/******************************************************************************* + * Project: Nebula + * @file TimerAddrinfo.cpp + * @brief + * @author Bwar + * @date: 2022-08-27 + * @note + * Modify history: + ******************************************************************************/ +#include "TimerAddrinfo.hpp" + +namespace neb +{ + +TimerAddrinfo::TimerAddrinfo(const std::string& strSessionId) + : Timer(strSessionId, 10.0), m_pAddrInfo(nullptr) +{ +} + +TimerAddrinfo::~TimerAddrinfo() +{ + if (m_pAddrInfo != nullptr) + { + freeaddrinfo(m_pAddrInfo); + } + m_pAddrInfo = nullptr; +} + +E_CMD_STATUS TimerAddrinfo::Timeout() +{ + if (m_pAddrInfo != nullptr) + { + freeaddrinfo(m_pAddrInfo); + } + m_pAddrInfo = nullptr; + return(CMD_STATUS_RUNNING); +} + +void TimerAddrinfo::MigrateAddrinfo(struct addrinfo* pAddrInfo) +{ + if (m_pAddrInfo != nullptr) + { + freeaddrinfo(m_pAddrInfo); + } + m_pAddrInfo = pAddrInfo; +} + +} /* namespace neb */ + diff --git a/src/actor/session/sys_session/TimerAddrinfo.hpp b/src/actor/session/sys_session/TimerAddrinfo.hpp new file mode 100644 index 00000000..4516f851 --- /dev/null +++ b/src/actor/session/sys_session/TimerAddrinfo.hpp @@ -0,0 +1,48 @@ +/******************************************************************************* + * Project: Nebula + * @file TimerAddrinfo.hpp + * @brief + * @author Bwar + * @date: 2022-08-27 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_SESSION_SYS_SESSION_TIMERADDRINFO_HPP_ +#define SRC_ACTOR_SESSION_SYS_SESSION_TIMERADDRINFO_HPP_ + +#include +#include +#include +#include +#include +#include "actor/session/Timer.hpp" +#include "actor/ActorSys.hpp" + +namespace neb +{ + +class TimerAddrinfo: public neb::Timer, + public DynamicCreator, + public ActorSys +{ +public: + TimerAddrinfo(const std::string& strSessionId); + virtual ~TimerAddrinfo(); + + virtual E_CMD_STATUS Timeout(); + + struct addrinfo* GetAddrinfo() + { + return(m_pAddrInfo); + } + + void MigrateAddrinfo(struct addrinfo* pAddrInfo); + +private: + struct addrinfo* m_pAddrInfo; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_SESSION_SYS_SESSION_TIMERADDRINFO_HPP_ */ + diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index c6f35e0d..4fb2e7a8 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -15,6 +15,7 @@ #include "pb/report.pb.h" #include "util/process_helper.h" #include "util/json/CJsonObject.hpp" +#include "util/encrypt/city.h" #include "labor/NodeInfo.hpp" #include "labor/Manager.hpp" #include "labor/Worker.hpp" @@ -69,7 +70,7 @@ E_CMD_STATUS SessionManager::Timeout() for (auto iter = m_mapWorkerInfo.begin(); iter != m_mapWorkerInfo.end(); ++iter) { uiLoad += iter->second->uiLoad; - uiConnect += iter->second->uiConnect; + uiConnect += iter->second->uiConnection; } pRecord = pReport->add_records(); pRecord->set_key("load"); @@ -193,6 +194,11 @@ void SessionManager::AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, i { start_num_iter->second++; } + while ((int)m_vecWorkerDataFd.size() <= iWorkerIndex) + { + m_vecWorkerDataFd.push_back(-1); + } + m_vecWorkerDataFd[iWorkerIndex] = iDataFd; } void SessionManager::AddLoaderInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd) @@ -272,11 +278,16 @@ bool SessionManager::SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad) if (it != m_mapWorkerInfo.end()) { oJsonLoad.Get("load", it->second->uiLoad); - oJsonLoad.Get("connect", it->second->uiConnect); + oJsonLoad.Get("connect", it->second->uiConnection); oJsonLoad.Get("recv_num", it->second->uiRecvNum); oJsonLoad.Get("recv_byte", it->second->uiRecvByte); oJsonLoad.Get("send_num", it->second->uiSendNum); oJsonLoad.Get("send_byte", it->second->uiSendByte); + LOG4_INFO("worker %u load %u, connection %u, recv_num %u, recv_byte %u," + "send_num %u, send_byte %u", it->second->iWorkerIndex, + it->second->uiLoad, it->second->uiConnection, + it->second->uiRecvNum, it->second->uiRecvByte, + it->second->uiSendNum, it->second->uiSendByte); it->second->dBeatTime = GetNowTime(); it->second->bStartBeatCheck = true; return(true); @@ -302,6 +313,78 @@ void SessionManager::SetLoaderActorBuilder(ActorBuilder* pActorBuilder) } } +int SessionManager::GetWorkerDataFd(const char* szRemoteAddr, int iRemotePort) +{ + if (m_bDirectToLoader && m_iLoaderDataFd != -1) + { + return(m_iLoaderDataFd); + } + else + { + if (m_vecWorkerDataFd.empty()) + { + return(-1); + } + char szIdentify[64] = {0}; + snprintf(szIdentify, 64, "%s:%d", szRemoteAddr, iRemotePort); + uint32 uiKeyHash = CityHash32(szIdentify, strlen(szIdentify)); + uint32 uiIndex = uiKeyHash % (m_vecWorkerDataFd.size() - 1); // loader▒~Z~Dworker▒~V▒~O▒为0 + return(m_vecWorkerDataFd[uiIndex + 1]); + } + return(-1); +} + +int SessionManager::GetMinLoadWorkerDataFd() +{ + LOG4_TRACE(" "); + int iMinLoadWorkerFd = 0; + int iMinLoadPid = 0; + int iMinLoad = -1; + uint32 uiMinLoadConnection = 0; + if (m_bDirectToLoader && m_iLoaderDataFd != -1) + { + return(m_iLoaderDataFd); + } + else + { + for (auto iter = m_mapWorkerInfo.begin(); iter != m_mapWorkerInfo.end(); ++iter) + { + if (m_iLoaderDataFd == iter->second->iDataFd) + { + continue; + } + if (iMinLoad == -1) + { + iMinLoadWorkerFd = iter->second->iDataFd; + iMinLoadPid = iter->first; + iMinLoad = iter->second->uiLoad; + uiMinLoadConnection = iter->second->uiConnection; + } + else if ((int)iter->second->uiLoad < iMinLoad) + { + iMinLoadWorkerFd = iter->second->iDataFd; + iMinLoadPid = iter->first; + iMinLoad = iter->second->uiLoad; + uiMinLoadConnection = iter->second->uiConnection; + } + else if ((int)iter->second->uiLoad == iMinLoad && iter->second->uiConnection < uiMinLoadConnection) + { + iMinLoadWorkerFd = iter->second->iDataFd; + iMinLoadPid = iter->first; + iMinLoad = iter->second->uiLoad; + uiMinLoadConnection = iter->second->uiConnection; + } + } + } + auto iter = m_mapWorkerInfo.find(iMinLoadPid); + if (iter != m_mapWorkerInfo.end()) + { + iter->second->uiLoad++; + iter->second->uiConnection++; + } + return(iMinLoadWorkerFd); +} + int SessionManager::GetNextWorkerDataFd() { if (m_bDirectToLoader && m_iLoaderDataFd != -1) @@ -335,45 +418,6 @@ int SessionManager::GetNextWorkerDataFd() } } -std::pair SessionManager::GetMinLoadWorkerDataFd() -{ - LOG4_TRACE(" "); - int iMinLoadWorkerFd = 0; - int iMinLoad = -1; - std::pair worker_pid_fd; - if (m_bDirectToLoader && m_iLoaderDataFd != -1) - { - auto it = m_mapWorkerFdPid.find(m_iLoaderDataFd); - if (it != m_mapWorkerFdPid.end()) - { - worker_pid_fd = std::pair(it->second, m_iLoaderDataFd); - } - else - { - worker_pid_fd = std::pair(0, m_iLoaderDataFd); - } - } - else - { - for (auto iter = m_mapWorkerInfo.begin(); iter != m_mapWorkerInfo.end(); ++iter) - { - if (iMinLoad == -1 && iter->second->iDataFd != m_iLoaderDataFd) - { - iMinLoadWorkerFd = iter->second->iDataFd; - iMinLoad = iter->second->uiLoad; - worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); - } - else if ((int)iter->second->uiLoad < iMinLoad && iter->second->iDataFd != m_iLoaderDataFd) - { - iMinLoadWorkerFd = iter->second->iDataFd; - iMinLoad = iter->second->uiLoad; - worker_pid_fd = std::pair(iter->first, iMinLoadWorkerFd); - } - } - } - return(worker_pid_fd); -} - bool SessionManager::CheckWorker() { LOG4_TRACE(" "); @@ -527,14 +571,14 @@ void SessionManager::MakeReportData(CJsonObject& oReportData) continue; } iLoad += worker_iter->second->uiLoad; - iConnect += worker_iter->second->uiConnect; + iConnect += worker_iter->second->uiConnection; iRecvNum += worker_iter->second->uiRecvNum; iRecvByte += worker_iter->second->uiRecvByte; iSendNum += worker_iter->second->uiSendNum; iSendByte += worker_iter->second->uiSendByte; oMember.Clear(); oMember.Add("load", worker_iter->second->uiLoad); - oMember.Add("connect", worker_iter->second->uiConnect); + oMember.Add("connect", worker_iter->second->uiConnection); oMember.Add("recv_num", worker_iter->second->uiRecvNum); oMember.Add("recv_byte", worker_iter->second->uiRecvByte); oMember.Add("send_num", worker_iter->second->uiSendNum); @@ -570,8 +614,8 @@ bool SessionManager::NewSocketWhenWorkerCreated(int iWorkerDataFd) x_sock_set_block(iFds[0], 0); x_sock_set_block(iFds[1], 0); LOG4_TRACE("Transfer fd to Loader and Worker."); - GetLabor(this)->GetDispatcher()->SendFd(m_iLoaderDataFd, iFds[0], PF_UNIX, CODEC_NEBULA_IN_NODE); - GetLabor(this)->GetDispatcher()->SendFd(iWorkerDataFd, iFds[1], PF_UNIX, CODEC_NEBULA_IN_NODE); + GetLabor(this)->GetDispatcher()->SendFd(m_iLoaderDataFd, iFds[0], PF_UNIX, CODEC_NEBULA_IN_NODE, GetNodeInfo().strHostForServer); + GetLabor(this)->GetDispatcher()->SendFd(iWorkerDataFd, iFds[1], PF_UNIX, CODEC_NEBULA_IN_NODE, GetNodeInfo().strHostForServer); return(true); } @@ -596,8 +640,8 @@ bool SessionManager::NewSocketWhenLoaderCreated() } x_sock_set_block(iFds[0], 0); x_sock_set_block(iFds[1], 0); - GetLabor(this)->GetDispatcher()->SendFd(m_iLoaderDataFd, iFds[0], PF_UNIX, CODEC_NEBULA_IN_NODE); - GetLabor(this)->GetDispatcher()->SendFd(iter->second->iDataFd, iFds[1], PF_UNIX, CODEC_NEBULA_IN_NODE); + GetLabor(this)->GetDispatcher()->SendFd(m_iLoaderDataFd, iFds[0], PF_UNIX, CODEC_NEBULA_IN_NODE, GetNodeInfo().strHostForServer); + GetLabor(this)->GetDispatcher()->SendFd(iter->second->iDataFd, iFds[1], PF_UNIX, CODEC_NEBULA_IN_NODE, GetNodeInfo().strHostForServer); } return(true); } diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index 596e8027..f8590bc4 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -44,8 +44,9 @@ class SessionManager : public Session, const WorkerInfo* GetWorkerInfo(int32 iWorkerIndex) const; bool SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad); void SetLoaderActorBuilder(ActorBuilder* pActorBuilder); + int GetWorkerDataFd(const char* szClientAddr, int iClientPort); int GetNextWorkerDataFd(); - std::pair GetMinLoadWorkerDataFd(); + int GetMinLoadWorkerDataFd(); bool CheckWorker(); bool WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& eLaborType); void SendOnlineNodesToWorker(); @@ -63,6 +64,7 @@ class SessionManager : public Session, private: bool m_bDirectToLoader = false; int m_iLoaderDataFd = -1; + std::vector m_vecWorkerDataFd; ///< only for fd transfer std::unordered_map m_mapWorker; ///< only thread worker std::unordered_map m_mapWorkerInfo; ///< 业务逻辑工作进程及进程属性,key为pid std::unordered_map::iterator m_iterWorkerInfo; diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index 7513f742..123b60aa 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -325,13 +325,15 @@ Labor* SocketChannel::GetLabor() return(nullptr); } -int SocketChannel::SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, std::shared_ptr pLogger) +int SocketChannel::SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, + const std::string& strRemoteAddr, std::shared_ptr pLogger) { ssize_t n; - struct iovec iov[1]; + struct iovec iov[2]; struct msghdr msg; tagChannelCtx stCh; int iError = 0; + char szAddress[64] = {0}; stCh.iFd = iSendFd; stCh.iAiFamily = iAiFamily; @@ -365,13 +367,16 @@ int SocketChannel::SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int msg.msg_flags = 0; - iov[0].iov_base = (char*)&stCh; + snprintf(szAddress, 64, "%s", strRemoteAddr.data()); + iov[0].iov_base = (void*)&stCh; iov[0].iov_len = sizeof(tagChannelCtx); + iov[1].iov_base = (void*)szAddress; + iov[1].iov_len = 64; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; - msg.msg_iovlen = 1; + msg.msg_iovlen = 2; n = sendmsg(iSocketFd, &msg, 0); @@ -385,26 +390,30 @@ int SocketChannel::SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int return(ERR_OK); } -int SocketChannel::RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, int& iCodecType, std::shared_ptr pLogger) +int SocketChannel::RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, int& iCodecType, + std::string& strRemoteAddr, std::shared_ptr pLogger) { ssize_t n; - struct iovec iov[1]; + struct iovec iov[2]; struct msghdr msg; tagChannelCtx stCh; int iError = 0; + char szAddress[64] = {0}; union { struct cmsghdr cm; char space[CMSG_SPACE(sizeof(int))]; } cmsg; - iov[0].iov_base = (char*)&stCh; + iov[0].iov_base = (void*)&stCh; iov[0].iov_len = sizeof(tagChannelCtx); + iov[1].iov_base = (void*)szAddress; + iov[1].iov_len = 64; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; - msg.msg_iovlen = 1; + msg.msg_iovlen = 2; msg.msg_control = (caddr_t) &cmsg; msg.msg_controllen = sizeof(cmsg); @@ -457,6 +466,7 @@ int SocketChannel::RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, in iRecvFd = stCh.iFd; iAiFamily = stCh.iAiFamily; iCodecType = stCh.iCodecType; + strRemoteAddr = szAddress; return(ERR_OK); } diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index 818246e7..4b4b1a10 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -41,8 +41,10 @@ class SocketChannel: public Channel SocketChannel(std::shared_ptr pLogger, bool bIsClient, bool bWithSsl); virtual ~SocketChannel(); - static int SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, std::shared_ptr pLogger); - static int RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, int& iCodecType, std::shared_ptr pLogger); + static int SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, + const std::string& strRemoteAddr, std::shared_ptr pLogger); + static int RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, int& iCodecType, + std::string& strRemoteAddr, std::shared_ptr pLogger); virtual bool IsClient() const; virtual bool WithSsl() const; diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 8404d5fb..97b2d586 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -466,7 +466,14 @@ E_CODEC_STATUS SocketChannelImpl::Send() LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); if (iHadWrittenLen >= 0) { - m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); + if (m_bIsClient) + { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen, IO_STAT_UPSTREAM_SEND_BYTE); + } + else + { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen, IO_STAT_DOWNSTREAM_SEND_BYTE); + } if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) { @@ -582,7 +589,14 @@ E_CODEC_STATUS SocketChannelImpl::SendRequest(uint32 uiStepSeq, Targs&&... ar LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); if (iHadWrittenLen >= 0) { - m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); + if (m_bIsClient) + { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen, IO_STAT_UPSTREAM_SEND_BYTE); + } + else + { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen, IO_STAT_DOWNSTREAM_SEND_BYTE); + } if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) { @@ -671,7 +685,14 @@ E_CODEC_STATUS SocketChannelImpl::SendResponse(Targs&&... args) LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); if (iHadWrittenLen >= 0) { - m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen); + if (m_bIsClient) + { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen, IO_STAT_UPSTREAM_SEND_BYTE); + } + else + { + m_pLabor->IoStatAddSendBytes(m_iFd, iHadWrittenLen, IO_STAT_DOWNSTREAM_SEND_BYTE); + } if (m_pSendBuff->Capacity() > CBuffer::BUFFER_MAX_READ && (m_pSendBuff->ReadableBytes() < m_pSendBuff->Capacity() / 2)) { @@ -736,7 +757,14 @@ E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) } } while (iReadLen > 0); - m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen); + if (m_bIsClient) + { + m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen, IO_STAT_UPSTREAM_RECV_BYTE); + } + else + { + m_pLabor->IoStatAddRecvBytes(m_iFd, iHadReadLen, IO_STAT_DOWNSTREAM_RECV_BYTE); + } if (iReadLen == 0) { @@ -796,7 +824,10 @@ E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) } else { - m_pRecvBuff->SetReadIndex(uiReadIndex); + if (!m_pCodec->DecodeWithStack()) + { + m_pRecvBuff->SetReadIndex(uiReadIndex); + } if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) { if (!IsClient()) @@ -850,7 +881,10 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(Targs&&... args) } else { - m_pRecvBuff->SetReadIndex(uiReadIndex); + if (!m_pCodec->DecodeWithStack()) + { + m_pRecvBuff->SetReadIndex(uiReadIndex); + } if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) { return(CODEC_STATUS_INVALID); diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 050ae2ec..5a06c082 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -99,6 +99,11 @@ class Codec return(false); } + virtual bool DecodeWithStack() const + { + return(false); + } + /** * @see CodecHttp2::ConnectionSetting() */ diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index d738ed56..25d7245f 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -73,41 +73,23 @@ E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply) { if (pBuff->ReadableBytes() < 1) { + ++m_uiIncompleteDecodeNum; return(CODEC_STATUS_PAUSE); } - char cFirstByte = 0; - size_t uiReadIndex = pBuff->GetReadIndex(); - pBuff->ReadByte(cFirstByte); - E_CODEC_STATUS eStatus = CODEC_STATUS_OK; - switch (cFirstByte) + if (m_bDecodeWithStack) { - case RESP_SIMPLE_STRING: - eStatus = DecodeSimpleString(pBuff, oReply); - break; - case RESP_INTEGER: - eStatus = DecodeInteger(pBuff, oReply); - break; - case RESP_BULK_STRING: - eStatus = DecodeBulkString(pBuff, oReply); - break; - case RESP_ARRAY: - eStatus = DecodeArray(pBuff, oReply); - break; - case RESP_ERROR: - eStatus = DecodeError(pBuff, oReply); - break; - default: - oReply.set_type(REDIS_REPLY_ERROR); - oReply.set_integer(REDIS_ERR_PROTOCOL); - pBuff->SetReadIndex(uiReadIndex); - LOG4_TRACE("cFirstByte = %d", int(cFirstByte)); - return(CODEC_STATUS_ERR); + return(DecodeWithStack(pBuff, oReply)); } - if (CODEC_STATUS_OK != eStatus) + else { - pBuff->SetReadIndex(uiReadIndex); + if (m_uiIncompleteDecodeNum > 100 + && (m_uiDecodedNum / m_uiIncompleteDecodeNum < 5)) // Over 20% of decoding is incomple + { + m_bDecodeWithStack = true; + return(Decode(pBuff, oReply)); + } } - return(eStatus); + return(DecodeRecursive(pBuff, oReply)); } E_CODEC_STATUS CodecResp::EncodeSimpleString(const RedisReply& oReply, CBuffer* pBuff) @@ -211,6 +193,43 @@ E_CODEC_STATUS CodecResp::EncodeNull(const RedisReply& oReply, CBuffer* pBuff) return(CODEC_STATUS_OK); } +E_CODEC_STATUS CodecResp::DecodeRecursive(CBuffer* pBuff, RedisReply& oReply) +{ + char cFirstByte = 0; + size_t uiReadIndex = pBuff->GetReadIndex(); + pBuff->ReadByte(cFirstByte); + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + switch (cFirstByte) + { + case RESP_SIMPLE_STRING: + eStatus = DecodeSimpleString(pBuff, oReply); + break; + case RESP_INTEGER: + eStatus = DecodeInteger(pBuff, oReply); + break; + case RESP_BULK_STRING: + eStatus = DecodeBulkString(pBuff, oReply); + break; + case RESP_ARRAY: + eStatus = DecodeArray(pBuff, oReply); + break; + case RESP_ERROR: + eStatus = DecodeError(pBuff, oReply); + break; + default: + oReply.set_type(REDIS_REPLY_ERROR); + oReply.set_integer(REDIS_ERR_PROTOCOL); + pBuff->SetReadIndex(uiReadIndex); + LOG4_TRACE("cFirstByte = %d", int(cFirstByte)); + return(CODEC_STATUS_ERR); + } + if (CODEC_STATUS_OK != eStatus) + { + pBuff->SetReadIndex(uiReadIndex); + } + return(eStatus); +} + E_CODEC_STATUS CodecResp::DecodeSimpleString(CBuffer* pBuff, RedisReply& oReply) { size_t uiReadableBytes = pBuff->ReadableBytes(); @@ -234,7 +253,7 @@ E_CODEC_STATUS CodecResp::DecodeSimpleString(CBuffer* pBuff, RedisReply& oReply) else { LOG4_ERROR("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", - uiReadableBytes, i, int(cLastChar), (int)pData[i]); + uiReadableBytes, i, (int)cLastChar, (int)pData[i]); return(CODEC_STATUS_ERR); } break; @@ -271,7 +290,7 @@ E_CODEC_STATUS CodecResp::DecodeError(CBuffer* pBuff, RedisReply& oReply) else { LOG4_ERROR("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", - uiReadableBytes, i, int(cLastChar), (int)pData[i]); + uiReadableBytes, i, (int)cLastChar, (int)pData[i]); return(CODEC_STATUS_ERR); } break; @@ -307,7 +326,7 @@ E_CODEC_STATUS CodecResp::DecodeInteger(CBuffer* pBuff, RedisReply& oReply) else { LOG4_ERROR("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", - uiReadableBytes, i, int(cLastChar), (int)pData[i]); + uiReadableBytes, i, (int)cLastChar, (int)pData[i]); return(CODEC_STATUS_ERR); } break; @@ -356,7 +375,7 @@ E_CODEC_STATUS CodecResp::DecodeBulkString(CBuffer* pBuff, RedisReply& oReply) return(CODEC_STATUS_OK); } i += (uint32)iStringLen; - if (uiReadableBytes < (i + 1 + 2)) // not enough data (pos i + 1 is the end of bulk string) + if (uiReadableBytes < (i + 1 + 2)) // not enough data (pos i+1 is the end of bulk string) { return(CODEC_STATUS_PAUSE); } @@ -365,7 +384,7 @@ E_CODEC_STATUS CodecResp::DecodeBulkString(CBuffer* pBuff, RedisReply& oReply) else { LOG4_ERROR("uiReadableBytes = %u, i = %d, iStringLen = %d, cLastChar = %d, pData[i] = %d", - uiReadableBytes, i, iStringLen, int(cLastChar), (int)pData[i]); + uiReadableBytes, i, iStringLen, (int)cLastChar, (int)pData[i]); return(CODEC_STATUS_ERR); } break; @@ -438,7 +457,7 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) oReply.set_type(REDIS_REPLY_ERROR); oReply.set_integer(REDIS_ERR_PROTOCOL); LOG4_ERROR("uiReadableBytes = %u, i = %d, cFirstByte = %d, cLastChar = %d, pData[i] = %d", - uiReadableBytes, i, (int)cFirstByte, (int)cLastChar, (int)pData[i]); + uiReadableBytes, i, int(cFirstByte), (int)cLastChar, (int)pData[i]); return(CODEC_STATUS_ERR); } if (CODEC_STATUS_OK != eStatus) @@ -451,9 +470,333 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) } } else + { + LOG4_ERROR("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", + uiReadableBytes, i, (int)cLastChar, (int)pData[i]); + return(CODEC_STATUS_ERR); + } + break; + case '\r': + cLastChar = '\r'; + break; + default: + ; + } + } + return(CODEC_STATUS_PAUSE); +} + +E_CODEC_STATUS CodecResp::DecodeWithStack(CBuffer* pBuff, RedisReply& oReply) +{ + if (m_iCurrentReadTaskIndex == -1) + { + m_oReadStack[0].Reset(); + m_iCurrentReadTaskIndex = 0; + } + + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + /* Process items in reply. */ + while (m_iCurrentReadTaskIndex >= 0) + { + eStatus = DecodeWithStack(pBuff); + if (eStatus != CODEC_STATUS_OK) + { + if (eStatus == CODEC_STATUS_ERR) + { + oReply.set_type(REDIS_REPLY_ERROR); + oReply.set_integer(m_iErrno); + oReply.set_str(m_szErrMsg); + } + return(eStatus); + } + } + + /* Emit a reply when there is one. */ + if (m_iCurrentReadTaskIndex == -1) + { + ++m_uiDecodedNum; + oReply = std::move(m_oReply); + } + else + { + ++m_uiIncompleteDecodeNum; + } + return(eStatus); +} + +E_CODEC_STATUS CodecResp::DecodeWithStack(CBuffer* pBuff) +{ + CodecRespReadTask* pCurrent = &(m_oReadStack[m_iCurrentReadTaskIndex]); + if (pBuff->ReadableBytes() < 1) + { + return(CODEC_STATUS_PAUSE); + } + + /* check if we need to read type */ + if (pCurrent->iType < 0) + { + char cFirstByte = 0; + pBuff->ReadByte(cFirstByte); + switch (cFirstByte) + { + case RESP_SIMPLE_STRING: + pCurrent->iType = REDIS_REPLY_STATUS; + break; + case RESP_INTEGER: + pCurrent->iType = REDIS_REPLY_INTEGER; + break; + case RESP_BULK_STRING: + pCurrent->iType = REDIS_REPLY_STRING; + break; + case RESP_ARRAY: + pCurrent->iType = REDIS_REPLY_ARRAY; + break; + case RESP_ERROR: + pCurrent->iType = REDIS_REPLY_ERROR; + break; + default: + m_iErrno = REDIS_ERR_PROTOCOL; + snprintf(m_szErrMsg, sizeof(m_szErrMsg), + "Protocol error, got %c as reply type byte", cFirstByte); + m_oReply.Clear(); // TODO 确认一下是否会有内存泄露 + LOG4_ERROR("Protocol error, got %c as reply type byte", cFirstByte); + return(CODEC_STATUS_ERR); + } + } + + + /* process typed item */ + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; + switch(pCurrent->iType) + { + case REDIS_REPLY_STATUS: + case REDIS_REPLY_INTEGER: + case REDIS_REPLY_ERROR: + eStatus = DecodeLineItem(pBuff); + break; + case REDIS_REPLY_STRING: + eStatus = DecodeBulkItem(pBuff); + break; + case REDIS_REPLY_ARRAY: + eStatus = DecodeArray(pBuff); + break; + default: + m_iErrno = REDIS_ERR_PROTOCOL; + snprintf(m_szErrMsg, sizeof(m_szErrMsg), + "Protocol error, reply type %d", pCurrent->iType); + LOG4_ERROR("Protocol error, reply type %d", pCurrent->iType); + return(CODEC_STATUS_ERR); + } + return(eStatus); +} + +E_CODEC_STATUS CodecResp::DecodeLineItem(CBuffer* pBuff) +{ + CodecRespReadTask* pCurrent = &(m_oReadStack[m_iCurrentReadTaskIndex]); + size_t uiReadableBytes = pBuff->ReadableBytes(); + char cLastChar = 0; + uint32 uiStringLen = 0; + const char* pData = pBuff->GetRawReadBuffer(); + const char* pPartBegin = pData; + for (size_t i = 0; i < uiReadableBytes; ++i) + { + switch (pData[i]) + { + case '\n': + if ('\r' == cLastChar) + { + cLastChar = '\n'; + pCurrent->pReply = NewReply(pCurrent->pParent); + if (pCurrent->pReply == nullptr) + { + return(CODEC_STATUS_ERR); + } + + if (REDIS_REPLY_INTEGER == pCurrent->iType) + { + pCurrent->pReply->set_type(REDIS_REPLY_INTEGER); + pCurrent->pReply->set_integer(StringConverter::RapidAtoi(pPartBegin)); + } + else + { + // Type will be error or status. + pCurrent->pReply->set_type(pCurrent->iType); + pCurrent->pReply->set_str(pPartBegin, uiStringLen); + } + pBuff->AdvanceReadIndex(i + 1); + + MoveToNextTask(); + return(CODEC_STATUS_OK); + } + else + { + LOG4_ERROR("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", + uiReadableBytes, i, int(cLastChar), (int)pData[i]); + m_iErrno = REDIS_ERR_PROTOCOL; + snprintf(m_szErrMsg, sizeof(m_szErrMsg), + "Protocol error, reply type %d, last char ascii %d", + pCurrent->iType, int(cLastChar)); + m_oReply.Clear(); + return(CODEC_STATUS_ERR); + } + break; + case '\r': + cLastChar = '\r'; + break; + default: + ++uiStringLen; + } + } + return(CODEC_STATUS_PAUSE); +} + +E_CODEC_STATUS CodecResp::DecodeBulkItem(CBuffer* pBuff) +{ + CodecRespReadTask* pCurrent = &(m_oReadStack[m_iCurrentReadTaskIndex]); + size_t uiReadableBytes = pBuff->ReadableBytes(); + char cLastChar = 0; + int32 iStringLen = 0; + bool bGotStringLen = false; + const char* pData = pBuff->GetRawReadBuffer(); + const char* pPartBegin = pData; + for (size_t i = 0; i < uiReadableBytes; ++i) + { + switch (pData[i]) + { + case '\n': + if ('\r' == cLastChar) + { + cLastChar = '\n'; + if (bGotStringLen) + { + pCurrent->pReply = NewReply(pCurrent->pParent); + if (pCurrent->pReply == nullptr) + { + return(CODEC_STATUS_ERR); + } + pCurrent->pReply->set_type(pCurrent->iType); + pCurrent->pReply->set_str(pPartBegin, iStringLen); + pBuff->AdvanceReadIndex(i + 1); + MoveToNextTask(); + return(CODEC_STATUS_OK); + } + else + { + iStringLen = StringConverter::RapidAtoi(pPartBegin); + pPartBegin = pData + i + 1; + bGotStringLen = true; + if (iStringLen == -1) + { + pCurrent->pReply = NewReply(pCurrent->pParent); + if (pCurrent->pReply == nullptr) + { + return(CODEC_STATUS_ERR); + } + pCurrent->pReply->set_type(REDIS_REPLY_NIL); + pBuff->AdvanceReadIndex(i + 1); + MoveToNextTask(); + return(CODEC_STATUS_OK); + } + i += (uint32)iStringLen; + if (uiReadableBytes < (i + 1 + 2)) // not enough data (pos i + 1 is the end of bulk string) + { + return(CODEC_STATUS_PAUSE); + } + } + } + else + { + LOG4_ERROR("uiReadableBytes = %u, i = %d, iStringLen = %d, cLastChar = %d, pData[i] = %d", + uiReadableBytes, i, iStringLen, int(cLastChar), (int)pData[i]); + m_iErrno = REDIS_ERR_PROTOCOL; + snprintf(m_szErrMsg, sizeof(m_szErrMsg), + "Protocol error, reply type %d, last char ascii %d", + pCurrent->iType, int(cLastChar)); + m_oReply.Clear(); + return(CODEC_STATUS_ERR); + } + break; + case '\r': + cLastChar = '\r'; + break; + default: + ; + } + } + return(CODEC_STATUS_PAUSE); +} + +E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff) +{ + CodecRespReadTask* pCurrent = &(m_oReadStack[m_iCurrentReadTaskIndex]); + + /* Set error for nested multi bulks with depth > 7 */ + if (m_iCurrentReadTaskIndex == 8) + { + m_iErrno = REDIS_ERR_PROTOCOL; + snprintf(m_szErrMsg, sizeof(m_szErrMsg), + "No support for nested multi bulk replies with depth > 7"); + m_oReply.Clear(); + LOG4_ERROR("No support for nested multi bulk replies with depth > 7"); + return(CODEC_STATUS_ERR); + } + + size_t uiReadableBytes = pBuff->ReadableBytes(); + char cLastChar = 0; + const char* pData = pBuff->GetRawReadBuffer(); + const char* pPartBegin = pData; + for (size_t i = 0; i < uiReadableBytes; ++i) + { + switch (pData[i]) + { + case '\n': + if ('\r' == cLastChar) + { + cLastChar = '\n'; + pCurrent->iElements = StringConverter::RapidAtoi(pPartBegin); + pBuff->AdvanceReadIndex(i + 1); + pCurrent->pReply = NewReply(pCurrent->pParent); + if (pCurrent->pReply == nullptr) + { + return(CODEC_STATUS_ERR); + } + + if (pCurrent->iElements == -1) + { + pCurrent->pReply->set_type(REDIS_REPLY_NIL); + MoveToNextTask(); + return(CODEC_STATUS_OK); + } + else + { + pCurrent->pReply->set_type(pCurrent->iType); + + /* Modify task stack when there are more than 0 elements. */ + if (pCurrent->iElements > 0) + { + m_iCurrentReadTaskIndex++; + m_oReadStack[m_iCurrentReadTaskIndex].iType = -1; + m_oReadStack[m_iCurrentReadTaskIndex].iElements = -1; + m_oReadStack[m_iCurrentReadTaskIndex].iIndex = 0; + m_oReadStack[m_iCurrentReadTaskIndex].pReply = nullptr; + m_oReadStack[m_iCurrentReadTaskIndex].pParent = pCurrent; + } + else + { + MoveToNextTask(); + } + return(CODEC_STATUS_OK); + } + } + else { LOG4_ERROR("uiReadableBytes = %u, i = %d, cLastChar = %d, pData[i] = %d", uiReadableBytes, i, (int)cLastChar, (int)pData[i]); + m_iErrno = REDIS_ERR_PROTOCOL; + snprintf(m_szErrMsg, sizeof(m_szErrMsg), + "Protocol error, reply type %d, last char ascii %d", + pCurrent->iType, int(cLastChar)); + m_oReply.Clear(); return(CODEC_STATUS_ERR); } break; @@ -467,6 +810,75 @@ E_CODEC_STATUS CodecResp::DecodeArray(CBuffer* pBuff, RedisReply& oReply) return(CODEC_STATUS_PAUSE); } +void CodecResp::MoveToNextTask() +{ + CodecRespReadTask* pCurrent; + CodecRespReadTask* pPrevious; + while (m_iCurrentReadTaskIndex >= 0) + { + /* Return a.s.a.p. when the stack is now empty. */ + if (m_iCurrentReadTaskIndex == 0) + { + --m_iCurrentReadTaskIndex; + return; + } + + pCurrent = &(m_oReadStack[m_iCurrentReadTaskIndex]); + pPrevious = &(m_oReadStack[m_iCurrentReadTaskIndex - 1]); + if (pCurrent->iIndex == pPrevious->iElements - 1) + { + --m_iCurrentReadTaskIndex; + } + else + { + /* Reset the type because the next item can be anything */ + pCurrent->iType = -1; + pCurrent->iElements = -1; + pCurrent->iIndex++; + return; + } + } +} + +RedisReply* CodecResp::NewReply(CodecRespReadTask *pParent) +{ + RedisReply* pReply = nullptr; + if (pParent) + { + auto pParentReply = pParent->pReply; + if (pParentReply == nullptr) + { + LOG4_ERROR("parent reply is nullptr."); + m_iErrno = REDIS_ERR; + snprintf(m_szErrMsg, sizeof(m_szErrMsg), "parent reply is nullptr."); + m_oReply.Clear(); + return(pReply); + } + pReply = pParentReply->add_element(); + if (pReply == nullptr) + { + LOG4_ERROR("Out of memory"); + m_iErrno = REDIS_ERR; + snprintf(m_szErrMsg, sizeof(m_szErrMsg), "Out of memory"); + m_oReply.Clear(); + } + return(pReply); + } + else + { + // Set reply if this is the root object. + if (m_iCurrentReadTaskIndex == 0) + { + return(&m_oReply); + } + LOG4_ERROR("reply is not root and parent reply is nullptr."); + m_iErrno = REDIS_ERR; + snprintf(m_szErrMsg, sizeof(m_szErrMsg), "reply is not root and parent reply is nullptr."); + m_oReply.Clear(); + return(pReply); + } +} + E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply, CBuffer* pReactBuff) { LOG4_ERROR("invalid"); diff --git a/src/codec/CodecResp.hpp b/src/codec/CodecResp.hpp index cb92839f..48d26fc1 100644 --- a/src/codec/CodecResp.hpp +++ b/src/codec/CodecResp.hpp @@ -10,6 +10,7 @@ #ifndef SRC_CODEC_CODECRESP_HPP_ #define SRC_CODEC_CODECRESP_HPP_ +#include #include "Codec.hpp" #include "pb/redis.pb.h" @@ -18,6 +19,29 @@ namespace neb class RedisStep; +struct CodecRespReadTask +{ + int iType; + int iElements; ///< number of elements in multibulk container + int iIndex; ///< index in parent (array) object + RedisReply* pReply; ///< holds user-generated value for a read task + CodecRespReadTask *pParent; ///< parent task + + CodecRespReadTask() + : iType(-1), iElements(-1), iIndex(-1), pReply(nullptr), pParent(nullptr) + { + } + + void Reset() + { + iType = -1; + iElements = -1; + iIndex = -1; + pReply = nullptr; + pParent = nullptr; + } +}; + class CodecResp: public neb::Codec { public: @@ -30,6 +54,11 @@ class CodecResp: public neb::Codec return(CODEC_RESP); } + virtual bool DecodeWithStack() const + { + return(m_bDecodeWithStack); + } + E_CODEC_STATUS Encode(CBuffer* pBuff); E_CODEC_STATUS Encode(const RedisReply& oReply, CBuffer* pBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, RedisReply& oReply); @@ -42,13 +71,32 @@ class CodecResp: public neb::Codec E_CODEC_STATUS EncodeBulkString(const RedisReply& oReply, CBuffer* pBuff); E_CODEC_STATUS EncodeArray(const RedisReply& oReply, CBuffer* pBuff); E_CODEC_STATUS EncodeNull(const RedisReply& oReply, CBuffer* pBuff); + + E_CODEC_STATUS DecodeRecursive(CBuffer* pBuff, RedisReply& oReply); E_CODEC_STATUS DecodeSimpleString(CBuffer* pBuff, RedisReply& oReply); E_CODEC_STATUS DecodeError(CBuffer* pBuff, RedisReply& oReply); E_CODEC_STATUS DecodeInteger(CBuffer* pBuff, RedisReply& oReply); E_CODEC_STATUS DecodeBulkString(CBuffer* pBuff, RedisReply& oReply); E_CODEC_STATUS DecodeArray(CBuffer* pBuff, RedisReply& oReply); + E_CODEC_STATUS DecodeWithStack(CBuffer* pBuff, RedisReply& oReply); + E_CODEC_STATUS DecodeWithStack(CBuffer* pBuff); + E_CODEC_STATUS DecodeLineItem(CBuffer* pBuff); + E_CODEC_STATUS DecodeBulkItem(CBuffer* pBuff); + E_CODEC_STATUS DecodeArray(CBuffer* pBuff); + void MoveToNextTask(); + RedisReply* NewReply(CodecRespReadTask *pParent); + private: + bool m_bDecodeWithStack = false; + unsigned int m_uiDecodedNum = 0; + unsigned int m_uiIncompleteDecodeNum = 0; + int m_iErrno = 0; /* Error flags, 0 when there is no error */ + char m_szErrMsg[128] = {0}; /* String representation of error when applicable */ + int m_iCurrentReadTaskIndex = -1; + CodecRespReadTask m_oReadStack[9]; + RedisReply m_oReply; ///< Temporary reply pointer + static const char RESP_SIMPLE_STRING; static const char RESP_ERROR; static const char RESP_INTEGER; diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 926acea2..b6e79819 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -211,7 +211,8 @@ bool Dispatcher::FdTransfer(int iFd) int iAcceptFd = -1; int iAiFamily = AF_INET; int iCodec = 0; - int iErrno = SocketChannel::RecvChannelFd(iFd, iAcceptFd, iAiFamily, iCodec, m_pLogger); + std::string strRemoteAddr; + int iErrno = SocketChannel::RecvChannelFd(iFd, iAcceptFd, iAiFamily, iCodec, strRemoteAddr, m_pLogger); if (iErrno != ERR_OK) { if (iErrno == ERR_CHANNEL_EOF) @@ -253,7 +254,7 @@ bool Dispatcher::FdTransfer(int iFd) } x_sock_set_block(iAcceptFd, 0); std::shared_ptr pChannel = nullptr; - LOG4_TRACE("fd[%d] transfer successfully.", iAcceptFd); + LOG4_TRACE("%s fd[%d] transfer successfully.", strRemoteAddr.c_str(), iAcceptFd); if ((CODEC_NEBULA != iCodec) && (CODEC_NEBULA_IN_NODE != iCodec) && m_pLabor->WithSsl()) { pChannel = CreateSocketChannel(iAcceptFd, E_CODEC_TYPE(iCodec), false, true); @@ -264,42 +265,7 @@ bool Dispatcher::FdTransfer(int iFd) } if (nullptr != pChannel) { - if (AF_INET == iAiFamily) - { - char szClientAddr[64] = {0}; - int z; /* status return code */ - struct sockaddr_in stClientAddr; - socklen_t iClientAddrSize = sizeof(stClientAddr); - z = getpeername(iAcceptFd, (struct sockaddr *)&stClientAddr, &iClientAddrSize); - if (z == 0) - { - inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientAddr, sizeof(szClientAddr)); - LOG4_TRACE("set fd %d's remote addr \"%s\"", iAcceptFd, szClientAddr); - pChannel->SetRemoteAddr(std::string(szClientAddr)); - } - else - { - LOG4_ERROR("getpeername error %d", errno); - } - } - else if (AF_INET6 == iAiFamily) // AF_INET6 - { - char szClientAddr[64] = {0}; - int z; /* status return code */ - struct sockaddr_in6 stClientAddr; - socklen_t iClientAddrSize = sizeof(stClientAddr); - z = getpeername(iAcceptFd, (struct sockaddr *)&stClientAddr, &iClientAddrSize); - if (z == 0) - { - inet_ntop(AF_INET6, &stClientAddr.sin6_addr, szClientAddr, sizeof(szClientAddr)); - LOG4_TRACE("set fd %d's remote addr \"%s\"", iAcceptFd, szClientAddr); - pChannel->SetRemoteAddr(std::string(szClientAddr)); - } - else - { - LOG4_ERROR("getpeername error %d", errno); - } - } + pChannel->SetRemoteAddr(strRemoteAddr); AddIoReadEvent(pChannel); if (CODEC_NEBULA == iCodec) { @@ -325,6 +291,7 @@ bool Dispatcher::FdTransfer(int iFd) pChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); AddIoTimeout(pChannel, dIoTimeout); } + m_pLabor->IoStatAddConnection(IO_STAT_DOWNSTREAM_NEW_CONNECTION); return(true); } else // 没有足够资源分配给新连接,直接close掉 @@ -609,6 +576,7 @@ std::shared_ptr Dispatcher::StressSend(const std::string& strIden AddIoWriteEvent(pChannel); pChannel->SetRemoteAddr(strHost); IO::SendRequest(this, 0, pChannel, iCmd, uiSeq, oMsgBody); + m_pLabor->IoStatAddConnection(IO_STAT_DOWNSTREAM_NEW_CONNECTION); return(pChannel); } else @@ -875,14 +843,13 @@ bool Dispatcher::DelEvent(ev_timer* timer_watcher) return(true); } -int Dispatcher::SendFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType) +int Dispatcher::SendFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, const std::string& strRemoteAddr) { - return(SocketChannel::SendChannelFd(iSocketFd, iSendFd, iAiFamily, iCodecType, m_pLogger)); + return(SocketChannel::SendChannelFd(iSocketFd, iSendFd, iAiFamily, iCodecType, strRemoteAddr, m_pLogger)); } -bool Dispatcher::CreateListenFd(const std::string& strHost, int32 iPort, int& iFd, int& iFamily) +bool Dispatcher::CreateListenFd(const std::string& strHost, int32 iPort, int iBacklog, int& iFd, int& iFamily) { - int queueLen = 100; int reuse = 1; int timeout = 1; @@ -933,7 +900,7 @@ bool Dispatcher::CreateListenFd(const std::string& strHost, int32 iPort, int& iF iFd = -1; continue; } - if (-1 == listen(iFd, queueLen)) + if (-1 == listen(iFd, iBacklog)) { close(iFd); iFd = -1; @@ -1067,6 +1034,14 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b pChannel->GetRemoteAddr().c_str(), pChannel->GetFd(), pChannel->GetSequence(), pChannel->GetIdentify().c_str()); + if (pChannel->IsClient()) + { + m_pLabor->IoStatAddConnection(IO_STAT_UPSTREAM_DESTROY_CONNECTION); + } + else + { + m_pLabor->IoStatAddConnection(IO_STAT_DOWNSTREAM_DESTROY_CONNECTION); + } if (bChannelNotice) { m_pLabor->GetActorBuilder()->ChannelNotice(pChannel, pChannel->GetIdentify(), pChannel->GetClientData()); @@ -1208,6 +1183,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) { char szClientAddr[64] = {0}; int iAcceptFd = -1; + int iClientPort = 0; if (AF_INET == iFamily) { struct sockaddr_in stClientAddr; @@ -1245,6 +1221,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) } x_sock_set_block(iAcceptFd, 0); inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientAddr, sizeof(szClientAddr)); + iClientPort = CodecUtil::N2H(stClientAddr.sin_port); LOG4_TRACE("accept connect from \"%s\"", szClientAddr); } else // AF_INET6 @@ -1284,6 +1261,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) } x_sock_set_block(iAcceptFd, 0); inet_ntop(AF_INET6, &stClientAddr.sin6_addr, szClientAddr, sizeof(szClientAddr)); + iClientPort = CodecUtil::N2H(stClientAddr.sin6_port); LOG4_TRACE("accept connect from \"%s\"", szClientAddr); } @@ -1306,15 +1284,15 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) } int iWorkerDataFd = -1; - //std::pair worker_pid_fd = ((Manager*)m_pLabor)->GetSessionManager()->GetMinLoadWorkerDataFd(); - //iWorkerDataFd = worker_pid_fd.second; - iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(); + iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetMinLoadWorkerDataFd(); + //iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(); + //iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetWorkerDataFd(szClientAddr, iClientPort); if (iWorkerDataFd > 0) { LOG4_TRACE("send new fd %d to worker communication fd %d", iAcceptFd, iWorkerDataFd); int iCodec = m_pLabor->GetNodeInfo().eCodec; - int iErrno = SocketChannel::SendChannelFd(iWorkerDataFd, iAcceptFd, iFamily, iCodec, m_pLogger); + int iErrno = SocketChannel::SendChannelFd(iWorkerDataFd, iAcceptFd, iFamily, iCodec, szClientAddr, m_pLogger); if (iErrno != ERR_OK) { LOG4_ERROR("error %d: %s", iErrno, strerror_r(iErrno, m_pErrBuff, gc_iErrBuffLen)); @@ -1322,7 +1300,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) close(iAcceptFd); return(true); } - LOG4_WARNING("GetMinLoadWorkerDataFd() found worker_pid_fd.second = %d", iWorkerDataFd); + LOG4_ERROR("no available data fd %d", iWorkerDataFd); close(iAcceptFd); return(false); } @@ -1373,6 +1351,7 @@ bool Dispatcher::AcceptServerConn(int iFd) ? m_pLabor->GetNodeInfo().dConnectionProtection : m_pLabor->GetNodeInfo().dIoTimeout; AddIoTimeout(pChannel, dIoTimeout); AddIoReadEvent(pChannel); + m_pLabor->IoStatAddConnection(IO_STAT_DOWNSTREAM_NEW_CONNECTION); } } return(false); diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 858a8b74..66cf1e67 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -155,9 +155,9 @@ class Dispatcher } std::shared_ptr CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient = false, bool bWithSsl = false); bool DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice = true); - bool CreateListenFd(const std::string& strHost, int32 iPort, int& iFd, int& iFamily); + bool CreateListenFd(const std::string& strHost, int32 iPort, int iBacklog, int& iFd, int& iFamily); std::shared_ptr GetChannel(int iFd); - int SendFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType); + int SendFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, const std::string& strRemoteAddr); protected: void Destroy(); diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index bfaf7371..de2b51e3 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -21,6 +21,7 @@ #include "actor/ActorBuilder.hpp" #include "actor/chain/Chain.hpp" #include "codec/CodecFactory.hpp" +#include "actor/session/sys_session/TimerAddrinfo.hpp" namespace neb { @@ -137,6 +138,7 @@ class IO template static bool AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, const std::string& strHost, int iPort, int iRemoteWorkerIndex, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); + static in_addr_t inet_addr(const char* text, uint32 len); }; template @@ -227,7 +229,14 @@ bool IO::SendResponse(Dispatcher* pDispatcher, std::shared_ptr eStatus = std::static_pointer_cast>( pChannel->m_pImpl)->SendResponse(std::forward(args)...); } - pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd()); + if (pChannel->IsClient()) + { + pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd(), IO_STAT_UPSTREAM_SEND_NUM); + } + else + { + pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd(), IO_STAT_DOWNSTREAM_SEND_NUM); + } pDispatcher->m_pLastActivityChannel = pChannel; switch (eStatus) { @@ -318,7 +327,14 @@ bool IO::SendRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_p eStatus = std::static_pointer_cast>( pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); } - pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd()); + if (pChannel->IsClient()) + { + pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd(), IO_STAT_UPSTREAM_SEND_NUM); + } + else + { + pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd(), IO_STAT_DOWNSTREAM_SEND_NUM); + } pDispatcher->m_pLastActivityChannel = pChannel; switch (eStatus) { @@ -763,20 +779,42 @@ template bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, const std::string& strHost, int iPort, int iRemoteWorkerIndex, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "%s", strIdentify.c_str()); + LOG4_TRACE_DISPATCH("%s", strIdentify.c_str()); struct addrinfo stAddrHints; - struct addrinfo* pAddrResult; - struct addrinfo* pAddrCurrent; - memset(&stAddrHints, 0, sizeof(struct addrinfo)); - stAddrHints.ai_family = AF_UNSPEC; - stAddrHints.ai_socktype = iSocketType; - stAddrHints.ai_protocol = IPPROTO_IP; - int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); - if (0 != iCode) - { - LOG4_TRACE_DISPATCH("getaddrinfo(\"%s\", \"%d\") error %d: %s", - strHost.c_str(), iPort, iCode, gai_strerror(iCode)); - return(false); + struct addrinfo* pAddrResult = nullptr; + struct addrinfo* pAddrCurrent = nullptr; + auto pSessionAddr = std::static_pointer_cast(pDispatcher->m_pLabor->GetActorBuilder()->GetSession(strIdentify)); + if (pSessionAddr != nullptr) + { + pAddrResult = pSessionAddr->GetAddrinfo(); + } + if (pAddrResult == nullptr) + { + memset(&stAddrHints, 0, sizeof(struct addrinfo)); + stAddrHints.ai_family = AF_UNSPEC; + stAddrHints.ai_socktype = iSocketType; + stAddrHints.ai_protocol = IPPROTO_IP; + if (inet_addr(strHost.data(), strHost.length()) != 0) + { + stAddrHints.ai_flags |= (AI_NUMERICHOST | AI_NUMERICSERV); + } + LOG4_TRACE_DISPATCH("get addrinfo for %s", strIdentify.c_str()); + int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); + if (0 != iCode) + { + LOG4_TRACE_DISPATCH("getaddrinfo(\"%s\", \"%d\") error %d: %s", + strHost.c_str(), iPort, iCode, gai_strerror(iCode)); + return(false); + } + if (pSessionAddr == nullptr) + { + pSessionAddr = std::static_pointer_cast( + pDispatcher->m_pLabor->GetActorBuilder()->MakeSharedSession(nullptr, "neb::TimerAddrinfo", strIdentify)); + } + if (pSessionAddr != nullptr) + { + pSessionAddr->MigrateAddrinfo(pAddrResult); + } } int iFd = -1; for (pAddrCurrent = pAddrResult; @@ -792,14 +830,6 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::strin break; } - /* No address succeeded */ - if (pAddrCurrent == NULL) - { - LOG4_TRACE_DISPATCH("Could not connect to \"%s:%d\"", strHost.c_str(), iPort); - freeaddrinfo(pAddrResult); /* No longer needed */ - return(false); - } - x_sock_set_block(iFd, 0); int nREUSEADDR = 1; int iKeepAlive = 1; @@ -819,7 +849,7 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::strin if (nullptr != pChannel) { connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); - freeaddrinfo(pAddrResult); /* No longer needed */ + pDispatcher->m_pLabor->IoStatAddConnection(IO_STAT_UPSTREAM_NEW_CONNECTION); ev_tstamp dIoTimeout = (pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection > 0) ? pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection : pDispatcher->m_pLabor->GetNodeInfo().dIoTimeout; pDispatcher->AddIoTimeout(pChannel, dIoTimeout); @@ -844,7 +874,14 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::strin eCodecStatus = std::static_pointer_cast>( pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); } - pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd()); + if (pChannel->IsClient()) + { + pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd(), IO_STAT_UPSTREAM_SEND_NUM); + } + else + { + pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd(), IO_STAT_DOWNSTREAM_SEND_NUM); + } pDispatcher->m_pLastActivityChannel = pChannel; if (CODEC_STATUS_OK != eCodecStatus && CODEC_STATUS_PAUSE != eCodecStatus @@ -870,6 +907,49 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::strin } } +template +in_addr_t IO::inet_addr(const char* text, uint32 len) +{ + const char *p; + unsigned char c; + in_addr_t addr; + unsigned int octet, n; + + addr = 0; + octet = 0; + n = 0; + + for (p = text; p < text + len; p++) { + c = *p; + + if (c >= '0' && c <= '9') { + octet = octet * 10 + (c - '0'); + + if (octet > 255) { + return 0; + } + + continue; + } + + if (c == '.') { + addr = (addr << 8) + octet; + octet = 0; + n++; + continue; + } + + return 0; + } + + if (n == 3) { + addr = (addr << 8) + octet; + return htonl(addr); + } + + return 0; +} + template template E_CODEC_STATUS IO::Recv(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args) @@ -904,7 +984,14 @@ E_CODEC_STATUS IO::Recv(Dispatcher* pDispatcher, std::shared_ptrm_pLabor->IoStatAddRecvNum(pChannel->GetFd()); + if (pChannel->IsClient()) + { + pDispatcher->m_pLabor->IoStatAddRecvNum(pChannel->GetFd(), IO_STAT_UPSTREAM_RECV_NUM); + } + else + { + pDispatcher->m_pLabor->IoStatAddRecvNum(pChannel->GetFd(), IO_STAT_DOWNSTREAM_RECV_NUM); + } pDispatcher->m_pLastActivityChannel = pChannel; } return(eStatus); @@ -930,7 +1017,14 @@ E_CODEC_STATUS IO::Fetch(Dispatcher* pDispatcher, std::shared_ptrm_pImpl)->Fetch(std::forward(args)...); if (CODEC_STATUS_OK == eStatus) { - pDispatcher->m_pLabor->IoStatAddRecvNum(pChannel->GetFd()); + if (pChannel->IsClient()) + { + pDispatcher->m_pLabor->IoStatAddRecvNum(pChannel->GetFd(), IO_STAT_UPSTREAM_RECV_NUM); + } + else + { + pDispatcher->m_pLabor->IoStatAddRecvNum(pChannel->GetFd(), IO_STAT_DOWNSTREAM_RECV_NUM); + } pDispatcher->m_pLastActivityChannel = pChannel; } return(eStatus); diff --git a/src/labor/Labor.hpp b/src/labor/Labor.hpp index 9d36ebc8..4a6cca28 100644 --- a/src/labor/Labor.hpp +++ b/src/labor/Labor.hpp @@ -73,10 +73,11 @@ class Labor { return(false); } - virtual void IoStatAddRecvNum(int iFd){} - virtual void IoStatAddRecvBytes(int iFd, uint32 uiBytes){} - virtual void IoStatAddSendNum(int iFd){} - virtual void IoStatAddSendBytes(int iFd, uint32 uiBytes){} + virtual void IoStatAddRecvNum(int iFd, uint32 uiIoType){} + virtual void IoStatAddRecvBytes(int iFd, uint32 uiBytes, uint32 uiIoType){} + virtual void IoStatAddSendNum(int iFd, uint32 uiIoType){} + virtual void IoStatAddSendBytes(int iFd, uint32 uiBytes, uint32 uiIoType){} + virtual void IoStatAddConnection(uint32 uiIoType){} pid_t gettid() { if (m_iPid == 0) diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 21818d89..66ac39ab 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -228,29 +228,29 @@ void Manager::StartService() if (m_oCurrentConf.Get("bind_ip", strBindIp) && strBindIp.length() > 0) { m_pDispatcher->CreateListenFd(strBindIp, - m_stNodeInfo.iPortForServer, m_stManagerInfo.iS2SListenFd, - m_stManagerInfo.iS2SFamily); + m_stNodeInfo.iPortForServer, m_stNodeInfo.iBacklog, + m_stManagerInfo.iS2SListenFd, m_stManagerInfo.iS2SFamily); if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) { // 接入节点才需要监听客户端连接 m_pDispatcher->CreateListenFd(strBindIp, - m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd, - m_stManagerInfo.iC2SFamily); + m_stNodeInfo.iPortForClient, m_stNodeInfo.iBacklog, + m_stManagerInfo.iC2SListenFd, m_stManagerInfo.iC2SFamily); } } else { m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForServer, - m_stNodeInfo.iPortForServer, m_stManagerInfo.iS2SListenFd, - m_stManagerInfo.iS2SFamily); + m_stNodeInfo.iPortForServer, m_stNodeInfo.iBacklog, + m_stManagerInfo.iS2SListenFd, m_stManagerInfo.iS2SFamily); if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) { // 接入节点才需要监听客户端连接 m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForClient, - m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd, - m_stManagerInfo.iC2SFamily); + m_stNodeInfo.iPortForClient, m_stNodeInfo.iBacklog, + m_stManagerInfo.iC2SListenFd, m_stManagerInfo.iC2SFamily); } } @@ -353,6 +353,7 @@ bool Manager::GetConf() m_oCurrentConf.Get("gateway", m_stNodeInfo.strGateway); m_oCurrentConf.Get("gateway_port", m_stNodeInfo.iGatewayPort); m_oCurrentConf.Get("access_socket_type", strSocketType); + m_oCurrentConf.Get("backlog", m_stNodeInfo.iBacklog); if (strSocketType == "UDP" || strSocketType == "udp") { m_stNodeInfo.iForClientSocketType = SOCK_DGRAM; diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index 849c2020..679e8976 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -32,6 +32,7 @@ struct NodeInfo int32 iPortForClient = 0; ///< 对Client通信监听端口,对应 iC2SListenFd int32 iForClientSocketType = 0; ///< 对Client通信的socket类型 int32 iGatewayPort = 0; ///< 对Client服务的真实端口 + int32 iBacklog = 100; ///< 监听队列长度 bool bThreadMode = false; ///< 是否线程模型 bool bAsyncLogger = false; ///< 是否启用异步文件日志 bool bIsAccess = false; ///< 是否接入Server @@ -51,6 +52,27 @@ struct NodeInfo std::string strNodeIdentify; }; +enum E_IO_STAT +{ + IO_STAT_RECV_NUM = 0x0001, + IO_STAT_RECV_BYTE = 0x0002, + IO_STAT_SEND_NUM = 0x0004, + IO_STAT_SEND_BYTE = 0x0008, + IO_STAT_UPSTREAM_RECV_NUM = 0x0010, + IO_STAT_UPSTREAM_RECV_BYTE = 0x0020, + IO_STAT_UPSTREAM_SEND_NUM = 0x0040, + IO_STAT_UPSTREAM_SEND_BYTE = 0x0080, + IO_STAT_DOWNSTREAM_RECV_NUM = 0x0100, + IO_STAT_DOWNSTREAM_RECV_BYTE = 0x0200, + IO_STAT_DOWNSTREAM_SEND_NUM = 0x0400, + IO_STAT_DOWNSTREAM_SEND_BYTE = 0x0800, + IO_STAT_UPSTREAM_NEW_CONNECTION = 0x1000, + IO_STAT_UPSTREAM_DESTROY_CONNECTION = 0x2000, + IO_STAT_DOWNSTREAM_NEW_CONNECTION = 0x4000, + IO_STAT_DOWNSTREAM_DESTROY_CONNECTION = 0x8000, +}; + + /** * @brief 工作进程属性 */ @@ -61,17 +83,50 @@ struct WorkerInfo int iControlFd = -1; ///< 与Manager进程通信的文件描述符(控制流) int iDataFd = -1; ///< 与Manager进程通信的文件描述符(数据流) uint32 uiLoad = 0; ///< 负载 - uint32 uiConnect = 0; ///< 连接数量 + uint32 uiConnection = 0; ///< 连接数量 uint32 uiRecvNum = 0; ///< 接收数据包数量 uint32 uiRecvByte = 0; ///< 接收字节数 uint32 uiSendNum = 0; ///< 发送数据包数量 uint32 uiSendByte = 0; ///< 发送字节数 + uint32 uiUpStreamRecvNum = 0; ///< upstream + uint32 uiUpStreamRecvByte = 0; ///< upstream + uint32 uiUpStreamSendNum = 0; ///< upstream + uint32 uiUpStreamSendByte = 0; ///< upstream + uint32 uiDownStreamRecvNum = 0; ///< downstream + uint32 uiDownStreamRecvByte = 0; ///< downstream + uint32 uiDownStreamSendNum = 0; ///< downstream + uint32 uiDownStreamSendByte = 0; ///< downstream + uint32 uiNewUpStreamConnection = 0; ///< + uint32 uiDestroyUpStreamConnection = 0; ///< + uint32 uiNewDownStreamConnection = 0; ///< + uint32 uiDestroyDownStreamConnection = 0; ///< ev_tstamp dBeatTime = 0.0; ///< 心跳时间 bool bStartBeatCheck = 0.0; ///< 是否需要心跳检查,worker或loader进程启动时可能需要加载数据而处于繁忙状态无法响应Manager的心跳,需等待其就绪之后才开始心跳检查。 WorkerInfo(){} WorkerInfo(const WorkerInfo& stAttr) = delete; WorkerInfo& operator=(const WorkerInfo& stAttr) = delete; + void ResetStat() + { + uiLoad = 0; + uiConnection = 0; + uiRecvNum = 0; + uiRecvByte = 0; + uiSendNum = 0; + uiSendByte = 0; + uiUpStreamRecvNum = 0; + uiUpStreamRecvByte = 0; + uiUpStreamSendNum = 0; + uiUpStreamSendByte = 0; + uiDownStreamRecvNum = 0; + uiDownStreamRecvByte = 0; + uiDownStreamSendNum = 0; + uiDownStreamSendByte = 0; + uiNewUpStreamConnection = 0; + uiDestroyUpStreamConnection = 0; + uiNewDownStreamConnection = 0; + uiDestroyDownStreamConnection = 0; + } }; } /* namespace neb */ diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 619ce1db..510b8fff 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -111,11 +111,11 @@ bool Worker::CheckParent() void Worker::DataReport() { - m_stWorkerInfo.uiConnect = m_pDispatcher->GetConnectionNum(); + m_stWorkerInfo.uiConnection = m_pDispatcher->GetConnectionNum(); MsgBody oMsgBody; CJsonObject oJsonLoad; - oJsonLoad.Add("load", int32(m_stWorkerInfo.uiConnect + m_pActorBuilder->GetStepNum())); - oJsonLoad.Add("connect", m_stWorkerInfo.uiConnect); + oJsonLoad.Add("load", int32(m_pActorBuilder->GetStepNum())); + oJsonLoad.Add("connect", m_stWorkerInfo.uiConnection); oJsonLoad.Add("recv_num", m_stWorkerInfo.uiRecvNum); oJsonLoad.Add("recv_byte", m_stWorkerInfo.uiRecvByte); oJsonLoad.Add("send_num", m_stWorkerInfo.uiSendNum); @@ -143,12 +143,57 @@ void Worker::DataReport() pRecord->set_key("send_byte"); pRecord->set_item("nebula"); pRecord->add_value(m_stWorkerInfo.uiSendByte); + pRecord = pReport->add_records(); + pRecord->set_key("new_upstream_connection"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiNewUpStreamConnection); + pRecord = pReport->add_records(); + pRecord->set_key("destroy_upstream_connection"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiDestroyUpStreamConnection); + pRecord = pReport->add_records(); + pRecord->set_key("new_downstream_connection"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiNewDownStreamConnection); + pRecord = pReport->add_records(); + pRecord->set_key("destroy_downstream_connection"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiDestroyDownStreamConnection); + pRecord = pReport->add_records(); + pRecord->set_key("upstream_recv_num"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiUpStreamRecvNum); + pRecord = pReport->add_records(); + pRecord->set_key("upstream_recv_byte"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiUpStreamRecvByte); + pRecord = pReport->add_records(); + pRecord->set_key("upstream_send_num"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiUpStreamSendNum); + pRecord = pReport->add_records(); + pRecord->set_key("upstream_send_byte"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiUpStreamSendByte); + pRecord = pReport->add_records(); + pRecord->set_key("downstream_recv_num"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiDownStreamRecvNum); + pRecord = pReport->add_records(); + pRecord->set_key("downstream_recv_byte"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiDownStreamRecvByte); + pRecord = pReport->add_records(); + pRecord->set_key("downstream_send_num"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiDownStreamSendNum); + pRecord = pReport->add_records(); + pRecord->set_key("downstream_send_byte"); + pRecord->set_item("nebula"); + pRecord->add_value(m_stWorkerInfo.uiDownStreamSendByte); pSessionDataReport->AddReport(pReport); } - m_stWorkerInfo.uiRecvNum = 0; - m_stWorkerInfo.uiRecvByte = 0; - m_stWorkerInfo.uiSendNum = 0; - m_stWorkerInfo.uiSendByte = 0; + m_stWorkerInfo.ResetStat(); } bool Worker::Init(CJsonObject& oJsonConf) diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index e0c80f28..84d504d1 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -101,7 +101,7 @@ class Worker: public Labor const WorkerInfo& GetWorkerInfo() const; std::shared_ptr GetManagerControlChannel(); bool SetCustomConf(const CJsonObject& oJsonConf); - virtual void IoStatAddRecvNum(int iFd) + virtual void IoStatAddRecvNum(int iFd, uint32 uiIoType) { if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) { @@ -113,8 +113,16 @@ class Worker: public Labor return; } ++m_stWorkerInfo.uiRecvNum; + if (IO_STAT_UPSTREAM_RECV_NUM & uiIoType) + { + ++m_stWorkerInfo.uiUpStreamRecvNum; + } + if (IO_STAT_DOWNSTREAM_RECV_NUM & uiIoType) + { + ++m_stWorkerInfo.uiDownStreamRecvNum; + } } - virtual void IoStatAddRecvBytes(int iFd, uint32 uiBytes) + virtual void IoStatAddRecvBytes(int iFd, uint32 uiBytes, uint32 uiIoType) { if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) { @@ -126,8 +134,16 @@ class Worker: public Labor return; } m_stWorkerInfo.uiRecvByte += uiBytes; + if (IO_STAT_UPSTREAM_RECV_BYTE & uiIoType) + { + ++m_stWorkerInfo.uiUpStreamRecvByte; + } + if (IO_STAT_DOWNSTREAM_RECV_BYTE & uiIoType) + { + ++m_stWorkerInfo.uiDownStreamRecvByte; + } } - virtual void IoStatAddSendNum(int iFd) + virtual void IoStatAddSendNum(int iFd, uint32 uiIoType) { if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) { @@ -139,8 +155,16 @@ class Worker: public Labor return; } ++m_stWorkerInfo.uiSendNum; + if (IO_STAT_UPSTREAM_SEND_NUM & uiIoType) + { + ++m_stWorkerInfo.uiUpStreamSendNum; + } + if (IO_STAT_DOWNSTREAM_SEND_NUM & uiIoType) + { + ++m_stWorkerInfo.uiDownStreamSendNum; + } } - virtual void IoStatAddSendBytes(int iFd, uint32 uiBytes) + virtual void IoStatAddSendBytes(int iFd, uint32 uiBytes, uint32 uiIoType) { if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) { @@ -152,6 +176,34 @@ class Worker: public Labor return; } m_stWorkerInfo.uiSendByte += uiBytes; + if (IO_STAT_UPSTREAM_SEND_BYTE & uiIoType) + { + ++m_stWorkerInfo.uiUpStreamSendByte; + } + if (IO_STAT_DOWNSTREAM_SEND_BYTE & uiIoType) + { + ++m_stWorkerInfo.uiDownStreamSendByte; + } + } + + virtual void IoStatAddConnection(uint32 uiIoType) + { + if (IO_STAT_DOWNSTREAM_NEW_CONNECTION & uiIoType) + { + ++m_stWorkerInfo.uiNewDownStreamConnection; + } + else if (IO_STAT_DOWNSTREAM_DESTROY_CONNECTION & uiIoType) + { + ++m_stWorkerInfo.uiDestroyDownStreamConnection; + } + else if (IO_STAT_UPSTREAM_NEW_CONNECTION & uiIoType) + { + ++m_stWorkerInfo.uiNewUpStreamConnection; + } + else if (IO_STAT_UPSTREAM_DESTROY_CONNECTION & uiIoType) + { + ++m_stWorkerInfo.uiDestroyUpStreamConnection; + } } template From 36812d262ba1630d0e546045ad575f3212e681f8 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 17 Sep 2022 15:17:36 +0800 Subject: [PATCH 163/176] add readonly buffer --- src/ios/Dispatcher.cpp | 6 +++--- src/type/Notations.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++ src/type/Notations.hpp | 3 +++ src/util/CBuffer.hpp | 29 +++++++++++++++++++++----- 4 files changed, 77 insertions(+), 8 deletions(-) diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index b6e79819..9e63562d 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -1183,7 +1183,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) { char szClientAddr[64] = {0}; int iAcceptFd = -1; - int iClientPort = 0; + //int iClientPort = 0; if (AF_INET == iFamily) { struct sockaddr_in stClientAddr; @@ -1221,7 +1221,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) } x_sock_set_block(iAcceptFd, 0); inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientAddr, sizeof(szClientAddr)); - iClientPort = CodecUtil::N2H(stClientAddr.sin_port); + //iClientPort = CodecUtil::N2H(stClientAddr.sin_port); LOG4_TRACE("accept connect from \"%s\"", szClientAddr); } else // AF_INET6 @@ -1261,7 +1261,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) } x_sock_set_block(iAcceptFd, 0); inet_ntop(AF_INET6, &stClientAddr.sin6_addr, szClientAddr, sizeof(szClientAddr)); - iClientPort = CodecUtil::N2H(stClientAddr.sin6_port); + //iClientPort = CodecUtil::N2H(stClientAddr.sin6_port); LOG4_TRACE("accept connect from \"%s\"", szClientAddr); } diff --git a/src/type/Notations.cpp b/src/type/Notations.cpp index e294851f..a6f88fc6 100644 --- a/src/type/Notations.cpp +++ b/src/type/Notations.cpp @@ -57,6 +57,20 @@ bool Notations::EncodeString(const std::string& strValue, CBuffer* pBuff) } } +bool Notations::EncodeString(const char* szValue, uint32 uiSize, CBuffer* pBuff) +{ + if (uiSize > 65535) + { + return(false); + } + else + { + EncodeShort((uint16)uiSize, pBuff); + pBuff->Write(szValue, uiSize); + return(true); + } +} + bool Notations::EncodeLongString(const std::string& strValue, CBuffer* pBuff) { if (strValue.size() > 2147483647) @@ -71,6 +85,20 @@ bool Notations::EncodeLongString(const std::string& strValue, CBuffer* pBuff) } } +bool Notations::EncodeLongString(const char* szValue, uint32 uiSize, CBuffer* pBuff) +{ + if (uiSize > 2147483647) + { + return(false); + } + else + { + EncodeInt((int32)uiSize, pBuff); + pBuff->Write(szValue, uiSize); + return(true); + } +} + bool Notations::EncodeUuid(const std::string& strValue, CBuffer* pBuff) { if (strValue.size() != 16) @@ -117,6 +145,25 @@ bool Notations::EncodeBytes(const Bytes& stValue, CBuffer* pBuff) } } +bool Notations::EncodeBytes(const char* szValue, int32 iSize, CBuffer* pBuff) +{ + if (iSize < 0) + { + EncodeInt(iSize, pBuff); + return(true); + } + else if (iSize > 2147483647) + { + return(false); + } + else + { + EncodeInt(iSize, pBuff); + pBuff->Write(szValue, iSize); + return(true); + } +} + bool Notations::EncodeValue(const Value& stValue, CBuffer* pBuff) { return(EncodeBytes(stValue, pBuff)); diff --git a/src/type/Notations.hpp b/src/type/Notations.hpp index 4ff2a113..4ac08536 100644 --- a/src/type/Notations.hpp +++ b/src/type/Notations.hpp @@ -145,10 +145,13 @@ class Notations static void EncodeByte(uint8 ucValue, CBuffer* pBuff); static void EncodeShort(uint16 unValue, CBuffer* pBuff); static bool EncodeString(const std::string& strValue, CBuffer* pBuff); + static bool EncodeString(const char* szValue, uint32 uiSize, CBuffer* pBuff); static bool EncodeLongString(const std::string& strValue, CBuffer* pBuff); + static bool EncodeLongString(const char* szValue, uint32 uiSize, CBuffer* pBuff); static bool EncodeUuid(const std::string& strValue, CBuffer* pBuff); static bool EncodeStringList(const std::vector& vecValue, CBuffer* pBuff); static bool EncodeBytes(const Bytes& stValue, CBuffer* pBuff); + static bool EncodeBytes(const char* szValue, int32 iSize, CBuffer* pBuff); static bool EncodeValue(const Value& stValue, CBuffer* pBuff); static bool EncodeShortBytes(const Bytes& stValue, CBuffer* pBuff); static bool EncodeOption(uint16 unId, const Value& stValue, std::function WithOptionValue, CBuffer* pBuff); diff --git a/src/util/CBuffer.hpp b/src/util/CBuffer.hpp index d154b943..6f1ca964 100644 --- a/src/util/CBuffer.hpp +++ b/src/util/CBuffer.hpp @@ -40,6 +40,7 @@ class CBuffer { private: char *m_buffer; //raw buffer + char *m_external_readonly_buffer; //readonly raw buffer /** total allocation available in the buffer field. */ size_t m_buffer_len; //raw buffer length @@ -49,8 +50,15 @@ class CBuffer static const size_t BUFFER_MAX_READ = 8192; static const size_t DEFAULT_BUFFER_SIZE = 32; inline CBuffer() : - m_buffer(NULL), m_buffer_len(0), m_write_idx(0), - m_read_idx(0) + m_buffer(NULL), m_external_readonly_buffer(NULL), + m_buffer_len(0), m_write_idx(0), m_read_idx(0) + { + } + //readonly + inline CBuffer(const char* data, size_t len) : + m_buffer(const_cast(data)), + m_external_readonly_buffer(const_cast(data)), + m_buffer_len(0), m_write_idx(0), m_read_idx(0) { } inline size_t GetReadIndex() @@ -96,6 +104,10 @@ class CBuffer inline size_t Compact(size_t leastLength) { + if (m_external_readonly_buffer != NULL) + { + return 0; + } uint32_t writableBytes = WriteableBytes(); if (writableBytes < leastLength) { @@ -126,6 +138,10 @@ class CBuffer inline bool EnsureWritableBytes(size_t minWritableBytes) { + if (m_external_readonly_buffer != NULL) + { + return false; + } if (WriteableBytes() >= minWritableBytes) { return true; @@ -338,11 +354,14 @@ class CBuffer } inline ~CBuffer() { - if (m_buffer != NULL) + if (m_external_readonly_buffer == NULL) { - free(m_buffer); + if (m_buffer != NULL) + { + free(m_buffer); + } + m_buffer = NULL; } - m_buffer = NULL; } }; From b1e691aafd5e799678ad84c89c8bba9d653a2741 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 15 Oct 2022 17:49:04 +0800 Subject: [PATCH 164/176] add console log; bug fixed --- README.md | 1 + README_cn.md | 10 ++- conf/nebula.json | 6 ++ src/actor/ActorBuilder.hpp | 2 +- .../session/sys_session/TimerAddrinfo.cpp | 2 + .../session/sys_session/TimerAddrinfo.hpp | 2 +- src/actor/step/sys_step/StepConnectWorker.cpp | 3 +- src/actor/step/sys_step/StepConnectWorker.hpp | 1 + src/actor/step/sys_step/StepIoTimeout.cpp | 3 +- src/actor/step/sys_step/StepIoTimeout.hpp | 1 + src/actor/step/sys_step/StepTellWorker.cpp | 3 +- src/actor/step/sys_step/StepTellWorker.hpp | 3 +- src/channel/SocketChannelImpl.hpp | 6 +- src/codec/CodecHttp.cpp | 11 ++- src/codec/CodecHttp.hpp | 3 +- src/codec/CodecPrivate.cpp | 8 +- src/codec/CodecPrivate.hpp | 3 +- src/codec/CodecProto.cpp | 31 +++++-- src/codec/CodecProto.hpp | 3 +- src/codec/CodecRaw.cpp | 7 +- src/codec/CodecRaw.hpp | 3 +- src/codec/CodecResp.cpp | 7 +- src/codec/CodecResp.hpp | 3 +- src/codec/CodecWsExtentJson.cpp | 8 +- src/codec/CodecWsExtentJson.hpp | 3 +- src/codec/CodecWsExtentPb.cpp | 8 +- src/codec/CodecWsExtentPb.hpp | 3 +- src/codec/cass/CodecCass.cpp | 7 +- src/codec/cass/CodecCass.hpp | 3 +- src/codec/http2/CodecHttp2.cpp | 7 +- src/codec/http2/CodecHttp2.hpp | 3 +- src/ios/Dispatcher.cpp | 23 +++-- src/ios/Dispatcher.hpp | 7 ++ src/ios/IO.hpp | 7 +- src/ios/Nodes.hpp | 1 + src/labor/Manager.cpp | 29 +++++-- src/labor/NodeInfo.hpp | 1 + src/labor/Worker.cpp | 4 +- src/logger/FileLogger.cpp | 16 +++- src/logger/FileLogger.hpp | 87 +++++++++++++++++-- src/logger/NetLogger.cpp | 6 +- src/logger/NetLogger.hpp | 1 + 42 files changed, 285 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index d49d4b89..573e8543 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ A simple testing can be start with a NebulaInterface only, and also can be start - Optimize worker mini load forwarding - add dns cache - add upstream and downstren monitor + - add console log #### v1.7.2 - codec bind channel circular reference bug fixed - CodecProto no packet communication bug fixed diff --git a/README_cn.md b/README_cn.md index 2b0e6df9..2958e4cb 100644 --- a/README_cn.md +++ b/README_cn.md @@ -135,13 +135,15 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 #### v1.7.3 - - 修复熔断节点探测导致错误回调问题 - - 修复redis cluster执行批量写命令返回、集群主从节点切换和asking命令bug - - 修改channel创建方式,改由CodecFactory创建 - 增加连接队列配置 - - 优化worker最小负载转发 - 增加dns cache - 增加上下游连接和数据包监控 + - 增加后台运行开关和控制台日志开关 + - 修改channel创建方式,改由CodecFactory创建 + - 优化worker最小负载转发 + - 修复熔断节点探测导致错误回调问题 + - 修复redis cluster执行批量写命令返回、集群主从节点切换和asking命令bug + - 修复集群内部服务间连接初始化 #### v1.7.2 - 修复codec bind channel循环引用问题 - 修复CodecProto无包体通信问题 diff --git a/conf/nebula.json b/conf/nebula.json index d07af757..800447e6 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -31,6 +31,9 @@ "new_client_to_loader":false, "//cpu_affinity":"是否设置进程CPU亲和度(绑定CPU)", "cpu_affinity":false, + "connection_dispatcher":{"round_robin":0, "min_load":1, "client_addr_hash":2}, + "connection_dispatch":0, + "daemonize":true, "//worker_capacity": "子进程最大工作负荷", "worker_capacity": 1000000, "//config_path": "配置文件路径(相对路径)", @@ -64,6 +67,7 @@ "log_levels": { "FATAL": 0, "CRITICAL": 1, "ERROR": 2, "NOTICE": 3, "WARNING": 4, "INFO": 5, "DEBUG": 6, "TRACE": 7 }, "log_level": 7, "net_log_level": 6, + "console_log":false, "//with_ssl": "SSL配置(可为空),路径为相对${WorkPath}的相对路径,公钥文件和私钥文件均为PEM格式", "with_ssl": { "config_path": "conf/ssl", @@ -72,6 +76,8 @@ }, "//data_report": "数据上报时间间隔,无统计数据时不上报", "data_report": 60, + "//service_start_notice":"是否需要通知每个worker服务已就绪", + "service_start_notice":false, "//refresh_interval": "刷新Server配置,检查、加载插件动态库时间周期", "refresh_interval": 60, "load_config":{ diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 87cd0fa7..83bbe553 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -149,10 +149,10 @@ class ActorBuilder bool ReloadCmdConf(); bool AddNetLogMsg(const MsgBody& oMsgBody); void AddChainConf(const std::string& strChainKey, std::queue >&& queChainBlocks); + void RemoveSession(std::shared_ptr pSession); protected: void RemoveStep(std::shared_ptr pStep); - void RemoveSession(std::shared_ptr pSession); void RemoveChain(uint32 uiChainId); void ChannelNotice(std::shared_ptr pChannel, const std::string& strIdentify, const std::string& strClientData); diff --git a/src/actor/session/sys_session/TimerAddrinfo.cpp b/src/actor/session/sys_session/TimerAddrinfo.cpp index 7b407eb0..c22c87de 100644 --- a/src/actor/session/sys_session/TimerAddrinfo.cpp +++ b/src/actor/session/sys_session/TimerAddrinfo.cpp @@ -22,6 +22,7 @@ TimerAddrinfo::~TimerAddrinfo() if (m_pAddrInfo != nullptr) { freeaddrinfo(m_pAddrInfo); + m_pAddrInfo = nullptr; } m_pAddrInfo = nullptr; } @@ -31,6 +32,7 @@ E_CMD_STATUS TimerAddrinfo::Timeout() if (m_pAddrInfo != nullptr) { freeaddrinfo(m_pAddrInfo); + m_pAddrInfo = nullptr; } m_pAddrInfo = nullptr; return(CMD_STATUS_RUNNING); diff --git a/src/actor/session/sys_session/TimerAddrinfo.hpp b/src/actor/session/sys_session/TimerAddrinfo.hpp index 4516f851..a939dde8 100644 --- a/src/actor/session/sys_session/TimerAddrinfo.hpp +++ b/src/actor/session/sys_session/TimerAddrinfo.hpp @@ -22,7 +22,7 @@ namespace neb { class TimerAddrinfo: public neb::Timer, - public DynamicCreator, + public DynamicCreator, public ActorSys { public: diff --git a/src/actor/step/sys_step/StepConnectWorker.cpp b/src/actor/step/sys_step/StepConnectWorker.cpp index 20982ffa..ff06cedf 100644 --- a/src/actor/step/sys_step/StepConnectWorker.cpp +++ b/src/actor/step/sys_step/StepConnectWorker.cpp @@ -30,7 +30,8 @@ E_CMD_STATUS StepConnectWorker::Emit( MsgHead oMsgHead; MsgBody oMsgBody; oMsgBody.set_data(std::to_string((int)m_iRemoteWorkerIndex)); - SendTo(m_pChannel, CMD_REQ_CONNECT_TO_WORKER, GetSequence(), oMsgBody); + oMsgBody.set_trace_id(GetTraceId()); + IO::SendRequest(this, m_pChannel, CMD_REQ_CONNECT_TO_WORKER, GetSequence(), oMsgBody); return(CMD_STATUS_COMPLETED); } diff --git a/src/actor/step/sys_step/StepConnectWorker.hpp b/src/actor/step/sys_step/StepConnectWorker.hpp index 1929abbd..100f8e53 100644 --- a/src/actor/step/sys_step/StepConnectWorker.hpp +++ b/src/actor/step/sys_step/StepConnectWorker.hpp @@ -13,6 +13,7 @@ #include #include #include +#include "ios/IO.hpp" namespace neb { diff --git a/src/actor/step/sys_step/StepIoTimeout.cpp b/src/actor/step/sys_step/StepIoTimeout.cpp index 151c6ba4..c83cad54 100644 --- a/src/actor/step/sys_step/StepIoTimeout.cpp +++ b/src/actor/step/sys_step/StepIoTimeout.cpp @@ -26,7 +26,8 @@ E_CMD_STATUS StepIoTimeout::Emit(int iErrno, const std::string& strErrMsg, void* data) { MsgBody oOutMsgBody; - if (SendTo(m_pChannel, CMD_REQ_BEAT, GetSequence(), oOutMsgBody)) + oOutMsgBody.set_trace_id(GetTraceId()); + if (IO::SendRequest(this, m_pChannel, CMD_REQ_BEAT, GetSequence(), oOutMsgBody)) { return(CMD_STATUS_RUNNING); } diff --git a/src/actor/step/sys_step/StepIoTimeout.hpp b/src/actor/step/sys_step/StepIoTimeout.hpp index d5f3693a..dd66dfae 100644 --- a/src/actor/step/sys_step/StepIoTimeout.hpp +++ b/src/actor/step/sys_step/StepIoTimeout.hpp @@ -13,6 +13,7 @@ #include "actor/ActorSys.hpp" #include "actor/step/PbStep.hpp" #include "Definition.hpp" +#include "ios/IO.hpp" namespace neb { diff --git a/src/actor/step/sys_step/StepTellWorker.cpp b/src/actor/step/sys_step/StepTellWorker.cpp index a86769bb..bf4ad8ec 100644 --- a/src/actor/step/sys_step/StepTellWorker.cpp +++ b/src/actor/step/sys_step/StepTellWorker.cpp @@ -32,7 +32,8 @@ E_CMD_STATUS StepTellWorker::Emit( oTargetWorker.set_worker_identify(GetNodeIdentify()); oTargetWorker.set_node_type(GetNodeType()); oOutMsgBody.set_data(oTargetWorker.SerializeAsString()); - Step::SendTo(m_pChannel, CMD_REQ_TELL_WORKER, GetSequence(), oOutMsgBody); + oOutMsgBody.set_trace_id(GetTraceId()); + IO::SendRequest(this, m_pChannel, CMD_REQ_TELL_WORKER, GetSequence(), oOutMsgBody); return(CMD_STATUS_RUNNING); } diff --git a/src/actor/step/sys_step/StepTellWorker.hpp b/src/actor/step/sys_step/StepTellWorker.hpp index d576f637..b99c6310 100644 --- a/src/actor/step/sys_step/StepTellWorker.hpp +++ b/src/actor/step/sys_step/StepTellWorker.hpp @@ -13,12 +13,13 @@ #include "actor/ActorSys.hpp" #include "actor/step/PbStep.hpp" #include "pb/neb_sys.pb.h" +#include "ios/IO.hpp" namespace neb { class StepTellWorker: public PbStep, - public DynamicCreator >, + public DynamicCreator& >, public ActorSys { public: diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 97b2d586..f4997cd1 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -544,7 +544,7 @@ E_CODEC_STATUS SocketChannelImpl::SendRequest(uint32 uiStepSeq, Targs&&... ar case CHANNEL_STATUS_CONNECTED: case CHANNEL_STATUS_TRY_CONNECT: case CHANNEL_STATUS_INIT: - eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pWaitForSendBuff); + eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pSendBuff, m_pWaitForSendBuff); if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) { eCodecStatus = CODEC_STATUS_PAUSE; @@ -706,6 +706,10 @@ E_CODEC_STATUS SocketChannelImpl::SendResponse(Targs&&... args) { return(CODEC_STATUS_EOF); } + if (m_pWaitForSendBuff->ReadableBytes() > 0) + { + return(CODEC_STATUS_PAUSE); + } return(eCodecStatus); } else diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 7e2e7cd5..22ce28ec 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -91,7 +91,7 @@ CodecHttp::~CodecHttp() { } -E_CODEC_STATUS CodecHttp::Encode(CBuffer* pBuff) +E_CODEC_STATUS CodecHttp::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); } @@ -490,6 +490,11 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) return(CODEC_STATUS_OK); } +E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + return(Encode(oHttpMsg, pBuff)); +} + E_CODEC_STATUS CodecHttp::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) { LOG4_TRACE(" "); @@ -597,8 +602,8 @@ const std::string& CodecHttp::ToString(const HttpMsg& oHttpMsg) m_strHttpString += prover; if (oHttpMsg.status_code() > 0) { - char tmp[10]; - snprintf(tmp, 10, " %d\r\n", oHttpMsg.status_code()); + char tmp[32]; + snprintf(tmp, 32, " %d\r\n", oHttpMsg.status_code()); m_strHttpString += tmp; } } diff --git a/src/codec/CodecHttp.hpp b/src/codec/CodecHttp.hpp index 226ea52d..a2d2b439 100644 --- a/src/codec/CodecHttp.hpp +++ b/src/codec/CodecHttp.hpp @@ -29,8 +29,9 @@ class CodecHttp: public Codec return(CODEC_HTTP); } - E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff); + E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff, CBuffer* pSecondlyBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg); E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff); diff --git a/src/codec/CodecPrivate.cpp b/src/codec/CodecPrivate.cpp index 4c30c454..96e4f274 100644 --- a/src/codec/CodecPrivate.cpp +++ b/src/codec/CodecPrivate.cpp @@ -25,7 +25,7 @@ CodecPrivate::~CodecPrivate() { } -E_CODEC_STATUS CodecPrivate::Encode(CBuffer* pBuff) +E_CODEC_STATUS CodecPrivate::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); } @@ -195,6 +195,12 @@ E_CODEC_STATUS CodecPrivate::Encode(const MsgHead& oMsgHead, const MsgBody& oMsg return(CODEC_STATUS_OK); } +E_CODEC_STATUS CodecPrivate::Encode(const MsgHead& oMsgHead, + const MsgBody& oMsgBody, CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + return(Encode(oMsgHead, oMsgBody, pBuff)); +} + E_CODEC_STATUS CodecPrivate::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) { LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); diff --git a/src/codec/CodecPrivate.hpp b/src/codec/CodecPrivate.hpp index d3bbaaad..36bb55e9 100644 --- a/src/codec/CodecPrivate.hpp +++ b/src/codec/CodecPrivate.hpp @@ -27,8 +27,9 @@ class CodecPrivate: public Codec return(CODEC_PRIVATE); } - E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); + E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff, CBuffer* pSecondlyBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff); }; diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp index 6da3e002..2c58b729 100644 --- a/src/codec/CodecProto.cpp +++ b/src/codec/CodecProto.cpp @@ -26,7 +26,7 @@ CodecProto::~CodecProto() { } -E_CODEC_STATUS CodecProto::Encode(CBuffer* pBuff) +E_CODEC_STATUS CodecProto::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); } @@ -70,6 +70,24 @@ E_CODEC_STATUS CodecProto::Encode(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgB } } +E_CODEC_STATUS CodecProto::Encode(int32 iCmd, uint32 uiSeq, + const MsgBody& oMsgBody, CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + switch (iCmd) + { + case CMD_RSP_TELL_WORKER: + return(Encode(iCmd, uiSeq, oMsgBody, pBuff)); + case CMD_REQ_TELL_WORKER: + return(Encode(iCmd, uiSeq, oMsgBody, pBuff)); + case CMD_RSP_CONNECT_TO_WORKER: + return(Encode(iCmd, uiSeq, oMsgBody, pBuff)); + case CMD_REQ_CONNECT_TO_WORKER: + return(Encode(iCmd, uiSeq, oMsgBody, pBuff)); + default: + return(Encode(iCmd, uiSeq, oMsgBody, pSecondlyBuff)); + } +} + E_CODEC_STATUS CodecProto::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) { LOG4_TRACE("pBuff->ReadableBytes()=%d, pBuff->GetReadIndex()=%d", @@ -166,10 +184,13 @@ E_CODEC_STATUS CodecProto::ChannelSticky(const MsgHead& oMsgHead, const MsgBody& pChannelImpl->SetChannelStatus(CHANNEL_STATUS_TRANSFER_TO_WORKER); break; default: - LOG4_TRACE("channel_fd[%d], channel_seq[%d], channel_status[from %d to %d] may be a fault.", - pChannelImpl->GetFd(), pChannelImpl->GetSequence(), - (int)pChannelImpl->GetChannelStatus(), CHANNEL_STATUS_ESTABLISHED); - pChannelImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + if (CODEC_PROTO == GetCodecType()) + { + LOG4_TRACE("cmd[%d] channel_fd[%d], channel_seq[%d], channel_status[from %d to %d] may be a fault.", + oMsgHead.cmd(), pChannelImpl->GetFd(), pChannelImpl->GetSequence(), + (int)pChannelImpl->GetChannelStatus(), CHANNEL_STATUS_ESTABLISHED); + pChannelImpl->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + } break; } } diff --git a/src/codec/CodecProto.hpp b/src/codec/CodecProto.hpp index dae36cab..57046058 100644 --- a/src/codec/CodecProto.hpp +++ b/src/codec/CodecProto.hpp @@ -27,8 +27,9 @@ class CodecProto: public Codec return(CODEC_PROTO); } - E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(int iCmd, uint32 uiSeq, const MsgBody& oMsgBody, CBuffer* pBuff); + E_CODEC_STATUS Encode(int iCmd, uint32 uiSeq, const MsgBody& oMsgBody, CBuffer* pBuff, CBuffer* pSecondlyBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff); diff --git a/src/codec/CodecRaw.cpp b/src/codec/CodecRaw.cpp index 28c2465e..991f1a64 100644 --- a/src/codec/CodecRaw.cpp +++ b/src/codec/CodecRaw.cpp @@ -25,7 +25,7 @@ CodecRaw::~CodecRaw() { } -E_CODEC_STATUS CodecRaw::Encode(CBuffer* pBuff) +E_CODEC_STATUS CodecRaw::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); } @@ -40,6 +40,11 @@ E_CODEC_STATUS CodecRaw::Encode(const char* pRaw, uint32 uiRawSize, CBuffer* pBu return(CODEC_STATUS_OK); } +E_CODEC_STATUS CodecRaw::Encode(const char* pRaw, uint32 uiRawSize, CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + return(Encode(pRaw, uiRawSize, pBuff)); +} + E_CODEC_STATUS CodecRaw::Decode(CBuffer* pBuff, CBuffer& oBuff) { oBuff.Write(pBuff->GetRawReadBuffer(), pBuff->ReadableBytes()); diff --git a/src/codec/CodecRaw.hpp b/src/codec/CodecRaw.hpp index f66bee48..a1c19f82 100644 --- a/src/codec/CodecRaw.hpp +++ b/src/codec/CodecRaw.hpp @@ -27,8 +27,9 @@ class CodecRaw: public Codec return(CODEC_RAW); } - E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(const char* pRaw, uint32 uiRawSize, CBuffer* pBuff); + E_CODEC_STATUS Encode(const char* pRaw, uint32 uiRawSize, CBuffer* pBuff, CBuffer* pSecondlyBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, CBuffer& oBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, CBuffer& oBuff, CBuffer* pReactBuff); }; diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index 25d7245f..14d520e6 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -29,7 +29,7 @@ CodecResp::~CodecResp() { } -E_CODEC_STATUS CodecResp::Encode(CBuffer* pBuff) +E_CODEC_STATUS CodecResp::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); } @@ -69,6 +69,11 @@ E_CODEC_STATUS CodecResp::Encode(const RedisReply& oReply, CBuffer* pBuff) return(eStatus); } +E_CODEC_STATUS CodecResp::Encode(const RedisReply& oReply, CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + return(Encode(oReply, pBuff)); +} + E_CODEC_STATUS CodecResp::Decode(CBuffer* pBuff, RedisReply& oReply) { if (pBuff->ReadableBytes() < 1) diff --git a/src/codec/CodecResp.hpp b/src/codec/CodecResp.hpp index 48d26fc1..01e08205 100644 --- a/src/codec/CodecResp.hpp +++ b/src/codec/CodecResp.hpp @@ -59,8 +59,9 @@ class CodecResp: public neb::Codec return(m_bDecodeWithStack); } - E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(const RedisReply& oReply, CBuffer* pBuff); + E_CODEC_STATUS Encode(const RedisReply& oReply, CBuffer* pBuff, CBuffer* pSecondlyBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, RedisReply& oReply); E_CODEC_STATUS Decode(CBuffer* pBuff, RedisReply& oReply, CBuffer* pReactBuff); diff --git a/src/codec/CodecWsExtentJson.cpp b/src/codec/CodecWsExtentJson.cpp index 0035fec2..aa275025 100644 --- a/src/codec/CodecWsExtentJson.cpp +++ b/src/codec/CodecWsExtentJson.cpp @@ -25,7 +25,7 @@ CodecWsExtentJson::~CodecWsExtentJson() { } -E_CODEC_STATUS CodecWsExtentJson::Encode(CBuffer* pBuff) +E_CODEC_STATUS CodecWsExtentJson::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); } @@ -212,6 +212,12 @@ E_CODEC_STATUS CodecWsExtentJson::Encode(const MsgHead& oMsgHead, return (CODEC_STATUS_OK); } +E_CODEC_STATUS CodecWsExtentJson::Encode(const MsgHead& oMsgHead, + const MsgBody& oMsgBody, CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + return(Encode(oMsgHead, oMsgBody, pBuff)); +} + E_CODEC_STATUS CodecWsExtentJson::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) { diff --git a/src/codec/CodecWsExtentJson.hpp b/src/codec/CodecWsExtentJson.hpp index 9e4cbef1..3439a725 100644 --- a/src/codec/CodecWsExtentJson.hpp +++ b/src/codec/CodecWsExtentJson.hpp @@ -29,8 +29,9 @@ class CodecWsExtentJson: public Codec return(CODEC_WS_EXTEND_JSON); } - E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); + E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff, CBuffer* pSecondlyBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff); diff --git a/src/codec/CodecWsExtentPb.cpp b/src/codec/CodecWsExtentPb.cpp index 816d4a92..8cb258e4 100644 --- a/src/codec/CodecWsExtentPb.cpp +++ b/src/codec/CodecWsExtentPb.cpp @@ -26,7 +26,7 @@ CodecWsExtentPb::~CodecWsExtentPb() { } -E_CODEC_STATUS CodecWsExtentPb::Encode(CBuffer* pBuff) +E_CODEC_STATUS CodecWsExtentPb::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); } @@ -209,6 +209,12 @@ E_CODEC_STATUS CodecWsExtentPb::Encode(const MsgHead& oMsgHead, return (CODEC_STATUS_OK); } +E_CODEC_STATUS CodecWsExtentPb::Encode(const MsgHead& oMsgHead, + const MsgBody& oMsgBody, CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + return(Encode(oMsgHead, oMsgBody, pBuff)); +} + E_CODEC_STATUS CodecWsExtentPb::Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody) { diff --git a/src/codec/CodecWsExtentPb.hpp b/src/codec/CodecWsExtentPb.hpp index 856b5883..7a40a785 100644 --- a/src/codec/CodecWsExtentPb.hpp +++ b/src/codec/CodecWsExtentPb.hpp @@ -28,8 +28,9 @@ class CodecWsExtentPb: public Codec return(CODEC_WS_EXTEND_PB); } - E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff); + E_CODEC_STATUS Encode(const MsgHead& oMsgHead, const MsgBody& oMsgBody, CBuffer* pBuff, CBuffer* pSecondlyBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody); E_CODEC_STATUS Decode(CBuffer* pBuff, MsgHead& oMsgHead, MsgBody& oMsgBody, CBuffer* pReactBuff); diff --git a/src/codec/cass/CodecCass.cpp b/src/codec/cass/CodecCass.cpp index 9b6eb8f1..0221bf7b 100644 --- a/src/codec/cass/CodecCass.cpp +++ b/src/codec/cass/CodecCass.cpp @@ -26,7 +26,7 @@ CodecCass::~CodecCass() { } -E_CODEC_STATUS CodecCass::Encode(CBuffer* pBuff) +E_CODEC_STATUS CodecCass::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { GenerateStreamId(); return(CODEC_STATUS_OK); @@ -88,6 +88,11 @@ E_CODEC_STATUS CodecCass::Encode(const CassMessage& oCassMsg, CBuffer* pBuff) } } +E_CODEC_STATUS CodecCass::Encode(const CassMessage& oCassMsg, CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + return(Encode(oCassMsg, pBuff)); +} + E_CODEC_STATUS CodecCass::Decode(CBuffer* pBuff, CassMessage& oCassMsg) { LOG4_ERROR("invalid"); diff --git a/src/codec/cass/CodecCass.hpp b/src/codec/cass/CodecCass.hpp index 2da9b52a..1db61157 100644 --- a/src/codec/cass/CodecCass.hpp +++ b/src/codec/cass/CodecCass.hpp @@ -53,8 +53,9 @@ class CodecCass: public Codec return(true); } - E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(const CassMessage& oCassMsg, CBuffer* pBuff); + E_CODEC_STATUS Encode(const CassMessage& oCassMsg, CBuffer* pBuff, CBuffer* pSecondlyBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, CassMessage& oCassMsg); E_CODEC_STATUS Decode(CBuffer* pBuff, CassMessage& oCassMsg, CBuffer* pReactBuff); diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index e17b1b93..22992661 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -106,7 +106,7 @@ void CodecHttp2::ConnectionSetting(CBuffer* pBuff) } } -E_CODEC_STATUS CodecHttp2::Encode(CBuffer* pBuff) +E_CODEC_STATUS CodecHttp2::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); } @@ -192,6 +192,11 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) return(eCodecStatus); } +E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + return(Encode(oHttpMsg, pBuff)); +} + E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) { LOG4_ERROR("invalid"); diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index 52a635e3..ec4ef621 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -49,8 +49,9 @@ class CodecHttp2: public Codec return(true); } - E_CODEC_STATUS Encode(CBuffer* pBuff); + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff); + E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff, CBuffer* pSecondlyBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg); E_CODEC_STATUS Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff); diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 9e63562d..d0c944c5 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -1183,7 +1183,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) { char szClientAddr[64] = {0}; int iAcceptFd = -1; - //int iClientPort = 0; + int iClientPort = 0; if (AF_INET == iFamily) { struct sockaddr_in stClientAddr; @@ -1221,7 +1221,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) } x_sock_set_block(iAcceptFd, 0); inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientAddr, sizeof(szClientAddr)); - //iClientPort = CodecUtil::N2H(stClientAddr.sin_port); + iClientPort = CodecUtil::N2H(stClientAddr.sin_port); LOG4_TRACE("accept connect from \"%s\"", szClientAddr); } else // AF_INET6 @@ -1261,7 +1261,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) } x_sock_set_block(iAcceptFd, 0); inet_ntop(AF_INET6, &stClientAddr.sin6_addr, szClientAddr, sizeof(szClientAddr)); - //iClientPort = CodecUtil::N2H(stClientAddr.sin6_port); + iClientPort = CodecUtil::N2H(stClientAddr.sin6_port); LOG4_TRACE("accept connect from \"%s\"", szClientAddr); } @@ -1284,9 +1284,20 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) } int iWorkerDataFd = -1; - iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetMinLoadWorkerDataFd(); - //iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(); - //iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetWorkerDataFd(szClientAddr, iClientPort); + switch (m_pLabor->GetNodeInfo().iConnectionDispatch) + { + case DISPATCH_ROUND_ROBIN: + iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(); + break; + case DISPATCH_MIN_LOAD: + iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetMinLoadWorkerDataFd(); + break; + case DISPATCH_CLIENT_ADDR_HASH: + iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetWorkerDataFd(szClientAddr, iClientPort); + break; + default: + iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(); + } if (iWorkerDataFd > 0) { LOG4_TRACE("send new fd %d to worker communication fd %d", diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 66cf1e67..7dcefff4 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -71,6 +71,13 @@ typedef void (*signal_callback)(struct ev_loop*,ev_signal*,int); typedef void (*timer_callback)(struct ev_loop*,ev_timer*,int); typedef void (*idle_callback)(struct ev_loop*,ev_idle*,int); +enum E_DISPATCHER +{ + DISPATCH_ROUND_ROBIN = 0, + DISPATCH_MIN_LOAD = 1, + DISPATCH_CLIENT_ADDR_HASH = 2, +}; + class Dispatcher { public: diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index de2b51e3..6d6b4e27 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -779,7 +779,8 @@ template bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, const std::string& strHost, int iPort, int iRemoteWorkerIndex, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) { - LOG4_TRACE_DISPATCH("%s", strIdentify.c_str()); + LOG4_TRACE_DISPATCH("identify %s, host %s port %d, remote_worker_index %d", + strIdentify.c_str(), strHost.c_str(), iPort, iRemoteWorkerIndex); struct addrinfo stAddrHints; struct addrinfo* pAddrResult = nullptr; struct addrinfo* pAddrCurrent = nullptr; @@ -849,6 +850,10 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::strin if (nullptr != pChannel) { connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); + if (pSessionAddr != nullptr && pAddrResult != pSessionAddr->GetAddrinfo()) + { + freeaddrinfo(pAddrResult); + } pDispatcher->m_pLabor->IoStatAddConnection(IO_STAT_UPSTREAM_NEW_CONNECTION); ev_tstamp dIoTimeout = (pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection > 0) ? pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection : pDispatcher->m_pLabor->GetNodeInfo().dIoTimeout; diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index bc5881b8..91e7105d 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -12,6 +12,7 @@ #define SRC_IOS_NODES_HPP_ #include +#include #include #include #include diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 66ac39ab..eb8b660e 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -48,8 +48,13 @@ Manager::Manager(const std::string& strConfFile) std::cerr << "GetConf() error!" << std::endl; exit(2); } - ngx_setproctitle(m_oCurrentConf("server_name").c_str()); - daemonize(m_oCurrentConf("server_name").c_str()); + bool bDaemonize = true; + m_oCurrentConf.Get("daemonize", bDaemonize); + if (bDaemonize) + { + ngx_setproctitle(m_oCurrentConf("server_name").c_str()); + daemonize(m_oCurrentConf("server_name").c_str()); + } if (!Init()) { exit(3); @@ -138,25 +143,26 @@ bool Manager::InitLogger(const CJsonObject& oJsonConf) std::string strLoggingHost; std::string strLogPath; std::string strLogname; + std::string strProtitle = m_oCurrentConf("server_name"); if (oJsonConf.Get("log_path", strLogPath)) { if (strLogPath[0] == '/') { - strLogname = strLogPath + std::string("/") + getproctitle() + std::string(".log"); + strLogname = strLogPath + std::string("/") + strProtitle + std::string(".log"); } else { strLogname = m_stNodeInfo.strWorkPath + std::string("/") - + strLogPath + std::string("/") + getproctitle() + std::string(".log"); + + strLogPath + std::string("/") + strProtitle + std::string(".log"); } } else { - strLogname = m_stNodeInfo.strWorkPath + std::string("/logs/") + getproctitle() + std::string(".log"); + strLogname = m_stNodeInfo.strWorkPath + std::string("/logs/") + strProtitle + std::string(".log"); } std::string strParttern = "[%D,%d{%q}][%p] [%l] %m%n"; std::ostringstream ssServerName; - ssServerName << getproctitle() << " " << m_stNodeInfo.strHostForServer << ":" << m_stNodeInfo.iPortForServer; + ssServerName << strProtitle << " " << m_stNodeInfo.strHostForServer << ":" << m_stNodeInfo.iPortForServer; oJsonConf.Get("max_log_file_size", iMaxLogFileSize); oJsonConf.Get("max_log_file_num", iMaxLogFileNum); oJsonConf.Get("log_max_line_len", iMaxLogLineLen); @@ -174,6 +180,8 @@ bool Manager::InitLogger(const CJsonObject& oJsonConf) } else { + bool bConsoleLog = false; + m_oCurrentConf.Get("console_log", bConsoleLog); m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, bAlwaysFlushLog, this); } m_pLogger->SetNetLogLevel(iNetLogLevel); @@ -267,6 +275,14 @@ void Manager::StartService() m_pDispatcher->SetChannelStatus(pChannelListen, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->AddIoReadEvent(pChannelListen); + bool bServiceStartNotice = false; + m_oCurrentConf.Get("service_start_notice", bServiceStartNotice); + if (bServiceStartNotice) + { + MsgBody oMsgBody; + m_pSessionManager->SendToChild(CMD_REQ_START_SERVICE, GetSequence(), oMsgBody); + } + // 创建到beacon的连接信息 for (int i = 0; i < m_oCurrentConf["beacon"].GetArraySize(); ++i) { @@ -354,6 +370,7 @@ bool Manager::GetConf() m_oCurrentConf.Get("gateway_port", m_stNodeInfo.iGatewayPort); m_oCurrentConf.Get("access_socket_type", strSocketType); m_oCurrentConf.Get("backlog", m_stNodeInfo.iBacklog); + m_oCurrentConf.Get("connection_dispatch", m_stNodeInfo.iConnectionDispatch); if (strSocketType == "UDP" || strSocketType == "udp") { m_stNodeInfo.iForClientSocketType = SOCK_DGRAM; diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index 679e8976..c0d5597a 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -33,6 +33,7 @@ struct NodeInfo int32 iForClientSocketType = 0; ///< 对Client通信的socket类型 int32 iGatewayPort = 0; ///< 对Client服务的真实端口 int32 iBacklog = 100; ///< 监听队列长度 + int32 iConnectionDispatch = 0; ///< 新建连接分发方式 bool bThreadMode = false; ///< 是否线程模型 bool bAsyncLogger = false; ///< 是否启用异步文件日志 bool bIsAccess = false; ///< 是否接入Server diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 510b8fff..71c11b02 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -367,7 +367,9 @@ bool Worker::InitLogger(const CJsonObject& oJsonConf, const std::string& strLogN } else { - m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, bAlwaysFlushLog, this); + bool bConsoleLog = false; + oJsonConf.Get("console_log", bConsoleLog); + m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, bAlwaysFlushLog, bConsoleLog, this); } m_pLogger->SetNetLogLevel(iNetLogLevel); LOG4_NOTICE("%s program begin...", getproctitle()); diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp index 5b4021b5..008cd8ad 100644 --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -26,9 +26,9 @@ namespace neb FileLogger* FileLogger::s_pInstance = nullptr; FileLogger::FileLogger(const std::string& strLogFile, int iLogLev, - unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex, bool bAlwaysFlush) + unsigned int uiMaxFileSize, unsigned int uiMaxRollFileIndex, bool bAlwaysFlush, bool bConsoleLog) : m_iLogLevel(iLogLev), m_uiLogNum(0), m_uiMaxFileSize(uiMaxFileSize), - m_uiMaxRollFileIndex(uiMaxRollFileIndex), m_bAlwaysFlush(bAlwaysFlush), + m_uiMaxRollFileIndex(uiMaxRollFileIndex), m_bAlwaysFlush(bAlwaysFlush), m_bConsoleLog(bConsoleLog), m_lTimeSec(0), m_strLogFileBase(strLogFile) { #if __GNUC__ < 5 @@ -60,13 +60,17 @@ int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLi } if(!m_fout.good()) { - std::cerr << "Write log error: no log file handle." << std::endl; + std::cerr <<__FILE__ << ":" << __LINE__ << " Write log error: no log file handle." << std::endl; return -1; } AppendLogPattern(iLev, szFileName, uiFileLine, szFunction); Append(strContent); m_fout << "\n"; + if (m_bConsoleLog) + { + std::cout << std::endl; + } if (m_bAlwaysFlush) { @@ -91,13 +95,17 @@ int FileLogger::WriteLog(const std::string& strTraceId, int iLev, const char* sz } if(!m_fout.good()) { - std::cerr << "Write log error: no log file handle." << std::endl; + std::cerr <<__FILE__ << ":" << __LINE__ << " Write log error: no log file handle." << std::endl; return -1; } AppendLogPattern(strTraceId, iLev, szFileName, uiFileLine, szFunction); Append(strContent); m_fout << "\n"; + if (m_bConsoleLog) + { + std::cout << std::endl; + } if (m_bAlwaysFlush) { diff --git a/src/logger/FileLogger.hpp b/src/logger/FileLogger.hpp index 4951a58f..4adc2cee 100644 --- a/src/logger/FileLogger.hpp +++ b/src/logger/FileLogger.hpp @@ -30,7 +30,7 @@ class FileLogger: public Logger int iLogLev = Logger::INFO, unsigned int uiMaxFileSize = neb::gc_uiMaxLogFileSize, unsigned int uiMaxRollFileIndex = neb::gc_uiMaxRollLogFileIndex, - bool bAlwaysFlush = true); + bool bAlwaysFlush = true, bool bConsoleLog = false); virtual ~FileLogger(); static FileLogger* Instance(const std::string& strLogFile = "../log/default.log", @@ -90,6 +90,7 @@ class FileLogger: public Logger unsigned int m_uiMaxFileSize; // 日志文件大小 unsigned int m_uiMaxRollFileIndex; // 滚动日志文件数量 bool m_bAlwaysFlush; + bool m_bConsoleLog; time_t m_lTimeSec; std::string m_strLogFileBase; // 日志文件基本名(如 log/program_name.log) std::string m_strLogFormatTime; @@ -112,13 +113,17 @@ int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLi } if(!m_fout.good()) { - std::cerr << "Write log error: no log file handle." << std::endl; + std::cerr << __FILE__ << ":" << __LINE__ << " Write log error: no log file handle." << std::endl; return -1; } AppendLogPattern(iLev, szFileName, uiFileLine, szFunction); Append(szLogFormat, std::forward(args)...); m_fout << "\n"; + if (m_bConsoleLog) + { + std::cout << std::endl; + } if (m_bAlwaysFlush) { @@ -145,13 +150,17 @@ int FileLogger::WriteLog(const std::string& strTraceId, int iLev, } if(!m_fout.good()) { - std::cerr << "Write log error: no log file handle." << std::endl; + std::cerr << __FILE__ << ":" << __LINE__ << " Write log error: no log file handle." << std::endl; return -1; } AppendLogPattern(strTraceId, iLev, szFileName, uiFileLine, szFunction); Append(szLogFormat, std::forward(args)...); m_fout << "\n"; + if (m_bConsoleLog) + { + std::cout << std::endl; + } if (m_bAlwaysFlush) { @@ -177,13 +186,17 @@ int FileLogger::WriteLog(int iLev, const char* szFileName, unsigned int uiFileLi } if(!m_fout.good()) { - std::cerr << "Write log error: no log file handle." << std::endl; + std::cerr << __FILE__ << ":" << __LINE__ << " Write log error: no log file handle." << std::endl; return -1; } AppendLogPattern(iLev, szFileName, uiFileLine, szFunction); Append(std::forward(args)...); m_fout << "\n"; + if (m_bConsoleLog) + { + std::cout << std::endl; + } if (m_bAlwaysFlush) { @@ -210,13 +223,17 @@ int FileLogger::WriteLog(const std::string& strTraceId, int iLev, } if(!m_fout.good()) { - std::cerr << "Write log error: no log file handle." << std::endl; + std::cerr << __FILE__ << ":" << __LINE__ << " Write log error: no log file handle." << std::endl; return -1; } AppendLogPattern(strTraceId, iLev, szFileName, uiFileLine, szFunction); Append(std::forward(args)...); m_fout << "\n"; + if (m_bConsoleLog) + { + std::cout << std::endl; + } if (m_bAlwaysFlush) { @@ -238,6 +255,10 @@ template void FileLogger::Append(T&& arg) { m_fout << arg; + if (m_bConsoleLog) + { + std::cout << arg; + } } template @@ -279,8 +300,16 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) { m_strOutStr.assign(szFormat, i); m_fout << m_strOutStr; + if (m_bConsoleLog) + { + std::cout << m_strOutStr; + } } m_fout << arg; + if (m_bConsoleLog) + { + std::cout << arg; + } return(nullptr); case '%': if (bPlaceholder) @@ -288,6 +317,10 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) m_strOutStr.append("%"); pPos = szFormat + i + 1; m_fout << m_strOutStr; + if (m_bConsoleLog) + { + std::cout << m_strOutStr; + } return(PrintfAppend(pPos, std::forward(arg))); } else @@ -302,6 +335,11 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) pPos = szFormat + i + 1; m_fout << m_strOutStr; m_fout << std::fixed << arg; + if (m_bConsoleLog) + { + std::cout << m_strOutStr; + std::cout << std::fixed << arg; + } return(pPos); } break; @@ -317,6 +355,11 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) pPos = szFormat + i + 1; m_fout << m_strOutStr; m_fout << arg; + if (m_bConsoleLog) + { + std::cout << m_strOutStr; + std::cout << arg; + } return(pPos); } break; @@ -328,6 +371,11 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) pPos = szFormat + i + 1; m_fout << m_strOutStr; m_fout << std::dec << arg; + if (m_bConsoleLog) + { + std::cout << m_strOutStr; + std::cout << std::dec << arg; + } return(pPos); } break; @@ -337,6 +385,11 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) pPos = szFormat + i + 1; m_fout << m_strOutStr; m_fout << std::hex << std::nouppercase << arg; + if (m_bConsoleLog) + { + std::cout << m_strOutStr; + std::cout << std::hex << std::nouppercase << arg; + } return(pPos); } break; @@ -346,6 +399,11 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) pPos = szFormat + i + 1; m_fout << m_strOutStr; m_fout << std::hex << std::uppercase << arg; + if (m_bConsoleLog) + { + std::cout << m_strOutStr; + std::cout << std::hex << std::uppercase << arg; + } return(pPos); } break; @@ -355,6 +413,11 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) pPos = szFormat + i + 1; m_fout << m_strOutStr; m_fout << std::oct << arg; + if (m_bConsoleLog) + { + std::cout << m_strOutStr; + std::cout << std::oct << arg; + } return(pPos); } break; @@ -365,6 +428,11 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) pPos = szFormat + i + 1; m_fout << m_strOutStr; m_fout << std::scientific << std::nouppercase << arg; + if (m_bConsoleLog) + { + std::cout << m_strOutStr; + std::cout << std::scientific << std::nouppercase << arg; + } return(pPos); } break; @@ -375,6 +443,11 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) pPos = szFormat + i + 1; m_fout << m_strOutStr; m_fout << std::scientific << std::uppercase << arg; + if (m_bConsoleLog) + { + std::cout << m_strOutStr; + std::cout << std::scientific << std::uppercase << arg; + } return(pPos); } break; @@ -384,6 +457,10 @@ const char* FileLogger::PrintfAppend(const char* szFormat, T&& arg) } m_strOutStr.assign(szFormat, i); m_fout << m_strOutStr; + if (m_bConsoleLog) + { + std::cout << m_strOutStr; + } return(nullptr); } diff --git a/src/logger/NetLogger.cpp b/src/logger/NetLogger.cpp index 1849e5b7..ad7af36c 100644 --- a/src/logger/NetLogger.cpp +++ b/src/logger/NetLogger.cpp @@ -22,14 +22,14 @@ namespace neb { NetLogger::NetLogger(const std::string& strLogFile, int iLogLev, unsigned int uiMaxFileSize, - unsigned int uiMaxRollFileIndex, bool bAlwaysFlush, Labor* pLabor) + unsigned int uiMaxRollFileIndex, bool bAlwaysFlush, bool bConsoleLog, Labor* pLabor) : m_iLogLevel(iLogLev), m_iNetLogLevel(Logger::INFO), m_iWorkerIndex(0), m_bEnableNetLogger(false), m_pLabor(pLabor), m_pLog(nullptr) { #if __cplusplus >= 201401L - m_pLog = std::make_unique(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex, bAlwaysFlush); + m_pLog = std::make_unique(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex, bConsoleLog, bAlwaysFlush); #else - m_pLog = std::unique_ptr(new FileLogger(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex, bAlwaysFlush)); + m_pLog = std::unique_ptr(new FileLogger(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex, bConsoleLog, bAlwaysFlush)); #endif } diff --git a/src/logger/NetLogger.hpp b/src/logger/NetLogger.hpp index 4c69075c..aa1682c1 100644 --- a/src/logger/NetLogger.hpp +++ b/src/logger/NetLogger.hpp @@ -31,6 +31,7 @@ class NetLogger: public Logger unsigned int uiMaxFileSize = gc_uiMaxLogFileSize, unsigned int uiMaxRollFileIndex = gc_uiMaxRollLogFileIndex, bool bAlwaysFlush = true, + bool bConsoleLog = false, Labor* pLabor = nullptr); NetLogger( int iWorkerIndex, From ae14a17ee03e7c87bf499214ff7602b3033a792f Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 4 Dec 2022 11:02:04 +0800 Subject: [PATCH 165/176] redis cluster client bug fixed --- README_cn.md | 2 +- src/actor/Actor.hpp | 2 +- src/actor/step/sys_step/StepRedisCluster.cpp | 290 +++++++++++-------- src/actor/step/sys_step/StepRedisCluster.hpp | 5 +- src/channel/SocketChannelImpl.hpp | 23 +- src/channel/SocketChannelSslImpl.hpp | 5 +- src/channel/SocketChannelSslImpl.inl | 27 +- src/codec/CodecFactory.cpp | 22 +- src/util/StringCoder.cpp | 22 +- 9 files changed, 213 insertions(+), 185 deletions(-) diff --git a/README_cn.md b/README_cn.md index 2958e4cb..229975eb 100644 --- a/README_cn.md +++ b/README_cn.md @@ -142,7 +142,7 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 - 修改channel创建方式,改由CodecFactory创建 - 优化worker最小负载转发 - 修复熔断节点探测导致错误回调问题 - - 修复redis cluster执行批量写命令返回、集群主从节点切换和asking命令bug + - 修复redis cluster执行批量写命令返回、集群主从节点切换和asking命令、tag路由bug - 修复集群内部服务间连接初始化 #### v1.7.2 - 修复codec bind channel循环引用问题 diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 44aefff9..59d9f3d2 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -124,7 +124,7 @@ class Actor: public std::enable_shared_from_this uint32 GetSequence(); -protected: +public: uint32 GetNodeId() const; uint32 GetWorkerIndex() const; const std::string& GetNodeType() const; diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index 267f76b9..36e5e844 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -103,7 +103,7 @@ const std::unordered_set StepRedisCluster::s_setMultipleKeyValueCmd StepRedisCluster::StepRedisCluster( const std::string& strIdentify, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) - : RedisStep(30.0), + : RedisStep(7.0), m_bWithSsl(bWithSsl), m_bPipeline(bPipeline), m_bEnableReadOnly(bEnableReadOnly), m_uiAddressIndex(0), m_lLastCheckTime(0), m_strIdentify(strIdentify) @@ -125,15 +125,36 @@ E_CMD_STATUS StepRedisCluster::Callback( std::shared_ptr pChannel, const RedisReply& oRedisReply) { LOG4_TRACE("callback from %s:\n%s", pChannel->GetIdentify().c_str(), oRedisReply.DebugString().c_str()); - auto step_iter = m_mapPipelineRequest.find(pChannel->GetIdentify()); - if (step_iter == m_mapPipelineRequest.end()) + std::shared_ptr pRedisRequest = nullptr; + uint32 uiRealStepSeq = 0; + if (m_bPipeline) { - LOG4_ERROR("no \"%s\" found in m_mapPipelineStep", pChannel->GetIdentify().c_str()); - return(CMD_STATUS_FAULT); + auto step_iter = m_mapPipelineRequest.find(pChannel->GetIdentify()); + if (step_iter == m_mapPipelineRequest.end()) + { + LOG4_ERROR("no \"%s\" found in m_mapPipelineRequest", pChannel->GetIdentify().c_str()); + return(CMD_STATUS_FAULT); + } + pRedisRequest = step_iter->second.front(); + uiRealStepSeq = (uint32)pRedisRequest->integer(); + step_iter->second.pop(); + } + else + { + uint32 uiChannelSeq = pChannel->GetSequence(); + auto iter = m_mapRequest.find(uiChannelSeq); + if (iter == m_mapRequest.end()) + { + LOG4_ERROR("no \"%u\" found in m_mapRequest", pChannel->GetSequence()); + return(CMD_STATUS_FAULT); + } + else + { + pRedisRequest = iter->second; + uiRealStepSeq = (uint32)pRedisRequest->integer(); + m_mapRequest.erase(iter); + } } - auto pRedisRequest = step_iter->second.front(); - uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); - step_iter->second.pop(); if (uiRealStepSeq == GetSequence()) { if (pRedisRequest->element(0).str() == "ASK") @@ -317,7 +338,7 @@ E_CMD_STATUS StepRedisCluster::Callback( E_CMD_STATUS StepRedisCluster::Timeout() { - HealthCheck(); + SendCmdClusterSlots(); return(CMD_STATUS_RUNNING); } @@ -334,81 +355,108 @@ E_CMD_STATUS StepRedisCluster::ErrBack( void StepRedisCluster::CmdErrBack( std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) { - auto step_iter = m_mapPipelineRequest.find(pChannel->GetIdentify()); - if (step_iter == m_mapPipelineRequest.end()) + if (m_bPipeline) { - LOG4_TRACE("no \"%s\" found in m_mapPipelineStep", pChannel->GetIdentify().c_str()); + auto step_iter = m_mapPipelineRequest.find(pChannel->GetIdentify()); + if (step_iter == m_mapPipelineRequest.end()) + { + LOG4_TRACE("no \"%s\" found in m_mapPipelineRequest", pChannel->GetIdentify().c_str()); + } + else + { + uint32 uiErrNum = step_iter->second.size(); + for (uint32 i = 0; i < uiErrNum; ++i) + { + auto pRedisRequest = step_iter->second.front(); + uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); + step_iter->second.pop(); + CmdErrBack(pChannel, iErrno, strErrMsg, uiRealStepSeq, pRedisRequest); + } + } } else { - while (step_iter->second.size() > 0) + uint32 uiChannelSeq = pChannel->GetSequence(); + auto iter = m_mapRequest.find(uiChannelSeq); + if (iter == m_mapRequest.end()) { - auto pRedisRequest = step_iter->second.front(); - uint32 uiRealStepSeq = (uint32)pRedisRequest->integer(); - step_iter->second.pop(); - if (uiRealStepSeq == GetSequence()) + LOG4_TRACE("no \"%u\" found in m_mapRequest", pChannel->GetSequence()); + } + else + { + auto pRedisRequest = iter->second; + auto uiRealStepSeq = (uint32)pRedisRequest->integer(); + m_mapRequest.erase(iter); + CmdErrBack(pChannel, iErrno, strErrMsg, uiRealStepSeq, pRedisRequest); + } + } +} + +void StepRedisCluster::CmdErrBack( + std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg, + uint32 uiRealStepSeq, std::shared_ptr pRequest) +{ + if (uiRealStepSeq == GetSequence()) + { + ; // SendCmdClusterSlots(); + } + else + { + auto num_iter = m_mapStepEmitNum.find(uiRealStepSeq); + if (num_iter == m_mapStepEmitNum.end()) // 单key请求的响应 + { + GetLabor(this)->GetActorBuilder()->OnError(pChannel, uiRealStepSeq, iErrno, strErrMsg); + } + else + { + auto reply_iter = m_mapReply.find(uiRealStepSeq); + if (reply_iter == m_mapReply.end()) { - ; // SendCmdClusterSlots(); + LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); + return; } - else + std::vector vecElementIndex; + for (int i = 1; i < pRequest->element_size(); ++i) // element(0).str() is cmd { - auto num_iter = m_mapStepEmitNum.find(uiRealStepSeq); - if (num_iter == m_mapStepEmitNum.end()) // 单key请求的响应 + if (pRequest->element(i).integer() > 0 || i == 1) { - GetLabor(this)->GetActorBuilder()->OnError(pChannel, uiRealStepSeq, iErrno, strErrMsg); + vecElementIndex.push_back((uint32)pRequest->element(i).integer()); } - else + } + RedisReply oRedisReply; + oRedisReply.set_type(REDIS_REPLY_ERROR); + oRedisReply.set_str(strErrMsg); + for (uint32 j = 0; j < vecElementIndex.size(); ++j) + { + if (vecElementIndex[j] < reply_iter->second.size()) { - auto reply_iter = m_mapReply.find(uiRealStepSeq); - if (reply_iter == m_mapReply.end()) - { - LOG4_ERROR("no m_mapReply found for step %u", uiRealStepSeq); - continue; - } - std::vector vecElementIndex; - for (int i = 1; i < pRedisRequest->element_size(); ++i) // element(0).str() is cmd + if (reply_iter->second[vecElementIndex[j]] == nullptr) { - if (pRedisRequest->element(i).integer() > 0 || i == 1) - { - vecElementIndex.push_back((uint32)pRedisRequest->element(i).integer()); - } - } - RedisReply oRedisReply; - oRedisReply.set_type(REDIS_REPLY_ERROR); - oRedisReply.set_str(strErrMsg); - for (uint32 j = 0; j < vecElementIndex.size(); ++j) - { - if (vecElementIndex[j] < reply_iter->second.size()) - { - if (reply_iter->second[vecElementIndex[j]] == nullptr) - { - reply_iter->second[vecElementIndex[j]] = new RedisReply(); - } - reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply); - } - else - { - LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); - } - } - num_iter->second--; - if (num_iter->second == 0) - { - RedisReply oFinalReply; - oFinalReply.set_type(REDIS_REPLY_ARRAY); - for (size_t k = 0; k < reply_iter->second.size(); ++k) - { - oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); - reply_iter->second[k] = nullptr; - } - IO::OnResponse(GetLabor(this)->GetActorBuilder(), pChannel, uiRealStepSeq, oFinalReply); - m_mapStepEmitNum.erase(num_iter); - m_mapReply.erase(reply_iter); + reply_iter->second[vecElementIndex[j]] = new RedisReply(); } + reply_iter->second[vecElementIndex[j]]->CopyFrom(oRedisReply); + } + else + { + LOG4_ERROR("element index %u larger than reply size %u", vecElementIndex[j], reply_iter->second.size()); } } + num_iter->second--; + if (num_iter->second == 0) + { + RedisReply oFinalReply; + oFinalReply.set_type(REDIS_REPLY_ARRAY); + for (size_t k = 0; k < reply_iter->second.size(); ++k) + { + oFinalReply.mutable_element()->AddAllocated(reply_iter->second[k]); + reply_iter->second[k] = nullptr; + } + IO::OnResponse(GetLabor(this)->GetActorBuilder(), pChannel, uiRealStepSeq, oFinalReply); + m_mapStepEmitNum.erase(num_iter); + m_mapReply.erase(reply_iter); + } } - } + } } bool StepRedisCluster::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, @@ -432,53 +480,67 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptr> queStep; - queStep.push(pRedisMsg); - m_mapPipelineRequest.insert(std::make_pair(strIdentify, std::move(queStep))); + auto iter = m_mapPipelineRequest.find(strIdentify); + if (iter == m_mapPipelineRequest.end()) + { + std::queue> queStep; + queStep.push(pRedisMsg); + m_mapPipelineRequest.insert(std::make_pair(strIdentify, std::move(queStep))); + } + else + { + iter->second.push(pRedisMsg); + } } else { - iter->second.push(pRedisMsg); + uint32 uiChannelSeq = GetLastActivityChannel()->GetSequence(); + auto iter = m_mapRequest.find(uiChannelSeq); + if (iter == m_mapRequest.end()) + { + m_mapRequest.insert(std::make_pair(uiChannelSeq, pRedisMsg)); + } + else + { + LOG4_ERROR("not a pipeline channel."); + } } } return(bResult); } bool StepRedisCluster::ExtractCmd(const RedisMsg& oRedisMsg, - std::string& strCmd, std::vector& vecHashKeys, + std::string& strCmd, std::vector& vecSlotId, int& iReadOrWrite, int& iKeyInterval) { - auto GetTag = [](const std::string& strKey, std::string& strTag)->void + auto CalcSlotId = [](const char* szKey, uint32 uiKeyLength)->uint16 { - size_t uiTagBegin = strKey.find_first_of("{"); - if (std::string::npos == uiTagBegin) + uint32 s,e; + for (s = 0; s < uiKeyLength; s++) { - strTag = strKey; - } - else - { - size_t uiTagEnd = strKey.find_last_of("}"); - if (std::string::npos == uiTagEnd || uiTagEnd <= uiTagBegin + 1) + if (szKey[s] == '{') { - strTag = strKey; + break; } - else + } + if (s == uiKeyLength) + { + return(crc16(szKey, uiKeyLength) & 16383); + } + for (e = s + 1; e < uiKeyLength; e++) + { + if (szKey[e] == '}') { - ++uiTagBegin; - size_t uiTagLen = uiTagEnd - uiTagBegin; - if (uiTagLen > 0) - { - strTag = strKey.substr(uiTagBegin, uiTagLen); - } - else - { - strTag = strKey; - } + break; } } + if (e == uiKeyLength || e == s + 1) + { + return(crc16(szKey, uiKeyLength) & 16383); + } + return(crc16(szKey + s + 1, e - s - 1) & 16383); }; if (REDIS_REPLY_ARRAY == oRedisMsg.type()) @@ -501,7 +563,6 @@ bool StepRedisCluster::ExtractCmd(const RedisMsg& oRedisMsg, LOG4_ERROR("cmd %s not supported by StepRedisCluster", strCmd.c_str()); return(false); } - std::string strTag; if (s_setMultipleKeyCmd.find(strCmd) != s_setMultipleKeyCmd.end()) { if (oRedisMsg.element_size() < 2) @@ -518,8 +579,7 @@ bool StepRedisCluster::ExtractCmd(const RedisMsg& oRedisMsg, "invalid redis cmd: %s", oRedisMsg.DebugString().c_str()); return(false); } - GetTag(oRedisMsg.element(i).str(), strTag); - vecHashKeys.push_back(strTag); + vecSlotId.push_back(CalcSlotId(oRedisMsg.element(i).str().data(), oRedisMsg.element(i).str().length())); } iKeyInterval = 1; if (s_setWriteCmd.find(strCmd) == s_setWriteCmd.end()) @@ -550,8 +610,7 @@ bool StepRedisCluster::ExtractCmd(const RedisMsg& oRedisMsg, "invalid redis cmd: %s", oRedisMsg.DebugString().c_str()); return(false); } - GetTag(oRedisMsg.element(i).str(), strTag); - vecHashKeys.push_back(strTag); + vecSlotId.push_back(CalcSlotId(oRedisMsg.element(i).str().data(), oRedisMsg.element(i).str().length())); } } iKeyInterval = 2; @@ -570,7 +629,7 @@ bool StepRedisCluster::ExtractCmd(const RedisMsg& oRedisMsg, { if (oRedisMsg.element_size() < 2) { - strTag = ""; + vecSlotId.push_back(0); } else { @@ -579,9 +638,8 @@ bool StepRedisCluster::ExtractCmd(const RedisMsg& oRedisMsg, LOG4_ERROR("cmd element be a REDIS_REPLY_STRING, invalid redis cmd: %s", oRedisMsg.DebugString().c_str()); return(false); } - GetTag(oRedisMsg.element(1).str(), strTag); } - vecHashKeys.emplace_back(std::move(strTag)); + vecSlotId.push_back(CalcSlotId(oRedisMsg.element(1).str().data(), oRedisMsg.element(1).str().length())); if (s_setWriteCmd.find(strCmd) == s_setWriteCmd.end()) { iReadOrWrite = REDIS_CMD_WRITE; @@ -825,20 +883,18 @@ void StepRedisCluster::CmdReadOnlyCallback(std::shared_ptr pChann bool StepRedisCluster::Dispatch(const RedisMsg& oRedisMsg, uint32 uiStepSeq) { std::string strCmd; - std::vector vecHashKey; + std::vector vecSlotId; int iKeyInterval = 0; int iReadOrWrite = 0; - if (!ExtractCmd(oRedisMsg, strCmd, vecHashKey, iReadOrWrite, iKeyInterval)) + if (!ExtractCmd(oRedisMsg, strCmd, vecSlotId, iReadOrWrite, iKeyInterval)) { return(false); } bool bIsMasterNode = false; std::string strRedisNode; - if (vecHashKey.size() == 1) + if (vecSlotId.size() == 1) { - uint16 unHash = crc16(vecHashKey[0].c_str(), vecHashKey[0].size()); - int iSlotId = (int)(unHash % sc_unClusterSlots); - if (!GetRedisNode(iSlotId, iReadOrWrite, strRedisNode, bIsMasterNode)) + if (!GetRedisNode(vecSlotId[0], iReadOrWrite, strRedisNode, bIsMasterNode)) { return(false); } @@ -850,19 +906,17 @@ bool StepRedisCluster::Dispatch(const RedisMsg& oRedisMsg, uint32 uiStepSeq) } return(SendTo(strRedisNode, pRedisRequest)); } - else if (vecHashKey.size() > 1) + else if (vecSlotId.size() > 1) { - if ((int)vecHashKey.size() * iKeyInterval >= oRedisMsg.element_size()) + if ((int)vecSlotId.size() * iKeyInterval >= oRedisMsg.element_size()) { LOG4_ERROR("element size error."); return(false); } std::unordered_map> mapRedisRequest; - for (uint32 i = 0; i < vecHashKey.size(); ++i) + for (uint32 i = 0; i < vecSlotId.size(); ++i) { - uint16 unHash = crc16(vecHashKey[i].c_str(), vecHashKey[i].size()); - int iSlotId = (int)(unHash % sc_unClusterSlots); - auto req_iter = mapRedisRequest.find(iSlotId); + auto req_iter = mapRedisRequest.find(vecSlotId[i]); if (req_iter == mapRedisRequest.end()) { auto pSubRequest = std::make_shared(); @@ -878,7 +932,7 @@ bool StepRedisCluster::Dispatch(const RedisMsg& oRedisMsg, uint32 uiStepSeq) pElement->CopyFrom(oRedisMsg.element(i * iKeyInterval + j)); // value } pSubRequest->set_integer(uiStepSeq); // 借用integer暂存seq - mapRedisRequest.insert(std::make_pair(iSlotId, pSubRequest)); + mapRedisRequest.insert(std::make_pair(vecSlotId[i], pSubRequest)); } else { @@ -909,7 +963,7 @@ bool StepRedisCluster::Dispatch(const RedisMsg& oRedisMsg, uint32 uiStepSeq) auto iter_bool = m_mapReply.insert(std::make_pair(uiStepSeq, vecReply)); if (iter_bool.second == true) { - iter_bool.first->second.resize(vecHashKey.size(), nullptr); + iter_bool.first->second.resize(vecSlotId.size(), nullptr); } return(iter_bool.second); } diff --git a/src/actor/step/sys_step/StepRedisCluster.hpp b/src/actor/step/sys_step/StepRedisCluster.hpp index bf361996..90829a9c 100644 --- a/src/actor/step/sys_step/StepRedisCluster.hpp +++ b/src/actor/step/sys_step/StepRedisCluster.hpp @@ -81,7 +81,7 @@ class StepRedisCluster: public RedisStep, protected: bool SendTo(const std::string& strIdentify, std::shared_ptr pRedisMsg); bool ExtractCmd(const RedisMsg& oRedisMsg, std::string& strCmd, - std::vector& vecHashKeys, int& iReadOrWrite, int& iKeyInterval); + std::vector& vecSlotId, int& iReadOrWrite, int& iKeyInterval); bool GetRedisNode(int iSlotId, int iReadOrWrite, std::string& strNodeIdentify, bool& bIsMaster) const; bool NeedSetReadOnly(const std::string& strNode) const; bool SendCmdClusterSlots(); @@ -93,6 +93,8 @@ class StepRedisCluster: public RedisStep, bool SendCmdReadOnly(const std::string& strIdentify); void CmdReadOnlyCallback(std::shared_ptr pChannel, const std::string& strIdentify, const RedisReply& oRedisReply); void CmdErrBack(std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg); + void CmdErrBack(std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg, + uint32 uiRealStepSeq, std::shared_ptr pRequest); bool Dispatch(const RedisMsg& oRedisMsg, uint32 uiStepSeq); void SendWaittingRequest(); void RegisterStep(uint32 uiStepSeq); @@ -110,6 +112,7 @@ class StepRedisCluster: public RedisStep, std::vector m_vecAddress; ///< 集群地址 std::unordered_map> m_mapSlot2Node; // redis cluster slot对应的节点 std::unordered_map>> m_mapPipelineRequest; ///< 等待回调的请求 + std::unordered_map> m_mapRequest; ///< 等待回调的请求 std::unordered_map m_mapStepEmitNum; // 每个step发出请求(等待响应)数量 std::unordered_map> m_mapReply; std::map> m_mapTimeoutStep; diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index f4997cd1..022d0b38 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -48,7 +48,8 @@ template class SocketChannelImpl: public SocketChannel { public: - SocketChannelImpl(Labor* pLabor, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive = 0.0); + SocketChannelImpl(Labor* pLabor, std::shared_ptr pLogger, + bool bIsClient, bool bWithSsl, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive = 0.0); virtual ~SocketChannelImpl(); static bool NewCodec(std::shared_ptr pChannel, Labor* pLabor, std::shared_ptr pLogger, E_CODEC_TYPE eCodecType); @@ -67,8 +68,6 @@ class SocketChannelImpl: public SocketChannel virtual E_CODEC_STATUS Send(); - template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) const; - public: virtual int GetFd() const override { @@ -251,7 +250,6 @@ class SocketChannelImpl: public SocketChannel std::unordered_map m_mapStreamStepSeq; ///< 等待回调的http2 step seq std::set m_setSkipCodecType; ///< Codec转换需跳过的CodecType Labor* m_pLabor; - std::shared_ptr m_pLogger; friend class CodecFactory; friend class Dispatcher; @@ -259,14 +257,14 @@ class SocketChannelImpl: public SocketChannel template SocketChannelImpl::SocketChannelImpl(Labor* pLabor, std::shared_ptr pLogger, - int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) - : m_ucChannelStatus(CHANNEL_STATUS_INIT),m_eLastCodecStatus(CODEC_STATUS_OK), + bool bIsClient, bool bWithSsl, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) + : SocketChannel(pLogger, bIsClient, bWithSsl), + m_ucChannelStatus(CHANNEL_STATUS_INIT),m_eLastCodecStatus(CODEC_STATUS_OK), m_iRemoteWorkerIdx(-1), m_iFd(iFd), m_uiSeq(ulSeq), m_bPipeline(true), m_uiUnitTimeMsgNum(0), m_uiMsgNum(0), m_dActiveTime(0.0), m_dKeepAlive(dKeepAlive), m_pRecvBuff(nullptr), m_pSendBuff(nullptr), m_pWaitForSendBuff(nullptr), - m_pCodec(nullptr), m_pHoldingHttpMsg(nullptr), m_iErrno(0), m_pLabor(pLabor), - m_pLogger(pLogger) + m_pCodec(nullptr), m_pHoldingHttpMsg(nullptr), m_iErrno(0), m_pLabor(pLabor) { try { @@ -374,13 +372,6 @@ bool SocketChannelImpl::NeedAliveCheck() const return(true); } -template -template -void SocketChannelImpl::Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args) const -{ - m_pLogger->WriteLog(iLogLevel, szFileName, uiFileLine, szFunction, std::forward(args)...); -} - template bool SocketChannelImpl::NewCodec(std::shared_ptr pChannel, Labor* pLabor, std::shared_ptr pLogger, E_CODEC_TYPE eCodecType) { @@ -402,7 +393,7 @@ bool SocketChannelImpl::NewCodec(std::shared_ptr pChannel, Lab } if (pChannel->m_pImpl == nullptr) { - auto pImpl = std::make_shared>(pLabor, pLogger, pChannel->GetFd(), eCodecType); + auto pImpl = std::make_shared>(pLabor, pLogger, pChannel->IsClient(), pChannel->WithSsl(), pChannel->GetFd(), eCodecType); pImpl->SetCodec(pCodec); pChannel->InitImpl(std::static_pointer_cast(pImpl)); } diff --git a/src/channel/SocketChannelSslImpl.hpp b/src/channel/SocketChannelSslImpl.hpp index 5f7bcb3b..c0accdbf 100644 --- a/src/channel/SocketChannelSslImpl.hpp +++ b/src/channel/SocketChannelSslImpl.hpp @@ -32,7 +32,8 @@ template class SocketChannelSslImpl : public SocketChannelImpl { public: - SocketChannelSslImpl(Labor* pLabor, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive = 0.0); + SocketChannelSslImpl(Labor* pLabor, std::shared_ptr pLogger, + bool bIsClient, bool bWithSsl, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive = 0.0); virtual ~SocketChannelSslImpl(); int SslClientCtxCreate(); @@ -40,7 +41,6 @@ class SocketChannelSslImpl : public SocketChannelImpl int SslHandshake(); int SslShutdown(); - bool Init(bool bIsClient = false); template E_CODEC_STATUS SendRequest(uint32 uiStepSeq, Targs&&... args); template @@ -58,7 +58,6 @@ class SocketChannelSslImpl : public SocketChannelImpl private: E_SSL_CHANNEL_STATUS m_eSslChannelStatus; - bool m_bIsClientConnection; SSL* m_pSslConnection; }; diff --git a/src/channel/SocketChannelSslImpl.inl b/src/channel/SocketChannelSslImpl.inl index 1a77f7f7..1476cde2 100644 --- a/src/channel/SocketChannelSslImpl.inl +++ b/src/channel/SocketChannelSslImpl.inl @@ -15,9 +15,10 @@ namespace neb template SocketChannelSslImpl::SocketChannelSslImpl( - Labor* pLabor, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) - : SocketChannelImpl(pLabor, pLogger, iFd, ulSeq, dKeepAlive), - m_eSslChannelStatus(SSL_CHANNEL_INIT), m_bIsClientConnection(false), m_pSslConnection(NULL) + Labor* pLabor, std::shared_ptr pLogger, + bool bIsClient, bool bWithSsl, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive) + : SocketChannelImpl(pLabor, pLogger, bIsClient, bWithSsl, iFd, ulSeq, dKeepAlive), + m_eSslChannelStatus(SSL_CHANNEL_INIT), m_pSslConnection(NULL) { } @@ -31,7 +32,7 @@ SocketChannelSslImpl::~SocketChannelSslImpl() template int SocketChannelSslImpl::SslCreateConnection() { - if (m_bIsClientConnection) + if (SocketChannelImpl::IsClient()) { m_pSslConnection = SSL_new(SslContext::ClientSslCtx()); } @@ -52,7 +53,7 @@ int SocketChannelSslImpl::SslCreateConnection() return(ERR_SSL_NEW_CONNECTION); } - if (m_bIsClientConnection) + if (SocketChannelImpl::IsClient()) { SSL_set_connect_state(m_pSslConnection); } @@ -234,7 +235,7 @@ int SocketChannelSslImpl::SslShutdown() m_eSslChannelStatus = SSL_CHANNEL_SHUTDOWN; } - if (m_bIsClientConnection) + if (SocketChannelImpl::IsClient()) { SSL_CTX_remove_session(SslContext::ClientSslCtx(), SSL_get0_session(m_pSslConnection)); } @@ -258,18 +259,6 @@ int SocketChannelSslImpl::SslShutdown() return(ERR_OK); } -template -bool SocketChannelSslImpl::Init(bool bIsClient) -{ - m_bIsClientConnection = bIsClient; - if (ERR_OK != SslCreateConnection()) - { - return(false); - } - LOG4_TRACE("SslCreateConnection() successfully."); - return(true); -} - template template E_CODEC_STATUS SocketChannelSslImpl::SendRequest(uint32 uiStepSeq, Targs&&... args) @@ -428,7 +417,7 @@ E_CODEC_STATUS SocketChannelSslImpl::Recv(Targs&& ...args) { if (m_eSslChannelStatus == SSL_CHANNEL_ESTABLISHED) { - if (m_bIsClientConnection) + if (SocketChannelImpl::IsClient()) { return(CODEC_STATUS_WANT_WRITE); } diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp index 3394abb7..65c33fdd 100644 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -51,13 +51,12 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s { #ifdef WITH_OPENSSL auto pImpl = std::make_shared>( - pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); - pImpl->Init(bIsClient); + pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); #else auto pImpl = std::make_shared>( - pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); #endif @@ -65,7 +64,7 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s else { auto pImpl = std::make_shared>( - pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } @@ -76,13 +75,12 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s { #ifdef WITH_OPENSSL auto pImpl = std::make_shared>( - pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); - pImpl->Init(bIsClient); + pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); #else auto pImpl = std::make_shared>( - pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); #endif @@ -90,7 +88,7 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s else { auto pImpl = std::make_shared>( - pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } @@ -99,7 +97,7 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s { pCodec = new CodecHttp2(pLogger, eCodecType, pChannel); auto pImpl = std::make_shared>( - pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } @@ -108,7 +106,7 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s { pCodec = new CodecResp(pLogger, eCodecType, pChannel); auto pImpl = std::make_shared>( - pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } @@ -117,7 +115,7 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s { pCodec = new CodecPrivate(pLogger, eCodecType, pChannel); auto pImpl = std::make_shared>( - pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } @@ -126,7 +124,7 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s { pCodec = new CodecCass(pLogger, eCodecType, pChannel); auto pImpl = std::make_shared>( - pLabor, pLogger, iFd, pLabor->GetSequence(), dKeepAlive); + pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } diff --git a/src/util/StringCoder.cpp b/src/util/StringCoder.cpp index cbd21086..8349341d 100644 --- a/src/util/StringCoder.cpp +++ b/src/util/StringCoder.cpp @@ -8,6 +8,7 @@ * Modify history: ******************************************************************************/ #include "StringCoder.hpp" +#include namespace neb { @@ -183,21 +184,14 @@ void DecodeParameter(const std::string& strParameter, std::map& vecDest) { vecDest.clear(); - size_t uiBegin = 0; - size_t uiEnd = 0; - std::string strSub; - while (uiBegin < strSrc.size()) + char* p; + char* buff = const_cast(strSrc.data()); + p = strsep(&buff, strPattern.data()); + while (p != NULL) { - uiEnd = strSrc.find_first_of(strPattern, uiBegin); - if (std::string::npos == uiEnd) - { - strSub = strSrc.substr(uiBegin); - vecDest.push_back(strSub); - break; - } - strSub = strSrc.substr(uiBegin, uiEnd - uiBegin); - vecDest.push_back(strSub); - uiBegin = uiEnd + 1; + printf("%s\n", p); + vecDest.push_back(p); + p = strsep(&buff, strPattern.data()); } } From 71d06934db5a4fe5fb668eb16ea6d07b2bda81de Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 10 Dec 2022 11:30:53 +0800 Subject: [PATCH 166/176] v1.7.3 release --- src/actor/step/sys_step/StepRedisCluster.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index 36e5e844..74bc1646 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -124,7 +124,6 @@ E_CMD_STATUS StepRedisCluster::Emit(int iErrno, const std::string& strErrMsg, vo E_CMD_STATUS StepRedisCluster::Callback( std::shared_ptr pChannel, const RedisReply& oRedisReply) { - LOG4_TRACE("callback from %s:\n%s", pChannel->GetIdentify().c_str(), oRedisReply.DebugString().c_str()); std::shared_ptr pRedisRequest = nullptr; uint32 uiRealStepSeq = 0; if (m_bPipeline) @@ -475,7 +474,6 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, const RedisMsg& oR bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptr pRedisMsg) { - LOG4_TRACE("%s: %s", strIdentify.c_str(), pRedisMsg->DebugString().c_str()); bool bResult = IO::SendTo(this, strIdentify, SOCKET_STREAM, m_bWithSsl, m_bPipeline, (*pRedisMsg.get())); if (bResult) From 413020dc1d22b97f62689714e9cd74ef61190934 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 1 Jan 2023 11:19:30 +0800 Subject: [PATCH 167/176] add spec channel --- conf/nebula.json | 3 + proto/neb_sys.proto | 7 + src/Error.hpp | 7 + src/actor/Actor.cpp | 24 +- src/actor/Actor.hpp | 4 + src/actor/ActorBuilder.cpp | 7 +- src/actor/cmd/CW.hpp | 2 + .../cmd/sys_cmd/CmdSpecChannelCreated.cpp | 79 ++++ .../cmd/sys_cmd/CmdSpecChannelCreated.hpp | 34 ++ .../sys_session/manager/SessionManager.cpp | 133 ++++-- .../sys_session/manager/SessionManager.hpp | 13 +- src/channel/SelfChannel.hpp | 16 - src/channel/SocketChannel.cpp | 5 + src/channel/SocketChannel.hpp | 1 + src/channel/SpecChannel.hpp | 281 +++++++++++++ src/codec/Codec.hpp | 1 + src/codec/CodecFactory.cpp | 181 ++++++++ src/codec/CodecFactory.hpp | 3 + src/codec/CodecHttp.hpp | 65 +++ src/codec/CodecProto.hpp | 112 +++++ src/codec/CodecRaw.hpp | 70 ++++ src/codec/CodecResp.hpp | 65 +++ src/codec/cass/CassResponse.cpp | 17 + src/codec/cass/CassResponse.hpp | 2 + src/ios/ChannelWatcher.hpp | 8 - src/ios/Dispatcher.cpp | 44 +- src/ios/Dispatcher.hpp | 6 +- src/ios/IO.hpp | 41 +- src/ios/SpecChannelWatcher.cpp | 68 +++ src/ios/SpecChannelWatcher.hpp | 62 +++ src/labor/LaborShared.cpp | 197 +++++++++ src/labor/LaborShared.hpp | 80 ++++ src/labor/Manager.cpp | 90 +++- src/labor/Manager.hpp | 1 + src/labor/Worker.cpp | 2 + src/pb/neb_sys.pb.cc | 393 +++++++++++++++++- src/pb/neb_sys.pb.h | 145 +++++++ 37 files changed, 2176 insertions(+), 93 deletions(-) create mode 100644 src/actor/cmd/sys_cmd/CmdSpecChannelCreated.cpp create mode 100644 src/actor/cmd/sys_cmd/CmdSpecChannelCreated.hpp create mode 100644 src/channel/SpecChannel.hpp create mode 100644 src/ios/SpecChannelWatcher.cpp create mode 100644 src/ios/SpecChannelWatcher.hpp create mode 100644 src/labor/LaborShared.cpp create mode 100644 src/labor/LaborShared.hpp diff --git a/conf/nebula.json b/conf/nebula.json index 800447e6..68662835 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -5,6 +5,7 @@ "access_host": "192.168.18.81", "//access_port": "对系统外提供服务监听的端口", "access_port": 9988, + "access_ports":[], "//access_codec": "接入端编解码器,见codec/Codec.hpp中E_CODEC_TYPE枚举定义" "access_codec": 4, "access_socket_type":"TCP", @@ -33,6 +34,8 @@ "cpu_affinity":false, "connection_dispatcher":{"round_robin":0, "min_load":1, "client_addr_hash":2}, "connection_dispatch":0, + "//dispatch_with_port":"根据接入端口(奇偶)进行连接到worker的转发", + "dispatch_with_port":false, "daemonize":true, "//worker_capacity": "子进程最大工作负荷", "worker_capacity": 1000000, diff --git a/proto/neb_sys.proto b/proto/neb_sys.proto index 850d8dc6..dd6fc19d 100644 --- a/proto/neb_sys.proto +++ b/proto/neb_sys.proto @@ -55,3 +55,10 @@ message TraceLog bytes log_content = 8; } +message SpecChannelInfo +{ + uint32 codec_type = 1; + uint32 from_labor = 2; + uint32 to_labor = 3; +} + diff --git a/src/Error.hpp b/src/Error.hpp index 6d3463b0..a30ee988 100644 --- a/src/Error.hpp +++ b/src/Error.hpp @@ -44,6 +44,13 @@ enum E_ERROR_NO ERR_FILE_NOT_EXIST = 10020, ///< 文件不存在 ERR_CONNECTION = 10021, ///< 连接错误 + ERR_SPEC_CHANNEL_CREATE = 10100, ///< 创建spec channel失败 + ERR_SPEC_CHANNEL_CAST = 10101, ///< spec channel转换 + ERR_SPEC_CHANNEL_FULL = 10102, ///< spec channel写满 + ERR_SPEC_CHANNEL_TARGET = 10103, ///< spec channel源和目标相同 + ERR_SPEC_CHANNEL_LABOR_ID = 10104, ///< labor id 超出labor数量 + ERR_SPEC_CHANNEL_MANAGER = 10105, ///< manager to worker或worker to manager spec channel不存在 + /* 存储代理错误码段 11000~11999 */ ERR_INCOMPLET_DATAPROXY_DATA = 11001, ///< DataProxy请求数据包不完整 ERR_INVALID_REDIS_ROUTE = 11002, ///< 无效的redis路由信息 diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 5bf0050b..c2f6c2c3 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -22,7 +22,7 @@ namespace neb Actor::Actor(ACTOR_TYPE eActorType, ev_tstamp dTimeout) : m_eActorType(eActorType), m_uiActorStatus(ACT_STATUS_UNREGISTER), - m_uiSequence(0), m_dActiveTime(0.0), m_dTimeout(dTimeout), + m_uiSequence(0), m_uiPeerStepSeq(0), m_dActiveTime(0.0), m_dTimeout(dTimeout), m_pLabor(nullptr), m_pWatcher(nullptr), m_pContext(nullptr) { } @@ -65,6 +65,18 @@ uint32 Actor::GetWorkerIndex() const return(((Worker*)m_pLabor)->GetWorkerInfo().iWorkerIndex); } +uint32 Actor::GetLaborId() const +{ + if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) + { + return(m_pLabor->GetNodeInfo().uiWorkerNum + 1); + } + else + { + return(((Worker*)m_pLabor)->GetWorkerInfo().iWorkerIndex); + } +} + const std::string& Actor::GetNodeType() const { return(m_pLabor->GetNodeInfo().strNodeType); @@ -153,6 +165,16 @@ void Actor::SetContext(std::shared_ptr pContext) m_pContext = pContext; } +uint32 Actor::GetPeerStepSeq() const +{ + return(m_uiPeerStepSeq); +} + +void Actor::SetPeerStepSeq(uint32 uiPeerStepSeq) +{ + m_uiPeerStepSeq = uiPeerStepSeq; +} + bool Actor::SendTo(std::shared_ptr pChannel) { return(IO::Send(pChannel)); diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index 59d9f3d2..bedb0808 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -127,6 +127,7 @@ class Actor: public std::enable_shared_from_this public: uint32 GetNodeId() const; uint32 GetWorkerIndex() const; + uint32 GetLaborId() const; const std::string& GetNodeType() const; const std::string& GetWorkPath() const; const std::string& GetNodeIdentify() const; @@ -147,6 +148,8 @@ class Actor: public std::enable_shared_from_this std::shared_ptr GetOperator(const std::string& strOperatorName); std::shared_ptr GetContext(); void SetContext(std::shared_ptr pContext); + uint32 GetPeerStepSeq() const; + void SetPeerStepSeq(uint32 uiPeerStepSeq); void AddAssemblyLine(std::shared_ptr pSession); protected: @@ -349,6 +352,7 @@ class Actor: public std::enable_shared_from_this ACTOR_TYPE m_eActorType; uint32 m_uiActorStatus; uint32 m_uiSequence; + uint32 m_uiPeerStepSeq; ev_tstamp m_dActiveTime; ev_tstamp m_dTimeout; Labor* m_pLabor; diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index fba9bc53..5eb4e472 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -283,7 +283,10 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH } else // 回调 { - pChannel->m_pImpl->PopStepSeq(); + if (pChannel->GetCodecType() != CODEC_TRANSFER) + { + pChannel->m_pImpl->PopStepSeq(); + } auto step_iter = m_mapCallbackStep.find(oMsgHead.seq()); if (step_iter != m_mapCallbackStep.end()) // 步骤回调 { @@ -457,6 +460,7 @@ void ActorBuilder::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdOnGetCustomConf", (int)CMD_REQ_GET_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdOnStartService", (int)CMD_REQ_START_SERVICE); MakeSharedCmd(nullptr, "neb::CmdDataReport", (int)CMD_REQ_DATA_REPORT); + MakeSharedCmd(nullptr, "neb::CmdSpecChannelCreated", (int)CMD_REQ_SPEC_CHANNEL); std::string strModulePath = "/healthy"; MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); strModulePath = "/health"; @@ -474,6 +478,7 @@ void ActorBuilder::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdSetNodeConf", (int)CMD_REQ_SET_NODE_CONFIG); MakeSharedCmd(nullptr, "neb::CmdSetNodeCustomConf", (int)CMD_REQ_SET_NODE_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdReloadCustomConf", (int)CMD_REQ_RELOAD_CUSTOM_CONFIG); + MakeSharedCmd(nullptr, "neb::CmdSpecChannelCreated", (int)CMD_REQ_SPEC_CHANNEL); std::string strModulePath = "/healthy"; MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); strModulePath = "/health"; diff --git a/src/actor/cmd/CW.hpp b/src/actor/cmd/CW.hpp index d3580a74..121cfc7e 100644 --- a/src/actor/cmd/CW.hpp +++ b/src/actor/cmd/CW.hpp @@ -40,6 +40,8 @@ enum E_CMD CMD_RSP_UPDATE_WORKER_LOAD = 14, ///< 更新Worker进程负载信息应答(一般无须应答) CMD_REQ_START_SERVICE = 15, ///< 服务就绪请求 CMD_RSP_START_SERVICE = 16, ///< 服务就绪响应(无须响应) + CMD_REQ_SPEC_CHANNEL = 17, ///< 创建SpecChannel请求 + CMD_RSP_SPEC_CHANNEL = 18, ///< 创建SpecChannel响应(无须响应) CMD_REQ_NODE_STATUS_REPORT = 101, ///< 节点Server状态上报请求(各节点向控制中心上报自身状态信息) CMD_RSP_NODE_STATUS_REPORT = 102, ///< 节点Server状态上报应答 diff --git a/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.cpp b/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.cpp new file mode 100644 index 00000000..860d027d --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.cpp @@ -0,0 +1,79 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdSpecChannelCreated.cpp + * @brief + * @author Bwar + * @date: 2022-12-10 + * @note + * Modify history: + ******************************************************************************/ +#include "CmdSpecChannelCreated.hpp" +#include "codec/CodecFactory.hpp" +#include "labor/LaborShared.hpp" +#include "channel/SpecChannel.hpp" + +namespace neb +{ + +CmdSpecChannelCreated::CmdSpecChannelCreated(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdSpecChannelCreated::~CmdSpecChannelCreated() +{ +} + +bool CmdSpecChannelCreated::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + SpecChannelInfo oSpecInfo; + if (!oSpecInfo.ParseFromString(oInMsgBody.data())) + { + LOG4_ERROR("SpecChannelInfo ParseFromString failed."); + return(false); + } + LOG4_INFO("this labor %u, spec channel from %u to %u created, with codec type %u", + GetLaborId(), oSpecInfo.from_labor(), oSpecInfo.to_labor(), oSpecInfo.codec_type()); + if (oSpecInfo.to_labor() == GetLaborId()) + { + CodecFactory::OnSpecChannelCreated(oSpecInfo.codec_type(), oSpecInfo.from_labor(), oSpecInfo.to_labor()); + } + else // forward notifications through manager + { + auto pChannel = LaborShared::Instance()->GetSpecChannel(CodecNebulaInNode::Type(), GetLaborId(), oSpecInfo.to_labor()); + if (pChannel == nullptr) + { + LOG4_ERROR("no codec_type %u channel from %u to %u", CodecNebulaInNode::Type(), GetLaborId(), oSpecInfo.to_labor()); + return(false); + } + else + { + auto pNoticeSpecChannel = std::static_pointer_cast>(pChannel); + if (pNoticeSpecChannel == nullptr) + { + LOG4_ERROR("spec channel cast failed."); + return(false); + } + int iResult = pNoticeSpecChannel->Write(gc_uiCmdReq, 0, + std::move(const_cast(oInMsgHead)), + std::move(const_cast(oInMsgBody))); + if (iResult == ERR_OK) + { + LaborShared::Instance()->GetDispatcher(oSpecInfo.to_labor())->AsyncSend( + pNoticeSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + else + { + LOG4_ERROR("no codec_type %u channel write error %d", CodecNebulaInNode::Type(), iResult); + return(false); + } + } + } + return(true); +} + +} /* namespace neb */ + diff --git a/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.hpp b/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.hpp new file mode 100644 index 00000000..fbb5caa0 --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.hpp @@ -0,0 +1,34 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdSpecChannelCreated.hpp + * @brief + * @author Bwar + * @date: 2022-12-10 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_SYS_CMD_CMDSPECCHANNELCREATED_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_CMDSPECCHANNELCREATED_HPP_ + +#include "actor/ActorSys.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class CmdSpecChannelCreated: public Cmd, + public DynamicCreator, public ActorSys +{ +public: + CmdSpecChannelCreated(int32 iCmd); + virtual ~CmdSpecChannelCreated(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody); +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_CMDSPECCHANNELCREATED_HPP_ */ + diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 4fb2e7a8..93247c81 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -30,10 +30,10 @@ namespace neb std::mutex SessionManager::s_mutexWorker; std::vector SessionManager::s_vecWorkerThreadId; -SessionManager::SessionManager(bool bDirectToLoader, ev_tstamp dStatInterval) - : Session("neb::SessionManager", dStatInterval), m_bDirectToLoader(bDirectToLoader) +SessionManager::SessionManager(bool bDirectToLoader, bool bDispatchWithPort, ev_tstamp dStatInterval) + : Session("neb::SessionManager", dStatInterval), + m_bDirectToLoader(bDirectToLoader), m_bDispatchWithPort(bDispatchWithPort) { - m_iterWorkerInfo = m_mapWorkerInfo.begin(); } SessionManager::~SessionManager() @@ -50,7 +50,6 @@ SessionManager::~SessionManager() it->second = nullptr; } m_mapWorkerInfo.clear(); - m_iterWorkerInfo = m_mapWorkerInfo.begin(); m_mapWorkerStartNum.clear(); m_mapWorkerFdPid.clear(); m_mapOnlineNodes.clear(); @@ -67,10 +66,35 @@ E_CMD_STATUS SessionManager::Timeout() neb::ReportRecord* pRecord = nullptr; uint32 uiLoad = 0; uint32 uiConnect = 0; + char szKey[256] = {0}; for (auto iter = m_mapWorkerInfo.begin(); iter != m_mapWorkerInfo.end(); ++iter) { uiLoad += iter->second->uiLoad; uiConnect += iter->second->uiConnection; + pRecord = pReport->add_records(); + snprintf(szKey, 256, "W%u-recv_num", iter->first); + pRecord->set_key(szKey); + pRecord->set_item("nebula"); + pRecord->add_value(iter->second->uiRecvNum); + pRecord->set_value_type(ReportRecord::VALUE_ACC); + pRecord = pReport->add_records(); + snprintf(szKey, 256, "W%u-recv_bytes", iter->first); + pRecord->set_key(szKey); + pRecord->set_item("nebula"); + pRecord->add_value(iter->second->uiRecvByte); + pRecord->set_value_type(ReportRecord::VALUE_ACC); + pRecord = pReport->add_records(); + snprintf(szKey, 256, "W%u-send_num", iter->first); + pRecord->set_key(szKey); + pRecord->set_item("nebula"); + pRecord->add_value(iter->second->uiSendNum); + pRecord->set_value_type(ReportRecord::VALUE_ACC); + pRecord = pReport->add_records(); + snprintf(szKey, 256, "W%u-send_bytes", iter->first); + pRecord->set_key(szKey); + pRecord->set_item("nebula"); + pRecord->add_value(iter->second->uiSendByte); + pRecord->set_value_type(ReportRecord::VALUE_ACC); } pRecord = pReport->add_records(); pRecord->set_key("load"); @@ -174,14 +198,12 @@ void SessionManager::AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, i if (GetLabor(this)->GetNodeInfo().bThreadMode) { m_mapWorkerInfo.insert(std::make_pair(iWorkerIndex, pWorkerAttr)); - m_iterWorkerInfo = m_mapWorkerInfo.begin(); m_mapWorkerFdPid.insert(std::pair(iControlFd, iWorkerIndex)); m_mapWorkerFdPid.insert(std::pair(iDataFd, iWorkerIndex)); } else { m_mapWorkerInfo.insert(std::make_pair(iPid, pWorkerAttr)); - m_iterWorkerInfo = m_mapWorkerInfo.begin(); m_mapWorkerFdPid.insert(std::pair(iControlFd, iPid)); m_mapWorkerFdPid.insert(std::pair(iDataFd, iPid)); } @@ -313,7 +335,7 @@ void SessionManager::SetLoaderActorBuilder(ActorBuilder* pActorBuilder) } } -int SessionManager::GetWorkerDataFd(const char* szRemoteAddr, int iRemotePort) +int SessionManager::GetWorkerDataFd(const char* szRemoteAddr, int iRemotePort, int iBonding) { if (m_bDirectToLoader && m_iLoaderDataFd != -1) { @@ -328,13 +350,67 @@ int SessionManager::GetWorkerDataFd(const char* szRemoteAddr, int iRemotePort) char szIdentify[64] = {0}; snprintf(szIdentify, 64, "%s:%d", szRemoteAddr, iRemotePort); uint32 uiKeyHash = CityHash32(szIdentify, strlen(szIdentify)); - uint32 uiIndex = uiKeyHash % (m_vecWorkerDataFd.size() - 1); // loader▒~Z~Dworker▒~V▒~O▒为0 + uint32 uiIndex = uiKeyHash % (m_vecWorkerDataFd.size() - 1) + 1; + if (m_bDispatchWithPort && m_vecWorkerDataFd.size() > 2 + && (0x00000001 & iBonding) != (0x00000001 & uiIndex)) + { + if (uiIndex > 1) + { + uiIndex -= 1; + } + else + { + uiIndex = m_vecWorkerDataFd.size() - 1; + if ((0x00000001 & iBonding) != (0x00000001 & uiIndex)) + { + uiIndex -= 1; + } + } + } return(m_vecWorkerDataFd[uiIndex + 1]); } return(-1); } -int SessionManager::GetMinLoadWorkerDataFd() +int SessionManager::GetNextWorkerDataFd(int iBonding) +{ + if (m_bDirectToLoader && m_iLoaderDataFd != -1) + { + return(m_iLoaderDataFd); + } + else + { + if (m_mapWorkerInfo.empty()) + { + return(-1); + } + if (m_bDispatchWithPort && m_vecWorkerDataFd.size() > 2) + { + uint32 i = 0x00000001 & iBonding; + do + { + m_uiWorkerDataFdIndex[i]++; + if (m_uiWorkerDataFdIndex[i] >= m_vecWorkerDataFd.size()) + { + m_uiWorkerDataFdIndex[i] = 1; + } + } + while ((0x00000001 & iBonding) != (0x00000001 & m_uiWorkerDataFdIndex[i])); + return(m_vecWorkerDataFd[m_uiWorkerDataFdIndex[i]]); + } + else + { + m_uiWorkerDataFdIndex[0]++; + if (m_uiWorkerDataFdIndex[0] >= m_vecWorkerDataFd.size()) + { + m_uiWorkerDataFdIndex[0] = 1; + } + return(m_vecWorkerDataFd[m_uiWorkerDataFdIndex[0]]); + } + } +} + +int SessionManager::GetMinLoadWorkerDataFd(int iBonding) { LOG4_TRACE(" "); int iMinLoadWorkerFd = 0; @@ -353,6 +429,11 @@ int SessionManager::GetMinLoadWorkerDataFd() { continue; } + if (m_bDispatchWithPort && m_vecWorkerDataFd.size() > 2 + && (0x00000001 & iBonding) != (0x00000001 & iter->second->iWorkerIndex)) + { + continue; + } if (iMinLoad == -1) { iMinLoadWorkerFd = iter->second->iDataFd; @@ -385,39 +466,6 @@ int SessionManager::GetMinLoadWorkerDataFd() return(iMinLoadWorkerFd); } -int SessionManager::GetNextWorkerDataFd() -{ - if (m_bDirectToLoader && m_iLoaderDataFd != -1) - { - return(m_iLoaderDataFd); - } - else - { - if (m_mapWorkerInfo.empty()) - { - return(-1); - } - ++m_iterWorkerInfo; - if (m_iterWorkerInfo == m_mapWorkerInfo.end()) - { - m_iterWorkerInfo = m_mapWorkerInfo.begin(); - } - if (m_iterWorkerInfo->second->iDataFd == m_iLoaderDataFd) - { - ++m_iterWorkerInfo; - if (m_iterWorkerInfo == m_mapWorkerInfo.end()) - { - if (m_mapWorkerInfo.size() == 1) - { - return(-1); - } - m_iterWorkerInfo = m_mapWorkerInfo.begin(); - } - } - return(m_iterWorkerInfo->second->iDataFd); - } -} - bool SessionManager::CheckWorker() { LOG4_TRACE(" "); @@ -471,7 +519,6 @@ bool SessionManager::WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pDataChannel); delete worker_iter->second; m_mapWorkerInfo.erase(worker_iter); - m_iterWorkerInfo = m_mapWorkerInfo.begin(); auto restart_num_iter = m_mapWorkerStartNum.find(iWorkerIndex); if (restart_num_iter != m_mapWorkerStartNum.end()) diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index f8590bc4..d3de3598 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -23,11 +23,11 @@ namespace neb class Loader; class SessionManager : public Session, - public DynamicCreator, + public DynamicCreator, public ActorSys { public: - SessionManager(bool bDirectToLoader, ev_tstamp dStatInterval); + SessionManager(bool bDirectToLoader, bool bDispatchWithPort, ev_tstamp dStatInterval); virtual ~SessionManager(); virtual E_CMD_STATUS Timeout(); @@ -44,9 +44,9 @@ class SessionManager : public Session, const WorkerInfo* GetWorkerInfo(int32 iWorkerIndex) const; bool SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad); void SetLoaderActorBuilder(ActorBuilder* pActorBuilder); - int GetWorkerDataFd(const char* szClientAddr, int iClientPort); - int GetNextWorkerDataFd(); - int GetMinLoadWorkerDataFd(); + int GetWorkerDataFd(const char* szClientAddr, int iClientPort, int iBonding); + int GetNextWorkerDataFd(int iBonding); + int GetMinLoadWorkerDataFd(int iBonding); bool CheckWorker(); bool WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& eLaborType); void SendOnlineNodesToWorker(); @@ -63,11 +63,12 @@ class SessionManager : public Session, private: bool m_bDirectToLoader = false; + bool m_bDispatchWithPort = false; int m_iLoaderDataFd = -1; + uint32 m_uiWorkerDataFdIndex[2] = {0}; ///< for round robin fd transfer std::vector m_vecWorkerDataFd; ///< only for fd transfer std::unordered_map m_mapWorker; ///< only thread worker std::unordered_map m_mapWorkerInfo; ///< 业务逻辑工作进程及进程属性,key为pid - std::unordered_map::iterator m_iterWorkerInfo; std::unordered_map m_mapWorkerStartNum; ///< 进程被启动次数,key为WorkerIdx std::unordered_map m_mapWorkerFdPid; ///< 工作进程通信FD对应的进程号 std::unordered_map m_mapOnlineNodes; ///< 订阅的节点在线信息 diff --git a/src/channel/SelfChannel.hpp b/src/channel/SelfChannel.hpp index 2bf54e77..2816a508 100644 --- a/src/channel/SelfChannel.hpp +++ b/src/channel/SelfChannel.hpp @@ -41,21 +41,6 @@ class SelfChannel: public SocketChannel return(false); } - virtual const std::string& GetIdentify() const override - { - return(strEmpty); - } - - virtual const std::string& GetRemoteAddr() const override - { - return(strEmpty); - } - - virtual const std::string& GetClientData() const override - { - return(strEmpty); - } - virtual E_CODEC_TYPE GetCodecType() const override { return(CODEC_DIRECT); @@ -85,7 +70,6 @@ class SelfChannel: public SocketChannel bool m_bIsResponse; uint32 m_uiChannelSeq; uint32 m_uiStepSeq; - std::string strEmpty; }; } /* namespace neb */ diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index 123b60aa..7b3304b0 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -471,6 +471,11 @@ int SocketChannel::RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, in return(ERR_OK); } +uint32 SocketChannel::GetPeerStepSeq() const +{ + return(0); +} + ChannelWatcher* SocketChannel::MutableWatcher() { if (nullptr == m_pWatcher) diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index 4b4b1a10..a2c736e9 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -68,6 +68,7 @@ class SocketChannel: public Channel virtual uint32 GetMsgNum() const; virtual uint32 GetUnitTimeMsgNum() const; virtual E_CODEC_STATUS Send(); + virtual uint32 GetPeerStepSeq() const; ChannelWatcher* MutableWatcher(); template diff --git a/src/channel/SpecChannel.hpp b/src/channel/SpecChannel.hpp new file mode 100644 index 00000000..c0cf1867 --- /dev/null +++ b/src/channel/SpecChannel.hpp @@ -0,0 +1,281 @@ +/******************************************************************************* + * Project: Nebula + * @file SpecChannel.hpp + * @brief lock-free/wait-free spec channel + * @author Bwar + * @date: 2022-12-03 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CHANNEL_SPECCHANNEL_HPP_ +#define SRC_CHANNEL_SPECCHANNEL_HPP_ + +#include +#include +#include "SocketChannel.hpp" +#include "ios/SpecChannelWatcher.hpp" + +namespace neb +{ + +template +class SpecChannel: public SocketChannel +{ +public: + SpecChannel(uint32 uiWriteWorker, uint32 uiReadWorker, + uint32 uiQueueSize, bool bWithHeader = false); + virtual ~SpecChannel(); + + int Write(uint32 uiFlags, uint32 uiStepSeq, Tdata&& oData); + + int Write(uint32 uiFlags, uint32 uiStepSeq, Thead&& oHead, Tdata&& oData); + + bool Read(uint32& uiFlags, uint32& uiStepSeq, Tdata& oData); + + bool Read(uint32& uiFlags, uint32& uiStepSeq, Thead& oHead, Tdata& oData); + + bool IsEmpty() const; + + void GetEnds(uint32& uiFrom, uint32& uiTo) const; + + virtual int GetFd() const override + { + return(0); + } + + virtual uint32 GetSequence() const override + { + return(0); + } + + virtual bool IsClient() const override + { + return(false); + } + + virtual bool IsPipeline() const override + { + return(true); + } + + virtual const std::string& GetIdentify() const override + { + return(m_strIdentify); + } + + virtual const std::string& GetRemoteAddr() const override + { + return(m_strRemoteAddr); + } + + virtual E_CODEC_TYPE GetCodecType() const override + { + return(CODEC_TRANSFER); + } + + virtual uint32 GetPeerStepSeq() const + { + return(m_uiPeerStepSeq); + } + + uint32 GetOwnerId() const + { + return(m_uiReadLaborIndex); + } + + SpecChannelWatcher* MutableWatcher(); + +protected: + virtual void SetIdentify(const std::string& strIdentify); + virtual void SetRemoteAddr(const std::string& strRemoteAddr); + void WriteData(Tdata&& oData); + void WriteHeadAndData(Thead&& oHead, Tdata&& oData); + +private: + bool m_bWithHeader; + uint32 m_uiWriteLaborIndex; + uint32 m_uiReadLaborIndex; + uint32 m_uiSize; + uint32 m_uiEffectiveSize; + uint32 m_uiPeerStepSeq; + std::atomic m_uiWriteIndex; + std::atomic m_uiReadIndex; + std::string m_strIdentify; + std::string m_strRemoteAddr; + std::vector m_vecFlags; + std::vector> m_vecData; + std::vector m_vecHead; + SpecChannelWatcher* m_pWatcher; +}; + +template +SpecChannel::SpecChannel( + uint32 uiWriteWorker, uint32 uiReadWorker, uint32 uiQueueSize, bool bWithHeader) + : m_bWithHeader(bWithHeader), + m_uiWriteLaborIndex(uiWriteWorker), m_uiReadLaborIndex(uiReadWorker), + m_uiSize(uiQueueSize), m_uiEffectiveSize(0), m_uiPeerStepSeq(0), + m_uiWriteIndex(0), m_uiReadIndex(0), + m_pWatcher(nullptr) +{ +} + +template +SpecChannel::~SpecChannel() +{ + if (nullptr != m_pWatcher) + { + delete m_pWatcher; + m_pWatcher = nullptr; + } +} + +template +int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Tdata&& oData) +{ + auto const uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); + auto uiNextRecord = uiCurrentWrite + 1; + uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; + if (m_uiEffectiveSize < m_uiSize) + { + m_vecFlags.push_back(uiFlags); + m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, std::forward(oData)))); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + ++m_uiEffectiveSize; + return(ERR_OK); + } + else + { + if (uiNextRecord == m_uiReadIndex.load(std::memory_order_acquire)) // queue is full + { + return(ERR_SPEC_CHANNEL_FULL); + } + else + { + m_vecFlags[uiCurrentWrite] = uiFlags; + m_vecData[uiCurrentWrite].first = uiStepSeq; + m_vecData[uiCurrentWrite].second = std::forward(oData); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + return(ERR_OK); + } + } +} + +template +int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Thead&& oHead, Tdata&& oData) +{ + auto const uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); + auto uiNextRecord = uiCurrentWrite + 1; + uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; + if (m_uiEffectiveSize < m_uiSize) + { + m_vecFlags.push_back(uiFlags); + m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, std::forward(oData)))); + m_vecHead.emplace_back(std::forward(oHead)); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + ++m_uiEffectiveSize; + return(ERR_OK); + } + else + { + if (uiNextRecord == m_uiReadIndex.load(std::memory_order_acquire)) // queue is full + { + return(ERR_SPEC_CHANNEL_FULL); + } + else + { + m_vecFlags[uiCurrentWrite] = uiFlags; + m_vecData[uiCurrentWrite].first = uiStepSeq; + m_vecData[uiCurrentWrite].second = std::forward(oData); + m_vecHead[uiCurrentWrite] = std::forward(oHead); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + return(ERR_OK); + } + } +} + +template +bool SpecChannel::Read(uint32& uiFlags, uint32& uiStepSeq, Tdata& oData) +{ + auto const uiCurrentRead = m_uiReadIndex.load(std::memory_order_relaxed); + if (uiCurrentRead == m_uiWriteIndex.load(std::memory_order_acquire)) // queue is empty + { + return(false); + } + + auto uiNextRecord = uiCurrentRead + 1; + uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; + uiFlags = m_vecFlags[uiCurrentRead]; + uiStepSeq = m_vecData[uiCurrentRead].first; + oData = std::move(m_vecData[uiCurrentRead].second); + m_uiPeerStepSeq = uiStepSeq; + m_uiReadIndex.store(uiNextRecord, std::memory_order_release); + return(true); +} + +template +bool SpecChannel::Read(uint32& uiFlags, uint32& uiStepSeq, Thead& oHead, Tdata& oData) +{ + auto const uiCurrentRead = m_uiReadIndex.load(std::memory_order_relaxed); + if (uiCurrentRead == m_uiWriteIndex.load(std::memory_order_acquire)) // queue is empty + { + return(false); + } + + auto uiNextRecord = uiCurrentRead + 1; + uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; + uiFlags = m_vecFlags[uiCurrentRead]; + uiStepSeq = m_vecData[uiCurrentRead].first; + oData = std::move(m_vecData[uiCurrentRead].second); + oHead = std::move(m_vecHead[uiCurrentRead]); + m_uiPeerStepSeq = uiStepSeq; + m_uiReadIndex.store(uiNextRecord, std::memory_order_release); + return(true); +} + +template +bool SpecChannel::IsEmpty() const +{ + return(m_uiReadIndex.load(std::memory_order_acquire) + == m_uiWriteIndex.load(std::memory_order_acquire)); +} + +template +void SpecChannel::GetEnds(uint32& uiFrom, uint32& uiTo) const +{ + uiFrom = m_uiWriteLaborIndex; + uiTo = m_uiReadLaborIndex; +} + +template +void SpecChannel::SetIdentify(const std::string& strIdentify) +{ + m_strIdentify = strIdentify; +} + +template +void SpecChannel::SetRemoteAddr(const std::string& strRemoteAddr) +{ + m_strRemoteAddr = strRemoteAddr; +} + +template +SpecChannelWatcher* SpecChannel::MutableWatcher() +{ + if (nullptr == m_pWatcher) + { + try + { + m_pWatcher = new SpecChannelWatcher(); + } + catch(std::bad_alloc& e) + { + LOG4_TRACE("new AsyncWatcher error %s", e.what()); + } + } + return(m_pWatcher); +} + +} /* namespace neb */ + +#endif /* SRC_CHANNEL_SPECCHANNEL_HPP_ */ + diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 5a06c082..5487969c 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -48,6 +48,7 @@ enum E_CODEC_TYPE CODEC_DIRECT = 11, ///< 虚拟编解码类型,用于SelfChannel,以参数方式直接传递数据包 CODEC_CASS = 12, ///< cassandra scylladb CODEC_RAW = 13, ///< 裸数据传输 + CODEC_TRANSFER = 14, ///< 虚拟编解码类型,用于SpecChannel }; /** diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp index 65c33fdd..1f22c8b0 100644 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -12,6 +12,8 @@ #include "channel/SocketChannel.hpp" #include "channel/SocketChannelImpl.hpp" #include "channel/SocketChannelSslImpl.hpp" +#include "channel/SpecChannel.hpp" +#include "labor/LaborShared.hpp" namespace neb { @@ -171,6 +173,95 @@ Codec* CodecFactory::CreateCodec(std::shared_ptr pLogger, E_CODEC_TYP return(pCodec); } +E_CODEC_STATUS CodecFactory::OnEvent(SpecChannelWatcher* pAsyncWatcher, std::shared_ptr pChannel) +{ + uint32 uiFlags = 0; + uint32 uiStepSeq = 0; + switch (pAsyncWatcher->GetCodecType()) + { + case CODEC_PROTO: + case CODEC_NEBULA: + case CODEC_NEBULA_IN_NODE: + { + MsgHead oMsgHead; + MsgBody oMsgBody; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->m_pLastActivityChannel = pChannel; + while (pSpecChannel->Read(uiFlags, uiStepSeq, oMsgHead, oMsgBody)) + { + if (gc_uiCmdReq & oMsgHead.cmd()) + { + IO::OnMessage(pDispatcher, pChannel, oMsgHead, oMsgBody); + } + else + { + IO::OnMessage(pDispatcher, pChannel, oMsgHead, oMsgBody); + } + } + } + break; + case CODEC_HTTP: + case CODEC_HTTP2: + { + HttpMsg oHttpMsg; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->m_pLastActivityChannel = pChannel; + while (pSpecChannel->Read(uiFlags, uiStepSeq, oHttpMsg)) + { + if (gc_uiCmdReq & uiFlags) + { + IO::OnRequest(pDispatcher, pChannel, oHttpMsg.path(), oHttpMsg); + } + else + { + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oHttpMsg); + } + } + } + break; + case CODEC_RESP: + { + RedisMsg oRedisMsg; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->m_pLastActivityChannel = pChannel; + while (pSpecChannel->Read(uiFlags, uiStepSeq, oRedisMsg)) + { + if (gc_uiCmdReq & uiFlags) + { + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_REDIS_PROXY, oRedisMsg); + } + else + { + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oRedisMsg); + } + } + } + break; + case CODEC_CASS: + { + CassResponse oCassResponse; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->m_pLastActivityChannel = pChannel; + while (pSpecChannel->Read(uiFlags, uiStepSeq, oCassResponse)) + { + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oCassResponse); + } + } + break; + case CODEC_PRIVATE: + break; + case CODEC_UNKNOW: + break; + default: + ; + } + return(CODEC_STATUS_OK); +} + E_CODEC_STATUS CodecFactory::OnEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel) { uint32 uiLastCodecPos = 0; @@ -223,6 +314,96 @@ E_CODEC_STATUS CodecFactory::OnEvent(Dispatcher* pDispatcher, std::shared_ptrGetSpecChannel(uiCodecType, uiFromLabor, uiToLabor); + uint32 uiFlags = 0; + uint32 uiStepSeq = 0; + switch (uiCodecType) + { + case CODEC_PROTO: + case CODEC_NEBULA: + case CODEC_NEBULA_IN_NODE: + { + MsgHead oMsgHead; + MsgBody oMsgBody; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->AddEvent(pSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); + while (pSpecChannel->Read(uiFlags, uiStepSeq, oMsgHead, oMsgBody)) + { + if (gc_uiCmdReq & oMsgHead.cmd()) + { + IO::OnMessage(pDispatcher, pChannel, oMsgHead, oMsgBody); + } + else + { + IO::OnMessage(pDispatcher, pChannel, oMsgHead, oMsgBody); + } + } + } + break; + case CODEC_HTTP: + case CODEC_HTTP2: + { + HttpMsg oHttpMsg; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->AddEvent(pSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); + while (pSpecChannel->Read(uiFlags, uiStepSeq, oHttpMsg)) + { + if (gc_uiCmdReq & uiFlags) + { + IO::OnRequest(pDispatcher, pChannel, oHttpMsg.path(), oHttpMsg); + } + else + { + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oHttpMsg); + } + } + } + break; + case CODEC_RESP: + { + RedisMsg oRedisMsg; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->AddEvent(pSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); + while (pSpecChannel->Read(uiFlags, uiStepSeq, oRedisMsg)) + { + if (gc_uiCmdReq & uiFlags) + { + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_REDIS_PROXY, oRedisMsg); + } + else + { + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oRedisMsg); + } + } + } + break; + case CODEC_CASS: + { + CassResponse oCassResponse; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->AddEvent(pSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); + while (pSpecChannel->Read(uiFlags, uiStepSeq, oCassResponse)) + { + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oCassResponse); + } + } + break; + case CODEC_PRIVATE: + break; + case CODEC_UNKNOW: + break; + default: + ; + } + return(CODEC_STATUS_OK); +} + bool CodecFactory::OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { MsgHead oMsgHead; diff --git a/src/codec/CodecFactory.hpp b/src/codec/CodecFactory.hpp index 49704960..ecaf0853 100644 --- a/src/codec/CodecFactory.hpp +++ b/src/codec/CodecFactory.hpp @@ -36,6 +36,7 @@ namespace neb class Dispatcher; class SocketChannel; class SelfChannel; +class SpecChannelWatcher; class CodecFactory { @@ -46,7 +47,9 @@ class CodecFactory static std::shared_ptr CreateChannel(Labor* pLabor, std::shared_ptr pLogger, int iFd, E_CODEC_TYPE eCodecType, bool bIsClient, bool bWithSsl); static Codec* CreateCodec(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel); + static E_CODEC_STATUS OnEvent(SpecChannelWatcher* pAsyncWatcher, std::shared_ptr pChannel); static E_CODEC_STATUS OnEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel); + static bool OnSpecChannelCreated(uint32 uiCodecType, uint32 uiFromLabor, uint32 uiToLabor); static bool OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); static bool OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); diff --git a/src/codec/CodecHttp.hpp b/src/codec/CodecHttp.hpp index a2d2b439..6245bbb0 100644 --- a/src/codec/CodecHttp.hpp +++ b/src/codec/CodecHttp.hpp @@ -13,6 +13,8 @@ #include "util/http/http_parser.h" #include "pb/http.pb.h" #include "Codec.hpp" +#include "channel/SpecChannel.hpp" +#include "labor/LaborShared.hpp" namespace neb { @@ -28,6 +30,14 @@ class CodecHttp: public Codec { return(CODEC_HTTP); } + + // request + template + static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg); + + // response + template + static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg); E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff); @@ -80,6 +90,61 @@ class CodecHttp: public Codec std::unordered_map m_mapAddingHttpHeader; ///< encode前添加的http头,encode之后要清空 }; +// request +template +int CodecHttp::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg) +{ + if (uiFromLabor == uiToLabor) + { + return(ERR_SPEC_CHANNEL_TARGET); + } + std::shared_ptr> pSpecChannel = nullptr; + auto pLaborShared = LaborShared::Instance(); + auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); + if (pChannel == nullptr) + { + pSpecChannel = std::make_shared>( + uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CREATE); + } + pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, Type()); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oHttpMsg))); + if (iResult == ERR_OK) + { + return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); + } + return(iResult); + } + else + { + pSpecChannel = std::static_pointer_cast>(pChannel); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CAST); + } + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oHttpMsg))); + if (iResult == ERR_OK) + { + pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + return(iResult); + } +} + +// response +template +int CodecHttp::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg) +{ + uint32 uiFrom; + uint32 uiTo; + std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); + return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, oHttpMsg)); +} + } /* namespace neb */ #endif /* SRC_CODEC_CODECHTTP_HPP_ */ diff --git a/src/codec/CodecProto.hpp b/src/codec/CodecProto.hpp index 57046058..1f8d84fe 100644 --- a/src/codec/CodecProto.hpp +++ b/src/codec/CodecProto.hpp @@ -11,6 +11,9 @@ #define SRC_CODEC_CODECPROTO_HPP_ #include "Codec.hpp" +#include "ios/Dispatcher.hpp" +#include "channel/SpecChannel.hpp" +#include "labor/LaborShared.hpp" namespace neb { @@ -27,6 +30,21 @@ class CodecProto: public Codec return(CODEC_PROTO); } + // request + template + static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const MsgHead& oMsgHead, const MsgBody& oMsgBody) + { + return(CodecProto::Write(Type(), uiToLabor, uiFlags, uiStepSeq, + std::move(const_cast(oMsgHead)), std::move(const_cast(oMsgBody)))); + } + + // response + template + static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) + { + return(CodecProto::Write(Type(), pChannel, uiFlags, uiStepSeq, iCmd, uiSeq, std::move(const_cast(oMsgBody)))); + } + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(int iCmd, uint32 uiSeq, const MsgBody& oMsgBody, CBuffer* pBuff); E_CODEC_STATUS Encode(int iCmd, uint32 uiSeq, const MsgBody& oMsgBody, CBuffer* pBuff, CBuffer* pSecondlyBuff); @@ -36,6 +54,13 @@ class CodecProto: public Codec protected: E_CODEC_STATUS ChannelSticky(const MsgHead& oMsgHead, const MsgBody& oMsgBody); + // request + template + static int Write(uint32 uiCodecType, uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, MsgHead&& oMsgHead, MsgBody&& oMsgBody); + // response + template + static int Write(uint32 uiCodecType, std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, int32 iCmd, uint32 uiSeq, MsgBody&& oMsgBody); + private: uint32 m_uiForeignSeq = 0; }; @@ -56,6 +81,21 @@ class CodecNebula: public CodecProto { return(CODEC_NEBULA); } + + // request + template + static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const MsgHead& oMsgHead, const MsgBody& oMsgBody) + { + return(CodecProto::Write(Type(), uiToLabor, uiFlags, uiStepSeq, + std::move(const_cast(oMsgHead)), std::move(const_cast(oMsgBody)))); + } + + // response + template + static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) + { + return(CodecProto::Write(Type(), pChannel, uiFlags, uiStepSeq, iCmd, uiSeq, std::move(const_cast(oMsgBody)))); + } }; class CodecNebulaInNode: public CodecProto @@ -74,8 +114,80 @@ class CodecNebulaInNode: public CodecProto { return(CODEC_NEBULA_IN_NODE); } + + // request + template + static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const MsgHead& oMsgHead, const MsgBody& oMsgBody) + { + return(CodecProto::Write(Type(), uiToLabor, uiFlags, uiStepSeq, + std::move(const_cast(oMsgHead)), std::move(const_cast(oMsgBody)))); + } + + // response + template + static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) + { + return(CodecProto::Write(Type(), pChannel, uiFlags, uiStepSeq, iCmd, uiSeq, std::move(const_cast(oMsgBody)))); + } }; +template +int CodecProto::Write(uint32 uiCodecType, uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, MsgHead&& oMsgHead, MsgBody&& oMsgBody) +{ + if (uiFromLabor == uiToLabor) + { + return(ERR_SPEC_CHANNEL_TARGET); + } + std::shared_ptr> pSpecChannel = nullptr; + auto pLaborShared = LaborShared::Instance(); + auto pChannel = pLaborShared->GetSpecChannel(uiCodecType, uiFromLabor, uiToLabor); + if (pChannel == nullptr) + { + pSpecChannel = std::make_shared>( + uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CREATE); + } + pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, uiCodecType); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::forward(oMsgHead), std::forward(oMsgBody)); + if (iResult == ERR_OK) + { + return(pLaborShared->AddSpecChannel(uiCodecType, uiFromLabor, uiToLabor, pChannel)); + } + return(iResult); + } + else + { + pSpecChannel = std::static_pointer_cast>(pChannel); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CAST); + } + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::forward(oMsgHead), std::forward(oMsgBody)); + if (iResult == ERR_OK) + { + pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + return(iResult); + } +} + +template +int CodecProto::Write(uint32 uiCodecType, std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, int32 iCmd, uint32 uiSeq, MsgBody&& oMsgBody) +{ + uint32 uiFrom; + uint32 uiTo; + MsgHead oMsgHead; + oMsgHead.set_cmd(iCmd); + oMsgHead.set_seq(uiSeq); + std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); + return(Write(uiCodecType, uiTo, uiFrom, uiFlags, uiStepSeq, std::forward(oMsgHead), std::forward(oMsgBody))); +} + + } /* namespace neb */ #endif /* SRC_CODEC_CODECPROTO_HPP_ */ diff --git a/src/codec/CodecRaw.hpp b/src/codec/CodecRaw.hpp index a1c19f82..d87dd6f0 100644 --- a/src/codec/CodecRaw.hpp +++ b/src/codec/CodecRaw.hpp @@ -11,6 +11,9 @@ #define SRC_CODEC_CODECRAW_HPP_ #include "Codec.hpp" +#include "type/Notations.hpp" +#include "channel/SpecChannel.hpp" +#include "labor/LaborShared.hpp" namespace neb { @@ -27,6 +30,14 @@ class CodecRaw: public Codec return(CODEC_RAW); } + // request + template + static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const char* pRaw, uint32 uiRawSize); + + // response + template + static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const char* pRaw, uint32 uiRawSize); + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(const char* pRaw, uint32 uiRawSize, CBuffer* pBuff); E_CODEC_STATUS Encode(const char* pRaw, uint32 uiRawSize, CBuffer* pBuff, CBuffer* pSecondlyBuff); @@ -34,6 +45,65 @@ class CodecRaw: public Codec E_CODEC_STATUS Decode(CBuffer* pBuff, CBuffer& oBuff, CBuffer* pReactBuff); }; +// request +template +int CodecRaw::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const char* pRaw, uint32 uiRawSize) +{ + if (uiFromLabor == uiToLabor) + { + return(ERR_SPEC_CHANNEL_TARGET); + } + std::shared_ptr> pSpecChannel = nullptr; + auto pLaborShared = LaborShared::Instance(); + auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); + if (pChannel == nullptr) + { + pSpecChannel = std::make_shared>( + uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CREATE); + } + pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, Type()); + Bytes oBytes; + oBytes.Assign(pRaw, uiRawSize); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(oBytes)); + if (iResult == ERR_OK) + { + return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); + } + return(iResult); + } + else + { + pSpecChannel = std::static_pointer_cast>(pChannel); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CAST); + } + Bytes oBytes; + oBytes.Assign(pRaw, uiRawSize); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(oBytes)); + if (iResult == ERR_OK) + { + pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + return(iResult); + } +} + +// response +template +int CodecRaw::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const char* pRaw, uint32 uiRawSize) +{ + uint32 uiFrom; + uint32 uiTo; + std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); + return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, pRaw, uiRawSize)); +} + } /* namespace neb */ #endif /* SRC_CODEC_CODECRAW_HPP_ */ diff --git a/src/codec/CodecResp.hpp b/src/codec/CodecResp.hpp index 01e08205..036e4ced 100644 --- a/src/codec/CodecResp.hpp +++ b/src/codec/CodecResp.hpp @@ -13,6 +13,8 @@ #include #include "Codec.hpp" #include "pb/redis.pb.h" +#include "channel/SpecChannel.hpp" +#include "labor/LaborShared.hpp" namespace neb { @@ -54,6 +56,14 @@ class CodecResp: public neb::Codec return(CODEC_RESP); } + // request + template + static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const RedisReply& oReply); + + // response + template + static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const RedisReply& oReply); + virtual bool DecodeWithStack() const { return(m_bDecodeWithStack); @@ -105,6 +115,61 @@ class CodecResp: public neb::Codec static const char RESP_ARRAY; }; +// request +template +int CodecResp::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const RedisReply& oReply) +{ + if (uiFromLabor == uiToLabor) + { + return(ERR_SPEC_CHANNEL_TARGET); + } + std::shared_ptr> pSpecChannel = nullptr; + auto pLaborShared = LaborShared::Instance(); + auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); + if (pChannel == nullptr) + { + pSpecChannel = std::make_shared>( + uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CREATE); + } + pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, Type()); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oReply))); + if (iResult == ERR_OK) + { + return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); + } + return(iResult); + } + else + { + pSpecChannel = std::static_pointer_cast>(pChannel); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CAST); + } + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oReply))); + if (iResult == ERR_OK) + { + pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + return(iResult); + } +} + +// response +template +int CodecResp::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const RedisReply& oReply) +{ + uint32 uiFrom; + uint32 uiTo; + std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); + return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, oReply)); +} + } /* namespace neb */ #endif /* SRC_CODEC_CODECRESP_HPP_ */ diff --git a/src/codec/cass/CassResponse.cpp b/src/codec/cass/CassResponse.cpp index 46549582..e1ebb529 100644 --- a/src/codec/cass/CassResponse.cpp +++ b/src/codec/cass/CassResponse.cpp @@ -22,6 +22,23 @@ CassResponse::~CassResponse() { } +CassResponse::CassResponse(CassResponse&& oMessage) +{ + m_bCassOk = oMessage.m_bCassOk; + m_iErrCode = oMessage.m_iErrCode; + m_strErrMsg = std::move(oMessage.m_strErrMsg); + m_oResult = std::move(oMessage.m_oResult); +} + +CassResponse& CassResponse::operator=(CassResponse&& oMessage) +{ + m_bCassOk = oMessage.m_bCassOk; + m_iErrCode = oMessage.m_iErrCode; + m_strErrMsg = std::move(oMessage.m_strErrMsg); + m_oResult = std::move(oMessage.m_oResult); + return(*this); +} + bool CassResponse::DecodeError(CBuffer* pBuff) { m_bCassOk = false; diff --git a/src/codec/cass/CassResponse.hpp b/src/codec/cass/CassResponse.hpp index 82c9ea3c..0e62df8a 100644 --- a/src/codec/cass/CassResponse.hpp +++ b/src/codec/cass/CassResponse.hpp @@ -23,8 +23,10 @@ class CassResponse: public CassMessage public: CassResponse(); CassResponse(const CassResponse&) = delete; + CassResponse(CassResponse&&); virtual ~CassResponse(); CassResponse& operator=(const CassResponse&) = delete; + CassResponse& operator=(CassResponse&&); bool IsSuccess() const { diff --git a/src/ios/ChannelWatcher.hpp b/src/ios/ChannelWatcher.hpp index 5722af6b..59947b26 100644 --- a/src/ios/ChannelWatcher.hpp +++ b/src/ios/ChannelWatcher.hpp @@ -26,14 +26,6 @@ namespace neb class SocketChannel; -enum class ACTOR_CB_TYPE -{ - ACTOR_CB_NONE = 0x00, ///< 未定义回调 - ACTOR_CB_TIMER = 0x01, ///< 定时器回调 - ACTOR_CB_IO_SEQ = 0x02, ///< IO seq回调 - ACTOR_CB_IO_POINTER = 0x04, ///< IO 指针回调 -}; - class ChannelWatcher { public: diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index d0c944c5..4dc51580 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -113,6 +113,16 @@ void Dispatcher::SignalCallback(struct ev_loop* loop, struct ev_signal* watcher, } } +void Dispatcher::AsyncCallback(struct ev_loop* loop, struct ev_async* watcher, int revents) +{ + if (watcher->data != NULL) + { + auto pWatcher = static_cast(watcher->data); + auto pChannel = pWatcher->GetSocketChannel(); + CodecFactory::OnEvent(pWatcher, pChannel); + } +} + void Dispatcher::ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) { if (watcher->data != NULL) @@ -128,6 +138,7 @@ bool Dispatcher::OnIoRead(std::shared_ptr pChannel) m_pLastActivityChannel = pChannel; if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) { + auto& setAccessFd = ((Manager*)m_pLabor)->GetManagerInfo().setAccessFd; if (pChannel->GetFd() == ((Manager*)m_pLabor)->GetManagerInfo().iS2SListenFd) { return(AcceptServerConn(pChannel->GetFd())); @@ -136,7 +147,12 @@ bool Dispatcher::OnIoRead(std::shared_ptr pChannel) && pChannel->GetFd() == ((Manager*)m_pLabor)->GetManagerInfo().iC2SListenFd) { return(AcceptFdAndTransfer(((Manager*)m_pLabor)->GetManagerInfo().iC2SListenFd, - ((Manager*)m_pLabor)->GetManagerInfo().iC2SFamily)); + ((Manager*)m_pLabor)->GetManagerInfo().iC2SFamily, 1)); + } + else if (setAccessFd.find(pChannel->GetFd()) != setAccessFd.end()) + { + return(AcceptFdAndTransfer(pChannel->GetFd(), + ((Manager*)m_pLabor)->GetManagerInfo().iC2SFamily, 0)); } else { @@ -811,6 +827,17 @@ bool Dispatcher::AddEvent(ev_idle* idle_watcher, idle_callback pFunc) return(true); } +bool Dispatcher::AddEvent(ev_async* async_watcher, async_callback pFunc) +{ + if (NULL == async_watcher) + { + return(false); + } + ev_async_init(async_watcher, pFunc); + ev_async_start(m_loop, async_watcher); + return(true); +} + bool Dispatcher::RefreshEvent(ev_timer* timer_watcher, ev_tstamp dTimeout) { if (NULL == timer_watcher) @@ -949,6 +976,11 @@ bool Dispatcher::Init() return(true); } +void Dispatcher::AsyncSend(ev_async* pWatcher) +{ + ev_async_send(m_loop, pWatcher); +} + void Dispatcher::Destroy() { m_mapSocketChannel.clear(); @@ -1179,7 +1211,7 @@ bool Dispatcher::AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTim return(true); } -bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) +bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily, int iBonding) { char szClientAddr[64] = {0}; int iAcceptFd = -1; @@ -1287,16 +1319,16 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily) switch (m_pLabor->GetNodeInfo().iConnectionDispatch) { case DISPATCH_ROUND_ROBIN: - iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(); + iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(iBonding); break; case DISPATCH_MIN_LOAD: - iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetMinLoadWorkerDataFd(); + iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetMinLoadWorkerDataFd(iBonding); break; case DISPATCH_CLIENT_ADDR_HASH: - iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetWorkerDataFd(szClientAddr, iClientPort); + iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetWorkerDataFd(szClientAddr, iClientPort, iBonding); break; default: - iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(); + iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(iBonding); } if (iWorkerDataFd > 0) { diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 7dcefff4..6157b2bc 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -70,6 +70,7 @@ template class IO; typedef void (*signal_callback)(struct ev_loop*,ev_signal*,int); typedef void (*timer_callback)(struct ev_loop*,ev_timer*,int); typedef void (*idle_callback)(struct ev_loop*,ev_idle*,int); +typedef void (*async_callback)(struct ev_loop*,ev_async*,int); enum E_DISPATCHER { @@ -107,6 +108,7 @@ class Dispatcher static void IoTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); static void PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, int revents); static void SignalCallback(struct ev_loop* loop, struct ev_signal* watcher, int revents); + static void AsyncCallback(struct ev_loop* loop, struct ev_async* watcher, int revents); static void ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); bool OnIoRead(std::shared_ptr pChannel); @@ -165,6 +167,7 @@ class Dispatcher bool CreateListenFd(const std::string& strHost, int32 iPort, int iBacklog, int& iFd, int& iFamily); std::shared_ptr GetChannel(int iFd); int SendFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, const std::string& strRemoteAddr); + void AsyncSend(ev_async* pWatcher); protected: void Destroy(); @@ -174,13 +177,14 @@ class Dispatcher bool AddEvent(ev_signal* signal_watcher, signal_callback pFunc, int iSignum); bool AddEvent(ev_timer* timer_watcher, timer_callback pFunc, ev_tstamp dTimeout); bool AddEvent(ev_idle* idle_watcher, idle_callback pFunc); + bool AddEvent(ev_async* async_watcher, async_callback pFunc); bool RefreshEvent(ev_timer* timer_watcher, ev_tstamp dTimeout); bool DelEvent(ev_io* io_watcher); bool DelEvent(ev_timer* timer_watcher); int32 GetConnectionNum() const; void SetChannelStatus(std::shared_ptr pChannel, E_CHANNEL_STATUS eStatus); bool AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout = 60.0); - bool AcceptFdAndTransfer(int iFd, int iFamily = AF_INET); + bool AcceptFdAndTransfer(int iFd, int iFamily = AF_INET, int iBonding = 0); bool AcceptServerConn(int iFd); void CheckFailedNode(); void EvBreak(); diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index 6d6b4e27..dec1c783 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -17,6 +17,7 @@ #include "ios/ActorWatcher.hpp" #include "ios/Dispatcher.hpp" #include "labor/Labor.hpp" +#include "labor/LaborShared.hpp" #include "actor/Actor.hpp" #include "actor/ActorBuilder.hpp" #include "actor/chain/Chain.hpp" @@ -50,6 +51,10 @@ class IO template static bool SendRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, Targs&&... args); + // for spec channel + template + static bool TransmitTo(Actor* pActor, uint32 uiTargetLaborId, uint32 uiCallbackStepSeq, Targs&&... args); + template static bool SendTo(Actor* pActor, const std::string& strIdentify, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); @@ -111,7 +116,7 @@ class IO template static bool OnResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, uint32 uiStreamId, E_CODEC_STATUS eCodecStatus, Targs&&... args); - // SelfChannel response + // SelfChannel response, SpecChannel response template static bool OnResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, uint32 uiStepSeq, Targs&&... args); @@ -173,6 +178,24 @@ bool IO::SendResponse(Actor* pActor, std::shared_ptr pChannel, } else { + if (pChannel->GetCodecType() == CODEC_TRANSFER) // spec channel + { + uint32 uiPeerStepSeq = pActor->GetPeerStepSeq(); + if (uiPeerStepSeq == 0) + { + pActor->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "no peer step seq for response"); + return(false); + } + int iResult = T::Write(pChannel, 0, uiPeerStepSeq, std::forward(args)...); + if (ERR_OK == iResult) + { + return(true); + } + pActor->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "spec channel error %d", iResult); + return(false); + } return(SendResponse(pActor->m_pLabor->GetDispatcher(), pChannel, std::forward(args)...)); } } @@ -186,7 +209,7 @@ bool IO::SendResponse(Dispatcher* pDispatcher, std::shared_ptr LOG4_TRACE_DISPATCH("CODEC_UNKNOW is invalid, channel had not been init?"); return(false); } - if (pChannel->GetCodecType() == CODEC_DIRECT) + if (pChannel->GetCodecType() == CODEC_DIRECT) // self channel { auto pSelfChannel = std::dynamic_pointer_cast(pChannel); if (pSelfChannel == nullptr) @@ -361,6 +384,20 @@ bool IO::SendRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_p } } +template +template +bool IO::TransmitTo(Actor* pActor, uint32 uiTargetLaborId, uint32 uiCallbackStepSeq, Targs&&... args) +{ + int iResult = T::Write(pActor->GetLaborId(), uiTargetLaborId, gc_uiCmdReq, uiCallbackStepSeq, std::forward(args)...); + if (ERR_OK == iResult) + { + return(true); + } + pActor->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "spec channel error %d", iResult); + return(false); +} + template template bool IO::SendTo(Actor* pActor, const std::string& strIdentify, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) diff --git a/src/ios/SpecChannelWatcher.cpp b/src/ios/SpecChannelWatcher.cpp new file mode 100644 index 00000000..7ab80460 --- /dev/null +++ b/src/ios/SpecChannelWatcher.cpp @@ -0,0 +1,68 @@ +/******************************************************************************* + * Project: Nebula + * @file SpecChannelWatcher.cpp + * @brief + * @author Bwar + * @date: 2022-12-03 + * @note + * Modify history: + ******************************************************************************/ +#include "SpecChannelWatcher.hpp" + +namespace neb +{ + +SpecChannelWatcher::SpecChannelWatcher() + : m_uiSpecChannelCodecType(0), m_pAsyncWatcher(nullptr), m_pSpecChannel(nullptr) +{ +} + +SpecChannelWatcher::SpecChannelWatcher(std::shared_ptr pChannel) + : m_uiSpecChannelCodecType(0), m_pAsyncWatcher(nullptr), m_pSpecChannel(pChannel) +{ +} + +SpecChannelWatcher::~SpecChannelWatcher() +{ + Reset(); +} + +ev_async* SpecChannelWatcher::MutableAsyncWatcher() +{ + if (nullptr == m_pSpecChannel) + { + return(nullptr); + } + if (nullptr == m_pAsyncWatcher) + { + m_pAsyncWatcher = new ev_async(); + if (nullptr != m_pAsyncWatcher) + { + memset(m_pAsyncWatcher, 0, sizeof(ev_async)); + m_pAsyncWatcher->data = this; + } + } + return(m_pAsyncWatcher); +} + +void SpecChannelWatcher::Set(std::shared_ptr pChannel, uint32 uiSpecChannelCodecType) +{ + if (m_pSpecChannel == nullptr) + { + m_pSpecChannel = pChannel; + } + m_uiSpecChannelCodecType = uiSpecChannelCodecType; +} + +void SpecChannelWatcher::Reset() +{ + m_pSpecChannel = nullptr; + if (nullptr != m_pAsyncWatcher) + { + delete m_pAsyncWatcher; + m_pAsyncWatcher = nullptr; + } +} + +} /* namespace neb */ + diff --git a/src/ios/SpecChannelWatcher.hpp b/src/ios/SpecChannelWatcher.hpp new file mode 100644 index 00000000..921fe0c1 --- /dev/null +++ b/src/ios/SpecChannelWatcher.hpp @@ -0,0 +1,62 @@ +/******************************************************************************* + * Project: Nebula + * @file SpecChannelWatcher.hpp + * @brief + * @author Bwar + * @date: 2022-12-03 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_IOS_SPECCHANNELWATCHER_HPP_ +#define SRC_IOS_SPECCHANNELWATCHER_HPP_ + +#include +#include "Definition.hpp" + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif +#include "ev.h" +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +namespace neb +{ + +class SocketChannel; + +class SpecChannelWatcher +{ +public: + SpecChannelWatcher(); + SpecChannelWatcher(std::shared_ptr pChannel); + virtual ~SpecChannelWatcher(); + + ev_async* MutableAsyncWatcher(); + + inline std::shared_ptr GetSocketChannel() const + { + return(m_pSpecChannel); + } + + uint32 GetCodecType() const + { + return(m_uiSpecChannelCodecType); + } + + void Set(std::shared_ptr pChannel, uint32 uiSpecChannelCodecType); + void Reset(); + +private: + uint32 m_uiSpecChannelCodecType; + ev_async* m_pAsyncWatcher; + std::shared_ptr m_pSpecChannel; +}; + +} /* namespace neb */ + +#endif /* SRC_IOS_SPECCHANNELWATCHER_HPP_ */ + + diff --git a/src/labor/LaborShared.cpp b/src/labor/LaborShared.cpp new file mode 100644 index 00000000..3bb3d89f --- /dev/null +++ b/src/labor/LaborShared.cpp @@ -0,0 +1,197 @@ +/******************************************************************************* + * Project: Nebula + * @file LaborShared.cpp + * @brief + * @author Bwar + * @date: 2022-12-04 + * @note + * Modify history: + ******************************************************************************/ +#include "LaborShared.hpp" +#include "pb/neb_sys.pb.h" +#include "codec/CodecProto.hpp" +#include "channel/SpecChannel.hpp" + +namespace neb +{ + +LaborShared* LaborShared::s_pInstance = nullptr; +std::mutex LaborShared::s_mutex; + +LaborShared::LaborShared(uint32 uiLaborNum) + : m_uiLaborNum(uiLaborNum), m_uiSpecChannelQueueSize(128), m_uiCodecSize(0) +{ +} + +LaborShared::~LaborShared() +{ +} + +Dispatcher* LaborShared::GetDispatcher(uint32 uiLaborId) +{ + if (uiLaborId < m_vecDispatcher.size()) + { + return(m_vecDispatcher[uiLaborId]); + } + else + { + return(nullptr); + } +} + +void LaborShared::AddDispatcher(uint32 uiLaborId, Dispatcher* pDispatcher) +{ + if (uiLaborId < m_vecDispatcher.size()) + { + m_vecDispatcher[uiLaborId] = pDispatcher; + } + else + { + std::lock_guard guard(s_mutex); + for (uint32 i = m_vecDispatcher.size(); i <= uiLaborId; ++i) + { + m_vecDispatcher.push_back(nullptr); + } + m_vecDispatcher[uiLaborId] = pDispatcher; + } +} + +std::shared_ptr LaborShared::GetSpecChannel(uint32 uiCodecType, uint32 uiFrom, uint32 uiTo) +{ + if (uiFrom >= m_uiLaborNum || uiTo >= m_uiLaborNum) + { + return(nullptr); + } + if (uiCodecType < m_uiCodecSize.load(std::memory_order_relaxed)) + { + return(m_vecSpecChannel[uiCodecType][uiFrom][uiTo]); + } + else + { + return(nullptr); + } +} + +int LaborShared::AddSpecChannel(uint32 uiCodecType, uint32 uiFrom, uint32 uiTo, std::shared_ptr pChannel) +{ + if (uiFrom >= m_uiLaborNum || uiTo >= m_uiLaborNum) + { + return(ERR_SPEC_CHANNEL_LABOR_ID); + } + if (uiCodecType < m_vecSpecChannel.size()) + { + m_vecSpecChannel[uiCodecType][uiFrom][uiTo] = pChannel; + } + else + { + std::lock_guard guard(s_mutex); + for (uint32 i = m_vecSpecChannel.size(); i <= uiCodecType; ++i) + { + T_VEC_CHANNEL_FROM vecFrom; + for (uint32 j = 0; j < m_uiLaborNum; ++j) + { + auto pTo = std::make_shared(); + T_VEC_CHANNEL_TO vecTo; + vecTo.resize(m_uiLaborNum, nullptr); + vecFrom.emplace_back(std::move(vecTo)); + } + m_vecSpecChannel.emplace_back(std::move(vecFrom)); + } + m_vecSpecChannel[uiCodecType][uiFrom][uiTo] = pChannel; + m_uiCodecSize.store(m_vecSpecChannel.size(), std::memory_order_release); + } + + // notice spec channel created + SpecChannelInfo oSpecInfo; + MsgHead oMsgHead; + MsgBody oMsgBody; + std::string strSpecInfo; + oSpecInfo.set_codec_type(uiCodecType); + oSpecInfo.set_from_labor(uiFrom); + oSpecInfo.set_to_labor(uiTo); + oSpecInfo.SerializeToString(&strSpecInfo); + oMsgBody.set_data(strSpecInfo); + oMsgHead.set_cmd(CMD_REQ_SPEC_CHANNEL); + std::shared_ptr> pNoticeSpecChannel = nullptr; + uint32 uiNoticeLabor = 0; + if (uiFrom == m_uiLaborNum - 1) // manager + { + uiNoticeLabor = uiTo; + auto pChannel = GetSpecChannel(CodecNebulaInNode::Type(), uiFrom, uiNoticeLabor); + if (pChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_MANAGER); + } + else + { + pNoticeSpecChannel = std::static_pointer_cast>(pChannel); + } + } + else + { + uiNoticeLabor = m_uiLaborNum - 1; // manager labor id + auto pChannel = GetSpecChannel(CodecNebulaInNode::Type(), uiFrom, uiNoticeLabor); // forward notifications through manager + if (pChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_MANAGER); + } + else + { + pNoticeSpecChannel = std::static_pointer_cast>(pChannel); + } + } + if (pNoticeSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CAST); + } + int iResult = pNoticeSpecChannel->Write(gc_uiCmdReq, 0, std::forward(oMsgHead), std::forward(oMsgBody)); + if (iResult == ERR_OK) + { + GetDispatcher(uiNoticeLabor)->AsyncSend(pNoticeSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + return(iResult); +} + +std::shared_ptr> LaborShared::CreateInternalSpecChannel(uint32 uiFrom, uint32 uiTo) +{ + uint32 uiCodecType = CodecNebulaInNode::Type(); + if (uiFrom >= m_uiLaborNum || uiTo >= m_uiLaborNum) + { + return(nullptr); + } + auto pSpecChannel = std::make_shared>( + uiFrom, uiTo, m_uiSpecChannelQueueSize, true); + if (pSpecChannel == nullptr) + { + return(nullptr); + } + auto pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, uiCodecType); + if (uiCodecType < m_vecSpecChannel.size()) + { + m_vecSpecChannel[uiCodecType][uiFrom][uiTo] = pChannel; + } + else + { + std::lock_guard guard(s_mutex); + for (uint32 i = m_vecSpecChannel.size(); i <= uiCodecType; ++i) + { + T_VEC_CHANNEL_FROM vecFrom; + for (uint32 j = 0; j < m_uiLaborNum; ++j) + { + auto pTo = std::make_shared(); + T_VEC_CHANNEL_TO vecTo; + vecTo.resize(m_uiLaborNum, nullptr); + vecFrom.emplace_back(std::move(vecTo)); + } + m_vecSpecChannel.emplace_back(std::move(vecFrom)); + } + m_vecSpecChannel[uiCodecType][uiFrom][uiTo] = pChannel; + m_uiCodecSize.store(m_vecSpecChannel.size(), std::memory_order_release); + } + return(pSpecChannel); +} + +} /* namespace neb */ + diff --git a/src/labor/LaborShared.hpp b/src/labor/LaborShared.hpp new file mode 100644 index 00000000..243a6646 --- /dev/null +++ b/src/labor/LaborShared.hpp @@ -0,0 +1,80 @@ +/******************************************************************************* + * Project: Nebula + * @file LaborShared.hpp + * @brief + * @author Bwar + * @date: 2022-12-04 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_LABOR_LABORSHARED_HPP_ +#define SRC_LABOR_LABORSHARED_HPP_ + +#include +#include +#include +#include +#include "Definition.hpp" +#include "ios/Dispatcher.hpp" +#include "channel/SocketChannel.hpp" + +namespace neb +{ + +class SocketChannel; + +template class SpecChannel; + +class LaborShared +{ +public: + // To improve performance use vector instead of map + typedef std::vector> T_VEC_CHANNEL_TO; + typedef std::vector T_VEC_CHANNEL_FROM; + typedef std::vector T_VECCHANNEL_CODEC_TYPE; + + virtual ~LaborShared(); + + static inline LaborShared* Instance(uint32 uiLaborNum = 2) + { + if (s_pInstance == nullptr) + { + s_pInstance = new LaborShared(uiLaborNum); + } + return(s_pInstance); + } + + Dispatcher* GetDispatcher(uint32 uiLaborId); + // AddDispatcher() if and only if the service starts + void AddDispatcher(uint32 uiLaborId, Dispatcher* pDispatcher); + std::shared_ptr GetSpecChannel(uint32 uiCodecType, uint32 uiFrom, uint32 uiTo); + int AddSpecChannel(uint32 uiCodecType, uint32 uiFrom, uint32 uiTo, std::shared_ptr pChannel); + std::shared_ptr> CreateInternalSpecChannel(uint32 uiFrom, uint32 uiTo); + + uint32 GetSpecChannelQueueSize() const + { + return(m_uiSpecChannelQueueSize); + } + + void SetSpecChannelQueueSize(uint32 uiSize) + { + m_uiSpecChannelQueueSize = uiSize; + } + +private: + explicit LaborShared(uint32 uiLaborNum); + static LaborShared* s_pInstance; + static std::mutex s_mutex; + +private: + uint32 m_uiLaborNum; + uint32 m_uiSpecChannelQueueSize; + std::atomic m_uiCodecSize; + std::vector m_vecDispatcher; + T_VECCHANNEL_CODEC_TYPE m_vecSpecChannel; +}; + +} /* namespace neb */ + +#endif /* SRC_LABOR_LABORSHARE_HPP_ */ + diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index eb8b660e..58445d2f 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -22,7 +22,9 @@ extern "C" { #include "Manager.hpp" #include "Worker.hpp" #include "Loader.hpp" +#include "LaborShared.hpp" #include "channel/SocketChannel.hpp" +#include "channel/SpecChannel.hpp" #include "ios/Dispatcher.hpp" #include "actor/ActorBuilder.hpp" #include "actor/step/Step.hpp" @@ -201,6 +203,7 @@ bool Manager::InitDispatcher() LOG4_ERROR("new Dispatcher error: %s", e.what()); return(false); } + LaborShared::Instance(m_stNodeInfo.uiWorkerNum + 2)->AddDispatcher(m_stNodeInfo.uiWorkerNum + 1, m_pDispatcher); return(m_pDispatcher->Init()); } @@ -239,12 +242,35 @@ void Manager::StartService() m_stNodeInfo.iPortForServer, m_stNodeInfo.iBacklog, m_stManagerInfo.iS2SListenFd, m_stManagerInfo.iS2SFamily); - if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) + if (m_stNodeInfo.strHostForClient.size() > 0) { // 接入节点才需要监听客户端连接 - m_pDispatcher->CreateListenFd(strBindIp, - m_stNodeInfo.iPortForClient, m_stNodeInfo.iBacklog, - m_stManagerInfo.iC2SListenFd, m_stManagerInfo.iC2SFamily); + if (m_stNodeInfo.iPortForClient > 0) + { + m_pDispatcher->CreateListenFd(strBindIp, + m_stNodeInfo.iPortForClient, m_stNodeInfo.iBacklog, + m_stManagerInfo.iC2SListenFd, m_stManagerInfo.iC2SFamily); + int iPort = 0; + int iListenFd = 0; + std::unordered_set setPort; + for (int i = 0; i < m_oCurrentConf["access_ports"].GetArraySize(); ++i) + { + if (m_oCurrentConf["access_ports"].Get(i, iPort) && iPort > 0) + { + if (m_stNodeInfo.iPortForClient != iPort) + { + setPort.insert(iPort); + } + } + } + for (auto it = setPort.begin(); it != setPort.end(); ++it) + { + m_pDispatcher->CreateListenFd(strBindIp, + *it, m_stNodeInfo.iBacklog, + iListenFd, m_stManagerInfo.iC2SFamily); + m_stManagerInfo.setAccessFd.insert(iListenFd); + } + } } } else @@ -253,12 +279,35 @@ void Manager::StartService() m_stNodeInfo.iPortForServer, m_stNodeInfo.iBacklog, m_stManagerInfo.iS2SListenFd, m_stManagerInfo.iS2SFamily); - if (m_stNodeInfo.strHostForClient.size() > 0 && m_stNodeInfo.iPortForClient > 0) + if (m_stNodeInfo.strHostForClient.size() > 0) { // 接入节点才需要监听客户端连接 - m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForClient, + if (m_stNodeInfo.iPortForClient > 0) + { + m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForClient, m_stNodeInfo.iPortForClient, m_stNodeInfo.iBacklog, m_stManagerInfo.iC2SListenFd, m_stManagerInfo.iC2SFamily); + int iPort = 0; + int iListenFd = 0; + std::unordered_set setPort; + for (int i = 0; i < m_oCurrentConf["access_ports"].GetArraySize(); ++i) + { + if (m_oCurrentConf["access_ports"].Get(i, iPort) && iPort > 0) + { + if (m_stNodeInfo.iPortForClient != iPort) + { + setPort.insert(iPort); + } + } + } + for (auto it = setPort.begin(); it != setPort.end(); ++it) + { + m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForClient, + *it, m_stNodeInfo.iBacklog, + iListenFd, m_stManagerInfo.iC2SFamily); + m_stManagerInfo.setAccessFd.insert(iListenFd); + } + } } } @@ -269,6 +318,12 @@ void Manager::StartService() pChannelListen = m_pDispatcher->CreateSocketChannel(m_stManagerInfo.iC2SListenFd, m_stNodeInfo.eCodec); m_pDispatcher->SetChannelStatus(pChannelListen, CHANNEL_STATUS_ESTABLISHED); m_pDispatcher->AddIoReadEvent(pChannelListen); + for (auto it = m_stManagerInfo.setAccessFd.begin(); it != m_stManagerInfo.setAccessFd.end(); ++it) + { + pChannelListen = m_pDispatcher->CreateSocketChannel(*it, m_stNodeInfo.eCodec); + m_pDispatcher->SetChannelStatus(pChannelListen, CHANNEL_STATUS_ESTABLISHED); + m_pDispatcher->AddIoReadEvent(pChannelListen); + } } LOG4_TRACE("S2SListenFd[%d]", m_stManagerInfo.iS2SListenFd); pChannelListen = m_pDispatcher->CreateSocketChannel(m_stManagerInfo.iS2SListenFd, CODEC_NEBULA); @@ -397,6 +452,9 @@ bool Manager::Init() m_oCurrentConf.Get("thread_mode", m_stNodeInfo.bThreadMode); if (m_stNodeInfo.bThreadMode) { + uint32 uiSpecChannelQueueSize = 128; + m_oCurrentConf.Get("spec_channel_queue_size", uiSpecChannelQueueSize); + LaborShared::Instance(m_stNodeInfo.uiWorkerNum + 2)->SetSpecChannelQueueSize(uiSpecChannelQueueSize); m_oCurrentConf.Get("async_logger", m_stNodeInfo.bAsyncLogger); } if (!InitLogger(m_oCurrentConf) || !InitDispatcher() || !InitActorBuilder()) @@ -453,10 +511,12 @@ bool Manager::CreateEvents() m_pDispatcher->AddEvent(fpe_signal_watcher, Dispatcher::SignalCallback, SIGFPE); bool bDirectToLoader = false; + bool bDispatchWithPort = false; m_oCurrentConf.Get("new_client_to_loader", bDirectToLoader); + m_oCurrentConf.Get("dispatch_with_port", bDispatchWithPort); m_pSessionManager = std::dynamic_pointer_cast( m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", - bDirectToLoader, m_stNodeInfo.dDataReportInterval)); + bDirectToLoader, bDispatchWithPort, m_stNodeInfo.dDataReportInterval)); AddPeriodicTaskEvent(); return(true); @@ -559,6 +619,14 @@ void Manager::CreateLoaderThread() return; } m_pLoaderActorBuilder = pWorker->GetActorBuilder(); + auto pManagerToLoaderSpecChannel = LaborShared::Instance( + m_stNodeInfo.uiWorkerNum + 2)->CreateInternalSpecChannel(m_stNodeInfo.uiWorkerNum + 1, 0); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pManagerToLoaderSpecChannel->GetOwnerId()); + pDispatcher->AddEvent(pManagerToLoaderSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); + LOG4_TRACE("spec channel from %u to %u has been created.", m_stNodeInfo.uiWorkerNum + 1, 0); + auto pLoaderToManagerSpecChannel = LaborShared::Instance()->CreateInternalSpecChannel(0, m_stNodeInfo.uiWorkerNum + 1); + m_pDispatcher->AddEvent(pLoaderToManagerSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); + LOG4_TRACE("spec channel from %u to %u has been created.", 0, m_stNodeInfo.uiWorkerNum + 1); std::thread t(&Worker::Run, pWorker); t.detach(); m_stNodeInfo.uiLoaderNum = 1; @@ -665,6 +733,14 @@ void Manager::CreateWorkerThread() continue; } pWorker->SetLoaderActorBuilder(m_pLoaderActorBuilder); + auto pManagerToWorkerSpecChannel = LaborShared::Instance( + m_stNodeInfo.uiWorkerNum + 2)->CreateInternalSpecChannel(m_stNodeInfo.uiWorkerNum + 1, i); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pManagerToWorkerSpecChannel->GetOwnerId()); + pDispatcher->AddEvent(pManagerToWorkerSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); + LOG4_TRACE("spec channel from %u to %u has been created.", m_stNodeInfo.uiWorkerNum + 1, i); + auto pWorkerToManagerSpecChannel = LaborShared::Instance()->CreateInternalSpecChannel(i, m_stNodeInfo.uiWorkerNum + 1); + m_pDispatcher->AddEvent(pWorkerToManagerSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); + LOG4_TRACE("spec channel from %u to %u has been created.", i, m_stNodeInfo.uiWorkerNum + 1); std::thread t(&Worker::Run, pWorker); t.detach(); m_pSessionManager->AddWorkerInfo(i, getpid(), iControlFds[0], iDataFds[0]); diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index a5848b60..57cedd70 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -51,6 +51,7 @@ class Manager: public Labor int iS2SFamily = 0; ///< int iC2SListenFd = -1; ///< Client to Server监听文件描述符(Client与Server之间的连接较多,但每个Client只需连接某个Server的某个Worker) int iC2SFamily = 0; ///< + std::unordered_set setAccessFd; ///< the same as iC2SListenFd }; public: diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index 71c11b02..c9bd36c3 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -19,6 +19,7 @@ extern "C" { } #endif #include "Worker.hpp" +#include "LaborShared.hpp" #include "ios/Dispatcher.hpp" #include "ios/IO.hpp" #include "actor/ActorBuilder.hpp" @@ -408,6 +409,7 @@ bool Worker::NewDispatcher() LOG4_ERROR("new Dispatcher error: %s", e.what()); return(false); } + LaborShared::Instance(m_stNodeInfo.uiWorkerNum + 2)->AddDispatcher(m_stWorkerInfo.iWorkerIndex, m_pDispatcher); return(true); } diff --git a/src/pb/neb_sys.pb.cc b/src/pb/neb_sys.pb.cc index 0d433cf6..45ccb24a 100644 --- a/src/pb/neb_sys.pb.cc +++ b/src/pb/neb_sys.pb.cc @@ -36,6 +36,9 @@ const ::google::protobuf::internal::GeneratedMessageReflection* const ::google::protobuf::Descriptor* TraceLog_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* TraceLog_reflection_ = NULL; +const ::google::protobuf::Descriptor* SpecChannelInfo_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + SpecChannelInfo_reflection_ = NULL; } // namespace @@ -134,6 +137,23 @@ void protobuf_AssignDesc_neb_5fsys_2eproto() { sizeof(TraceLog), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(TraceLog, _internal_metadata_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(TraceLog, _is_default_instance_)); + SpecChannelInfo_descriptor_ = file->message_type(5); + static const int SpecChannelInfo_offsets_[3] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SpecChannelInfo, codec_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SpecChannelInfo, from_labor_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SpecChannelInfo, to_labor_), + }; + SpecChannelInfo_reflection_ = + ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( + SpecChannelInfo_descriptor_, + SpecChannelInfo::default_instance_, + SpecChannelInfo_offsets_, + -1, + -1, + -1, + sizeof(SpecChannelInfo), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SpecChannelInfo, _internal_metadata_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SpecChannelInfo, _is_default_instance_)); } namespace { @@ -157,6 +177,8 @@ void protobuf_RegisterTypes(const ::std::string&) { LogLevel_descriptor_, &LogLevel::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( TraceLog_descriptor_, &TraceLog::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + SpecChannelInfo_descriptor_, &SpecChannelInfo::default_instance()); } } // namespace @@ -172,6 +194,8 @@ void protobuf_ShutdownFile_neb_5fsys_2eproto() { delete LogLevel_reflection_; delete TraceLog::default_instance_; delete TraceLog_reflection_; + delete SpecChannelInfo::default_instance_; + delete SpecChannelInfo_reflection_; } void protobuf_AddDesc_neb_5fsys_2eproto() GOOGLE_ATTRIBUTE_COLD; @@ -192,8 +216,9 @@ void protobuf_AddDesc_neb_5fsys_2eproto() { "\022\021\n\tnode_type\030\002 \001(\t\022\025\n\rnode_identify\030\003 \001" "(\t\022\021\n\tlog_level\030\004 \001(\t\022\026\n\016code_file_name\030" "\005 \001(\t\022\026\n\016code_file_line\030\006 \001(\r\022\025\n\rcode_fu" - "nction\030\007 \001(\t\022\023\n\013log_content\030\010 \001(\014b\006proto" - "3", 441); + "nction\030\007 \001(\t\022\023\n\013log_content\030\010 \001(\014\"K\n\017Spe" + "cChannelInfo\022\022\n\ncodec_type\030\001 \001(\r\022\022\n\nfrom" + "_labor\030\002 \001(\r\022\020\n\010to_labor\030\003 \001(\rb\006proto3", 518); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "neb_sys.proto", &protobuf_RegisterTypes); ConfigInfo::default_instance_ = new ConfigInfo(); @@ -201,11 +226,13 @@ void protobuf_AddDesc_neb_5fsys_2eproto() { TargetWorker::default_instance_ = new TargetWorker(); LogLevel::default_instance_ = new LogLevel(); TraceLog::default_instance_ = new TraceLog(); + SpecChannelInfo::default_instance_ = new SpecChannelInfo(); ConfigInfo::default_instance_->InitAsDefaultInstance(); WorkerLoad::default_instance_->InitAsDefaultInstance(); TargetWorker::default_instance_->InitAsDefaultInstance(); LogLevel::default_instance_->InitAsDefaultInstance(); TraceLog::default_instance_->InitAsDefaultInstance(); + SpecChannelInfo::default_instance_->InitAsDefaultInstance(); ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_neb_5fsys_2eproto); } @@ -2611,6 +2638,368 @@ void TraceLog::clear_log_content() { #endif // PROTOBUF_INLINE_NOT_IN_HEADERS +// =================================================================== + +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +const int SpecChannelInfo::kCodecTypeFieldNumber; +const int SpecChannelInfo::kFromLaborFieldNumber; +const int SpecChannelInfo::kToLaborFieldNumber; +#endif // !defined(_MSC_VER) || _MSC_VER >= 1900 + +SpecChannelInfo::SpecChannelInfo() + : ::google::protobuf::Message(), _internal_metadata_(NULL) { + SharedCtor(); + // @@protoc_insertion_point(constructor:neb.SpecChannelInfo) +} + +void SpecChannelInfo::InitAsDefaultInstance() { + _is_default_instance_ = true; +} + +SpecChannelInfo::SpecChannelInfo(const SpecChannelInfo& from) + : ::google::protobuf::Message(), + _internal_metadata_(NULL) { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:neb.SpecChannelInfo) +} + +void SpecChannelInfo::SharedCtor() { + _is_default_instance_ = false; + _cached_size_ = 0; + codec_type_ = 0u; + from_labor_ = 0u; + to_labor_ = 0u; +} + +SpecChannelInfo::~SpecChannelInfo() { + // @@protoc_insertion_point(destructor:neb.SpecChannelInfo) + SharedDtor(); +} + +void SpecChannelInfo::SharedDtor() { + if (this != default_instance_) { + } +} + +void SpecChannelInfo::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* SpecChannelInfo::descriptor() { + protobuf_AssignDescriptorsOnce(); + return SpecChannelInfo_descriptor_; +} + +const SpecChannelInfo& SpecChannelInfo::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_neb_5fsys_2eproto(); + return *default_instance_; +} + +SpecChannelInfo* SpecChannelInfo::default_instance_ = NULL; + +SpecChannelInfo* SpecChannelInfo::New(::google::protobuf::Arena* arena) const { + SpecChannelInfo* n = new SpecChannelInfo; + if (arena != NULL) { + arena->Own(n); + } + return n; +} + +void SpecChannelInfo::Clear() { +// @@protoc_insertion_point(message_clear_start:neb.SpecChannelInfo) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(SpecChannelInfo, f) \ + _Pragma("clang diagnostic pop") +#else +#define ZR_HELPER_(f) reinterpret_cast(\ + &reinterpret_cast(16)->f) +#endif + +#define ZR_(first, last) do {\ + ::memset(&first, 0,\ + ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\ +} while (0) + + ZR_(codec_type_, to_labor_); + +#undef ZR_HELPER_ +#undef ZR_ + +} + +bool SpecChannelInfo::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:neb.SpecChannelInfo) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional uint32 codec_type = 1; + case 1: { + if (tag == 8) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &codec_type_))); + + } else { + goto handle_unusual; + } + if (input->ExpectTag(16)) goto parse_from_labor; + break; + } + + // optional uint32 from_labor = 2; + case 2: { + if (tag == 16) { + parse_from_labor: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &from_labor_))); + + } else { + goto handle_unusual; + } + if (input->ExpectTag(24)) goto parse_to_labor; + break; + } + + // optional uint32 to_labor = 3; + case 3: { + if (tag == 24) { + parse_to_labor: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &to_labor_))); + + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag)); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:neb.SpecChannelInfo) + return true; +failure: + // @@protoc_insertion_point(parse_failure:neb.SpecChannelInfo) + return false; +#undef DO_ +} + +void SpecChannelInfo::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:neb.SpecChannelInfo) + // optional uint32 codec_type = 1; + if (this->codec_type() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(1, this->codec_type(), output); + } + + // optional uint32 from_labor = 2; + if (this->from_labor() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(2, this->from_labor(), output); + } + + // optional uint32 to_labor = 3; + if (this->to_labor() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(3, this->to_labor(), output); + } + + // @@protoc_insertion_point(serialize_end:neb.SpecChannelInfo) +} + +::google::protobuf::uint8* SpecChannelInfo::InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:neb.SpecChannelInfo) + // optional uint32 codec_type = 1; + if (this->codec_type() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(1, this->codec_type(), target); + } + + // optional uint32 from_labor = 2; + if (this->from_labor() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(2, this->from_labor(), target); + } + + // optional uint32 to_labor = 3; + if (this->to_labor() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(3, this->to_labor(), target); + } + + // @@protoc_insertion_point(serialize_to_array_end:neb.SpecChannelInfo) + return target; +} + +int SpecChannelInfo::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:neb.SpecChannelInfo) + int total_size = 0; + + // optional uint32 codec_type = 1; + if (this->codec_type() != 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->codec_type()); + } + + // optional uint32 from_labor = 2; + if (this->from_labor() != 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->from_labor()); + } + + // optional uint32 to_labor = 3; + if (this->to_labor() != 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->to_labor()); + } + + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void SpecChannelInfo::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:neb.SpecChannelInfo) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + const SpecChannelInfo* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); + if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:neb.SpecChannelInfo) + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:neb.SpecChannelInfo) + MergeFrom(*source); + } +} + +void SpecChannelInfo::MergeFrom(const SpecChannelInfo& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:neb.SpecChannelInfo) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + if (from.codec_type() != 0) { + set_codec_type(from.codec_type()); + } + if (from.from_labor() != 0) { + set_from_labor(from.from_labor()); + } + if (from.to_labor() != 0) { + set_to_labor(from.to_labor()); + } +} + +void SpecChannelInfo::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:neb.SpecChannelInfo) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void SpecChannelInfo::CopyFrom(const SpecChannelInfo& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:neb.SpecChannelInfo) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool SpecChannelInfo::IsInitialized() const { + + return true; +} + +void SpecChannelInfo::Swap(SpecChannelInfo* other) { + if (other == this) return; + InternalSwap(other); +} +void SpecChannelInfo::InternalSwap(SpecChannelInfo* other) { + std::swap(codec_type_, other->codec_type_); + std::swap(from_labor_, other->from_labor_); + std::swap(to_labor_, other->to_labor_); + _internal_metadata_.Swap(&other->_internal_metadata_); + std::swap(_cached_size_, other->_cached_size_); +} + +::google::protobuf::Metadata SpecChannelInfo::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = SpecChannelInfo_descriptor_; + metadata.reflection = SpecChannelInfo_reflection_; + return metadata; +} + +#if PROTOBUF_INLINE_NOT_IN_HEADERS +// SpecChannelInfo + +// optional uint32 codec_type = 1; +void SpecChannelInfo::clear_codec_type() { + codec_type_ = 0u; +} + ::google::protobuf::uint32 SpecChannelInfo::codec_type() const { + // @@protoc_insertion_point(field_get:neb.SpecChannelInfo.codec_type) + return codec_type_; +} + void SpecChannelInfo::set_codec_type(::google::protobuf::uint32 value) { + + codec_type_ = value; + // @@protoc_insertion_point(field_set:neb.SpecChannelInfo.codec_type) +} + +// optional uint32 from_labor = 2; +void SpecChannelInfo::clear_from_labor() { + from_labor_ = 0u; +} + ::google::protobuf::uint32 SpecChannelInfo::from_labor() const { + // @@protoc_insertion_point(field_get:neb.SpecChannelInfo.from_labor) + return from_labor_; +} + void SpecChannelInfo::set_from_labor(::google::protobuf::uint32 value) { + + from_labor_ = value; + // @@protoc_insertion_point(field_set:neb.SpecChannelInfo.from_labor) +} + +// optional uint32 to_labor = 3; +void SpecChannelInfo::clear_to_labor() { + to_labor_ = 0u; +} + ::google::protobuf::uint32 SpecChannelInfo::to_labor() const { + // @@protoc_insertion_point(field_get:neb.SpecChannelInfo.to_labor) + return to_labor_; +} + void SpecChannelInfo::set_to_labor(::google::protobuf::uint32 value) { + + to_labor_ = value; + // @@protoc_insertion_point(field_set:neb.SpecChannelInfo.to_labor) +} + +#endif // PROTOBUF_INLINE_NOT_IN_HEADERS + // @@protoc_insertion_point(namespace_scope) } // namespace neb diff --git a/src/pb/neb_sys.pb.h b/src/pb/neb_sys.pb.h index 102daeb5..15465c33 100644 --- a/src/pb/neb_sys.pb.h +++ b/src/pb/neb_sys.pb.h @@ -38,6 +38,7 @@ void protobuf_ShutdownFile_neb_5fsys_2eproto(); class ConfigInfo; class LogLevel; +class SpecChannelInfo; class TargetWorker; class TraceLog; class WorkerLoad; @@ -596,6 +597,102 @@ class TraceLog : public ::google::protobuf::Message /* @@protoc_insertion_point( void InitAsDefaultInstance(); static TraceLog* default_instance_; }; +// ------------------------------------------------------------------- + +class SpecChannelInfo : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:neb.SpecChannelInfo) */ { + public: + SpecChannelInfo(); + virtual ~SpecChannelInfo(); + + SpecChannelInfo(const SpecChannelInfo& from); + + inline SpecChannelInfo& operator=(const SpecChannelInfo& from) { + CopyFrom(from); + return *this; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const SpecChannelInfo& default_instance(); + + void Swap(SpecChannelInfo* other); + + // implements Message ---------------------------------------------- + + inline SpecChannelInfo* New() const { return New(NULL); } + + SpecChannelInfo* New(::google::protobuf::Arena* arena) const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const SpecChannelInfo& from); + void MergeFrom(const SpecChannelInfo& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const { + return InternalSerializeWithCachedSizesToArray(false, output); + } + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(SpecChannelInfo* other); + private: + inline ::google::protobuf::Arena* GetArenaNoVirtual() const { + return _internal_metadata_.arena(); + } + inline void* MaybeArenaPtr() const { + return _internal_metadata_.raw_arena_ptr(); + } + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional uint32 codec_type = 1; + void clear_codec_type(); + static const int kCodecTypeFieldNumber = 1; + ::google::protobuf::uint32 codec_type() const; + void set_codec_type(::google::protobuf::uint32 value); + + // optional uint32 from_labor = 2; + void clear_from_labor(); + static const int kFromLaborFieldNumber = 2; + ::google::protobuf::uint32 from_labor() const; + void set_from_labor(::google::protobuf::uint32 value); + + // optional uint32 to_labor = 3; + void clear_to_labor(); + static const int kToLaborFieldNumber = 3; + ::google::protobuf::uint32 to_labor() const; + void set_to_labor(::google::protobuf::uint32 value); + + // @@protoc_insertion_point(class_scope:neb.SpecChannelInfo) + private: + + ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + bool _is_default_instance_; + ::google::protobuf::uint32 codec_type_; + ::google::protobuf::uint32 from_labor_; + ::google::protobuf::uint32 to_labor_; + mutable int _cached_size_; + friend void protobuf_AddDesc_neb_5fsys_2eproto(); + friend void protobuf_AssignDesc_neb_5fsys_2eproto(); + friend void protobuf_ShutdownFile_neb_5fsys_2eproto(); + + void InitAsDefaultInstance(); + static SpecChannelInfo* default_instance_; +}; // =================================================================== @@ -1218,6 +1315,52 @@ inline void TraceLog::set_allocated_log_content(::std::string* log_content) { // @@protoc_insertion_point(field_set_allocated:neb.TraceLog.log_content) } +// ------------------------------------------------------------------- + +// SpecChannelInfo + +// optional uint32 codec_type = 1; +inline void SpecChannelInfo::clear_codec_type() { + codec_type_ = 0u; +} +inline ::google::protobuf::uint32 SpecChannelInfo::codec_type() const { + // @@protoc_insertion_point(field_get:neb.SpecChannelInfo.codec_type) + return codec_type_; +} +inline void SpecChannelInfo::set_codec_type(::google::protobuf::uint32 value) { + + codec_type_ = value; + // @@protoc_insertion_point(field_set:neb.SpecChannelInfo.codec_type) +} + +// optional uint32 from_labor = 2; +inline void SpecChannelInfo::clear_from_labor() { + from_labor_ = 0u; +} +inline ::google::protobuf::uint32 SpecChannelInfo::from_labor() const { + // @@protoc_insertion_point(field_get:neb.SpecChannelInfo.from_labor) + return from_labor_; +} +inline void SpecChannelInfo::set_from_labor(::google::protobuf::uint32 value) { + + from_labor_ = value; + // @@protoc_insertion_point(field_set:neb.SpecChannelInfo.from_labor) +} + +// optional uint32 to_labor = 3; +inline void SpecChannelInfo::clear_to_labor() { + to_labor_ = 0u; +} +inline ::google::protobuf::uint32 SpecChannelInfo::to_labor() const { + // @@protoc_insertion_point(field_get:neb.SpecChannelInfo.to_labor) + return to_labor_; +} +inline void SpecChannelInfo::set_to_labor(::google::protobuf::uint32 value) { + + to_labor_ = value; + // @@protoc_insertion_point(field_set:neb.SpecChannelInfo.to_labor) +} + #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS // ------------------------------------------------------------------- @@ -1227,6 +1370,8 @@ inline void TraceLog::set_allocated_log_content(::std::string* log_content) { // ------------------------------------------------------------------- +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) From 111ca619a65e5759e968f6dbe7973c01ad13da8e Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 15 Jan 2023 20:45:26 +0800 Subject: [PATCH 168/176] 1. replace fd transfer with spec channel 2. add channel migration 3. remove process mode --- conf/nebula.json | 13 +- example/spec_channel/CmdSpecChannelTest.cpp | 196 +++++++ example/spec_channel/CmdSpecChannelTest.hpp | 37 ++ example/spec_channel/StepSpecChannelTest.cpp | 53 ++ example/spec_channel/StepSpecChannelTest.hpp | 38 ++ proto/neb_sys.proto | 9 + src/Definition.hpp | 5 +- src/Error.hpp | 2 + src/actor/ActorBuilder.cpp | 3 + src/actor/cmd/CW.hpp | 4 + src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp | 34 ++ src/actor/cmd/sys_cmd/CmdChannelMigrate.hpp | 47 ++ src/actor/cmd/sys_cmd/CmdFdTransfer.cpp | 88 +++ src/actor/cmd/sys_cmd/CmdFdTransfer.hpp | 34 ++ .../manager/CmdOnOrientationFdTransfer.cpp | 23 +- .../cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp | 11 +- .../sys_session/manager/SessionManager.cpp | 545 ++++++----------- .../sys_session/manager/SessionManager.hpp | 54 +- src/actor/step/sys_step/StepRedisCluster.cpp | 22 +- src/channel/SocketChannel.cpp | 175 +----- src/channel/SocketChannel.hpp | 20 +- src/channel/SocketChannelImpl.hpp | 36 +- src/channel/SpecChannel.hpp | 10 + src/channel/migrate/SocketChannelMigrate.cpp | 39 ++ src/channel/migrate/SocketChannelMigrate.hpp | 116 ++++ src/channel/migrate/SocketChannelPack.cpp | 58 ++ src/channel/migrate/SocketChannelPack.hpp | 39 ++ src/codec/Codec.cpp | 6 + src/codec/Codec.hpp | 4 + src/codec/CodecFactory.cpp | 22 + src/codec/CodecProto.hpp | 6 +- src/ios/Dispatcher.cpp | 263 ++++----- src/ios/Dispatcher.hpp | 16 +- src/ios/IO.hpp | 12 + src/labor/LaborShared.cpp | 18 + src/labor/LaborShared.hpp | 17 + src/labor/Loader.cpp | 4 +- src/labor/Loader.hpp | 2 +- src/labor/Manager.cpp | 314 +--------- src/labor/Manager.hpp | 4 - src/labor/NodeInfo.hpp | 2 - src/labor/Worker.cpp | 67 +-- src/labor/Worker.hpp | 42 +- src/pb/neb_sys.pb.cc | 547 +++++++++++++++++- src/pb/neb_sys.pb.h | 222 +++++++ 45 files changed, 2146 insertions(+), 1133 deletions(-) create mode 100644 example/spec_channel/CmdSpecChannelTest.cpp create mode 100644 example/spec_channel/CmdSpecChannelTest.hpp create mode 100644 example/spec_channel/StepSpecChannelTest.cpp create mode 100644 example/spec_channel/StepSpecChannelTest.hpp create mode 100644 src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp create mode 100644 src/actor/cmd/sys_cmd/CmdChannelMigrate.hpp create mode 100644 src/actor/cmd/sys_cmd/CmdFdTransfer.cpp create mode 100644 src/actor/cmd/sys_cmd/CmdFdTransfer.hpp create mode 100644 src/channel/migrate/SocketChannelMigrate.cpp create mode 100644 src/channel/migrate/SocketChannelMigrate.hpp create mode 100644 src/channel/migrate/SocketChannelPack.cpp create mode 100644 src/channel/migrate/SocketChannelPack.hpp diff --git a/conf/nebula.json b/conf/nebula.json index 68662835..84a3927f 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -24,18 +24,17 @@ "server_name": "AsyncServer", "//worker_num": "进程数量", "worker_num": 10, - "//thread_mode":"是否采用线程模型", - "thread_mode":false, - "//with_loader":"是否启动loader进程", + "//with_loader":"是否启动loader程", "with_loader":false, - "//new_client_to_loader":"集群外部(从access_port端口进来)的新连接直接转发到loader,不转发给worker", - "new_client_to_loader":false, "//cpu_affinity":"是否设置进程CPU亲和度(绑定CPU)", "cpu_affinity":false, "connection_dispatcher":{"round_robin":0, "min_load":1, "client_addr_hash":2}, "connection_dispatch":0, - "//dispatch_with_port":"根据接入端口(奇偶)进行连接到worker的转发", - "dispatch_with_port":false, + "//access_port_to_worker":"端口到worker映射关系", + "access_port_to_worker":[ + "9919":[1,3,5,7,9,11,13,15], + "9921":[2,4,6,8,10,12,14,16] + ], "daemonize":true, "//worker_capacity": "子进程最大工作负荷", "worker_capacity": 1000000, diff --git a/example/spec_channel/CmdSpecChannelTest.cpp b/example/spec_channel/CmdSpecChannelTest.cpp new file mode 100644 index 00000000..0e9c1d22 --- /dev/null +++ b/example/spec_channel/CmdSpecChannelTest.cpp @@ -0,0 +1,196 @@ +#include "CmdSpecChannelTest.hpp" +#include +#include +#include +#include +#include "CmdOfdb.hpp" + +namespace neb +{ + +CmdSpecChannelTest::CmdSpecChannelTest(int32 iCmd) + : neb::RedisCmd(iCmd) +{ +} + +CmdSpecChannelTest::~CmdSpecChannelTest() +{ +} + +bool CmdSpecChannelTest::Init() +{ + return(true); +} + +bool CmdSpecChannelTest::AnyMessage( + std::shared_ptr pChannel, + const neb::RedisMsg& oRedisMsg) +{ + LOG4_DEBUG("%s", oRedisMsg.DebugString().c_str()); + std::string strCmd = oRedisMsg.element(0).str(); + std::transform(strCmd.begin(), strCmd.end(), strCmd.begin(), [](unsigned char c)->unsigned char{return std::toupper(c);}); + if (ofdb::CmdOfdb::IsValidCmd(strCmd)) + { + if (strCmd == "PING") + { + Pong(pChannel, oRedisMsg); + return(true); + } + if (strCmd == "COMMAND") + { + Command(pChannel, oRedisMsg); + return(true); + } + if (strCmd == "QUIT") + { + Quit(pChannel, oRedisMsg); + return(true); + } + } + else + { + LOG4_ERROR("ERR unknown command %s!", oRedisMsg.element(0).str()); + SendErrorReply(pChannel, "ERR unknown command '" + oRedisMsg.element(0).str() + "'"); + return(false); + } + + uint16 uiSlotNumPerWorker = 16384 / GetNodeInfo().uiWorkerNum; + uint16 uiSlotFrom = uiSlotNumPerWorker * (GetWorkerIndex() - 1); + uint16 uiSlotTo = (uiSlotNumPerWorker * GetWorkerIndex()) - 1; + uint16 uiSlotId = HashSlot(oRedisMsg.element(1).str().data(), oRedisMsg.element(1).str().length()); + LOG4_DEBUG("TTTTTTTTTTTTTTTTT: worker %u slot from %u to %u, and slot_id = %u", + GetWorkerIndex(), uiSlotFrom, uiSlotTo, uiSlotId); + if (uiSlotId >= uiSlotFrom && uiSlotId <= uiSlotTo) + { + char szResult[256] = {0}; + neb::RedisReply oRedisReply; + snprintf(szResult, 256, "slot %u, reply from worker %u", uiSlotId, GetWorkerIndex()); + LOG4_DEBUG("TTTTTTTTTTTTTTTTT: %s", szResult); + oRedisReply.set_type(REDIS_REPLY_STRING); + oRedisReply.set_str(szResult); + if (pChannel->GetCodecType() == CODEC_TRANSFER) // spec channel + { + uint32 uiPeerStepSeq = pChannel->GetPeerStepSeq(); + if (uiPeerStepSeq == 0) + { + LOG4_ERROR("spec channel uiPeerStepSeq is 0"); + return(false); + } + SetPeerStepSeq(uiPeerStepSeq); + } + return(SendTo(pChannel, oRedisReply)); + } + else + { + uint32 uiToWorker = uiSlotId / uiSlotNumPerWorker; + if (uiSlotId % uiSlotNumPerWorker != 0) + { + uiToWorker++; + } + LOG4_DEBUG("TTTTTTTTTTTTTTTTT: transmit to %u", uiToWorker); + auto pStep = MakeSharedStep("neb::StepSpecChannelTest", pChannel, oRedisMsg, uiToWorker); + if (pStep != nullptr) + { + pStep->Emit(); + } + } + return(true); +} + +void CmdSpecChannelTest::Command(std::shared_ptr pChannel, const neb::RedisMsg& oRedisMsg) +{ + if (m_oCommand.element_size() == 0) + { + m_oCommand.set_type(neb::REDIS_REPLY_ARRAY); + auto pElement = m_oCommand.add_element(); + pElement->set_type(neb::REDIS_REPLY_ARRAY); + auto pE = pElement->add_element(); + pE->set_type(neb::REDIS_REPLY_STRING); + pE->set_str("HMGET"); + pE = pElement->add_element(); + pE->set_type(neb::REDIS_REPLY_INTEGER); + pE->set_integer(-3); + pE = pElement->add_element(); + pE->set_type(neb::REDIS_REPLY_ARRAY); + auto e = pE->add_element(); + e->set_type(neb::REDIS_REPLY_STATUS); + e->set_str("readonly"); + e = pE->add_element(); + e->set_type(neb::REDIS_REPLY_STATUS); + e->set_str("fast"); + pE = pElement->add_element(); + pE->set_type(neb::REDIS_REPLY_INTEGER); + pE->set_integer(1); + pE = pElement->add_element(); + pE->set_type(neb::REDIS_REPLY_INTEGER); + pE->set_integer(1); + pE = pElement->add_element(); + pE->set_type(neb::REDIS_REPLY_INTEGER); + pE->set_integer(1); + } + SendTo(pChannel, m_oCommand); +} + +void CmdSpecChannelTest::Pong(std::shared_ptr pChannel, const neb::RedisMsg& oRedisMsg) +{ + if (oRedisMsg.element_size() > 1 && oRedisMsg.element(1).str().size() > 0) + { + neb::RedisReply oPong; + oPong.set_type(neb::REDIS_REPLY_STATUS); + oPong.set_str(oRedisMsg.element(1).str()); + SendTo(pChannel, oPong); + } + else + { + if (m_oPong.element_size() == 0) + { + m_oPong.set_type(neb::REDIS_REPLY_STATUS); + m_oPong.set_str("PONG"); + } + SendTo(pChannel, m_oPong); + } +} + +void CmdSpecChannelTest::Quit(std::shared_ptr pChannel, const neb::RedisMsg& oRedisMsg) +{ + neb::RedisReply oReply; + oReply.set_type(neb::REDIS_REPLY_STATUS); + oReply.set_str("OK"); + SendTo(pChannel, oReply); +} + +void CmdSpecChannelTest::SendErrorReply( + std::shared_ptr pChannel, const std::string& strErrMsg) +{ + neb::RedisReply oRedisReply; + oRedisReply.set_type(neb::REDIS_REPLY_ERROR); + oRedisReply.set_str(strErrMsg); + SendTo(pChannel, oRedisReply); +} + + +uint16 CmdSpecChannelTest::HashSlot(const char* key, uint32 keylen) +{ + uint32 s, e; /* start-end indexes of { and } */ + + /* Search the first occurrence of '{'. */ + for (s = 0; s < keylen; s++) + if (key[s] == '{') break; + + /* No '{' ? Hash the whole key. This is the base case. */ + if (s == keylen) return crc16(key,keylen) & 16383; + + /* '{' found? Check if we have the corresponding '}'. */ + for (e = s+1; e < keylen; e++) + if (key[e] == '}') break; + + /* No '}' or nothing between {} ? Hash the whole key. */ + if (e == keylen || e == s+1) return crc16(key,keylen) & 16383; + + /* If we are here there is both a { and a } on its right. Hash + * what is in the middle between { and }. */ + return crc16(key+s+1,e-s-1) & 16383; +} + +} /* namespace neb */ + diff --git a/example/spec_channel/CmdSpecChannelTest.hpp b/example/spec_channel/CmdSpecChannelTest.hpp new file mode 100644 index 00000000..dba51617 --- /dev/null +++ b/example/spec_channel/CmdSpecChannelTest.hpp @@ -0,0 +1,37 @@ +#ifndef SRC_TEST_CMDSPECCHANNELTEST_HPP_ +#define SRC_TEST_CMDSPECCHANNELTEST_HPP_ + +#include + +namespace neb +{ + +class CmdSpecChannelTest: public neb::RedisCmd, + public neb::DynamicCreator +{ +public: + CmdSpecChannelTest(int32 iCmd); + virtual ~CmdSpecChannelTest(); + + virtual bool Init(); + + virtual bool AnyMessage( + std::shared_ptr pChannel, + const neb::RedisMsg& oRedisMsg); + +protected: + void Command(std::shared_ptr pChannel, const neb::RedisMsg& oRedisMsg); + void Pong(std::shared_ptr pChannel, const neb::RedisMsg& oRedisMsg); + void Quit(std::shared_ptr pChannel, const neb::RedisMsg& oRedisMsg); + void SendErrorReply(std::shared_ptr pChannel, const std::string& strErrMsg); + uint16 HashSlot(const char* key, uint32 keylen); + +private: + neb::RedisReply m_oCommand; + neb::RedisReply m_oPong; +}; + +} /* namespace neb */ + +#endif /* SRC_TEST_CMDSPECCHANNELTEST_HPP_ */ + diff --git a/example/spec_channel/StepSpecChannelTest.cpp b/example/spec_channel/StepSpecChannelTest.cpp new file mode 100644 index 00000000..1813ccdc --- /dev/null +++ b/example/spec_channel/StepSpecChannelTest.cpp @@ -0,0 +1,53 @@ +#include "StepSpecChannelTest.hpp" +#include + +namespace neb +{ + +StepSpecChannelTest::StepSpecChannelTest( + std::shared_ptr pChannel, const neb::RedisMsg& oRedisMsg, uint32 uiToWorker) + : neb::RedisStep(2.0), + m_uiToWorker(uiToWorker), m_pReqChannel(pChannel), m_oReqRedisMsg(oRedisMsg) +{ +} + +StepSpecChannelTest::~StepSpecChannelTest() +{ +} + +neb::E_CMD_STATUS StepSpecChannelTest::Emit(int iErrno, const std::string& strErrMsg, void* data) +{ + if (IO::TransmitTo(this, m_uiToWorker, GetSequence(), m_oReqRedisMsg)) + { + return(neb::CMD_STATUS_RUNNING); + } + LOG4_ERROR("IO::TransmitTo failed."); + return(neb::CMD_STATUS_FAULT); +} + +neb::E_CMD_STATUS StepSpecChannelTest::Callback( + std::shared_ptr pChannel, + const neb::RedisReply& oRedisReply) +{ + if (SendTo(m_pReqChannel, oRedisReply)) + { + LOG4_ERROR("failed to send response."); + } + return(neb::CMD_STATUS_COMPLETED); +} + +neb::E_CMD_STATUS StepSpecChannelTest::ErrBack( + std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) +{ + LOG4_ERROR("%s", strErrMsg.c_str()); + return(neb::CMD_STATUS_FAULT); +} + +neb::E_CMD_STATUS StepSpecChannelTest::Timeout() +{ + LOG4_ERROR(""); + return(neb::CMD_STATUS_FAULT); +} + +} /* namespace neb */ + diff --git a/example/spec_channel/StepSpecChannelTest.hpp b/example/spec_channel/StepSpecChannelTest.hpp new file mode 100644 index 00000000..10f0a0a0 --- /dev/null +++ b/example/spec_channel/StepSpecChannelTest.hpp @@ -0,0 +1,38 @@ +#ifndef SRC_TEST_STEPSPECCHANNELTEST_HPP_ +#define SRC_TEST_STEPSPECCHANNELTEST_HPP_ + +#include + +namespace neb +{ + +class StepSpecChannelTest: public neb::RedisStep, + public neb::DynamicCreator&, const neb::RedisMsg&, uint32&> +{ +public: + StepSpecChannelTest( + std::shared_ptr pChannel, + const neb::RedisMsg& oRedisMsg, uint32 uiToWorker); + virtual ~StepSpecChannelTest(); + + virtual neb::E_CMD_STATUS Emit(int iErrno, const std::string& strErrMsg, void* data = NULL); + + virtual neb::E_CMD_STATUS Callback( + std::shared_ptr pChannel, + const neb::RedisReply& oRedisReply); + virtual neb::E_CMD_STATUS ErrBack(std::shared_ptr pChannel, + int iErrno, const std::string& strErrMsg); + + virtual neb::E_CMD_STATUS Timeout(); + +private: + uint32 m_uiToWorker; + std::shared_ptr m_pReqChannel; + neb::RedisMsg m_oReqRedisMsg; +}; + +} /* namespace neb */ + +#endif /* SRC_TEST_STEPSPECCHANNELTEST_HPP_ */ + diff --git a/proto/neb_sys.proto b/proto/neb_sys.proto index dd6fc19d..61e24f7e 100644 --- a/proto/neb_sys.proto +++ b/proto/neb_sys.proto @@ -62,3 +62,12 @@ message SpecChannelInfo uint32 to_labor = 3; } +message FdTransfer +{ + int32 fd = 1; + int32 addr_family = 2; + string client_addr = 3; + int32 client_port = 4; + int32 codec_type = 5; +} + diff --git a/src/Definition.hpp b/src/Definition.hpp index 71e4fdea..be148e30 100644 --- a/src/Definition.hpp +++ b/src/Definition.hpp @@ -216,8 +216,9 @@ enum E_CHANNEL_STATUS CHANNEL_STATUS_WORKER = 4, ///< 连接成功从Manager传送给Worker CHANNEL_STATUS_TELL_WORKER = 5, ///< 将本Worker信息告知对端Worker CHANNEL_STATUS_ESTABLISHED = 6, ///< 与对端Worker的连接就绪(可以正常收发消息) - CHANNEL_STATUS_BROKEN = 7, ///< 连接已断开,待回收处理 - CHANNEL_STATUS_CLOSED = 8, ///< 被丢弃待回收 + CHANNEL_STATUS_MIGRATED = 7, ///< channel迁移到其他labor + CHANNEL_STATUS_BROKEN = 8, ///< 连接已断开,待回收处理 + CHANNEL_STATUS_CLOSED = 9, ///< 被丢弃待回收 }; } diff --git a/src/Error.hpp b/src/Error.hpp index a30ee988..ec123b4a 100644 --- a/src/Error.hpp +++ b/src/Error.hpp @@ -50,6 +50,8 @@ enum E_ERROR_NO ERR_SPEC_CHANNEL_TARGET = 10103, ///< spec channel源和目标相同 ERR_SPEC_CHANNEL_LABOR_ID = 10104, ///< labor id 超出labor数量 ERR_SPEC_CHANNEL_MANAGER = 10105, ///< manager to worker或worker to manager spec channel不存在 + ERR_SPEC_CHANNEL_CALL = 10106, ///< inappropriate call + ERR_SPEC_CHANNEL_MIGRATE = 10107, ///< channel migrate failed /* 存储代理错误码段 11000~11999 */ ERR_INCOMPLET_DATAPROXY_DATA = 11001, ///< DataProxy请求数据包不完整 diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 5eb4e472..07b019c1 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -461,6 +461,7 @@ void ActorBuilder::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdOnStartService", (int)CMD_REQ_START_SERVICE); MakeSharedCmd(nullptr, "neb::CmdDataReport", (int)CMD_REQ_DATA_REPORT); MakeSharedCmd(nullptr, "neb::CmdSpecChannelCreated", (int)CMD_REQ_SPEC_CHANNEL); + MakeSharedCmd(nullptr, "neb::CmdChannelMigrate", (int)CMD_REQ_CHANNEL_MIGRATE); std::string strModulePath = "/healthy"; MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); strModulePath = "/health"; @@ -479,6 +480,8 @@ void ActorBuilder::LoadSysCmd() MakeSharedCmd(nullptr, "neb::CmdSetNodeCustomConf", (int)CMD_REQ_SET_NODE_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdReloadCustomConf", (int)CMD_REQ_RELOAD_CUSTOM_CONFIG); MakeSharedCmd(nullptr, "neb::CmdSpecChannelCreated", (int)CMD_REQ_SPEC_CHANNEL); + MakeSharedCmd(nullptr, "neb::CmdFdTransfer", (int)CMD_REQ_FD_TRANSFER); + MakeSharedCmd(nullptr, "neb::CmdChannelMigrate", (int)CMD_REQ_CHANNEL_MIGRATE); std::string strModulePath = "/healthy"; MakeSharedModule(nullptr, "neb::ModuleHealth", strModulePath); strModulePath = "/health"; diff --git a/src/actor/cmd/CW.hpp b/src/actor/cmd/CW.hpp index 121cfc7e..78a6e5d6 100644 --- a/src/actor/cmd/CW.hpp +++ b/src/actor/cmd/CW.hpp @@ -42,6 +42,10 @@ enum E_CMD CMD_RSP_START_SERVICE = 16, ///< 服务就绪响应(无须响应) CMD_REQ_SPEC_CHANNEL = 17, ///< 创建SpecChannel请求 CMD_RSP_SPEC_CHANNEL = 18, ///< 创建SpecChannel响应(无须响应) + CMD_REQ_FD_TRANSFER = 19, ///< 通过SpecChannel传送文件描述符请求 + CMD_RSP_FD_TRANSFER = 20, ///< 通过SpecChannel传送文件描述符响应(无须响应) + CMD_REQ_CHANNEL_MIGRATE = 21, ///< 通过SpecChannel迁移SocketChannel请求 + CMD_RSP_CHANNEL_MIGRATE = 22, ///< 通过SpecChannel迁移SocketChannel响应(无须响应) CMD_REQ_NODE_STATUS_REPORT = 101, ///< 节点Server状态上报请求(各节点向控制中心上报自身状态信息) CMD_RSP_NODE_STATUS_REPORT = 102, ///< 节点Server状态上报应答 diff --git a/src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp b/src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp new file mode 100644 index 00000000..74f7a4f7 --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp @@ -0,0 +1,34 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdChannelMigrate.cpp + * @brief + * @author Bwar + * @date: 2023-01-08 + * @note + * Modify history: + ******************************************************************************/ +#include "CmdChannelMigrate.hpp" +#include "ios/Dispatcher.hpp" + +namespace neb +{ + +CmdChannelMigrate::CmdChannelMigrate(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdChannelMigrate::~CmdChannelMigrate() +{ +} + +bool CmdChannelMigrate::AnyMessage( + std::shared_ptr pChannel, SocketChannelPack& oPack) +{ + auto pMigratedChannel = oPack.UnpackChannel(); + GetLabor(this)->GetDispatcher()->AddChannelToLoop(pMigratedChannel); + return(true); +} + +} /* namespace neb */ + diff --git a/src/actor/cmd/sys_cmd/CmdChannelMigrate.hpp b/src/actor/cmd/sys_cmd/CmdChannelMigrate.hpp new file mode 100644 index 00000000..3e1d7332 --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdChannelMigrate.hpp @@ -0,0 +1,47 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdChannelMigrate.hpp + * @brief + * @author Bwar + * @date: 2023-01-08 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_SYS_CMD_CMDCHANNELMIGRATE_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_CMDCHANNELMIGRATE_HPP_ + +#include "actor/ActorSys.hpp" +#include "actor/cmd/Cmd.hpp" +#include "channel/migrate/SocketChannelPack.hpp" + +namespace neb +{ + +class CmdChannelMigrate: public Cmd, + public DynamicCreator, public ActorSys +{ +public: + CmdChannelMigrate(int32 iCmd); + virtual ~CmdChannelMigrate(); + + virtual bool Init() + { + return(true); + } + + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, const MsgBody& oMsgBody) + { + return(false); + } + + bool AnyMessage( + std::shared_ptr pChannel, + SocketChannelPack& oPack); +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_CMDCHANNELMIGRATE_HPP_ */ + diff --git a/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp b/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp new file mode 100644 index 00000000..d88687d0 --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp @@ -0,0 +1,88 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdFdTransfer.cpp + * @brief + * @author Bwar + * @date: 2023-01-08 + * @note + * Modify history: + ******************************************************************************/ +#include "CmdFdTransfer.hpp" +#include "pb/neb_sys.pb.h" +#include "channel/SocketChannel.hpp" +#include "ios/Dispatcher.hpp" +#include "labor/NodeInfo.hpp" +#include "actor/step/PbStep.hpp" + +namespace neb +{ + +CmdFdTransfer::CmdFdTransfer(int32 iCmd) + : Cmd(iCmd) +{ +} + +CmdFdTransfer::~CmdFdTransfer() +{ +} + +bool CmdFdTransfer::AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody) +{ + FdTransfer oFdTransferInfo; + if (!oFdTransferInfo.ParseFromString(oInMsgBody.data())) + { + LOG4_ERROR("SpecChannelInfo ParseFromString failed."); + return(false); + } + LOG4_INFO("%s:%d fd[%d] addr_family %d with codec_type %d", + oFdTransferInfo.client_addr().c_str(), + oFdTransferInfo.client_port(), oFdTransferInfo.fd(), + oFdTransferInfo.addr_family(), oFdTransferInfo.codec_type()); + std::shared_ptr pNewChannel = nullptr; + if ((CODEC_NEBULA != oFdTransferInfo.codec_type()) && (CODEC_NEBULA_IN_NODE != oFdTransferInfo.codec_type()) && GetLabor(this)->WithSsl()) + { + pNewChannel = GetLabor(this)->GetDispatcher()->CreateSocketChannel( + oFdTransferInfo.fd(), E_CODEC_TYPE(oFdTransferInfo.codec_type()), false, true); + } + else + { + pNewChannel = GetLabor(this)->GetDispatcher()->CreateSocketChannel( + oFdTransferInfo.fd(), E_CODEC_TYPE(oFdTransferInfo.codec_type()), false, false); + } + if (nullptr != pNewChannel) + { + pNewChannel->SetRemoteAddr(oFdTransferInfo.client_addr()); + GetLabor(this)->GetDispatcher()->AddIoReadEvent(pNewChannel); + if (CODEC_NEBULA == oFdTransferInfo.codec_type()) + { + GetLabor(this)->GetDispatcher()->AddIoTimeout(pNewChannel, GetNodeInfo().dIoTimeout); + std::shared_ptr pStepTellWorker + = GetLabor(this)->GetActorBuilder()->MakeSharedStep(nullptr, "neb::StepTellWorker", pNewChannel); + if (nullptr == pStepTellWorker) + { + return(false); + } + pStepTellWorker->Emit(ERR_OK); + } + else + { + pNewChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + ev_tstamp dIoTimeout = (GetNodeInfo().dConnectionProtection > 0) + ? GetNodeInfo().dConnectionProtection : GetNodeInfo().dIoTimeout; + GetLabor(this)->GetDispatcher()->AddIoTimeout(pNewChannel, dIoTimeout); + } + GetLabor(this)->IoStatAddConnection(IO_STAT_DOWNSTREAM_NEW_CONNECTION); + return(true); + } + else // 没有足够资源分配给新连接,直接close掉 + { + close(oFdTransferInfo.fd()); + } + return(true); +} + +} /* namespace neb */ + diff --git a/src/actor/cmd/sys_cmd/CmdFdTransfer.hpp b/src/actor/cmd/sys_cmd/CmdFdTransfer.hpp new file mode 100644 index 00000000..59acca5b --- /dev/null +++ b/src/actor/cmd/sys_cmd/CmdFdTransfer.hpp @@ -0,0 +1,34 @@ +/******************************************************************************* + * Project: Nebula + * @file CmdFdTransfer.hpp + * @brief + * @author Bwar + * @date: 2023-01-08 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_SYS_CMD_CMDFDTRANSFER_HPP_ +#define SRC_ACTOR_CMD_SYS_CMD_CMDFDTRANSFER_HPP_ + +#include "actor/ActorSys.hpp" +#include "actor/cmd/Cmd.hpp" + +namespace neb +{ + +class CmdFdTransfer: public Cmd, + public DynamicCreator, public ActorSys +{ +public: + CmdFdTransfer(int32 iCmd); + virtual ~CmdFdTransfer(); + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oInMsgHead, + const MsgBody& oInMsgBody); +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_CMD_SYS_CMD_CMDFDTRANSFER_HPP_ */ + diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp index 19ce163d..a7eb0209 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp @@ -12,6 +12,7 @@ #include "labor/Manager.hpp" #include "ios/Dispatcher.hpp" #include "actor/session/sys_session/manager/SessionManager.hpp" +#include "codec/CodecProto.hpp" namespace neb { @@ -50,25 +51,19 @@ bool CmdOnOrientationFdTransfer::AnyMessage( SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); return(false); } - std::unordered_map::iterator worker_iter; - int iErrno = GetLabor(this)->GetDispatcher()->SendFd( - pWorkerInfo->iDataFd, pChannel->GetFd(), - ((Manager*)GetLabor(this))->GetManagerInfo().iS2SFamily, - (int)pChannel->GetCodecType(), pChannel->GetRemoteAddr()); - if (iErrno != ERR_OK) + uint32 uiManagerLaborId = GetLabor(this)->GetNodeInfo().uiWorkerNum + 1; + bool bResult = GetLabor(this)->GetDispatcher()->MigrateSocketChannel(uiManagerLaborId, iWorkerIndex, pChannel); + if (bResult) + { + return(true); + } + else { - GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pChannel); oOutMsgBody.mutable_rsp_result()->set_code(ERR_TRANSFER_FD); - oOutMsgBody.mutable_rsp_result()->set_msg("send fd error!"); + oOutMsgBody.mutable_rsp_result()->set_msg("channel migration error"); SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); return(false); } - - GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pChannel); - oOutMsgBody.mutable_rsp_result()->set_code(ERR_OK); - oOutMsgBody.mutable_rsp_result()->set_msg("success"); - SendTo(pChannel, oInMsgHead.cmd() + 1, oInMsgHead.seq(), oOutMsgBody); - return(true); } } /* namespace neb */ diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp index 27c04a45..ba6253da 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp @@ -9,9 +9,9 @@ ******************************************************************************/ #include "CmdOnWorkerLoad.hpp" - -#include "util/json/CJsonObject.hpp" +#include "pb/report.pb.h" #include "channel/SocketChannel.hpp" +#include "channel/SpecChannel.hpp" #include "actor/session/sys_session/manager/SessionManager.hpp" namespace neb @@ -41,10 +41,11 @@ bool CmdOnWorkerLoad::AnyMessage( return(false); } } - CJsonObject oJsonLoad; - if (oJsonLoad.Parse(oInMsgBody.data())) + ReportRecord oWorkerStatus; + if (oWorkerStatus.ParseFromString(oInMsgBody.data())) { - return(m_pSessionManager->SetWorkerLoad(pChannel->GetFd(), oJsonLoad)); + auto pSpecChannel = std::static_pointer_cast>(pChannel); + return(m_pSessionManager->SetWorkerLoad(pSpecChannel->GetFromLaborId(), oWorkerStatus)); } else { diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 93247c81..f31bbc7b 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -16,23 +16,21 @@ #include "util/process_helper.h" #include "util/json/CJsonObject.hpp" #include "util/encrypt/city.h" +#include "util/StringConverter.hpp" #include "labor/NodeInfo.hpp" #include "labor/Manager.hpp" #include "labor/Worker.hpp" #include "labor/Loader.hpp" #include "ios/Dispatcher.hpp" +#include "ios/IO.hpp" #include "actor/cmd/CW.hpp" #include "actor/session/sys_session/SessionDataReport.hpp" namespace neb { -std::mutex SessionManager::s_mutexWorker; -std::vector SessionManager::s_vecWorkerThreadId; - -SessionManager::SessionManager(bool bDirectToLoader, bool bDispatchWithPort, ev_tstamp dStatInterval) - : Session("neb::SessionManager", dStatInterval), - m_bDirectToLoader(bDirectToLoader), m_bDispatchWithPort(bDispatchWithPort) +SessionManager::SessionManager(ev_tstamp dStatInterval) + : Session("neb::SessionManager", dStatInterval) { } @@ -44,14 +42,7 @@ SessionManager::~SessionManager() it->second = nullptr; } m_mapWorker.clear(); - for (auto it = m_mapWorkerInfo.begin(); it != m_mapWorkerInfo.end(); ++it) - { - delete it->second; - it->second = nullptr; - } - m_mapWorkerInfo.clear(); - m_mapWorkerStartNum.clear(); - m_mapWorkerFdPid.clear(); + m_vecWorkerInfo.clear(); m_mapOnlineNodes.clear(); } @@ -67,33 +58,37 @@ E_CMD_STATUS SessionManager::Timeout() uint32 uiLoad = 0; uint32 uiConnect = 0; char szKey[256] = {0}; - for (auto iter = m_mapWorkerInfo.begin(); iter != m_mapWorkerInfo.end(); ++iter) + for (uint32 i = 0; i < m_vecWorkerInfo.size(); ++i) { - uiLoad += iter->second->uiLoad; - uiConnect += iter->second->uiConnection; + if (m_vecWorkerInfo[i] == nullptr) + { + continue; + } + uiLoad += m_vecWorkerInfo[i]->uiLoad; + uiConnect += m_vecWorkerInfo[i]->uiConnection; pRecord = pReport->add_records(); - snprintf(szKey, 256, "W%u-recv_num", iter->first); + snprintf(szKey, 256, "W%u-recv_num", i); pRecord->set_key(szKey); pRecord->set_item("nebula"); - pRecord->add_value(iter->second->uiRecvNum); + pRecord->add_value(m_vecWorkerInfo[i]->uiRecvNum); pRecord->set_value_type(ReportRecord::VALUE_ACC); pRecord = pReport->add_records(); - snprintf(szKey, 256, "W%u-recv_bytes", iter->first); + snprintf(szKey, 256, "W%u-recv_bytes", i); pRecord->set_key(szKey); pRecord->set_item("nebula"); - pRecord->add_value(iter->second->uiRecvByte); + pRecord->add_value(m_vecWorkerInfo[i]->uiRecvByte); pRecord->set_value_type(ReportRecord::VALUE_ACC); pRecord = pReport->add_records(); - snprintf(szKey, 256, "W%u-send_num", iter->first); + snprintf(szKey, 256, "W%u-send_num", i); pRecord->set_key(szKey); pRecord->set_item("nebula"); - pRecord->add_value(iter->second->uiSendNum); + pRecord->add_value(m_vecWorkerInfo[i]->uiSendNum); pRecord->set_value_type(ReportRecord::VALUE_ACC); pRecord = pReport->add_records(); - snprintf(szKey, 256, "W%u-send_bytes", iter->first); + snprintf(szKey, 256, "W%u-send_bytes", i); pRecord->set_key(szKey); pRecord->set_item("nebula"); - pRecord->add_value(iter->second->uiSendByte); + pRecord->add_value(m_vecWorkerInfo[i]->uiSendByte); pRecord->set_value_type(ReportRecord::VALUE_ACC); } pRecord = pReport->add_records(); @@ -118,6 +113,57 @@ E_CMD_STATUS SessionManager::Timeout() return(CMD_STATUS_RUNNING); } +void SessionManager::AddPort2WorkerInfo(uint32 uiWorkerNum, uint32 uiLoaderNum, CJsonObject& oPort2WorkerJson) +{ + uint32 uiWorkerId = 0; + int32 iPort = 0; + std::string strPort; + while(oPort2WorkerJson.GetKey(strPort)) + { + std::vector vecWorkerId; + iPort = StringConverter::RapidAtoi(strPort.c_str()); + for (int i = 0; i < oPort2WorkerJson[strPort].GetArraySize(); ++i) + { + if (oPort2WorkerJson[strPort].Get(i, uiWorkerId)) + { + if (uiWorkerId <= uiWorkerNum) + { + if (uiLoaderNum > 0) + { + vecWorkerId.push_back(uiWorkerId); + } + else + { + if (uiWorkerId != 0) + { + vecWorkerId.push_back(uiWorkerId); + } + } + } + } + } + m_mapPort2Worker.insert(std::make_pair(iPort, vecWorkerId)); + } +} + +void SessionManager::AddPortListenFd(int32 iPort, int32 iFd) +{ + auto port_iter = m_mapPort2Worker.find(iPort); + if (port_iter == m_mapPort2Worker.end()) + { + ; // no this port to worker config + } + else + { + auto& vecWorkerId = port_iter->second; + auto fd_iter = m_mapListenFd2Worker.find(iFd); + if (fd_iter == m_mapListenFd2Worker.end()) + { + m_mapListenFd2Worker.insert(std::make_pair(iFd, std::make_pair(0, vecWorkerId))); + } + } +} + void SessionManager::AddOnlineNode(const std::string& strNodeIdentify, const std::string& strNodeInfo) { auto iter = m_mapOnlineNodes.find(strNodeIdentify); @@ -143,93 +189,75 @@ void SessionManager::DelOnlineNode(const std::string& strNodeIdentify) bool SessionManager::SendToChild(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - for (auto worker_iter = m_mapWorkerInfo.begin(); worker_iter != m_mapWorkerInfo.end(); ++worker_iter) + for (uint32 i = 0; i < m_vecWorkerInfo.size(); ++i) { - GetLabor(this)->GetDispatcher()->SendTo( - worker_iter->second->iControlFd, iCmd, uiSeq, oMsgBody); + if (m_vecWorkerInfo[i] == nullptr) + { + continue; + } + MsgHead oMsgHead; // It will be cleared after each successful sending + MsgBody oOutMsgBody(oMsgBody); + oMsgHead.set_cmd(iCmd); + oMsgHead.set_seq(uiSeq); + IO::TransmitTo(this, m_vecWorkerInfo[i]->iWorkerIndex, uiSeq, oMsgHead, oMsgBody); } return(true); } bool SessionManager::SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - for (auto worker_iter = m_mapWorkerInfo.begin(); worker_iter != m_mapWorkerInfo.end(); ++worker_iter) + for (uint32 i = 1; i < m_vecWorkerInfo.size(); ++i) { - if (m_iLoaderDataFd == worker_iter->second->iDataFd) + if (m_vecWorkerInfo[i] == nullptr) { continue; } - GetLabor(this)->GetDispatcher()->SendTo( - worker_iter->second->iControlFd, iCmd, uiSeq, oMsgBody); + MsgHead oMsgHead; // It will be cleared after each successful sending + MsgBody oOutMsgBody(oMsgBody); + oMsgHead.set_cmd(iCmd); + oMsgHead.set_seq(uiSeq); + IO::TransmitTo(this, m_vecWorkerInfo[i]->iWorkerIndex, uiSeq, oMsgHead, oMsgBody); } return(true); } bool SessionManager::SendToLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { - for (auto worker_iter = m_mapWorkerInfo.begin(); worker_iter != m_mapWorkerInfo.end(); ++worker_iter) + if (m_vecWorkerInfo.size() == 0) { - if (m_iLoaderDataFd == worker_iter->second->iDataFd) - { - GetLabor(this)->GetDispatcher()->SendTo( - worker_iter->second->iControlFd, iCmd, uiSeq, oMsgBody); - } + LOG4_ERROR("no worker info."); + return(false); } - return(true); + if (m_vecWorkerInfo[0] == nullptr) + { + LOG4_ERROR("no loader."); + return(false); + } + MsgHead oMsgHead; + oMsgHead.set_cmd(iCmd); + oMsgHead.set_seq(uiSeq); + return(IO::TransmitTo(this, 0, uiSeq, oMsgHead, oMsgBody)); } -void SessionManager::AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd) +void SessionManager::AddWorkerInfo(int iWorkerIndex, int iPid) { - WorkerInfo* pWorkerAttr = nullptr; - try + if (iWorkerIndex < 0) { - pWorkerAttr = new WorkerInfo(); + LOG4_ERROR("invalid worker index %d", iWorkerIndex); + return; } - catch(std::bad_alloc& e) + for (uint32 i = m_vecWorkerInfo.size(); i <= (uint32)iWorkerIndex; ++i) { - LOG4_ERROR("new WorkerInfo error: %s", e.what()); - return; + m_vecWorkerInfo.push_back(nullptr); } + auto pWorkerAttr = std::make_shared(); pWorkerAttr->iWorkerIndex = iWorkerIndex; pWorkerAttr->iWorkerPid = iPid; - pWorkerAttr->iControlFd = iControlFd; - pWorkerAttr->iDataFd = iDataFd; pWorkerAttr->dBeatTime = GetNowTime(); - if (GetLabor(this)->GetNodeInfo().bThreadMode) - { - m_mapWorkerInfo.insert(std::make_pair(iWorkerIndex, pWorkerAttr)); - m_mapWorkerFdPid.insert(std::pair(iControlFd, iWorkerIndex)); - m_mapWorkerFdPid.insert(std::pair(iDataFd, iWorkerIndex)); - } - else - { - m_mapWorkerInfo.insert(std::make_pair(iPid, pWorkerAttr)); - m_mapWorkerFdPid.insert(std::pair(iControlFd, iPid)); - m_mapWorkerFdPid.insert(std::pair(iDataFd, iPid)); - } - auto start_num_iter = m_mapWorkerStartNum.find(iWorkerIndex); - if (start_num_iter == m_mapWorkerStartNum.end()) - { - m_mapWorkerStartNum.insert(std::pair(iWorkerIndex, 1)); - } - else - { - start_num_iter->second++; - } - while ((int)m_vecWorkerDataFd.size() <= iWorkerIndex) - { - m_vecWorkerDataFd.push_back(-1); - } - m_vecWorkerDataFd[iWorkerIndex] = iDataFd; -} - -void SessionManager::AddLoaderInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd) -{ - m_iLoaderDataFd = iDataFd; - AddWorkerInfo(iWorkerIndex, iPid, iControlFd, iDataFd); + m_vecWorkerInfo[iWorkerIndex] = pWorkerAttr; } -Worker* SessionManager::MutableWorker(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd) +Worker* SessionManager::MutableWorker(int iWorkerIndex, const std::string& strWorkPath) { auto iter = m_mapWorker.find(iWorkerIndex); if (iter == m_mapWorker.end()) @@ -237,7 +265,7 @@ Worker* SessionManager::MutableWorker(int iWorkerIndex, const std::string& strWo Worker* pWorker = nullptr; try { - pWorker = new Worker(strWorkPath, iControlFd, iDataFd, iWorkerIndex); + pWorker = new Worker(strWorkPath, iWorkerIndex); } catch(std::bad_alloc& e) { @@ -253,7 +281,7 @@ Worker* SessionManager::MutableWorker(int iWorkerIndex, const std::string& strWo } } -Loader* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd) +Loader* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWorkPath) { auto iter = m_mapWorker.find(iWorkerIndex); if (iter == m_mapWorker.end()) @@ -261,7 +289,7 @@ Loader* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWo Loader* pLoader = nullptr; try { - pLoader = new Loader(strWorkPath, iControlFd, iDataFd, iWorkerIndex); + pLoader = new Loader(strWorkPath, iWorkerIndex); } catch(std::bad_alloc& e) { @@ -277,54 +305,37 @@ Loader* SessionManager::MutableLoader(int iWorkerIndex, const std::string& strWo } } -const WorkerInfo* SessionManager::GetWorkerInfo(int32 iWorkerIndex) const +std::shared_ptr SessionManager::GetWorkerInfo(int32 iWorkerIndex) const { - for (auto worker_iter = m_mapWorkerInfo.begin(); - worker_iter != m_mapWorkerInfo.end(); ++worker_iter) + if (iWorkerIndex < 0 || iWorkerIndex >= (int32)m_vecWorkerInfo.size()) { - if (iWorkerIndex == worker_iter->second->iWorkerIndex) - { - return(worker_iter->second); - } + return(nullptr); } - return(nullptr); + return(m_vecWorkerInfo[iWorkerIndex]); } -bool SessionManager::SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad) +bool SessionManager::SetWorkerLoad(uint32 uiWorkerIndex, const ReportRecord& oWorkerStatus) { - auto fd_pid_iter = m_mapWorkerFdPid.find(iWorkerFd); - if (fd_pid_iter != m_mapWorkerFdPid.end()) + if (uiWorkerIndex >= m_vecWorkerInfo.size()) { - auto iPid = fd_pid_iter->second; - auto it = m_mapWorkerInfo.find(iPid); - if (it != m_mapWorkerInfo.end()) - { - oJsonLoad.Get("load", it->second->uiLoad); - oJsonLoad.Get("connect", it->second->uiConnection); - oJsonLoad.Get("recv_num", it->second->uiRecvNum); - oJsonLoad.Get("recv_byte", it->second->uiRecvByte); - oJsonLoad.Get("send_num", it->second->uiSendNum); - oJsonLoad.Get("send_byte", it->second->uiSendByte); - LOG4_INFO("worker %u load %u, connection %u, recv_num %u, recv_byte %u," - "send_num %u, send_byte %u", it->second->iWorkerIndex, - it->second->uiLoad, it->second->uiConnection, - it->second->uiRecvNum, it->second->uiRecvByte, - it->second->uiSendNum, it->second->uiSendByte); - it->second->dBeatTime = GetNowTime(); - it->second->bStartBeatCheck = true; - return(true); - } - else - { - LOG4_ERROR("no worker info found for pid %d!", iPid); - return(false); - } + return(false); } - else + if (m_vecWorkerInfo[uiWorkerIndex] == nullptr) + { + return(false); + } + if (oWorkerStatus.value_size() < 6) { - LOG4_ERROR("%d is not a worker fd!", iWorkerFd); + LOG4_ERROR("value size %d error", oWorkerStatus.value_size()); return(false); } + m_vecWorkerInfo[uiWorkerIndex]->uiLoad = oWorkerStatus.value(0); + m_vecWorkerInfo[uiWorkerIndex]->uiConnection = oWorkerStatus.value(1); + m_vecWorkerInfo[uiWorkerIndex]->uiRecvNum = oWorkerStatus.value(2); + m_vecWorkerInfo[uiWorkerIndex]->uiRecvByte = oWorkerStatus.value(3); + m_vecWorkerInfo[uiWorkerIndex]->uiSendNum = oWorkerStatus.value(4); + m_vecWorkerInfo[uiWorkerIndex]->uiSendByte = oWorkerStatus.value(5); + return(true); } void SessionManager::SetLoaderActorBuilder(ActorBuilder* pActorBuilder) @@ -335,204 +346,69 @@ void SessionManager::SetLoaderActorBuilder(ActorBuilder* pActorBuilder) } } -int SessionManager::GetWorkerDataFd(const char* szRemoteAddr, int iRemotePort, int iBonding) +int32 SessionManager::GetDispatchWorkerId(int32 iListenFd) { - if (m_bDirectToLoader && m_iLoaderDataFd != -1) - { - return(m_iLoaderDataFd); - } - else + auto fd_iter = m_mapListenFd2Worker.find(iListenFd); + if (fd_iter == m_mapListenFd2Worker.end()) // no bonding { - if (m_vecWorkerDataFd.empty()) + ++m_uiRoundRobin; + if (m_uiRoundRobin > m_vecWorkerInfo.size() - 1) { - return(-1); + m_uiRoundRobin = 1; } - char szIdentify[64] = {0}; - snprintf(szIdentify, 64, "%s:%d", szRemoteAddr, iRemotePort); - uint32 uiKeyHash = CityHash32(szIdentify, strlen(szIdentify)); - uint32 uiIndex = uiKeyHash % (m_vecWorkerDataFd.size() - 1) + 1; - if (m_bDispatchWithPort && m_vecWorkerDataFd.size() > 2 - && (0x00000001 & iBonding) != (0x00000001 & uiIndex)) + if (m_uiRoundRobin > m_vecWorkerInfo.size() - 1) { - if (uiIndex > 1) - { - uiIndex -= 1; - } - else - { - uiIndex = m_vecWorkerDataFd.size() - 1; - if ((0x00000001 & iBonding) != (0x00000001 & uiIndex)) - { - uiIndex -= 1; - } - } + return(-1); // no worker } - return(m_vecWorkerDataFd[uiIndex + 1]); - } - return(-1); -} - -int SessionManager::GetNextWorkerDataFd(int iBonding) -{ - if (m_bDirectToLoader && m_iLoaderDataFd != -1) - { - return(m_iLoaderDataFd); + return(m_vecWorkerInfo[m_uiRoundRobin]->iWorkerIndex); } else { - if (m_mapWorkerInfo.empty()) - { - return(-1); - } - if (m_bDispatchWithPort && m_vecWorkerDataFd.size() > 2) - { - uint32 i = 0x00000001 & iBonding; - do - { - m_uiWorkerDataFdIndex[i]++; - if (m_uiWorkerDataFdIndex[i] >= m_vecWorkerDataFd.size()) - { - m_uiWorkerDataFdIndex[i] = 1; - } - } - while ((0x00000001 & iBonding) != (0x00000001 & m_uiWorkerDataFdIndex[i])); - return(m_vecWorkerDataFd[m_uiWorkerDataFdIndex[i]]); - } - else + fd_iter->second.first++; + if (fd_iter->second.first >= fd_iter->second.second.size()) { - m_uiWorkerDataFdIndex[0]++; - if (m_uiWorkerDataFdIndex[0] >= m_vecWorkerDataFd.size()) - { - m_uiWorkerDataFdIndex[0] = 1; - } - return(m_vecWorkerDataFd[m_uiWorkerDataFdIndex[0]]); + fd_iter->second.first = 0; } + return(fd_iter->second.second[fd_iter->second.first]); } } -int SessionManager::GetMinLoadWorkerDataFd(int iBonding) +int32 SessionManager::GetDispatchWorkerId(int32 iListenFd, const char* szRemoteAddr, int iRemotePort) { - LOG4_TRACE(" "); - int iMinLoadWorkerFd = 0; - int iMinLoadPid = 0; - int iMinLoad = -1; - uint32 uiMinLoadConnection = 0; - if (m_bDirectToLoader && m_iLoaderDataFd != -1) + char szIdentify[64] = {0}; + snprintf(szIdentify, 64, "%s:%d", szRemoteAddr, iRemotePort); + uint32 uiKeyHash = CityHash32(szIdentify, strlen(szIdentify)); + auto fd_iter = m_mapListenFd2Worker.find(iListenFd); + if (fd_iter == m_mapListenFd2Worker.end()) // no bonding { - return(m_iLoaderDataFd); + uint32 uiIndex = uiKeyHash % (m_vecWorkerInfo.size() - 1) + 1; // loader的worker编号为0 + return(m_vecWorkerInfo[uiIndex]->iWorkerIndex); } else { - for (auto iter = m_mapWorkerInfo.begin(); iter != m_mapWorkerInfo.end(); ++iter) - { - if (m_iLoaderDataFd == iter->second->iDataFd) - { - continue; - } - if (m_bDispatchWithPort && m_vecWorkerDataFd.size() > 2 - && (0x00000001 & iBonding) != (0x00000001 & iter->second->iWorkerIndex)) - { - continue; - } - if (iMinLoad == -1) - { - iMinLoadWorkerFd = iter->second->iDataFd; - iMinLoadPid = iter->first; - iMinLoad = iter->second->uiLoad; - uiMinLoadConnection = iter->second->uiConnection; - } - else if ((int)iter->second->uiLoad < iMinLoad) - { - iMinLoadWorkerFd = iter->second->iDataFd; - iMinLoadPid = iter->first; - iMinLoad = iter->second->uiLoad; - uiMinLoadConnection = iter->second->uiConnection; - } - else if ((int)iter->second->uiLoad == iMinLoad && iter->second->uiConnection < uiMinLoadConnection) - { - iMinLoadWorkerFd = iter->second->iDataFd; - iMinLoadPid = iter->first; - iMinLoad = iter->second->uiLoad; - uiMinLoadConnection = iter->second->uiConnection; - } - } + uint32 uiIndex = uiKeyHash % fd_iter->second.second.size(); // loader的worker编号为0 + return(fd_iter->second.second[uiIndex]); } - auto iter = m_mapWorkerInfo.find(iMinLoadPid); - if (iter != m_mapWorkerInfo.end()) - { - iter->second->uiLoad++; - iter->second->uiConnection++; - } - return(iMinLoadWorkerFd); } bool SessionManager::CheckWorker() { LOG4_TRACE(" "); - for (auto worker_iter = m_mapWorkerInfo.begin(); - worker_iter != m_mapWorkerInfo.end(); ++worker_iter) + for (uint32 i = 0; i < m_vecWorkerInfo.size(); ++i) { - if (worker_iter->second->bStartBeatCheck && !GetLabor(this)->GetNodeInfo().bThreadMode) + if (m_vecWorkerInfo[i]->bStartBeatCheck) { LOG4_TRACE("now %lf, worker_beat_time %lf, worker_beat %d", - GetNowTime(), worker_iter->second->dBeatTime, GetLabor(this)->GetNodeInfo().dDataReportInterval); - if (GetNowTime() - worker_iter->second->dBeatTime > GetLabor(this)->GetNodeInfo().dDataReportInterval) + GetNowTime(), m_vecWorkerInfo[i]->dBeatTime, GetLabor(this)->GetNodeInfo().dDataReportInterval); + if (GetNowTime() - m_vecWorkerInfo[i]->dBeatTime > GetLabor(this)->GetNodeInfo().dDataReportInterval) { - LOG4_ERROR("worker_%d pid %d is unresponsive, " - "terminate it.", worker_iter->second->iWorkerIndex, worker_iter->first); - kill(worker_iter->first, SIGKILL); //SIGINT); + LOG4_ERROR("worker_%d pid %d is unresponsive.", m_vecWorkerInfo[i]->iWorkerIndex, m_vecWorkerInfo[i]->iWorkerPid); } } } return(true); } -bool SessionManager::WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& eLaborType) -{ - auto worker_iter = m_mapWorkerInfo.find(iPid); - if (worker_iter != m_mapWorkerInfo.end()) - { - LOG4_TRACE("restart worker %d, close control fd %d and data fd %d first.", - worker_iter->second->iWorkerIndex, worker_iter->second->iControlFd, worker_iter->second->iDataFd); - iWorkerIndex = worker_iter->second->iWorkerIndex; - if (m_iLoaderDataFd == worker_iter->second->iDataFd) - { - eLaborType = Labor::LABOR_LOADER; - } - else - { - eLaborType = Labor::LABOR_WORKER; - } - auto fd_iter = m_mapWorkerFdPid.find(worker_iter->second->iControlFd); - if (fd_iter != m_mapWorkerFdPid.end()) - { - m_mapWorkerFdPid.erase(fd_iter); - } - fd_iter = m_mapWorkerFdPid.find(worker_iter->second->iDataFd); - if (fd_iter != m_mapWorkerFdPid.end()) - { - m_mapWorkerFdPid.erase(fd_iter); - } - auto pControlChannel = GetLabor(this)->GetDispatcher()->GetChannel(worker_iter->second->iControlFd); - GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pControlChannel); // 在io事件中已关闭连接,这里可以不需要 - auto pDataChannel = GetLabor(this)->GetDispatcher()->GetChannel(worker_iter->second->iDataFd); - GetLabor(this)->GetDispatcher()->DiscardSocketChannel(pDataChannel); - delete worker_iter->second; - m_mapWorkerInfo.erase(worker_iter); - - auto restart_num_iter = m_mapWorkerStartNum.find(iWorkerIndex); - if (restart_num_iter != m_mapWorkerStartNum.end()) - { - LOG4_INFO("worker %d had been restarted %d times!", iWorkerIndex, restart_num_iter->second); - } - return(true); - } - else - { - return(false); - } -} - void SessionManager::SendOnlineNodesToWorker() { // 重启Worker进程后下发其他节点的信息 @@ -560,7 +436,7 @@ void SessionManager::SendOnlineNodesToWorker() * "worker_num":10, * "active_time":16879561651.06, * "node":{ - * "load":1885792, "connect":495873, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222,"client":495870 + * "load":1885792, "connect":495873, "recv_num":98755266, "recv_byte":98856648832, "send_num":154846322, "send_byte":648469320222 * }, * "worker": * [ @@ -599,37 +475,36 @@ void SessionManager::MakeReportData(CJsonObject& oReportData) { oReportData.Add("access_port", GetLabor(this)->GetNodeInfo().iPortForClient); } - if (m_iLoaderDataFd == -1) + if (m_vecWorkerInfo.size() > 0 && m_vecWorkerInfo[0] == nullptr) // without Loader { - oReportData.Add("worker_num", (int)m_mapWorkerInfo.size()); + oReportData.Add("worker_num", (int)m_vecWorkerInfo.size() - 1); } else { - oReportData.Add("worker_num", (int)m_mapWorkerInfo.size() - 1); + oReportData.Add("worker_num", (int)m_vecWorkerInfo.size()); } oReportData.Add("active_time", (uint64)GetNowTime()); oReportData.Add("node", CJsonObject("{}")); oReportData.Add("worker", CJsonObject("[]")); - auto worker_iter = m_mapWorkerInfo.begin(); - for (; worker_iter != m_mapWorkerInfo.end(); ++worker_iter) + for (uint32 i = 0; i < m_vecWorkerInfo.size(); ++i) { - if (m_iLoaderDataFd == worker_iter->second->iDataFd) + if (m_vecWorkerInfo[i] == nullptr) { continue; } - iLoad += worker_iter->second->uiLoad; - iConnect += worker_iter->second->uiConnection; - iRecvNum += worker_iter->second->uiRecvNum; - iRecvByte += worker_iter->second->uiRecvByte; - iSendNum += worker_iter->second->uiSendNum; - iSendByte += worker_iter->second->uiSendByte; + iLoad += m_vecWorkerInfo[i]->uiLoad; + iConnect += m_vecWorkerInfo[i]->uiConnection; + iRecvNum += m_vecWorkerInfo[i]->uiRecvNum; + iRecvByte += m_vecWorkerInfo[i]->uiRecvByte; + iSendNum += m_vecWorkerInfo[i]->uiSendNum; + iSendByte += m_vecWorkerInfo[i]->uiSendByte; oMember.Clear(); - oMember.Add("load", worker_iter->second->uiLoad); - oMember.Add("connect", worker_iter->second->uiConnection); - oMember.Add("recv_num", worker_iter->second->uiRecvNum); - oMember.Add("recv_byte", worker_iter->second->uiRecvByte); - oMember.Add("send_num", worker_iter->second->uiSendNum); - oMember.Add("send_byte", worker_iter->second->uiSendByte); + oMember.Add("load", m_vecWorkerInfo[i]->uiLoad); + oMember.Add("connect", m_vecWorkerInfo[i]->uiConnection); + oMember.Add("recv_num", m_vecWorkerInfo[i]->uiRecvNum); + oMember.Add("recv_byte", m_vecWorkerInfo[i]->uiRecvByte); + oMember.Add("send_num", m_vecWorkerInfo[i]->uiSendNum); + oMember.Add("send_byte", m_vecWorkerInfo[i]->uiSendByte); oReportData["worker"].Add(oMember); } oReportData["node"].Add("load", iLoad); @@ -640,67 +515,5 @@ void SessionManager::MakeReportData(CJsonObject& oReportData) oReportData["node"].Add("send_byte", iSendByte); } -int SessionManager::GetLoaderDataFd() const -{ - return(m_iLoaderDataFd); -} - -bool SessionManager::NewSocketWhenWorkerCreated(int iWorkerDataFd) -{ - if (m_iLoaderDataFd == -1) - { - LOG4_TRACE("no Loader process."); - return(true); - } - int iFds[2]; - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iFds) < 0) - { - LOG4_ERROR("socketpair error %d!", errno); - return(false); - } - x_sock_set_block(iFds[0], 0); - x_sock_set_block(iFds[1], 0); - LOG4_TRACE("Transfer fd to Loader and Worker."); - GetLabor(this)->GetDispatcher()->SendFd(m_iLoaderDataFd, iFds[0], PF_UNIX, CODEC_NEBULA_IN_NODE, GetNodeInfo().strHostForServer); - GetLabor(this)->GetDispatcher()->SendFd(iWorkerDataFd, iFds[1], PF_UNIX, CODEC_NEBULA_IN_NODE, GetNodeInfo().strHostForServer); - return(true); -} - -bool SessionManager::NewSocketWhenLoaderCreated() -{ - if (m_iLoaderDataFd == -1) - { - LOG4_TRACE("no Loader process."); - return(true); - } - for (auto iter = m_mapWorkerInfo.begin(); iter != m_mapWorkerInfo.end(); ++iter) - { - if (m_iLoaderDataFd == iter->second->iDataFd) - { - continue; - } - int iFds[2]; - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iFds) < 0) - { - LOG4_ERROR("socketpair error %d!", errno); - return(false); - } - x_sock_set_block(iFds[0], 0); - x_sock_set_block(iFds[1], 0); - GetLabor(this)->GetDispatcher()->SendFd(m_iLoaderDataFd, iFds[0], PF_UNIX, CODEC_NEBULA_IN_NODE, GetNodeInfo().strHostForServer); - GetLabor(this)->GetDispatcher()->SendFd(iter->second->iDataFd, iFds[1], PF_UNIX, CODEC_NEBULA_IN_NODE, GetNodeInfo().strHostForServer); - } - return(true); -} - -void SessionManager::AddWorkerThreadId(uint64 ullThreadId) -{ - std::lock_guard guard(s_mutexWorker); - if (std::find(s_vecWorkerThreadId.begin(), s_vecWorkerThreadId.end(), ullThreadId) - == s_vecWorkerThreadId.end()) - { - s_vecWorkerThreadId.push_back(ullThreadId); - } -} - } /* namespace neb */ + diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp index d3de3598..416c1bf9 100644 --- a/src/actor/session/sys_session/manager/SessionManager.hpp +++ b/src/actor/session/sys_session/manager/SessionManager.hpp @@ -11,11 +11,10 @@ #ifndef SRC_ACTOR_SESSION_SYS_SESSION_MANAGER_SESSIONMANAGER_HPP_ #define SRC_ACTOR_SESSION_SYS_SESSION_MANAGER_SESSIONMANAGER_HPP_ -#include -#include #include "actor/ActorSys.hpp" #include "labor/NodeInfo.hpp" #include "actor/session/Session.hpp" +#include "pb/report.pb.h" namespace neb { @@ -23,58 +22,43 @@ namespace neb class Loader; class SessionManager : public Session, - public DynamicCreator, + public DynamicCreator, public ActorSys { public: - SessionManager(bool bDirectToLoader, bool bDispatchWithPort, ev_tstamp dStatInterval); + SessionManager(ev_tstamp dStatInterval); virtual ~SessionManager(); virtual E_CMD_STATUS Timeout(); + void AddPort2WorkerInfo(uint32 uiWorkerNum, uint32 uiLoaderNum, CJsonObject& oPort2WorkerJson); + void AddPortListenFd(int32 iPort, int32 iFd); void AddOnlineNode(const std::string& strNodeIdentify, const std::string& strNodeInfo); void DelOnlineNode(const std::string& strNodeIdentify); bool SendToChild(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); // 向Worker和Loader发送数据 bool SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); bool SendToLoader(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - void AddWorkerInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); - void AddLoaderInfo(int iWorkerIndex, int iPid, int iControlFd, int iDataFd); - Worker* MutableWorker(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd); - Loader* MutableLoader(int iWorkerIndex, const std::string& strWorkPath, int iControlFd, int iDataFd); - const WorkerInfo* GetWorkerInfo(int32 iWorkerIndex) const; - bool SetWorkerLoad(int iWorkerFd, CJsonObject& oJsonLoad); + void AddWorkerInfo(int iWorkerIndex, int iPid); + Worker* MutableWorker(int iWorkerIndex, const std::string& strWorkPath); + Loader* MutableLoader(int iWorkerIndex, const std::string& strWorkPath); + std::shared_ptr GetWorkerInfo(int32 iWorkerIndex) const; + bool SetWorkerLoad(uint32 uiWorkerIndex, const ReportRecord& oWorkerStatus); void SetLoaderActorBuilder(ActorBuilder* pActorBuilder); - int GetWorkerDataFd(const char* szClientAddr, int iClientPort, int iBonding); - int GetNextWorkerDataFd(int iBonding); - int GetMinLoadWorkerDataFd(int iBonding); + int32 GetDispatchWorkerId(int32 iListenFd); + int32 GetDispatchWorkerId(int32 iListenFd, const char* szRemoteAddr, int iRemotePort); bool CheckWorker(); - bool WorkerDeath(int iPid, int& iWorkerIndex, Labor::LABOR_TYPE& eLaborType); void SendOnlineNodesToWorker(); void MakeReportData(CJsonObject& oReportJson); - int GetLoaderDataFd() const; - bool NewSocketWhenWorkerCreated(int iWorkerDataFd); - bool NewSocketWhenLoaderCreated(); - - static const std::vector& GetWorkerThreadId() - { - return(s_vecWorkerThreadId); - } - static void AddWorkerThreadId(uint64 ullThreadId); private: - bool m_bDirectToLoader = false; - bool m_bDispatchWithPort = false; - int m_iLoaderDataFd = -1; - uint32 m_uiWorkerDataFdIndex[2] = {0}; ///< for round robin fd transfer - std::vector m_vecWorkerDataFd; ///< only for fd transfer - std::unordered_map m_mapWorker; ///< only thread worker - std::unordered_map m_mapWorkerInfo; ///< 业务逻辑工作进程及进程属性,key为pid - std::unordered_map m_mapWorkerStartNum; ///< 进程被启动次数,key为WorkerIdx - std::unordered_map m_mapWorkerFdPid; ///< 工作进程通信FD对应的进程号 - std::unordered_map m_mapOnlineNodes; ///< 订阅的节点在线信息 + uint32 m_uiRoundRobin = 0; + std::vector> m_vecWorkerInfo; + std::unordered_map>> m_mapListenFd2Worker; - static std::mutex s_mutexWorker; - static std::vector s_vecWorkerThreadId; + std::unordered_map m_mapWorker; + + std::map> m_mapPort2Worker; + std::unordered_map m_mapOnlineNodes; ///< 订阅的节点在线信息 }; } /* namespace neb */ diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index 74bc1646..c1fda1df 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -134,6 +134,11 @@ E_CMD_STATUS StepRedisCluster::Callback( LOG4_ERROR("no \"%s\" found in m_mapPipelineRequest", pChannel->GetIdentify().c_str()); return(CMD_STATUS_FAULT); } + if (step_iter->second.empty()) + { + LOG4_ERROR("channel \"%s\"'s m_mapPipelineRequest is empty", pChannel->GetIdentify().c_str()); + return(CMD_STATUS_FAULT); + } pRedisRequest = step_iter->second.front(); uiRealStepSeq = (uint32)pRedisRequest->integer(); step_iter->second.pop(); @@ -478,6 +483,21 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptrGetPenultimateActiveTime(); + auto dLastRecvTime = pChannel->GetLastRecvTime(); + auto dCheckTime = GetNowTime() - GetTimeout(); + if (dPenultimateActiveTime > dCheckTime + && (dLastRecvTime > 0 && dLastRecvTime < dCheckTime)) // 有请求但长时间未收到响应包 + { + LOG4_ERROR("%s channel[%d] may be a death connection, last_active_time = %lf," + "last_recv_time = %lf, redis_cluster_check_time = %lf, close it", + strIdentify.c_str(), pChannel->GetFd(), + dPenultimateActiveTime, dLastRecvTime, GetTimeout()); + CmdErrBack(pChannel, ERR_DISCONNECT, "death connection closed"); + GetLabor(this)->GetDispatcher()->Disconnect(pChannel); + return(false); + } if (m_bPipeline) { auto iter = m_mapPipelineRequest.find(strIdentify); @@ -494,7 +514,7 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptrGetSequence(); + uint32 uiChannelSeq = pChannel->GetSequence(); auto iter = m_mapRequest.find(uiChannelSeq); if (iter == m_mapRequest.end()) { diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index 7b3304b0..bb10792a 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -163,6 +163,26 @@ ev_tstamp SocketChannel::GetActiveTime() const return(m_pImpl->GetActiveTime()); } +ev_tstamp SocketChannel::GetPenultimateActiveTime() const +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + return(0.0); + } + return(m_pImpl->GetPenultimateActiveTime()); +} + +ev_tstamp SocketChannel::GetLastRecvTime() const +{ + if (m_pImpl == nullptr) + { + LOG4_TRACE("m_pImpl is nullptr"); + return(0.0); + } + return(m_pImpl->GetLastRecvTime()); +} + ev_tstamp SocketChannel::GetKeepAlive() const { if (m_pImpl == nullptr) @@ -309,6 +329,15 @@ bool SocketChannel::Close() return(m_pImpl->Close()); } +void SocketChannel::SetBonding(Labor* pLabor, std::shared_ptr pLogger, std::shared_ptr pBindChannel) +{ + if (m_pImpl != nullptr) + { + m_pImpl->SetBonding(pLabor, pLogger, pBindChannel); + } + m_pLogger = pLogger; +} + bool SocketChannel::InitImpl(std::shared_ptr pImpl) { if (pImpl == nullptr) @@ -325,152 +354,6 @@ Labor* SocketChannel::GetLabor() return(nullptr); } -int SocketChannel::SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, - const std::string& strRemoteAddr, std::shared_ptr pLogger) -{ - ssize_t n; - struct iovec iov[2]; - struct msghdr msg; - tagChannelCtx stCh; - int iError = 0; - char szAddress[64] = {0}; - - stCh.iFd = iSendFd; - stCh.iAiFamily = iAiFamily; - stCh.iCodecType = iCodecType; - - union - { - struct cmsghdr cm; - char space[CMSG_SPACE(sizeof(int))]; - } cmsg; - - if (stCh.iFd == -1) - { - msg.msg_control = NULL; - msg.msg_controllen = 0; - - } - else - { - msg.msg_control = (caddr_t) &cmsg; - msg.msg_controllen = sizeof(cmsg); - - memset(&cmsg, 0, sizeof(cmsg)); - - cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int)); - cmsg.cm.cmsg_level = SOL_SOCKET; - cmsg.cm.cmsg_type = SCM_RIGHTS; - - *(int *) CMSG_DATA(&cmsg.cm) = stCh.iFd; - } - - msg.msg_flags = 0; - - snprintf(szAddress, 64, "%s", strRemoteAddr.data()); - iov[0].iov_base = (void*)&stCh; - iov[0].iov_len = sizeof(tagChannelCtx); - iov[1].iov_base = (void*)szAddress; - iov[1].iov_len = 64; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 2; - - n = sendmsg(iSocketFd, &msg, 0); - - if (n == -1) - { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "sendmsg() failed, errno %d", errno); - iError = (errno == 0) ? ERR_TRANSFER_FD : errno; - return(iError); - } - - return(ERR_OK); -} - -int SocketChannel::RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, int& iCodecType, - std::string& strRemoteAddr, std::shared_ptr pLogger) -{ - ssize_t n; - struct iovec iov[2]; - struct msghdr msg; - tagChannelCtx stCh; - int iError = 0; - char szAddress[64] = {0}; - - union { - struct cmsghdr cm; - char space[CMSG_SPACE(sizeof(int))]; - } cmsg; - - iov[0].iov_base = (void*)&stCh; - iov[0].iov_len = sizeof(tagChannelCtx); - iov[1].iov_base = (void*)szAddress; - iov[1].iov_len = 64; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 2; - - msg.msg_control = (caddr_t) &cmsg; - msg.msg_controllen = sizeof(cmsg); - - n = recvmsg(iSocketFd, &msg, 0); - - if (n == -1) { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "recvmsg() failed, errno %d", errno); - iError = (errno == 0) ? ERR_TRANSFER_FD : errno; - return(iError); - } - - if (n == 0) { - pLogger->WriteLog(neb::Logger::WARNING, __FILE__, __LINE__, __FUNCTION__, "recvmsg() return zero, errno %d", errno); - iError = (errno == 0) ? ERR_TRANSFER_FD : errno; - return(ERR_CHANNEL_EOF); - } - - if ((size_t) n < sizeof(tagChannelCtx)) - { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "rrecvmsg() returned not enough data: %z, errno %d", n, errno); - iError = (errno == 0) ? ERR_TRANSFER_FD : errno; - return(iError); - } - - if (cmsg.cm.cmsg_len < (socklen_t) CMSG_LEN(sizeof(int))) - { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "recvmsg() returned too small ancillary data"); - iError = (errno == 0) ? ERR_TRANSFER_FD : errno; - return(iError); - } - - if (cmsg.cm.cmsg_level != SOL_SOCKET || cmsg.cm.cmsg_type != SCM_RIGHTS) - { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "recvmsg() returned invalid ancillary data level %d or type %d", cmsg.cm.cmsg_level, cmsg.cm.cmsg_type); - iError = (errno == 0) ? ERR_TRANSFER_FD : errno; - return(iError); - } - - stCh.iFd = *(int *) CMSG_DATA(&cmsg.cm); - - if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) - { - pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "recvmsg() truncated data"); - iError = (errno == 0) ? ERR_TRANSFER_FD : errno; - return(iError); - } - - iRecvFd = stCh.iFd; - iAiFamily = stCh.iAiFamily; - iCodecType = stCh.iCodecType; - strRemoteAddr = szAddress; - - return(ERR_OK); -} - uint32 SocketChannel::GetPeerStepSeq() const { return(0); diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index a2c736e9..7445fb0f 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -20,6 +20,8 @@ namespace neb class Dispatcher; class ChannelWatcher; +class SocketChannelMigrate; +class CmdChannelMigrate; class Labor; class CodecFactory; class CodecProto; @@ -41,11 +43,6 @@ class SocketChannel: public Channel SocketChannel(std::shared_ptr pLogger, bool bIsClient, bool bWithSsl); virtual ~SocketChannel(); - static int SendChannelFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, - const std::string& strRemoteAddr, std::shared_ptr pLogger); - static int RecvChannelFd(int iSocketFd, int& iRecvFd, int& iAiFamily, int& iCodecType, - std::string& strRemoteAddr, std::shared_ptr pLogger); - virtual bool IsClient() const; virtual bool WithSsl() const; virtual int GetFd() const; @@ -59,6 +56,8 @@ class SocketChannel: public Channel virtual uint32 PopStepSeq(uint32 uiStreamId = 0, E_CODEC_STATUS eStatus = CODEC_STATUS_OK); virtual bool PipelineIsEmpty() const; virtual ev_tstamp GetActiveTime() const; + virtual ev_tstamp GetPenultimateActiveTime() const; + virtual ev_tstamp GetLastRecvTime() const; virtual ev_tstamp GetKeepAlive() const; virtual int GetErrno() const; virtual const std::string& GetErrMsg() const; @@ -69,6 +68,10 @@ class SocketChannel: public Channel virtual uint32 GetUnitTimeMsgNum() const; virtual E_CODEC_STATUS Send(); virtual uint32 GetPeerStepSeq() const; + virtual void SetChannelStatus(E_CHANNEL_STATUS eStatus); + virtual void SetClientData(const std::string& strClientData); + virtual void SetIdentify(const std::string& strIdentify); + virtual void SetRemoteAddr(const std::string& strRemoteAddr); ChannelWatcher* MutableWatcher(); template @@ -76,11 +79,8 @@ class SocketChannel: public Channel protected: virtual Labor* GetLabor(); virtual int16 GetRemoteWorkerIndex() const; - virtual void SetChannelStatus(E_CHANNEL_STATUS eStatus); - virtual void SetClientData(const std::string& strClientData); - virtual void SetIdentify(const std::string& strIdentify); - virtual void SetRemoteAddr(const std::string& strRemoteAddr); virtual bool Close(); + virtual void SetBonding(Labor* pLabor, std::shared_ptr pLogger, std::shared_ptr pBindChannel); bool InitImpl(std::shared_ptr pImpl); private: @@ -92,6 +92,8 @@ class SocketChannel: public Channel std::shared_ptr m_pLogger; ChannelWatcher* m_pWatcher; + friend class SocketChannelMigrate; + friend class CmdChannelMigrate; friend class Dispatcher; friend class ActorBuilder; friend class CodecFactory; diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 022d0b38..6e445c54 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -118,6 +118,16 @@ class SocketChannelImpl: public SocketChannel return(m_dActiveTime); } + virtual ev_tstamp GetPenultimateActiveTime() const override + { + return(m_dPenultimateActiveTime); + } + + virtual ev_tstamp GetLastRecvTime() const override + { + return(m_dLastRecvTime); + } + virtual ev_tstamp GetKeepAlive() const override; virtual int GetErrno() const override @@ -215,6 +225,7 @@ class SocketChannelImpl: public SocketChannel void SetRemoteWorkerIndex(int16 iRemoteWorkerIndex); virtual bool Close() override; + virtual void SetBonding(Labor* pLabor, std::shared_ptr pLogger, std::shared_ptr pBindChannel); protected: virtual int Write(CBuffer* pBuff, int& iErrno); @@ -234,6 +245,8 @@ class SocketChannelImpl: public SocketChannel uint32 m_uiUnitTimeMsgNum; ///< 统计单位时间内接收消息数量 uint32 m_uiMsgNum; ///< 接收消息数量 ev_tstamp m_dActiveTime; ///< 最后一次访问时间 + ev_tstamp m_dPenultimateActiveTime; ///< 倒数第二次访问时间 + ev_tstamp m_dLastRecvTime; ///< 最后一次接收消息时间 ev_tstamp m_dKeepAlive; ///< 连接保持时间 CBuffer* m_pRecvBuff; CBuffer* m_pSendBuff; @@ -262,7 +275,7 @@ SocketChannelImpl::SocketChannelImpl(Labor* pLabor, std::shared_ptr::Send() { m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; } + m_dPenultimateActiveTime = m_dActiveTime; m_dActiveTime = m_pLabor->GetNowTime(); int iHadWrittenLen = 0; int iWrittenLen = 0; @@ -470,6 +484,7 @@ E_CODEC_STATUS SocketChannelImpl::Send() { m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); } + m_dPenultimateActiveTime = m_dActiveTime; m_dActiveTime = m_pLabor->GetNowTime(); if (iNeedWriteLen == iHadWrittenLen && 0 == m_pWaitForSendBuff->ReadableBytes()) { @@ -484,6 +499,7 @@ E_CODEC_STATUS SocketChannelImpl::Send() { if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { + m_dPenultimateActiveTime = m_dActiveTime; m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } @@ -593,6 +609,7 @@ E_CODEC_STATUS SocketChannelImpl::SendRequest(uint32 uiStepSeq, Targs&&... ar { m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); } + m_dPenultimateActiveTime = m_dActiveTime; m_dActiveTime = m_pLabor->GetNowTime(); if (iNeedWriteLen == iHadWrittenLen) { @@ -607,6 +624,7 @@ E_CODEC_STATUS SocketChannelImpl::SendRequest(uint32 uiStepSeq, Targs&&... ar { if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { + m_dPenultimateActiveTime = m_dActiveTime; m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } @@ -689,6 +707,7 @@ E_CODEC_STATUS SocketChannelImpl::SendResponse(Targs&&... args) { m_pSendBuff->Compact(m_pSendBuff->ReadableBytes() * 2); } + m_dPenultimateActiveTime = m_dActiveTime; m_dActiveTime = m_pLabor->GetNowTime(); if (iNeedWriteLen == iHadWrittenLen) { @@ -712,6 +731,7 @@ E_CODEC_STATUS SocketChannelImpl::SendResponse(Targs&&... args) { if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { + m_dPenultimateActiveTime = m_dActiveTime; m_dActiveTime = m_pLabor->GetNowTime(); return(CODEC_STATUS_PAUSE); } @@ -769,6 +789,7 @@ E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) { if (EAGAIN == m_iErrno || EINTR == m_iErrno) // 对非阻塞socket而言,EAGAIN不是一种错误;EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。 { + m_dPenultimateActiveTime = m_dActiveTime; m_dActiveTime = m_pLabor->GetNowTime(); m_eLastCodecStatus = CODEC_STATUS_PAUSE; } @@ -790,7 +811,9 @@ E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) { m_pRecvBuff->Compact(m_pRecvBuff->ReadableBytes() * 2); } + m_dPenultimateActiveTime = m_dActiveTime; m_dActiveTime = m_pLabor->GetNowTime(); + m_dLastRecvTime = m_dActiveTime; auto uiReadIndex = m_pRecvBuff->GetReadIndex(); E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; if (m_pCodec->DecodeWithReactor()) @@ -938,6 +961,17 @@ bool SocketChannelImpl::Close() } } +template +void SocketChannelImpl::SetBonding(Labor* pLabor, std::shared_ptr pLogger, std::shared_ptr pBindChannel) +{ + m_pLabor = pLabor; + m_pLogger = pLogger; + if (m_pCodec != nullptr) + { + m_pCodec->SetBonding(pLogger, pBindChannel); + } +} + template bool SocketChannelImpl::SetCodec(Codec* pCodec) { diff --git a/src/channel/SpecChannel.hpp b/src/channel/SpecChannel.hpp index c0cf1867..4423d2d5 100644 --- a/src/channel/SpecChannel.hpp +++ b/src/channel/SpecChannel.hpp @@ -83,6 +83,16 @@ class SpecChannel: public SocketChannel return(m_uiReadLaborIndex); } + uint32 GetFromLaborId() const + { + return(m_uiWriteLaborIndex); + } + + uint32 GetToLaborId() const + { + return(m_uiReadLaborIndex); + } + SpecChannelWatcher* MutableWatcher(); protected: diff --git a/src/channel/migrate/SocketChannelMigrate.cpp b/src/channel/migrate/SocketChannelMigrate.cpp new file mode 100644 index 00000000..c7c2064e --- /dev/null +++ b/src/channel/migrate/SocketChannelMigrate.cpp @@ -0,0 +1,39 @@ +/******************************************************************************* + * Project: Nebula + * @file SocketChannelMigrate.cpp + * @brief + * @author Bwar + * @date: 2023-01-07 + * @note + * Modify history: + ******************************************************************************/ +#include "SocketChannelMigrate.hpp" + +namespace neb +{ + +SocketChannelMigrate::SocketChannelMigrate() +{ +} + +SocketChannelMigrate::~SocketChannelMigrate() +{ +} + +bool SocketChannelMigrate::MigrateChannel(std::shared_ptr pSocketChannel, SocketChannelPack& oPack) +{ + auto pNewChannel = std::make_shared(); + pNewChannel->m_bIsClient = pSocketChannel->m_bIsClient; + pNewChannel->m_bWithSsl = pSocketChannel->m_bWithSsl; + pNewChannel->m_pImpl = pSocketChannel->m_pImpl; + pSocketChannel->m_pImpl = nullptr; + pNewChannel->m_pWatcher = pSocketChannel->m_pWatcher; + pNewChannel->m_pWatcher->Set(nullptr); + pSocketChannel->m_pWatcher = nullptr; + pNewChannel->SetBonding(nullptr, nullptr, nullptr); // remove the current relationship before migration + oPack.PackChannel(pNewChannel); + return(true); +} + +} /* namespace neb */ + diff --git a/src/channel/migrate/SocketChannelMigrate.hpp b/src/channel/migrate/SocketChannelMigrate.hpp new file mode 100644 index 00000000..694d479f --- /dev/null +++ b/src/channel/migrate/SocketChannelMigrate.hpp @@ -0,0 +1,116 @@ +/******************************************************************************* + * Project: Nebula + * @file SocketChannelMigrate.hpp + * @brief + * @author Bwar + * @date: 2023-01-07 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CHANNEL_MIGRATE_SOCKETCHANNELMIGRATE_HPP_ +#define SRC_CHANNEL_MIGRATE_SOCKETCHANNELMIGRATE_HPP_ + +#include "channel/SocketChannel.hpp" +#include "SocketChannelPack.hpp" +#include "codec/Codec.hpp" +#include "channel/SpecChannel.hpp" +#include "labor/LaborShared.hpp" +#include "ios/ChannelWatcher.hpp" + +namespace neb +{ + +class SocketChannelMigrate +{ +public: + SocketChannelMigrate(); + virtual ~SocketChannelMigrate(); + + static E_CODEC_TYPE Type() + { + return(CODEC_CHANNEL_MIGRATE); + } + + // request + template + static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, std::shared_ptr pSocketChannel); + + // response + template + static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, std::shared_ptr pSocketChannel); + +protected: + static bool MigrateChannel(std::shared_ptr pSocketChannel, SocketChannelPack& oPack); +}; + +// request +template +int SocketChannelMigrate::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, std::shared_ptr pSocketChannel) +{ + if (uiFromLabor == uiToLabor) + { + return(ERR_SPEC_CHANNEL_TARGET); + } + SocketChannelPack oPack; + if (!MigrateChannel(pSocketChannel, oPack)) + { + return(ERR_SPEC_CHANNEL_MIGRATE); + } + std::shared_ptr> pSpecChannel = nullptr; + auto pLaborShared = LaborShared::Instance(); + auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); + if (pChannel == nullptr) + { + pSpecChannel = std::make_shared>( + uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); + if (pSpecChannel == nullptr) + { + pSocketChannel.reset(oPack.UnpackChannel().get()); // recover from migration failed + return(ERR_SPEC_CHANNEL_CREATE); + } + pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, Type()); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(oPack)); + if (iResult == ERR_OK) + { + return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); + } + else + { + pSocketChannel.reset(oPack.UnpackChannel().get()); // recover from migration failed + } + return(iResult); + } + else + { + pSpecChannel = std::static_pointer_cast>(pChannel); + if (pSpecChannel == nullptr) + { + pSocketChannel.reset(oPack.UnpackChannel().get()); // recover from migration failed + return(ERR_SPEC_CHANNEL_CAST); + } + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(oPack)); + if (iResult == ERR_OK) + { + pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + else + { + pSocketChannel.reset(oPack.UnpackChannel().get()); // recover from migration failed + } + return(iResult); + } +} + +// response +template +int SocketChannelMigrate::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, std::shared_ptr pSocketChannel) +{ + return(ERR_SPEC_CHANNEL_CALL); +} + +} /* namespace neb */ + +#endif /* SRC_CHANNEL_MIGRATE_SOCKETCHANNELMIGRATE_HPP_ */ + diff --git a/src/channel/migrate/SocketChannelPack.cpp b/src/channel/migrate/SocketChannelPack.cpp new file mode 100644 index 00000000..823e9e00 --- /dev/null +++ b/src/channel/migrate/SocketChannelPack.cpp @@ -0,0 +1,58 @@ +/******************************************************************************* + * Project: Nebula + * @file SocketChannelPack.cpp + * @brief + * @author Bwar + * @date: 2023-01-07 + * @note + * Modify history: + ******************************************************************************/ +#include + +namespace neb +{ + +SocketChannelPack::SocketChannelPack() +{ +} + +SocketChannelPack::SocketChannelPack(const SocketChannelPack& oPack) +{ + m_pSocketChannel = oPack.m_pSocketChannel; +} + +SocketChannelPack::SocketChannelPack(SocketChannelPack&& oPack) +{ + m_pSocketChannel = oPack.m_pSocketChannel; + oPack.m_pSocketChannel = nullptr; +} + +SocketChannelPack::~SocketChannelPack() +{ +} + +SocketChannelPack& SocketChannelPack::operator=(const SocketChannelPack& oPack) +{ + m_pSocketChannel = oPack.m_pSocketChannel; + return(*this); +} + +SocketChannelPack& SocketChannelPack::operator=(SocketChannelPack&& oPack) +{ + m_pSocketChannel = oPack.m_pSocketChannel; + oPack.m_pSocketChannel = nullptr; + return(*this); +} + +void SocketChannelPack::PackChannel(std::shared_ptr pSocketChannel) +{ + m_pSocketChannel = pSocketChannel; +} + +std::shared_ptr SocketChannelPack::UnpackChannel() +{ + return(m_pSocketChannel); +} + +} /* namespace neb */ + diff --git a/src/channel/migrate/SocketChannelPack.hpp b/src/channel/migrate/SocketChannelPack.hpp new file mode 100644 index 00000000..bd06b0db --- /dev/null +++ b/src/channel/migrate/SocketChannelPack.hpp @@ -0,0 +1,39 @@ +/******************************************************************************* + * Project: Nebula + * @file SocketChannelPack.hpp + * @brief + * @author Bwar + * @date: 2023-01-07 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CHANNEL_MIGRATE_SOCKETCHANNELPACK_HPP_ +#define SRC_CHANNEL_MIGRATE_SOCKETCHANNELPACK_HPP_ + +#include "channel/SocketChannel.hpp" + +namespace neb +{ + +class SocketChannelPack +{ +public: + SocketChannelPack(); + SocketChannelPack(const SocketChannelPack& oPack); + SocketChannelPack(SocketChannelPack&& oPack); + virtual ~SocketChannelPack(); + + SocketChannelPack& operator=(const SocketChannelPack& oPack); + SocketChannelPack& operator=(SocketChannelPack&& oPack); + + void PackChannel(std::shared_ptr pSocketChannel); + std::shared_ptr UnpackChannel(); + +private: + std::shared_ptr m_pSocketChannel = nullptr; +}; + +} /* namespace neb */ + +#endif /* SRC_CHANNEL_MIGRATE_SOCKETCHANNELPACK_HPP_ */ + diff --git a/src/codec/Codec.cpp b/src/codec/Codec.cpp index 089b9d1d..a10bfe1c 100644 --- a/src/codec/Codec.cpp +++ b/src/codec/Codec.cpp @@ -25,6 +25,12 @@ Codec::~Codec() LOG4_TRACE("codec_type %d", m_eCodecType); } +void Codec::SetBonding(std::shared_ptr pLogger, std::shared_ptr pBindChannel) +{ + m_pLogger = pLogger; + m_pBindChannel = pBindChannel; +} + bool Codec::Zip(const std::string& strSrc, std::string& strDest) { /* diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 5487969c..10bd14ae 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -49,6 +49,9 @@ enum E_CODEC_TYPE CODEC_CASS = 12, ///< cassandra scylladb CODEC_RAW = 13, ///< 裸数据传输 CODEC_TRANSFER = 14, ///< 虚拟编解码类型,用于SpecChannel + CODEC_CHANNEL_MIGRATE = 15, ///< 虚拟编解码类型,用于SocketChannel迁移 + + CODEC_MAX }; /** @@ -134,6 +137,7 @@ class Codec m_iErrno = iErrno; } protected: + void SetBonding(std::shared_ptr pLogger, std::shared_ptr pBindChannel); const std::string& GetKey() const { return(m_strKey); diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp index 1f22c8b0..19963092 100644 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -13,7 +13,10 @@ #include "channel/SocketChannelImpl.hpp" #include "channel/SocketChannelSslImpl.hpp" #include "channel/SpecChannel.hpp" +#include "channel/migrate/SocketChannelPack.hpp" +#include "channel/migrate/SocketChannelMigrate.hpp" #include "labor/LaborShared.hpp" +#include "actor/cmd/sys_cmd/CmdChannelMigrate.hpp" namespace neb { @@ -254,6 +257,25 @@ E_CODEC_STATUS CodecFactory::OnEvent(SpecChannelWatcher* pAsyncWatcher, std::sha break; case CODEC_PRIVATE: break; + case CODEC_CHANNEL_MIGRATE: + { + SocketChannelPack oPack; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->m_pLastActivityChannel = pChannel; + while (pSpecChannel->Read(uiFlags, uiStepSeq, oPack)) + { + if (gc_uiCmdReq & uiFlags) + { + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_CHANNEL_MIGRATE, oPack); + } + else + { + ; // no response + } + } + } + break; case CODEC_UNKNOW: break; default: diff --git a/src/codec/CodecProto.hpp b/src/codec/CodecProto.hpp index 1f8d84fe..b3f37726 100644 --- a/src/codec/CodecProto.hpp +++ b/src/codec/CodecProto.hpp @@ -34,7 +34,7 @@ class CodecProto: public Codec template static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const MsgHead& oMsgHead, const MsgBody& oMsgBody) { - return(CodecProto::Write(Type(), uiToLabor, uiFlags, uiStepSeq, + return(CodecProto::Write(Type(), uiFromLabor, uiToLabor, uiFlags, uiStepSeq, std::move(const_cast(oMsgHead)), std::move(const_cast(oMsgBody)))); } @@ -86,7 +86,7 @@ class CodecNebula: public CodecProto template static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const MsgHead& oMsgHead, const MsgBody& oMsgBody) { - return(CodecProto::Write(Type(), uiToLabor, uiFlags, uiStepSeq, + return(CodecProto::Write(Type(), uiFromLabor, uiToLabor, uiFlags, uiStepSeq, std::move(const_cast(oMsgHead)), std::move(const_cast(oMsgBody)))); } @@ -119,7 +119,7 @@ class CodecNebulaInNode: public CodecProto template static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const MsgHead& oMsgHead, const MsgBody& oMsgBody) { - return(CodecProto::Write(Type(), uiToLabor, uiFlags, uiStepSeq, + return(CodecProto::Write(Type(), uiFromLabor, uiToLabor, uiFlags, uiStepSeq, std::move(const_cast(oMsgHead)), std::move(const_cast(oMsgBody)))); } diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 4dc51580..9e1508a8 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -21,6 +21,8 @@ #include "actor/session/sys_session/manager/SessionManager.hpp" #include "codec/CodecFactory.hpp" #include "channel/SocketChannelImpl.hpp" +#include "channel/migrate/SocketChannelMigrate.hpp" +#include "pb/neb_sys.pb.h" namespace neb { @@ -86,10 +88,6 @@ void Dispatcher::PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, i ((Manager*)(pDispatcher->m_pLabor))->GetSessionManager()->CheckWorker(); ((Manager*)(pDispatcher->m_pLabor))->RefreshServer(); } - else - { - ((Worker*)(pDispatcher->m_pLabor))->CheckParent(); - } pDispatcher->CheckFailedNode(); } ev_timer_stop (loop, watcher); @@ -102,14 +100,7 @@ void Dispatcher::SignalCallback(struct ev_loop* loop, struct ev_signal* watcher, if (watcher->data != NULL) { Labor* pLabor = (Labor*)watcher->data; - if (SIGCHLD == watcher->signum) - { - ((Manager*)pLabor)->OnChildTerminated(watcher); - } - else - { - pLabor->OnTerminated(watcher); - } + pLabor->OnTerminated(watcher); } } @@ -159,17 +150,6 @@ bool Dispatcher::OnIoRead(std::shared_ptr pChannel) return(DataRecvAndHandle(pChannel)); } } - else if (Labor::LABOR_WORKER == m_pLabor->GetLaborType() || Labor::LABOR_LOADER == m_pLabor->GetLaborType()) - { - if (pChannel->GetFd() == ((Worker*)m_pLabor)->GetWorkerInfo().iDataFd) - { - return(FdTransfer(pChannel->GetFd())); - } - else - { - return(DataRecvAndHandle(pChannel)); - } - } else { return(DataRecvAndHandle(pChannel)); @@ -221,103 +201,6 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) } } -bool Dispatcher::FdTransfer(int iFd) -{ - LOG4_TRACE(" "); - int iAcceptFd = -1; - int iAiFamily = AF_INET; - int iCodec = 0; - std::string strRemoteAddr; - int iErrno = SocketChannel::RecvChannelFd(iFd, iAcceptFd, iAiFamily, iCodec, strRemoteAddr, m_pLogger); - if (iErrno != ERR_OK) - { - if (iErrno == ERR_CHANNEL_EOF) - { - LOG4_WARNING("recv_fd from fd %d error %d", iFd, errno); - Destroy(); - exit(2); // manager与worker通信fd已关闭,worker进程退出 - } - } - else - { - if (iAiFamily != PF_UNIX) - { - int iKeepAlive = 1; - int iKeepIdle = 60; - int iKeepInterval = 5; - int iKeepCount = 3; - int iTcpNoDelay = 1; - if (setsockopt(iAcceptFd, SOL_SOCKET, SO_KEEPALIVE, (void*)&iKeepAlive, sizeof(iKeepAlive)) < 0) - { - LOG4_WARNING("fail to set SO_KEEPALIVE"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &iKeepIdle, sizeof(iKeepIdle)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPIDLE"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&iKeepInterval, sizeof(iKeepInterval)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPINTVL"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)) < 0) - { - LOG4_WARNING("fail to set TCP_KEEPCNT"); - } - if (setsockopt(iAcceptFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)) < 0) - { - LOG4_WARNING("fail to set TCP_NODELAY"); - } - } - x_sock_set_block(iAcceptFd, 0); - std::shared_ptr pChannel = nullptr; - LOG4_TRACE("%s fd[%d] transfer successfully.", strRemoteAddr.c_str(), iAcceptFd); - if ((CODEC_NEBULA != iCodec) && (CODEC_NEBULA_IN_NODE != iCodec) && m_pLabor->WithSsl()) - { - pChannel = CreateSocketChannel(iAcceptFd, E_CODEC_TYPE(iCodec), false, true); - } - else - { - pChannel = CreateSocketChannel(iAcceptFd, E_CODEC_TYPE(iCodec), false, false); - } - if (nullptr != pChannel) - { - pChannel->SetRemoteAddr(strRemoteAddr); - AddIoReadEvent(pChannel); - if (CODEC_NEBULA == iCodec) - { - AddIoTimeout(pChannel, m_pLabor->GetNodeInfo().dIoTimeout); - std::shared_ptr pStepTellWorker - = m_pLabor->GetActorBuilder()->MakeSharedStep(nullptr, "neb::StepTellWorker", pChannel); - if (nullptr == pStepTellWorker) - { - return(false); - } - pStepTellWorker->Emit(ERR_OK); - } - else if (CODEC_NEBULA_IN_NODE == iCodec) - { - pChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - m_mapLoaderAndWorkerChannel.insert(std::make_pair(pChannel->GetFd(), pChannel)); - m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); - } - else - { - ev_tstamp dIoTimeout = (m_pLabor->GetNodeInfo().dConnectionProtection > 0) - ? m_pLabor->GetNodeInfo().dConnectionProtection : m_pLabor->GetNodeInfo().dIoTimeout; - pChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - AddIoTimeout(pChannel, dIoTimeout); - } - m_pLabor->IoStatAddConnection(IO_STAT_DOWNSTREAM_NEW_CONNECTION); - return(true); - } - else // 没有足够资源分配给新连接,直接close掉 - { - close(iAcceptFd); - } - } - return(false); -} - bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) { if (CODEC_NEBULA == pChannel->GetCodecType()) // 系统内部Server间通信 @@ -504,13 +387,21 @@ bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBod } else { - auto pChannel = ((Worker*)m_pLabor)->GetManagerControlChannel(); - if (pChannel == nullptr) + MsgHead oMsgHead; + oMsgHead.set_cmd(iCmd); + oMsgHead.set_seq(uiSeq); + uint32 uiManagerLaborId = LaborShared::Instance()->GetManagerLaborId(); + int iResult = CodecNebulaInNode::Write(((Worker*)m_pLabor)->GetWorkerInfo().iWorkerIndex, + uiManagerLaborId, gc_uiCmdReq, m_pLabor->GetSequence(), oMsgHead, oMsgBody); + if (ERR_OK == iResult) + { + return(true); + } + else { - LOG4_ERROR("no connected channel to manager!"); + LOG4_ERROR("send data report to manager error %d", iResult); return(false); } - return(IO::SendRequest(this, 0, pChannel, iCmd, uiSeq, oMsgBody)); } } @@ -870,9 +761,19 @@ bool Dispatcher::DelEvent(ev_timer* timer_watcher) return(true); } -int Dispatcher::SendFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, const std::string& strRemoteAddr) +void Dispatcher::AddChannelToLoop(std::shared_ptr pChannel) { - return(SocketChannel::SendChannelFd(iSocketFd, iSendFd, iAiFamily, iCodecType, strRemoteAddr, m_pLogger)); + auto iter = m_mapSocketChannel.find(pChannel->GetFd()); + if (iter == m_mapSocketChannel.end()) + { + pChannel->SetBonding(m_pLabor, GetLogger(), pChannel); + pChannel->MutableWatcher()->Set(pChannel); + ev_tstamp dIoTimeout = (m_pLabor->GetNodeInfo().dConnectionProtection > 0) + ? m_pLabor->GetNodeInfo().dConnectionProtection : m_pLabor->GetNodeInfo().dIoTimeout; + AddIoTimeout(pChannel, dIoTimeout); + AddIoReadEvent(pChannel); + m_mapSocketChannel.insert(std::make_pair(pChannel->GetFd(), pChannel)); + } } bool Dispatcher::CreateListenFd(const std::string& strHost, int32 iPort, int iBacklog, int& iFd, int& iFamily) @@ -1110,6 +1011,68 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b } } +bool Dispatcher::MigrateSocketChannel(uint32 uiFromLabor, uint32 uiToLabor, std::shared_ptr pChannel) +{ + if (pChannel == nullptr) + { + LOG4_TRACE("pChannel not exist!"); + return(false); + } + if (pChannel->IsClient()) + { + LOG4_ERROR("client channel can not be migrate."); + return(false); + } + + auto named_iter = m_mapNamedSocketChannel.find(pChannel->m_pImpl->GetIdentify()); + if (named_iter != m_mapNamedSocketChannel.end()) + { + for (auto it = named_iter->second.begin(); + it != named_iter->second.end(); ++it) + { + if ((*it)->m_pImpl->GetSequence() == pChannel->m_pImpl->GetSequence()) + { + named_iter->second.erase(it); + LOG4_TRACE("erase channel %d from m_mapNamedSocketChannel.", pChannel->GetFd()); + break; + } + } + if (0 == named_iter->second.size()) + { + m_mapNamedSocketChannel.erase(named_iter); + } + } + + ev_io_stop (m_loop, pChannel->MutableWatcher()->MutableIoWatcher()); + if (nullptr != pChannel->MutableWatcher()->MutableTimerWatcher()) + { + ev_timer_stop (m_loop, pChannel->MutableWatcher()->MutableTimerWatcher()); + } + + auto channel_iter = m_mapSocketChannel.find(pChannel->GetFd()); + if (channel_iter != m_mapSocketChannel.end()) + { + m_mapSocketChannel.erase(channel_iter); + LOG4_TRACE("erase channel %d channel_seq %u from m_mapSocketChannel.", + pChannel->GetFd(), pChannel->GetSequence()); + } + int iResult = SocketChannelMigrate::Write(uiFromLabor, uiToLabor, gc_uiCmdReq, m_pLabor->GetSequence(), pChannel); + if (ERR_OK == iResult) + { + return(true); + } + LOG4_WARNING("failed to migrate channel"); + // recover from migration failed + pChannel->SetBonding(m_pLabor, GetLogger(), pChannel); + pChannel->MutableWatcher()->Set(pChannel); + m_mapSocketChannel.insert(std::make_pair(pChannel->GetFd(), pChannel)); + ev_tstamp dIoTimeout = (m_pLabor->GetNodeInfo().dConnectionProtection > 0) + ? m_pLabor->GetNodeInfo().dConnectionProtection : m_pLabor->GetNodeInfo().dIoTimeout; + AddIoTimeout(pChannel, dIoTimeout); + AddIoReadEvent(pChannel); + return(false); +} + bool Dispatcher::AddIoReadEvent(std::shared_ptr pChannel) { LOG4_TRACE("fd[%d], seq[%u]", pChannel->GetFd(), pChannel->GetSequence()); @@ -1315,35 +1278,45 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily, int iBonding) } } - int iWorkerDataFd = -1; + int iWorkerId = -1; switch (m_pLabor->GetNodeInfo().iConnectionDispatch) { case DISPATCH_ROUND_ROBIN: - iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(iBonding); - break; - case DISPATCH_MIN_LOAD: - iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetMinLoadWorkerDataFd(iBonding); + iWorkerId = ((Manager*)m_pLabor)->GetSessionManager()->GetDispatchWorkerId(iFd); break; case DISPATCH_CLIENT_ADDR_HASH: - iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetWorkerDataFd(szClientAddr, iClientPort, iBonding); + iWorkerId = ((Manager*)m_pLabor)->GetSessionManager()->GetDispatchWorkerId(iFd, szClientAddr, iClientPort); break; default: - iWorkerDataFd = ((Manager*)m_pLabor)->GetSessionManager()->GetNextWorkerDataFd(iBonding); - } - if (iWorkerDataFd > 0) - { - LOG4_TRACE("send new fd %d to worker communication fd %d", - iAcceptFd, iWorkerDataFd); - int iCodec = m_pLabor->GetNodeInfo().eCodec; - int iErrno = SocketChannel::SendChannelFd(iWorkerDataFd, iAcceptFd, iFamily, iCodec, szClientAddr, m_pLogger); - if (iErrno != ERR_OK) + iWorkerId = ((Manager*)m_pLabor)->GetSessionManager()->GetDispatchWorkerId(iFd); + } + if (iWorkerId > 0) + { + MsgHead oMsgHead; + MsgBody oMsgBody; + FdTransfer oFdTransferInfo; + std::string strFdTransferInfo; + oFdTransferInfo.set_fd(iAcceptFd); + oFdTransferInfo.set_addr_family(iFamily); + oFdTransferInfo.set_client_addr(szClientAddr); + oFdTransferInfo.set_client_port(iClientPort); + oFdTransferInfo.set_codec_type(m_pLabor->GetNodeInfo().eCodec); + oFdTransferInfo.SerializeToString(&strFdTransferInfo); + oMsgBody.set_data(strFdTransferInfo); + oMsgHead.set_cmd(CMD_REQ_FD_TRANSFER); + uint32 uiManagerLaborId = m_pLabor->GetNodeInfo().uiWorkerNum + 1; + int iResult = CodecNebulaInNode::Write(uiManagerLaborId, iWorkerId, gc_uiCmdReq, m_pLabor->GetSequence(), oMsgHead, oMsgBody); + if (ERR_OK == iResult) { - LOG4_ERROR("error %d: %s", iErrno, strerror_r(iErrno, m_pErrBuff, gc_iErrBuffLen)); + return(true); + } + else + { + LOG4_ERROR("transfer fd %d to worker %d error %d", iAcceptFd, iWorkerId, iResult); + return(false); } - close(iAcceptFd); - return(true); } - LOG4_ERROR("no available data fd %d", iWorkerDataFd); + LOG4_ERROR("no available worker"); close(iAcceptFd); return(false); } diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 6157b2bc..14cfdd2f 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -64,6 +64,7 @@ class CodecFactory; class LoadStress; // not in Nebula project class Actor; class ActorBuilder; +class CmdFdTransfer; struct tagClientConnWatcherData; template class IO; @@ -75,8 +76,7 @@ typedef void (*async_callback)(struct ev_loop*,ev_async*,int); enum E_DISPATCHER { DISPATCH_ROUND_ROBIN = 0, - DISPATCH_MIN_LOAD = 1, - DISPATCH_CLIENT_ADDR_HASH = 2, + DISPATCH_CLIENT_ADDR_HASH = 1, }; class Dispatcher @@ -113,7 +113,6 @@ class Dispatcher bool OnIoRead(std::shared_ptr pChannel); bool DataRecvAndHandle(std::shared_ptr pChannel); - bool FdTransfer(int iFd); bool OnIoWrite(std::shared_ptr pChannel); bool OnIoError(std::shared_ptr pChannel); bool OnIoTimeout(std::shared_ptr pChannel); @@ -164,9 +163,17 @@ class Dispatcher } std::shared_ptr CreateSocketChannel(int iFd, E_CODEC_TYPE eCodecType, bool bIsClient = false, bool bWithSsl = false); bool DiscardSocketChannel(std::shared_ptr pChannel, bool bChannelNotice = true); + /** + * @brief migrate socket channel + * @note + * 1. only server side socket channel can be migrate. 只有服务端的socket channel才可以迁移。 + * 2. make sure the socket channel that is moved out will no longer send response from outgoing side. + * 确保不再有来自迁出线程的响应包发送到迁出的socket channel,否则该响应将无法送达。 + */ + bool MigrateSocketChannel(uint32 uiFromLabor, uint32 uiToLabor, std::shared_ptr pChannel); bool CreateListenFd(const std::string& strHost, int32 iPort, int iBacklog, int& iFd, int& iFamily); std::shared_ptr GetChannel(int iFd); - int SendFd(int iSocketFd, int iSendFd, int iAiFamily, int iCodecType, const std::string& strRemoteAddr); + void AddChannelToLoop(std::shared_ptr pChannel); void AsyncSend(ev_async* pWatcher); protected: @@ -213,6 +220,7 @@ class Dispatcher friend class ActorBuilder; friend class LoadStress; friend class CodecFactory; + friend class CmdFdTransfer; template friend class IO; }; diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index dec1c783..8faeb34c 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -204,6 +204,12 @@ template template bool IO::SendResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args) { + if (pChannel->m_pImpl == nullptr) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "no channel impl"); + return(false); + } if (CODEC_UNKNOW == pChannel->GetCodecType()) { LOG4_TRACE_DISPATCH("CODEC_UNKNOW is invalid, channel had not been init?"); @@ -313,6 +319,12 @@ template template bool IO::SendRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, Targs&&... args) { + if (pChannel->m_pImpl == nullptr) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "no channel impl"); + return(false); + } if (CODEC_UNKNOW == pChannel->GetCodecType()) { LOG4_TRACE_DISPATCH("CODEC_UNKNOW is invalid, channel had not been init?"); diff --git a/src/labor/LaborShared.cpp b/src/labor/LaborShared.cpp index 3bb3d89f..a35b54bb 100644 --- a/src/labor/LaborShared.cpp +++ b/src/labor/LaborShared.cpp @@ -8,6 +8,7 @@ * Modify history: ******************************************************************************/ #include "LaborShared.hpp" +#include #include "pb/neb_sys.pb.h" #include "codec/CodecProto.hpp" #include "channel/SpecChannel.hpp" @@ -21,6 +22,8 @@ std::mutex LaborShared::s_mutex; LaborShared::LaborShared(uint32 uiLaborNum) : m_uiLaborNum(uiLaborNum), m_uiSpecChannelQueueSize(128), m_uiCodecSize(0) { + m_vecDispatcher.reserve(uiLaborNum); + m_vecSpecChannel.reserve(CODEC_MAX + 1); } LaborShared::~LaborShared() @@ -41,6 +44,10 @@ Dispatcher* LaborShared::GetDispatcher(uint32 uiLaborId) void LaborShared::AddDispatcher(uint32 uiLaborId, Dispatcher* pDispatcher) { + if (uiLaborId >= m_uiLaborNum) + { + return; + } if (uiLaborId < m_vecDispatcher.size()) { m_vecDispatcher[uiLaborId] = pDispatcher; @@ -88,6 +95,7 @@ int LaborShared::AddSpecChannel(uint32 uiCodecType, uint32 uiFrom, uint32 uiTo, for (uint32 i = m_vecSpecChannel.size(); i <= uiCodecType; ++i) { T_VEC_CHANNEL_FROM vecFrom; + vecFrom.reserve(m_uiLaborNum); for (uint32 j = 0; j < m_uiLaborNum; ++j) { auto pTo = std::make_shared(); @@ -193,5 +201,15 @@ std::shared_ptr> LaborShared::CreateInternalSpecCh return(pSpecChannel); } +void LaborShared::AddWorkerThreadId(uint64 ullThreadId) +{ + std::lock_guard guard(s_mutex); + if (std::find(m_vecWorkerThreadId.begin(), m_vecWorkerThreadId.end(), ullThreadId) + == m_vecWorkerThreadId.end()) + { + m_vecWorkerThreadId.push_back(ullThreadId); + } +} + } /* namespace neb */ diff --git a/src/labor/LaborShared.hpp b/src/labor/LaborShared.hpp index 243a6646..06d152dc 100644 --- a/src/labor/LaborShared.hpp +++ b/src/labor/LaborShared.hpp @@ -51,6 +51,16 @@ class LaborShared int AddSpecChannel(uint32 uiCodecType, uint32 uiFrom, uint32 uiTo, std::shared_ptr pChannel); std::shared_ptr> CreateInternalSpecChannel(uint32 uiFrom, uint32 uiTo); + uint32 GetLaborNum() const + { + return(m_uiLaborNum); + } + + uint32 GetManagerLaborId() const + { + return(m_uiLaborNum - 1); + } + uint32 GetSpecChannelQueueSize() const { return(m_uiSpecChannelQueueSize); @@ -61,6 +71,12 @@ class LaborShared m_uiSpecChannelQueueSize = uiSize; } + const std::vector& GetWorkerThreadId() + { + return(m_vecWorkerThreadId); + } + void AddWorkerThreadId(uint64 ullThreadId); + private: explicit LaborShared(uint32 uiLaborNum); static LaborShared* s_pInstance; @@ -72,6 +88,7 @@ class LaborShared std::atomic m_uiCodecSize; std::vector m_vecDispatcher; T_VECCHANNEL_CODEC_TYPE m_vecSpecChannel; + std::vector m_vecWorkerThreadId; }; } /* namespace neb */ diff --git a/src/labor/Loader.cpp b/src/labor/Loader.cpp index 305c728f..ef7a66b2 100644 --- a/src/labor/Loader.cpp +++ b/src/labor/Loader.cpp @@ -15,8 +15,8 @@ namespace neb { -Loader::Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex) - : Worker(strWorkPath, iControlFd, iDataFd, iWorkerIndex, Labor::LABOR_LOADER) +Loader::Loader(const std::string& strWorkPath, int iWorkerIndex) + : Worker(strWorkPath, iWorkerIndex, Labor::LABOR_LOADER) { } diff --git a/src/labor/Loader.hpp b/src/labor/Loader.hpp index 117a47ae..afecbbc1 100644 --- a/src/labor/Loader.hpp +++ b/src/labor/Loader.hpp @@ -19,7 +19,7 @@ namespace neb class Loader: public Worker { public: - Loader(const std::string& strWorkPath, int iControlFd, int iDataFd, int iWorkerIndex); + Loader(const std::string& strWorkPath, int iWorkerIndex); virtual ~Loader(); protected: diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 58445d2f..77baf6c9 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -62,17 +62,9 @@ Manager::Manager(const std::string& strConfFile) exit(3); } CreateEvents(); - if (m_stNodeInfo.bThreadMode) - { - CreateWorkerThread(); // Loader可能需要用到Worker线程ID,故先创建Worker - usleep(1000); - CreateLoaderThread(); - } - else - { - CreateLoader(); - CreateWorker(); - } + CreateWorkerThread(); // Loader可能需要用到Worker线程ID,故先创建Worker + usleep(1000); + CreateLoaderThread(); } Manager::~Manager() @@ -91,33 +83,6 @@ void Manager::OnTerminated(struct ev_signal* watcher) exit(iSignum); } -void Manager::OnChildTerminated(struct ev_signal* watcher) -{ - pid_t iPid = 0; - int iStatus = 0; - int iReturnCode = 0; - //WUNTRACED - while((iPid = waitpid(-1, &iStatus, WNOHANG)) > 0) - { - if (WIFEXITED(iStatus)) - { - iReturnCode = WEXITSTATUS(iStatus); - } - else if (WIFSIGNALED(iStatus)) - { - iReturnCode = WTERMSIG(iStatus); - } - else if (WIFSTOPPED(iStatus)) - { - iReturnCode = WSTOPSIG(iStatus); - } - - LOG4_FATAL("error %d: process %d exit and sent signal %d with code %d!", - iStatus, iPid, watcher->signum, iReturnCode); - RestartWorker(iPid); - } -} - void Manager::Run() { LOG4_TRACE(" "); @@ -235,6 +200,7 @@ bool Manager::InitActorBuilder() void Manager::StartService() { + m_pSessionManager->AddPort2WorkerInfo(m_stNodeInfo.uiWorkerNum, m_stNodeInfo.uiLoaderNum, m_oCurrentConf["access_port_to_worker"]); std::string strBindIp; if (m_oCurrentConf.Get("bind_ip", strBindIp) && strBindIp.length() > 0) { @@ -250,6 +216,7 @@ void Manager::StartService() m_pDispatcher->CreateListenFd(strBindIp, m_stNodeInfo.iPortForClient, m_stNodeInfo.iBacklog, m_stManagerInfo.iC2SListenFd, m_stManagerInfo.iC2SFamily); + m_pSessionManager->AddPortListenFd(m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd); int iPort = 0; int iListenFd = 0; std::unordered_set setPort; @@ -269,6 +236,7 @@ void Manager::StartService() *it, m_stNodeInfo.iBacklog, iListenFd, m_stManagerInfo.iC2SFamily); m_stManagerInfo.setAccessFd.insert(iListenFd); + m_pSessionManager->AddPortListenFd(*it, iListenFd); } } } @@ -287,6 +255,7 @@ void Manager::StartService() m_pDispatcher->CreateListenFd(m_stNodeInfo.strHostForClient, m_stNodeInfo.iPortForClient, m_stNodeInfo.iBacklog, m_stManagerInfo.iC2SListenFd, m_stManagerInfo.iC2SFamily); + m_pSessionManager->AddPortListenFd(m_stNodeInfo.iPortForClient, m_stManagerInfo.iC2SListenFd); int iPort = 0; int iListenFd = 0; std::unordered_set setPort; @@ -306,6 +275,7 @@ void Manager::StartService() *it, m_stNodeInfo.iBacklog, iListenFd, m_stManagerInfo.iC2SFamily); m_stManagerInfo.setAccessFd.insert(iListenFd); + m_pSessionManager->AddPortListenFd(*it, iListenFd); } } } @@ -510,81 +480,13 @@ bool Manager::CreateEvents() fpe_signal_watcher->data = (void*)this; m_pDispatcher->AddEvent(fpe_signal_watcher, Dispatcher::SignalCallback, SIGFPE); - bool bDirectToLoader = false; - bool bDispatchWithPort = false; - m_oCurrentConf.Get("new_client_to_loader", bDirectToLoader); - m_oCurrentConf.Get("dispatch_with_port", bDispatchWithPort); m_pSessionManager = std::dynamic_pointer_cast( - m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", - bDirectToLoader, bDispatchWithPort, m_stNodeInfo.dDataReportInterval)); + m_pActorBuilder->MakeSharedSession(nullptr, "neb::SessionManager", m_stNodeInfo.dDataReportInterval)); AddPeriodicTaskEvent(); return(true); } -void Manager::CreateLoader() -{ - bool bWithLoader = false; - m_oCurrentConf.Get("with_loader", bWithLoader); - if (!bWithLoader) - { - return; - } - LOG4_TRACE(" "); - int iControlFds[2]; - int iDataFds[2]; - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iControlFds) < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iDataFds) < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } - - int iPid = fork(); - if (iPid == 0) // 子进程 - { - close(m_stManagerInfo.iS2SListenFd); - if (m_stManagerInfo.iC2SListenFd > 2) - { - close(m_stManagerInfo.iC2SListenFd); - } - close(iControlFds[0]); - close(iDataFds[0]); - x_sock_set_block(iControlFds[1], 0); - x_sock_set_block(iDataFds[1], 0); - Loader oLoader(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], 0); - if (!oLoader.Init(m_oCurrentConf)) - { - exit(3); - } - oLoader.Run(); - exit(-2); - } - else if (iPid > 0) // 父进程 - { - close(iControlFds[1]); - close(iDataFds[1]); - x_sock_set_block(iControlFds[0], 0); - x_sock_set_block(iDataFds[0], 0); - m_stNodeInfo.uiLoaderNum = 1; - m_pSessionManager->AddLoaderInfo(0, iPid, iControlFds[0], iDataFds[0]); - std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); - std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); - m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->AddIoReadEvent(pChannelData); - m_pDispatcher->AddIoReadEvent(pChannelControl); - m_pSessionManager->SendOnlineNodesToWorker(); - m_pSessionManager->NewSocketWhenLoaderCreated(); - } - else - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } -} - void Manager::CreateLoaderThread() { bool bWithLoader = false; @@ -594,22 +496,7 @@ void Manager::CreateLoaderThread() return; } LOG4_TRACE(" "); - int iControlFds[2]; - int iDataFds[2]; - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iControlFds) < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iDataFds) < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } - - x_sock_set_block(iControlFds[0], 0); - x_sock_set_block(iDataFds[0], 0); - x_sock_set_block(iControlFds[1], 0); - x_sock_set_block(iDataFds[1], 0); - Worker* pWorker = m_pSessionManager->MutableLoader(0, m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1]); + Worker* pWorker = m_pSessionManager->MutableLoader(0, m_stNodeInfo.strWorkPath); if (pWorker == nullptr) { return; @@ -630,77 +517,9 @@ void Manager::CreateLoaderThread() std::thread t(&Worker::Run, pWorker); t.detach(); m_stNodeInfo.uiLoaderNum = 1; - m_pSessionManager->AddLoaderInfo(0, getpid(), iControlFds[0], iDataFds[0]); - std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); - std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); - m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->AddIoReadEvent(pChannelData); - m_pDispatcher->AddIoReadEvent(pChannelControl); + m_pSessionManager->AddWorkerInfo(0, getpid()); m_pSessionManager->SetLoaderActorBuilder(m_pLoaderActorBuilder); m_pSessionManager->SendOnlineNodesToWorker(); - m_pSessionManager->NewSocketWhenLoaderCreated(); -} - -void Manager::CreateWorker() -{ - LOG4_TRACE(" "); - int iPid = 0; - - for (unsigned int i = 1; i <= m_stNodeInfo.uiWorkerNum; ++i) - { - int iControlFds[2]; - int iDataFds[2]; - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iControlFds) < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iDataFds) < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } - - iPid = fork(); - if (iPid == 0) // 子进程 - { - close(m_stManagerInfo.iS2SListenFd); - if (m_stManagerInfo.iC2SListenFd > 2) - { - close(m_stManagerInfo.iC2SListenFd); - } - close(iControlFds[0]); - close(iDataFds[0]); - x_sock_set_block(iControlFds[1], 0); - x_sock_set_block(iDataFds[1], 0); - Worker oWorker(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], i); - if (!oWorker.Init(m_oCurrentConf)) - { - exit(3); - } - oWorker.Run(); - exit(-2); - } - else if (iPid > 0) // 父进程 - { - close(iControlFds[1]); - close(iDataFds[1]); - x_sock_set_block(iControlFds[0], 0); - x_sock_set_block(iDataFds[0], 0); - m_pSessionManager->AddWorkerInfo(i, iPid, iControlFds[0], iDataFds[0]); - std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); - std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); - m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->AddIoReadEvent(pChannelData); - m_pDispatcher->AddIoReadEvent(pChannelControl); - m_pSessionManager->NewSocketWhenWorkerCreated(iDataFds[0]); - m_pSessionManager->SendOnlineNodesToWorker(); // optional - } - else - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } - } } void Manager::CreateWorkerThread() @@ -708,22 +527,7 @@ void Manager::CreateWorkerThread() LOG4_TRACE(" "); for (unsigned int i = 1; i <= m_stNodeInfo.uiWorkerNum; ++i) { - int iControlFds[2]; - int iDataFds[2]; - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iControlFds) < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iDataFds) < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } - - x_sock_set_block(iControlFds[0], 0); - x_sock_set_block(iDataFds[0], 0); - x_sock_set_block(iControlFds[1], 0); - x_sock_set_block(iDataFds[1], 0); - Worker* pWorker = m_pSessionManager->MutableWorker(i, m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1]); + Worker* pWorker = m_pSessionManager->MutableWorker(i, m_stNodeInfo.strWorkPath); if (pWorker == nullptr) { continue; @@ -743,101 +547,9 @@ void Manager::CreateWorkerThread() LOG4_TRACE("spec channel from %u to %u has been created.", i, m_stNodeInfo.uiWorkerNum + 1); std::thread t(&Worker::Run, pWorker); t.detach(); - m_pSessionManager->AddWorkerInfo(i, getpid(), iControlFds[0], iDataFds[0]); - std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); - std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); - m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->AddIoReadEvent(pChannelData); - m_pDispatcher->AddIoReadEvent(pChannelControl); + m_pSessionManager->AddWorkerInfo(i, getpid()); m_pSessionManager->SendOnlineNodesToWorker(); // optional - m_pSessionManager->NewSocketWhenWorkerCreated(iDataFds[0]); - } -} - -bool Manager::RestartWorker(int iDeathPid) -{ - LOG4_DEBUG("%d", iDeathPid); - int iWorkerIndex = 0; - int iNewPid = 0; - Labor::LABOR_TYPE eLaborType; - if (m_pSessionManager->WorkerDeath(iDeathPid, iWorkerIndex, eLaborType)) - { - int iControlFds[2]; - int iDataFds[2]; - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iControlFds) < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } - if (socketpair(PF_UNIX, SOCK_STREAM, 0, iDataFds) < 0) - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } - - iNewPid = fork(); - if (iNewPid == 0) // 子进程 - { - close(m_stManagerInfo.iS2SListenFd); - if (m_stManagerInfo.iC2SListenFd > 2) - { - close(m_stManagerInfo.iC2SListenFd); - } - close(iControlFds[0]); - close(iDataFds[0]); - x_sock_set_block(iControlFds[1], 0); - x_sock_set_block(iDataFds[1], 0); - if (Labor::LABOR_LOADER == eLaborType) - { - Loader oLoader(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], 0); - if (!oLoader.Init(m_oCurrentConf)) - { - exit(-1); - } - oLoader.Run(); - } - else - { - Worker oWorker(m_stNodeInfo.strWorkPath, iControlFds[1], iDataFds[1], iWorkerIndex); - if (!oWorker.Init(m_oCurrentConf)) - { - exit(-1); - } - oWorker.Run(); - } - exit(-2); // 子进程worker没有正常运行 - } - else if (iNewPid > 0) // 父进程 - { - LOG4_INFO("worker %d restart successfully", iWorkerIndex); - close(iControlFds[1]); - close(iDataFds[1]); - x_sock_set_block(iControlFds[0], 0); - x_sock_set_block(iDataFds[0], 0); - m_pSessionManager->AddWorkerInfo(iWorkerIndex, iNewPid, iControlFds[0], iDataFds[0]); - std::shared_ptr pChannelData = m_pDispatcher->CreateSocketChannel(iControlFds[0], CODEC_NEBULA_IN_NODE); - std::shared_ptr pChannelControl = m_pDispatcher->CreateSocketChannel(iDataFds[0], CODEC_NEBULA_IN_NODE); - m_pDispatcher->SetChannelStatus(pChannelData, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->SetChannelStatus(pChannelControl, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->AddIoReadEvent(pChannelData); - m_pDispatcher->AddIoReadEvent(pChannelControl); - m_pSessionManager->SendOnlineNodesToWorker(); - if (Labor::LABOR_LOADER == eLaborType) - { - m_pSessionManager->AddLoaderInfo(iWorkerIndex, iNewPid, iControlFds[0], iDataFds[0]); - m_pSessionManager->NewSocketWhenLoaderCreated(); - } - else - { - m_pSessionManager->AddWorkerInfo(iWorkerIndex, iNewPid, iControlFds[0], iDataFds[0]); - m_pSessionManager->NewSocketWhenWorkerCreated(iDataFds[0]); - } - } - else - { - LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_pErrBuff, gc_iErrBuffLen)); - } } - return(false); } void Manager::RefreshServer() diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp index 57cedd70..36d645be 100644 --- a/src/labor/Manager.hpp +++ b/src/labor/Manager.hpp @@ -60,7 +60,6 @@ class Manager: public Labor void Run(); void OnTerminated(struct ev_signal* watcher); - void OnChildTerminated(struct ev_signal* watcher); template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); @@ -113,11 +112,8 @@ class Manager: public Labor void Destroy(); bool CreateEvents(); - void CreateLoader(); void CreateLoaderThread(); - void CreateWorker(); //muti process void CreateWorkerThread(); //muti thread - bool RestartWorker(int iDeathPid); bool AddPeriodicTaskEvent(); private: diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp index c0d5597a..4ad4c860 100644 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -81,8 +81,6 @@ struct WorkerInfo { int32 iWorkerPid = 0; int iWorkerIndex = 0; ///< 工作进程序号 - int iControlFd = -1; ///< 与Manager进程通信的文件描述符(控制流) - int iDataFd = -1; ///< 与Manager进程通信的文件描述符(数据流) uint32 uiLoad = 0; ///< 负载 uint32 uiConnection = 0; ///< 连接数量 uint32 uiRecvNum = 0; ///< 接收数据包数量 diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index c9bd36c3..f1f089aa 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -30,12 +30,10 @@ extern "C" { namespace neb { -Worker::Worker(const std::string& strWorkPath, int iControlFd, int iDataFd, +Worker::Worker(const std::string& strWorkPath, int iWorkerIndex, Labor::LABOR_TYPE eLaborType) : Labor(eLaborType) { - m_stWorkerInfo.iControlFd = iControlFd; - m_stWorkerInfo.iDataFd = iDataFd; m_stWorkerInfo.iWorkerIndex = iWorkerIndex; m_stWorkerInfo.iWorkerPid = getpid(); m_stNodeInfo.strWorkPath = strWorkPath; @@ -55,7 +53,7 @@ void Worker::Run() char szThreadName[64] = {0}; snprintf(szThreadName, sizeof(szThreadName), "%s_W%d", m_oNodeConf("server_name").c_str(), m_stWorkerInfo.iWorkerIndex); pthread_setname_np(pthread_self(), szThreadName); - SessionManager::AddWorkerThreadId(gettid()); + LaborShared::Instance()->AddWorkerThreadId(gettid()); } if (!CreateEvents()) @@ -95,35 +93,24 @@ void Worker::OnTerminated(struct ev_signal* watcher) exit(iSignum); } -bool Worker::CheckParent() -{ - if (!m_stNodeInfo.bThreadMode) - { - pid_t iParentPid = getppid(); - if (iParentPid == 1) // manager进程已不存在 - { - LOG4_INFO("no manager process exist, worker %d exit.", m_stWorkerInfo.iWorkerIndex); - Destroy(); - exit(0); - } - } - return(true); -} - void Worker::DataReport() { m_stWorkerInfo.uiConnection = m_pDispatcher->GetConnectionNum(); + MsgHead oMsgHead; MsgBody oMsgBody; - CJsonObject oJsonLoad; - oJsonLoad.Add("load", int32(m_pActorBuilder->GetStepNum())); - oJsonLoad.Add("connect", m_stWorkerInfo.uiConnection); - oJsonLoad.Add("recv_num", m_stWorkerInfo.uiRecvNum); - oJsonLoad.Add("recv_byte", m_stWorkerInfo.uiRecvByte); - oJsonLoad.Add("send_num", m_stWorkerInfo.uiSendNum); - oJsonLoad.Add("send_byte", m_stWorkerInfo.uiSendByte); - oMsgBody.set_data(oJsonLoad.ToString()); - LOG4_TRACE("%s", oJsonLoad.ToString().c_str()); - IO::SendRequest(m_pDispatcher, 0, m_pManagerControlChannel, CMD_REQ_UPDATE_WORKER_LOAD, GetSequence(), oMsgBody); + ReportRecord oWorkerStatus; + std::string strWorkerStatus; + oWorkerStatus.add_value(m_pActorBuilder->GetStepNum()); // load + oWorkerStatus.add_value(m_stWorkerInfo.uiConnection); // conection + oWorkerStatus.add_value(m_stWorkerInfo.uiRecvNum); + oWorkerStatus.add_value(m_stWorkerInfo.uiRecvByte); + oWorkerStatus.add_value(m_stWorkerInfo.uiSendNum); + oWorkerStatus.add_value(m_stWorkerInfo.uiSendByte); + oWorkerStatus.SerializeToString(&strWorkerStatus); + oMsgBody.set_data(strWorkerStatus); + oMsgHead.set_cmd(CMD_REQ_UPDATE_WORKER_LOAD); + uint32 uiManagerLaborId = LaborShared::Instance()->GetManagerLaborId(); + CodecNebulaInNode::Write(m_stWorkerInfo.iWorkerIndex, uiManagerLaborId, gc_uiCmdReq, 0, oMsgHead, oMsgBody); auto pSessionDataReport = std::dynamic_pointer_cast(m_pActorBuilder->GetSession("neb::SessionDataReport")); if (pSessionDataReport != nullptr) { @@ -438,22 +425,21 @@ bool Worker::CreateEvents() m_pDispatcher->AddEvent(signal_watcher, Dispatcher::SignalCallback, SIGINT); } AddPeriodicTaskEvent(); - - // 注册网络IO事件 - m_pManagerDataChannel = m_pDispatcher->CreateSocketChannel(m_stWorkerInfo.iDataFd, CODEC_NEBULA_IN_NODE); - m_pDispatcher->SetChannelStatus(m_pManagerDataChannel, CHANNEL_STATUS_ESTABLISHED); - m_pManagerControlChannel = m_pDispatcher->CreateSocketChannel(m_stWorkerInfo.iControlFd, CODEC_NEBULA_IN_NODE); - m_pDispatcher->SetChannelStatus(m_pManagerControlChannel, CHANNEL_STATUS_ESTABLISHED); - m_pDispatcher->AddIoReadEvent(m_pManagerDataChannel); - m_pDispatcher->AddIoReadEvent(m_pManagerControlChannel); return(true); } void Worker::StartService() { + MsgHead oMsgHead; MsgBody oMsgBody; + oMsgHead.set_cmd(CMD_REQ_START_SERVICE); oMsgBody.set_data(std::to_string(m_stWorkerInfo.iWorkerIndex)); - IO::SendRequest(m_pDispatcher, 0, m_pManagerControlChannel, CMD_REQ_START_SERVICE, GetSequence(), oMsgBody); + uint32 uiManagerLaborId = LaborShared::Instance()->GetManagerLaborId(); + int iResult = CodecNebulaInNode::Write(m_stWorkerInfo.iWorkerIndex, uiManagerLaborId, gc_uiCmdReq, GetSequence(), oMsgHead, oMsgBody); + if (ERR_OK != iResult) + { + LOG4_ERROR("send to manager error %d", iResult); + } } void Worker::Destroy() @@ -541,11 +527,6 @@ const CJsonObject& Worker::GetCustomConf() const return(m_oCustomConf); } -std::shared_ptr Worker::GetManagerControlChannel() -{ - return(m_pManagerControlChannel); -} - bool Worker::SetCustomConf(const CJsonObject& oJsonConf) { m_oCustomConf = oJsonConf; diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 84d504d1..9b675a58 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -44,7 +44,7 @@ class ActorBuilder; class Worker: public Labor { public: - Worker(const std::string& strWorkPath, int iControlFd, int iDataFd, + Worker(const std::string& strWorkPath, int iWorkerIndex, Labor::LABOR_TYPE eLaborType = Labor::LABOR_WORKER); Worker(const Worker&) = delete; Worker& operator=(const Worker&) = delete; @@ -52,7 +52,6 @@ class Worker: public Labor // timeout,worker进程无响应或与Manager通信通道异常,被manager进程终止时返回 void OnTerminated(struct ev_signal* watcher); - bool CheckParent(); void DataReport(); virtual bool Init(CJsonObject& oJsonConf); @@ -99,19 +98,9 @@ class Worker: public Labor virtual const CJsonObject& GetCustomConf() const; bool WithSsl(); const WorkerInfo& GetWorkerInfo() const; - std::shared_ptr GetManagerControlChannel(); bool SetCustomConf(const CJsonObject& oJsonConf); virtual void IoStatAddRecvNum(int iFd, uint32 uiIoType) { - if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) - { - return; - } - if (iFd == m_pManagerControlChannel->GetFd() - || iFd == m_pManagerDataChannel->GetFd()) - { - return; - } ++m_stWorkerInfo.uiRecvNum; if (IO_STAT_UPSTREAM_RECV_NUM & uiIoType) { @@ -124,15 +113,6 @@ class Worker: public Labor } virtual void IoStatAddRecvBytes(int iFd, uint32 uiBytes, uint32 uiIoType) { - if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) - { - return; - } - if (iFd == m_pManagerControlChannel->GetFd() - || iFd == m_pManagerDataChannel->GetFd()) - { - return; - } m_stWorkerInfo.uiRecvByte += uiBytes; if (IO_STAT_UPSTREAM_RECV_BYTE & uiIoType) { @@ -145,15 +125,6 @@ class Worker: public Labor } virtual void IoStatAddSendNum(int iFd, uint32 uiIoType) { - if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) - { - return; - } - if (iFd == m_pManagerControlChannel->GetFd() - || iFd == m_pManagerDataChannel->GetFd()) - { - return; - } ++m_stWorkerInfo.uiSendNum; if (IO_STAT_UPSTREAM_SEND_NUM & uiIoType) { @@ -166,15 +137,6 @@ class Worker: public Labor } virtual void IoStatAddSendBytes(int iFd, uint32 uiBytes, uint32 uiIoType) { - if (m_pManagerControlChannel == nullptr || m_pManagerDataChannel == nullptr) - { - return; - } - if (iFd == m_pManagerControlChannel->GetFd() - || iFd == m_pManagerDataChannel->GetFd()) - { - return; - } m_stWorkerInfo.uiSendByte += uiBytes; if (IO_STAT_UPSTREAM_SEND_BYTE & uiIoType) { @@ -233,8 +195,6 @@ class Worker: public Labor WorkerInfo m_stWorkerInfo; std::shared_ptr m_pLogger = nullptr; - std::shared_ptr m_pManagerControlChannel = nullptr; - std::shared_ptr m_pManagerDataChannel = nullptr; }; template diff --git a/src/pb/neb_sys.pb.cc b/src/pb/neb_sys.pb.cc index 45ccb24a..e0d5ffe2 100644 --- a/src/pb/neb_sys.pb.cc +++ b/src/pb/neb_sys.pb.cc @@ -39,6 +39,9 @@ const ::google::protobuf::internal::GeneratedMessageReflection* const ::google::protobuf::Descriptor* SpecChannelInfo_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* SpecChannelInfo_reflection_ = NULL; +const ::google::protobuf::Descriptor* FdTransfer_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + FdTransfer_reflection_ = NULL; } // namespace @@ -154,6 +157,25 @@ void protobuf_AssignDesc_neb_5fsys_2eproto() { sizeof(SpecChannelInfo), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SpecChannelInfo, _internal_metadata_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SpecChannelInfo, _is_default_instance_)); + FdTransfer_descriptor_ = file->message_type(6); + static const int FdTransfer_offsets_[5] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FdTransfer, fd_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FdTransfer, addr_family_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FdTransfer, client_addr_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FdTransfer, client_port_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FdTransfer, codec_type_), + }; + FdTransfer_reflection_ = + ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( + FdTransfer_descriptor_, + FdTransfer::default_instance_, + FdTransfer_offsets_, + -1, + -1, + -1, + sizeof(FdTransfer), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FdTransfer, _internal_metadata_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FdTransfer, _is_default_instance_)); } namespace { @@ -179,6 +201,8 @@ void protobuf_RegisterTypes(const ::std::string&) { TraceLog_descriptor_, &TraceLog::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( SpecChannelInfo_descriptor_, &SpecChannelInfo::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + FdTransfer_descriptor_, &FdTransfer::default_instance()); } } // namespace @@ -196,6 +220,8 @@ void protobuf_ShutdownFile_neb_5fsys_2eproto() { delete TraceLog_reflection_; delete SpecChannelInfo::default_instance_; delete SpecChannelInfo_reflection_; + delete FdTransfer::default_instance_; + delete FdTransfer_reflection_; } void protobuf_AddDesc_neb_5fsys_2eproto() GOOGLE_ATTRIBUTE_COLD; @@ -218,7 +244,10 @@ void protobuf_AddDesc_neb_5fsys_2eproto() { "\005 \001(\t\022\026\n\016code_file_line\030\006 \001(\r\022\025\n\rcode_fu" "nction\030\007 \001(\t\022\023\n\013log_content\030\010 \001(\014\"K\n\017Spe" "cChannelInfo\022\022\n\ncodec_type\030\001 \001(\r\022\022\n\nfrom" - "_labor\030\002 \001(\r\022\020\n\010to_labor\030\003 \001(\rb\006proto3", 518); + "_labor\030\002 \001(\r\022\020\n\010to_labor\030\003 \001(\r\"k\n\nFdTran" + "sfer\022\n\n\002fd\030\001 \001(\005\022\023\n\013addr_family\030\002 \001(\005\022\023\n" + "\013client_addr\030\003 \001(\t\022\023\n\013client_port\030\004 \001(\005\022" + "\022\n\ncodec_type\030\005 \001(\005b\006proto3", 627); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "neb_sys.proto", &protobuf_RegisterTypes); ConfigInfo::default_instance_ = new ConfigInfo(); @@ -227,12 +256,14 @@ void protobuf_AddDesc_neb_5fsys_2eproto() { LogLevel::default_instance_ = new LogLevel(); TraceLog::default_instance_ = new TraceLog(); SpecChannelInfo::default_instance_ = new SpecChannelInfo(); + FdTransfer::default_instance_ = new FdTransfer(); ConfigInfo::default_instance_->InitAsDefaultInstance(); WorkerLoad::default_instance_->InitAsDefaultInstance(); TargetWorker::default_instance_->InitAsDefaultInstance(); LogLevel::default_instance_->InitAsDefaultInstance(); TraceLog::default_instance_->InitAsDefaultInstance(); SpecChannelInfo::default_instance_->InitAsDefaultInstance(); + FdTransfer::default_instance_->InitAsDefaultInstance(); ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_neb_5fsys_2eproto); } @@ -3000,6 +3031,520 @@ void SpecChannelInfo::clear_to_labor() { #endif // PROTOBUF_INLINE_NOT_IN_HEADERS +// =================================================================== + +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +const int FdTransfer::kFdFieldNumber; +const int FdTransfer::kAddrFamilyFieldNumber; +const int FdTransfer::kClientAddrFieldNumber; +const int FdTransfer::kClientPortFieldNumber; +const int FdTransfer::kCodecTypeFieldNumber; +#endif // !defined(_MSC_VER) || _MSC_VER >= 1900 + +FdTransfer::FdTransfer() + : ::google::protobuf::Message(), _internal_metadata_(NULL) { + SharedCtor(); + // @@protoc_insertion_point(constructor:neb.FdTransfer) +} + +void FdTransfer::InitAsDefaultInstance() { + _is_default_instance_ = true; +} + +FdTransfer::FdTransfer(const FdTransfer& from) + : ::google::protobuf::Message(), + _internal_metadata_(NULL) { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:neb.FdTransfer) +} + +void FdTransfer::SharedCtor() { + _is_default_instance_ = false; + ::google::protobuf::internal::GetEmptyString(); + _cached_size_ = 0; + fd_ = 0; + addr_family_ = 0; + client_addr_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + client_port_ = 0; + codec_type_ = 0; +} + +FdTransfer::~FdTransfer() { + // @@protoc_insertion_point(destructor:neb.FdTransfer) + SharedDtor(); +} + +void FdTransfer::SharedDtor() { + client_addr_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + if (this != default_instance_) { + } +} + +void FdTransfer::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* FdTransfer::descriptor() { + protobuf_AssignDescriptorsOnce(); + return FdTransfer_descriptor_; +} + +const FdTransfer& FdTransfer::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_neb_5fsys_2eproto(); + return *default_instance_; +} + +FdTransfer* FdTransfer::default_instance_ = NULL; + +FdTransfer* FdTransfer::New(::google::protobuf::Arena* arena) const { + FdTransfer* n = new FdTransfer; + if (arena != NULL) { + arena->Own(n); + } + return n; +} + +void FdTransfer::Clear() { +// @@protoc_insertion_point(message_clear_start:neb.FdTransfer) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(FdTransfer, f) \ + _Pragma("clang diagnostic pop") +#else +#define ZR_HELPER_(f) reinterpret_cast(\ + &reinterpret_cast(16)->f) +#endif + +#define ZR_(first, last) do {\ + ::memset(&first, 0,\ + ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\ +} while (0) + + ZR_(fd_, addr_family_); + ZR_(client_port_, codec_type_); + client_addr_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + +#undef ZR_HELPER_ +#undef ZR_ + +} + +bool FdTransfer::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:neb.FdTransfer) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional int32 fd = 1; + case 1: { + if (tag == 8) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &fd_))); + + } else { + goto handle_unusual; + } + if (input->ExpectTag(16)) goto parse_addr_family; + break; + } + + // optional int32 addr_family = 2; + case 2: { + if (tag == 16) { + parse_addr_family: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &addr_family_))); + + } else { + goto handle_unusual; + } + if (input->ExpectTag(26)) goto parse_client_addr; + break; + } + + // optional string client_addr = 3; + case 3: { + if (tag == 26) { + parse_client_addr: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_client_addr())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->client_addr().data(), this->client_addr().length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "neb.FdTransfer.client_addr")); + } else { + goto handle_unusual; + } + if (input->ExpectTag(32)) goto parse_client_port; + break; + } + + // optional int32 client_port = 4; + case 4: { + if (tag == 32) { + parse_client_port: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &client_port_))); + + } else { + goto handle_unusual; + } + if (input->ExpectTag(40)) goto parse_codec_type; + break; + } + + // optional int32 codec_type = 5; + case 5: { + if (tag == 40) { + parse_codec_type: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &codec_type_))); + + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag)); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:neb.FdTransfer) + return true; +failure: + // @@protoc_insertion_point(parse_failure:neb.FdTransfer) + return false; +#undef DO_ +} + +void FdTransfer::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:neb.FdTransfer) + // optional int32 fd = 1; + if (this->fd() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->fd(), output); + } + + // optional int32 addr_family = 2; + if (this->addr_family() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->addr_family(), output); + } + + // optional string client_addr = 3; + if (this->client_addr().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->client_addr().data(), this->client_addr().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "neb.FdTransfer.client_addr"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 3, this->client_addr(), output); + } + + // optional int32 client_port = 4; + if (this->client_port() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(4, this->client_port(), output); + } + + // optional int32 codec_type = 5; + if (this->codec_type() != 0) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(5, this->codec_type(), output); + } + + // @@protoc_insertion_point(serialize_end:neb.FdTransfer) +} + +::google::protobuf::uint8* FdTransfer::InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:neb.FdTransfer) + // optional int32 fd = 1; + if (this->fd() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(1, this->fd(), target); + } + + // optional int32 addr_family = 2; + if (this->addr_family() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(2, this->addr_family(), target); + } + + // optional string client_addr = 3; + if (this->client_addr().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->client_addr().data(), this->client_addr().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "neb.FdTransfer.client_addr"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 3, this->client_addr(), target); + } + + // optional int32 client_port = 4; + if (this->client_port() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(4, this->client_port(), target); + } + + // optional int32 codec_type = 5; + if (this->codec_type() != 0) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(5, this->codec_type(), target); + } + + // @@protoc_insertion_point(serialize_to_array_end:neb.FdTransfer) + return target; +} + +int FdTransfer::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:neb.FdTransfer) + int total_size = 0; + + // optional int32 fd = 1; + if (this->fd() != 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->fd()); + } + + // optional int32 addr_family = 2; + if (this->addr_family() != 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->addr_family()); + } + + // optional string client_addr = 3; + if (this->client_addr().size() > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->client_addr()); + } + + // optional int32 client_port = 4; + if (this->client_port() != 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->client_port()); + } + + // optional int32 codec_type = 5; + if (this->codec_type() != 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->codec_type()); + } + + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void FdTransfer::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:neb.FdTransfer) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + const FdTransfer* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); + if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:neb.FdTransfer) + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:neb.FdTransfer) + MergeFrom(*source); + } +} + +void FdTransfer::MergeFrom(const FdTransfer& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:neb.FdTransfer) + if (GOOGLE_PREDICT_FALSE(&from == this)) { + ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__); + } + if (from.fd() != 0) { + set_fd(from.fd()); + } + if (from.addr_family() != 0) { + set_addr_family(from.addr_family()); + } + if (from.client_addr().size() > 0) { + + client_addr_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.client_addr_); + } + if (from.client_port() != 0) { + set_client_port(from.client_port()); + } + if (from.codec_type() != 0) { + set_codec_type(from.codec_type()); + } +} + +void FdTransfer::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:neb.FdTransfer) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void FdTransfer::CopyFrom(const FdTransfer& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:neb.FdTransfer) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FdTransfer::IsInitialized() const { + + return true; +} + +void FdTransfer::Swap(FdTransfer* other) { + if (other == this) return; + InternalSwap(other); +} +void FdTransfer::InternalSwap(FdTransfer* other) { + std::swap(fd_, other->fd_); + std::swap(addr_family_, other->addr_family_); + client_addr_.Swap(&other->client_addr_); + std::swap(client_port_, other->client_port_); + std::swap(codec_type_, other->codec_type_); + _internal_metadata_.Swap(&other->_internal_metadata_); + std::swap(_cached_size_, other->_cached_size_); +} + +::google::protobuf::Metadata FdTransfer::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = FdTransfer_descriptor_; + metadata.reflection = FdTransfer_reflection_; + return metadata; +} + +#if PROTOBUF_INLINE_NOT_IN_HEADERS +// FdTransfer + +// optional int32 fd = 1; +void FdTransfer::clear_fd() { + fd_ = 0; +} + ::google::protobuf::int32 FdTransfer::fd() const { + // @@protoc_insertion_point(field_get:neb.FdTransfer.fd) + return fd_; +} + void FdTransfer::set_fd(::google::protobuf::int32 value) { + + fd_ = value; + // @@protoc_insertion_point(field_set:neb.FdTransfer.fd) +} + +// optional int32 addr_family = 2; +void FdTransfer::clear_addr_family() { + addr_family_ = 0; +} + ::google::protobuf::int32 FdTransfer::addr_family() const { + // @@protoc_insertion_point(field_get:neb.FdTransfer.addr_family) + return addr_family_; +} + void FdTransfer::set_addr_family(::google::protobuf::int32 value) { + + addr_family_ = value; + // @@protoc_insertion_point(field_set:neb.FdTransfer.addr_family) +} + +// optional string client_addr = 3; +void FdTransfer::clear_client_addr() { + client_addr_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& FdTransfer::client_addr() const { + // @@protoc_insertion_point(field_get:neb.FdTransfer.client_addr) + return client_addr_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void FdTransfer::set_client_addr(const ::std::string& value) { + + client_addr_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:neb.FdTransfer.client_addr) +} + void FdTransfer::set_client_addr(const char* value) { + + client_addr_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:neb.FdTransfer.client_addr) +} + void FdTransfer::set_client_addr(const char* value, size_t size) { + + client_addr_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:neb.FdTransfer.client_addr) +} + ::std::string* FdTransfer::mutable_client_addr() { + + // @@protoc_insertion_point(field_mutable:neb.FdTransfer.client_addr) + return client_addr_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* FdTransfer::release_client_addr() { + // @@protoc_insertion_point(field_release:neb.FdTransfer.client_addr) + + return client_addr_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void FdTransfer::set_allocated_client_addr(::std::string* client_addr) { + if (client_addr != NULL) { + + } else { + + } + client_addr_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), client_addr); + // @@protoc_insertion_point(field_set_allocated:neb.FdTransfer.client_addr) +} + +// optional int32 client_port = 4; +void FdTransfer::clear_client_port() { + client_port_ = 0; +} + ::google::protobuf::int32 FdTransfer::client_port() const { + // @@protoc_insertion_point(field_get:neb.FdTransfer.client_port) + return client_port_; +} + void FdTransfer::set_client_port(::google::protobuf::int32 value) { + + client_port_ = value; + // @@protoc_insertion_point(field_set:neb.FdTransfer.client_port) +} + +// optional int32 codec_type = 5; +void FdTransfer::clear_codec_type() { + codec_type_ = 0; +} + ::google::protobuf::int32 FdTransfer::codec_type() const { + // @@protoc_insertion_point(field_get:neb.FdTransfer.codec_type) + return codec_type_; +} + void FdTransfer::set_codec_type(::google::protobuf::int32 value) { + + codec_type_ = value; + // @@protoc_insertion_point(field_set:neb.FdTransfer.codec_type) +} + +#endif // PROTOBUF_INLINE_NOT_IN_HEADERS + // @@protoc_insertion_point(namespace_scope) } // namespace neb diff --git a/src/pb/neb_sys.pb.h b/src/pb/neb_sys.pb.h index 15465c33..90621ec0 100644 --- a/src/pb/neb_sys.pb.h +++ b/src/pb/neb_sys.pb.h @@ -37,6 +37,7 @@ void protobuf_AssignDesc_neb_5fsys_2eproto(); void protobuf_ShutdownFile_neb_5fsys_2eproto(); class ConfigInfo; +class FdTransfer; class LogLevel; class SpecChannelInfo; class TargetWorker; @@ -693,6 +694,121 @@ class SpecChannelInfo : public ::google::protobuf::Message /* @@protoc_insertion void InitAsDefaultInstance(); static SpecChannelInfo* default_instance_; }; +// ------------------------------------------------------------------- + +class FdTransfer : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:neb.FdTransfer) */ { + public: + FdTransfer(); + virtual ~FdTransfer(); + + FdTransfer(const FdTransfer& from); + + inline FdTransfer& operator=(const FdTransfer& from) { + CopyFrom(from); + return *this; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const FdTransfer& default_instance(); + + void Swap(FdTransfer* other); + + // implements Message ---------------------------------------------- + + inline FdTransfer* New() const { return New(NULL); } + + FdTransfer* New(::google::protobuf::Arena* arena) const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const FdTransfer& from); + void MergeFrom(const FdTransfer& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const { + return InternalSerializeWithCachedSizesToArray(false, output); + } + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(FdTransfer* other); + private: + inline ::google::protobuf::Arena* GetArenaNoVirtual() const { + return _internal_metadata_.arena(); + } + inline void* MaybeArenaPtr() const { + return _internal_metadata_.raw_arena_ptr(); + } + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional int32 fd = 1; + void clear_fd(); + static const int kFdFieldNumber = 1; + ::google::protobuf::int32 fd() const; + void set_fd(::google::protobuf::int32 value); + + // optional int32 addr_family = 2; + void clear_addr_family(); + static const int kAddrFamilyFieldNumber = 2; + ::google::protobuf::int32 addr_family() const; + void set_addr_family(::google::protobuf::int32 value); + + // optional string client_addr = 3; + void clear_client_addr(); + static const int kClientAddrFieldNumber = 3; + const ::std::string& client_addr() const; + void set_client_addr(const ::std::string& value); + void set_client_addr(const char* value); + void set_client_addr(const char* value, size_t size); + ::std::string* mutable_client_addr(); + ::std::string* release_client_addr(); + void set_allocated_client_addr(::std::string* client_addr); + + // optional int32 client_port = 4; + void clear_client_port(); + static const int kClientPortFieldNumber = 4; + ::google::protobuf::int32 client_port() const; + void set_client_port(::google::protobuf::int32 value); + + // optional int32 codec_type = 5; + void clear_codec_type(); + static const int kCodecTypeFieldNumber = 5; + ::google::protobuf::int32 codec_type() const; + void set_codec_type(::google::protobuf::int32 value); + + // @@protoc_insertion_point(class_scope:neb.FdTransfer) + private: + + ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + bool _is_default_instance_; + ::google::protobuf::int32 fd_; + ::google::protobuf::int32 addr_family_; + ::google::protobuf::internal::ArenaStringPtr client_addr_; + ::google::protobuf::int32 client_port_; + ::google::protobuf::int32 codec_type_; + mutable int _cached_size_; + friend void protobuf_AddDesc_neb_5fsys_2eproto(); + friend void protobuf_AssignDesc_neb_5fsys_2eproto(); + friend void protobuf_ShutdownFile_neb_5fsys_2eproto(); + + void InitAsDefaultInstance(); + static FdTransfer* default_instance_; +}; // =================================================================== @@ -1361,6 +1477,110 @@ inline void SpecChannelInfo::set_to_labor(::google::protobuf::uint32 value) { // @@protoc_insertion_point(field_set:neb.SpecChannelInfo.to_labor) } +// ------------------------------------------------------------------- + +// FdTransfer + +// optional int32 fd = 1; +inline void FdTransfer::clear_fd() { + fd_ = 0; +} +inline ::google::protobuf::int32 FdTransfer::fd() const { + // @@protoc_insertion_point(field_get:neb.FdTransfer.fd) + return fd_; +} +inline void FdTransfer::set_fd(::google::protobuf::int32 value) { + + fd_ = value; + // @@protoc_insertion_point(field_set:neb.FdTransfer.fd) +} + +// optional int32 addr_family = 2; +inline void FdTransfer::clear_addr_family() { + addr_family_ = 0; +} +inline ::google::protobuf::int32 FdTransfer::addr_family() const { + // @@protoc_insertion_point(field_get:neb.FdTransfer.addr_family) + return addr_family_; +} +inline void FdTransfer::set_addr_family(::google::protobuf::int32 value) { + + addr_family_ = value; + // @@protoc_insertion_point(field_set:neb.FdTransfer.addr_family) +} + +// optional string client_addr = 3; +inline void FdTransfer::clear_client_addr() { + client_addr_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& FdTransfer::client_addr() const { + // @@protoc_insertion_point(field_get:neb.FdTransfer.client_addr) + return client_addr_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void FdTransfer::set_client_addr(const ::std::string& value) { + + client_addr_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:neb.FdTransfer.client_addr) +} +inline void FdTransfer::set_client_addr(const char* value) { + + client_addr_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:neb.FdTransfer.client_addr) +} +inline void FdTransfer::set_client_addr(const char* value, size_t size) { + + client_addr_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:neb.FdTransfer.client_addr) +} +inline ::std::string* FdTransfer::mutable_client_addr() { + + // @@protoc_insertion_point(field_mutable:neb.FdTransfer.client_addr) + return client_addr_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* FdTransfer::release_client_addr() { + // @@protoc_insertion_point(field_release:neb.FdTransfer.client_addr) + + return client_addr_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void FdTransfer::set_allocated_client_addr(::std::string* client_addr) { + if (client_addr != NULL) { + + } else { + + } + client_addr_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), client_addr); + // @@protoc_insertion_point(field_set_allocated:neb.FdTransfer.client_addr) +} + +// optional int32 client_port = 4; +inline void FdTransfer::clear_client_port() { + client_port_ = 0; +} +inline ::google::protobuf::int32 FdTransfer::client_port() const { + // @@protoc_insertion_point(field_get:neb.FdTransfer.client_port) + return client_port_; +} +inline void FdTransfer::set_client_port(::google::protobuf::int32 value) { + + client_port_ = value; + // @@protoc_insertion_point(field_set:neb.FdTransfer.client_port) +} + +// optional int32 codec_type = 5; +inline void FdTransfer::clear_codec_type() { + codec_type_ = 0; +} +inline ::google::protobuf::int32 FdTransfer::codec_type() const { + // @@protoc_insertion_point(field_get:neb.FdTransfer.codec_type) + return codec_type_; +} +inline void FdTransfer::set_codec_type(::google::protobuf::int32 value) { + + codec_type_ = value; + // @@protoc_insertion_point(field_set:neb.FdTransfer.codec_type) +} + #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS // ------------------------------------------------------------------- @@ -1372,6 +1592,8 @@ inline void SpecChannelInfo::set_to_labor(::google::protobuf::uint32 value) { // ------------------------------------------------------------------- +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) From 1cbd5683fba9f116a09fa9f17ede15409afe31df Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 23 Jan 2023 20:42:00 +0800 Subject: [PATCH 169/176] socket channel migrate test passing --- src/actor/ActorBuilder.cpp | 2 +- src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp | 24 ++++++++ src/actor/cmd/sys_cmd/CmdFdTransfer.cpp | 2 +- .../manager/CmdOnOrientationFdTransfer.cpp | 1 + .../sys_session/manager/SessionManager.cpp | 4 ++ src/actor/step/sys_step/StepTellWorker.cpp | 1 + .../sys_step/manager/StepReportToBeacon.cpp | 1 + src/channel/SocketChannel.cpp | 9 ++- src/channel/SocketChannel.hpp | 7 +++ src/channel/migrate/SocketChannelMigrate.cpp | 3 +- src/codec/CodecFactory.cpp | 43 ++++++++++++-- src/codec/CodecFactory.hpp | 2 + src/ios/Dispatcher.cpp | 58 ++++++++++++++----- src/ios/Dispatcher.hpp | 3 +- src/ios/IO.hpp | 2 +- 15 files changed, 137 insertions(+), 25 deletions(-) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index 07b019c1..c1de301b 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -179,7 +179,7 @@ bool ActorBuilder::OnChainTimeout(std::shared_ptr pChain) bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody) { - LOG4_DEBUG("cmd %u, seq %u", oMsgHead.cmd(), oMsgHead.seq()); + LOG4_TRACE("cmd %u, seq %u", oMsgHead.cmd(), oMsgHead.seq()); if (gc_uiCmdReq & oMsgHead.cmd()) // 新请求 { MsgHead oOutMsgHead; diff --git a/src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp b/src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp index 74f7a4f7..e6234496 100644 --- a/src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp +++ b/src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp @@ -8,7 +8,10 @@ * Modify history: ******************************************************************************/ #include "CmdChannelMigrate.hpp" +#include "channel/SocketChannel.hpp" #include "ios/Dispatcher.hpp" +#include "ios/ChannelWatcher.hpp" +#include "actor/step/Step.hpp" namespace neb { @@ -27,6 +30,27 @@ bool CmdChannelMigrate::AnyMessage( { auto pMigratedChannel = oPack.UnpackChannel(); GetLabor(this)->GetDispatcher()->AddChannelToLoop(pMigratedChannel); + LOG4_INFO("channel[%d] with codec_type %d migrate done.", + pMigratedChannel->GetFd(), pMigratedChannel->GetCodecType()); + switch (pMigratedChannel->GetCodecType()) + { + case CODEC_NEBULA: + { + auto pStepTellWorker = MakeSharedStep("neb::StepTellWorker", pMigratedChannel); + if (nullptr == pStepTellWorker) + { + return(false); + } + pStepTellWorker->Emit(); + } + break; + case CODEC_NEBULA_IN_NODE: + pMigratedChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + break; + default: + pMigratedChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + } + GetLabor(this)->GetDispatcher()->MigrateChannelRecvAndHandle(pMigratedChannel); return(true); } diff --git a/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp b/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp index d88687d0..bc2fc2ec 100644 --- a/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp +++ b/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp @@ -37,7 +37,7 @@ bool CmdFdTransfer::AnyMessage( LOG4_ERROR("SpecChannelInfo ParseFromString failed."); return(false); } - LOG4_INFO("%s:%d fd[%d] addr_family %d with codec_type %d", + LOG4_INFO("%s:%d fd[%d] transfer, addr_family %d with codec_type %d", oFdTransferInfo.client_addr().c_str(), oFdTransferInfo.client_port(), oFdTransferInfo.fd(), oFdTransferInfo.addr_family(), oFdTransferInfo.codec_type()); diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp index a7eb0209..9a842b5c 100644 --- a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp +++ b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp @@ -52,6 +52,7 @@ bool CmdOnOrientationFdTransfer::AnyMessage( return(false); } uint32 uiManagerLaborId = GetLabor(this)->GetNodeInfo().uiWorkerNum + 1; + LOG4_TRACE("channel[%d] migrate to worker %d", pChannel->GetFd(), iWorkerIndex); bool bResult = GetLabor(this)->GetDispatcher()->MigrateSocketChannel(uiManagerLaborId, iWorkerIndex, pChannel); if (bResult) { diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index f31bbc7b..799b1de6 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -396,6 +396,10 @@ bool SessionManager::CheckWorker() LOG4_TRACE(" "); for (uint32 i = 0; i < m_vecWorkerInfo.size(); ++i) { + if (m_vecWorkerInfo[i] == nullptr) + { + continue; + } if (m_vecWorkerInfo[i]->bStartBeatCheck) { LOG4_TRACE("now %lf, worker_beat_time %lf, worker_beat %d", diff --git a/src/actor/step/sys_step/StepTellWorker.cpp b/src/actor/step/sys_step/StepTellWorker.cpp index bf4ad8ec..69bdb60c 100644 --- a/src/actor/step/sys_step/StepTellWorker.cpp +++ b/src/actor/step/sys_step/StepTellWorker.cpp @@ -33,6 +33,7 @@ E_CMD_STATUS StepTellWorker::Emit( oTargetWorker.set_node_type(GetNodeType()); oOutMsgBody.set_data(oTargetWorker.SerializeAsString()); oOutMsgBody.set_trace_id(GetTraceId()); + LOG4_TRACE("send cmd %d to channel[%d]", CMD_REQ_TELL_WORKER, m_pChannel->GetFd()); IO::SendRequest(this, m_pChannel, CMD_REQ_TELL_WORKER, GetSequence(), oOutMsgBody); return(CMD_STATUS_RUNNING); } diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp index 713ce494..d3a3b5ba 100644 --- a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp +++ b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp @@ -46,6 +46,7 @@ E_CMD_STATUS StepReportToBeacon::Emit( } m_pSessionManager->MakeReportData(oReportData); oMsgBody.set_data(oReportData.ToString()); + LOG4_TRACE("report to beacon, cmd %d", CMD_REQ_NODE_STATUS_REPORT); IO::Broadcast(this, "BEACON", false, true, (int32)CMD_REQ_NODE_STATUS_REPORT, GetSequence(), oMsgBody); return(CMD_STATUS_RUNNING); diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index bb10792a..a54ff13f 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -25,12 +25,12 @@ namespace neb { SocketChannel::SocketChannel() - : m_bIsClient(false), m_bWithSsl(false), m_pImpl(nullptr), m_pLogger(nullptr), m_pWatcher(nullptr) + : m_bIsClient(false), m_bWithSsl(false), m_bMigrated(false), m_pImpl(nullptr), m_pLogger(nullptr), m_pWatcher(nullptr) { } SocketChannel::SocketChannel(std::shared_ptr pLogger, bool bIsClient, bool bWithSsl) - : m_bIsClient(bIsClient), m_bWithSsl(bWithSsl), m_pImpl(nullptr), m_pLogger(pLogger), m_pWatcher(nullptr) + : m_bIsClient(bIsClient), m_bWithSsl(bWithSsl), m_bMigrated(false), m_pImpl(nullptr), m_pLogger(pLogger), m_pWatcher(nullptr) { } @@ -338,6 +338,11 @@ void SocketChannel::SetBonding(Labor* pLabor, std::shared_ptr pLogger m_pLogger = pLogger; } +void SocketChannel::SetMigrated(bool bMigrated) +{ + m_bMigrated = bMigrated; +} + bool SocketChannel::InitImpl(std::shared_ptr pImpl) { if (pImpl == nullptr) diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index 7445fb0f..d07406e1 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -72,6 +72,11 @@ class SocketChannel: public Channel virtual void SetClientData(const std::string& strClientData); virtual void SetIdentify(const std::string& strIdentify); virtual void SetRemoteAddr(const std::string& strRemoteAddr); + + bool IsMigrated() const + { + return(m_bMigrated); + } ChannelWatcher* MutableWatcher(); template @@ -81,11 +86,13 @@ class SocketChannel: public Channel virtual int16 GetRemoteWorkerIndex() const; virtual bool Close(); virtual void SetBonding(Labor* pLabor, std::shared_ptr pLogger, std::shared_ptr pBindChannel); + void SetMigrated(bool bMigrated); bool InitImpl(std::shared_ptr pImpl); private: bool m_bIsClient; bool m_bWithSsl; + bool m_bMigrated; std::string m_strEmpty; // Hide most of the channel implementation for Actors std::shared_ptr m_pImpl; diff --git a/src/channel/migrate/SocketChannelMigrate.cpp b/src/channel/migrate/SocketChannelMigrate.cpp index c7c2064e..661f5e52 100644 --- a/src/channel/migrate/SocketChannelMigrate.cpp +++ b/src/channel/migrate/SocketChannelMigrate.cpp @@ -28,8 +28,9 @@ bool SocketChannelMigrate::MigrateChannel(std::shared_ptr pSocket pNewChannel->m_pImpl = pSocketChannel->m_pImpl; pSocketChannel->m_pImpl = nullptr; pNewChannel->m_pWatcher = pSocketChannel->m_pWatcher; - pNewChannel->m_pWatcher->Set(nullptr); + pNewChannel->m_pWatcher->Reset(); pSocketChannel->m_pWatcher = nullptr; + pSocketChannel->SetMigrated(true); pNewChannel->SetBonding(nullptr, nullptr, nullptr); // remove the current relationship before migration oPack.PackChannel(pNewChannel); return(true); diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp index 19963092..761e5fd3 100644 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -418,6 +418,25 @@ bool CodecFactory::OnSpecChannelCreated(uint32 uiCodecType, uint32 uiFromLabor, break; case CODEC_PRIVATE: break; + case CODEC_CHANNEL_MIGRATE: + { + SocketChannelPack oPack; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->m_pLastActivityChannel = pChannel; + while (pSpecChannel->Read(uiFlags, uiStepSeq, oPack)) + { + if (gc_uiCmdReq & uiFlags) + { + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_CHANNEL_MIGRATE, oPack); + } + else + { + ; // no response + } + } + } + break; case CODEC_UNKNOW: break; default: @@ -503,10 +522,14 @@ bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart) { - E_CODEC_STATUS eCodecStatus; + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; int i = iStart; for (; ; ++i) { + if (pChannel->IsMigrated()) + { + break; + } MsgHead oMsgHead; MsgBody oMsgBody; if (0 == i) @@ -554,10 +577,14 @@ E_CODEC_STATUS CodecFactory::OnNebulaEvent(Dispatcher* pDispatcher, std::shared_ E_CODEC_STATUS CodecFactory::OnHttpEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart) { - E_CODEC_STATUS eCodecStatus; + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; int i = iStart; for (; ; ++i) { + if (pChannel->IsMigrated()) + { + break; + } HttpMsg oHttpMsg; if (0 == i) { @@ -643,10 +670,14 @@ E_CODEC_STATUS CodecFactory::OnHttpEvent(Dispatcher* pDispatcher, std::shared_pt E_CODEC_STATUS CodecFactory::OnRedisEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart) { - E_CODEC_STATUS eCodecStatus; + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; int i = iStart; for (; ; ++i) { + if (pChannel->IsMigrated()) + { + break; + } RedisMsg oRedisMsg; if (0 == i) { @@ -678,10 +709,14 @@ E_CODEC_STATUS CodecFactory::OnRedisEvent(Dispatcher* pDispatcher, std::shared_p E_CODEC_STATUS CodecFactory::OnCassEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel, int iStart) { - E_CODEC_STATUS eCodecStatus; + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; int i = iStart; for (; ; ++i) { + if (pChannel->IsMigrated()) + { + break; + } CassResponse oCassResponse; if (0 == i) { diff --git a/src/codec/CodecFactory.hpp b/src/codec/CodecFactory.hpp index ecaf0853..5b7d4aa5 100644 --- a/src/codec/CodecFactory.hpp +++ b/src/codec/CodecFactory.hpp @@ -82,6 +82,8 @@ class CodecFactory private: static std::vector s_vecAutoSwitchCodec; + + friend class Dispatcher; }; /* diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index 9e1508a8..aa52f812 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -180,19 +180,22 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) LOG4_INFO("NodeFailed(%s)", pChannel->GetIdentify().c_str()); m_pSessionNode->NodeFailed(pChannel->GetIdentify()); } - auto& listUncompletedStep = std::static_pointer_cast>(pChannel->m_pImpl)->GetPipelineStepSeq(); - for (auto it = listUncompletedStep.begin(); - it != listUncompletedStep.end(); ++it) + if (pChannel->m_pImpl != nullptr) { - m_pLabor->GetActorBuilder()->OnError(pChannel, *it, - pChannel->GetErrno(), pChannel->GetErrMsg()); - } - auto& mapUncompletedStep = std::static_pointer_cast>(pChannel->m_pImpl)->GetStreamStepSeq(); - for (auto it = mapUncompletedStep.begin(); - it != mapUncompletedStep.end(); ++it) - { - m_pLabor->GetActorBuilder()->OnError(pChannel, it->second, - pChannel->GetErrno(), pChannel->GetErrMsg()); + auto& listUncompletedStep = std::static_pointer_cast>(pChannel->m_pImpl)->GetPipelineStepSeq(); + for (auto it = listUncompletedStep.begin(); + it != listUncompletedStep.end(); ++it) + { + m_pLabor->GetActorBuilder()->OnError(pChannel, *it, + pChannel->GetErrno(), pChannel->GetErrMsg()); + } + auto& mapUncompletedStep = std::static_pointer_cast>(pChannel->m_pImpl)->GetStreamStepSeq(); + for (auto it = mapUncompletedStep.begin(); + it != mapUncompletedStep.end(); ++it) + { + m_pLabor->GetActorBuilder()->OnError(pChannel, it->second, + pChannel->GetErrno(), pChannel->GetErrMsg()); + } } } LOG4_INFO("eCodecStatus = %d", eCodecStatus); @@ -201,6 +204,29 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) } } +bool Dispatcher::MigrateChannelRecvAndHandle(std::shared_ptr pChannel) +{ + LOG4_TRACE("codec type %d", pChannel->GetCodecType()); + E_CODEC_STATUS eCodecStatus = CodecFactory::OnEvent(this, pChannel, CODEC_STATUS_INVALID); + + switch (eCodecStatus) + { + case CODEC_STATUS_PAUSE: + case CODEC_STATUS_PART_OK: + case CODEC_STATUS_PART_ERR: + return(true); + case CODEC_STATUS_WANT_WRITE: + return(true); + case CODEC_STATUS_WANT_READ: + RemoveIoWriteEvent(pChannel); + return(true); + default: + LOG4_INFO("eCodecStatus = %d", eCodecStatus); + DiscardSocketChannel(pChannel); + return(false); + } +} + bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) { if (CODEC_NEBULA == pChannel->GetCodecType()) // 系统内部Server间通信 @@ -929,13 +955,13 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b return(false); } - auto named_iter = m_mapNamedSocketChannel.find(pChannel->m_pImpl->GetIdentify()); + auto named_iter = m_mapNamedSocketChannel.find(pChannel->GetIdentify()); if (named_iter != m_mapNamedSocketChannel.end()) { for (auto it = named_iter->second.begin(); it != named_iter->second.end(); ++it) { - if ((*it)->m_pImpl->GetSequence() == pChannel->m_pImpl->GetSequence()) + if ((*it)->GetSequence() == pChannel->GetSequence()) { named_iter->second.erase(it); LOG4_TRACE("erase channel %d from m_mapNamedSocketChannel.", pChannel->GetFd()); @@ -1056,6 +1082,8 @@ bool Dispatcher::MigrateSocketChannel(uint32 uiFromLabor, uint32 uiToLabor, std: LOG4_TRACE("erase channel %d channel_seq %u from m_mapSocketChannel.", pChannel->GetFd(), pChannel->GetSequence()); } + LOG4_INFO("migrate channel[%d] with codec_type %d from labor %u to labor %u", + pChannel->GetFd(), pChannel->GetCodecType(), uiFromLabor, uiToLabor); int iResult = SocketChannelMigrate::Write(uiFromLabor, uiToLabor, gc_uiCmdReq, m_pLabor->GetSequence(), pChannel); if (ERR_OK == iResult) { @@ -1064,6 +1092,7 @@ bool Dispatcher::MigrateSocketChannel(uint32 uiFromLabor, uint32 uiToLabor, std: LOG4_WARNING("failed to migrate channel"); // recover from migration failed pChannel->SetBonding(m_pLabor, GetLogger(), pChannel); + pChannel->SetMigrated(false); pChannel->MutableWatcher()->Set(pChannel); m_mapSocketChannel.insert(std::make_pair(pChannel->GetFd(), pChannel)); ev_tstamp dIoTimeout = (m_pLabor->GetNodeInfo().dConnectionProtection > 0) @@ -1305,6 +1334,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily, int iBonding) oMsgBody.set_data(strFdTransferInfo); oMsgHead.set_cmd(CMD_REQ_FD_TRANSFER); uint32 uiManagerLaborId = m_pLabor->GetNodeInfo().uiWorkerNum + 1; + LOG4_INFO("transfer fd %d from labor %u to labor %u", iAcceptFd, uiManagerLaborId, iWorkerId); int iResult = CodecNebulaInNode::Write(uiManagerLaborId, iWorkerId, gc_uiCmdReq, m_pLabor->GetSequence(), oMsgHead, oMsgBody); if (ERR_OK == iResult) { diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 14cfdd2f..ddb7dc22 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -112,11 +112,12 @@ class Dispatcher static void ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); bool OnIoRead(std::shared_ptr pChannel); - bool DataRecvAndHandle(std::shared_ptr pChannel); bool OnIoWrite(std::shared_ptr pChannel); bool OnIoError(std::shared_ptr pChannel); bool OnIoTimeout(std::shared_ptr pChannel); bool OnClientConnFrequencyTimeout(tagClientConnWatcherData* pData, ev_timer* watcher); + bool DataRecvAndHandle(std::shared_ptr pChannel); + bool MigrateChannelRecvAndHandle(std::shared_ptr pChannel); template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index 8faeb34c..7765e1e1 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -1109,7 +1109,7 @@ template template bool IO::OnRequest(ActorBuilder* pBuilder, std::shared_ptr pChannel, int32 iCmd, Targs&&... args) { - auto cmd_iter = pBuilder->m_mapCmd.find(CMD_REQ_REDIS_PROXY); + auto cmd_iter = pBuilder->m_mapCmd.find(iCmd); if (cmd_iter != pBuilder->m_mapCmd.end() && cmd_iter->second != nullptr) { auto pCmd = std::static_pointer_cast(cmd_iter->second); From dde33453afbc2436f9863ebed1bf337ccb605d30 Mon Sep 17 00:00:00 2001 From: Bwar Date: Tue, 24 Jan 2023 20:16:41 +0800 Subject: [PATCH 170/176] modify readme --- README.md | 5 +++++ README_cn.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/README.md b/README.md index 573e8543..4ce74c7b 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,11 @@ A simple testing can be start with a NebulaInterface only, and also can be start ## Change log +#### v2.0.0 + - add spec channel + - use SpecChannel instead of unix socket to transfer file descriptors + - add SocketChannel migration between threads + - remove process mode #### v1.7.3 - error callback caused by fuse node detection bug fixed - redis cluster returns when executing batch write commands and asking bug fixed diff --git a/README_cn.md b/README_cn.md index 229975eb..db062efb 100644 --- a/README_cn.md +++ b/README_cn.md @@ -134,6 +134,11 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 ## 版本历史 +#### v2.0.0 + - 增加线程间无锁通信队列通道SpecChannel + - 使用SpecChannel替代Unix Socket传送文件描述符 + - 增加SocketChannel在线程间迁移功能 + - 移除进程模式,只支持线程模式 #### v1.7.3 - 增加连接队列配置 - 增加dns cache From 2087cdbc520a4b08459aa0fa3716c75473a64668 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 12 Feb 2023 21:48:18 +0800 Subject: [PATCH 171/176] 1. add channel ping 2. add package deliver --- src/actor/Actor.cpp | 43 +- src/actor/Actor.hpp | 9 - src/actor/ActorSender.cpp | 66 +- src/actor/ActorSender.hpp | 3 - src/actor/cmd/sys_cmd/CmdFdTransfer.cpp | 2 + ...oTimeout.cpp => StepNebualChannelPing.cpp} | 14 +- ...oTimeout.hpp => StepNebualChannelPing.hpp} | 18 +- .../step/sys_step/StepRedisChannelPing.cpp | 55 + .../step/sys_step/StepRedisChannelPing.hpp | 46 + src/actor/step/sys_step/StepRedisCluster.cpp | 10 +- .../sys_step/manager/StepReportToBeacon.cpp | 4 +- src/channel/Channel.hpp | 52 + src/channel/SocketChannel.cpp | 8 + src/channel/SocketChannel.hpp | 1 + src/channel/SocketChannelImpl.hpp | 21 +- src/codec/Codec.hpp | 11 +- src/codec/CodecHttp.cpp | 53 + src/codec/CodecHttp.hpp | 57 - src/codec/CodecProto.cpp | 55 + src/codec/CodecProto.hpp | 65 - src/codec/CodecRaw.cpp | 57 + src/codec/CodecRaw.hpp | 61 - src/codec/CodecResp.cpp | 53 + src/codec/CodecResp.hpp | 57 - src/codec/virtual/CodecDeliver.cpp | 77 ++ src/codec/virtual/CodecDeliver.hpp | 42 + src/ios/Dispatcher.cpp | 144 +-- src/ios/Dispatcher.hpp | 14 +- src/ios/IO.hpp | 1109 +++++++++++++---- src/ios/Nodes.cpp | 39 +- src/ios/Nodes.hpp | 10 +- src/type/Package.cpp | 75 ++ src/type/Package.hpp | 109 ++ src/util/StringCoder.cpp | 1 - 34 files changed, 1760 insertions(+), 681 deletions(-) rename src/actor/step/sys_step/{StepIoTimeout.cpp => StepNebualChannelPing.cpp} (69%) rename src/actor/step/sys_step/{StepIoTimeout.hpp => StepNebualChannelPing.hpp} (69%) create mode 100644 src/actor/step/sys_step/StepRedisChannelPing.cpp create mode 100644 src/actor/step/sys_step/StepRedisChannelPing.hpp create mode 100644 src/codec/virtual/CodecDeliver.cpp create mode 100644 src/codec/virtual/CodecDeliver.hpp create mode 100644 src/type/Package.cpp create mode 100644 src/type/Package.hpp diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index c2f6c2c3..116d953a 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -204,41 +204,45 @@ bool Actor::SendTo(std::shared_ptr pChannel, const char* pRawData bool Actor::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { + ChannelOption stOption; + stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(IO::SendTo(this, strIdentify, SOCKET_STREAM, false, true, iCmd, uiSeq, oMsgBody)); + return(IO::SendTo(this, strIdentify, stOption, iCmd, uiSeq, oMsgBody)); } bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq) { - bool bWithSsl = false; - bool bPipeline = false; + ChannelOption stOption; if (oHttpMsg.headers().find("x-trace-id") == oHttpMsg.headers().end()) { (const_cast(oHttpMsg)).mutable_headers()->insert({"x-trace-id", GetTraceId()}); } if (oHttpMsg.http_major() >= 2) { - bPipeline = true; + stOption.bPipeline = true; } std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(":")); std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c)->unsigned char {return std::tolower(c);}); if (strSchema == std::string("https")) { - bWithSsl = true; + stOption.bWithSsl = true; } if (oHttpMsg.http_major() == 2) { - return(IO::SendTo(this, strHost, iPort, SOCKET_STREAM, bWithSsl, bPipeline, oHttpMsg)); + return(IO::SendTo(this, strHost, iPort, stOption, oHttpMsg)); } else { - return(IO::SendTo(this, strHost, iPort, SOCKET_STREAM, bWithSsl, bPipeline, oHttpMsg)); + return(IO::SendTo(this, strHost, iPort, stOption, oHttpMsg)); } } bool Actor::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(IO::SendTo(this, strIdentify, SOCKET_STREAM, bWithSsl, bPipeline, oRedisMsg)); + ChannelOption stOption; + stOption.bPipeline = bPipeline; + stOption.bWithSsl = bWithSsl; + return(IO::SendTo(this, strIdentify, stOption, oRedisMsg)); } bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) @@ -248,29 +252,32 @@ bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedis bool Actor::SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline) { - return(IO::SendRoundRobin(this, strIdentify, bWithSsl, bPipeline, oRedisMsg)); -} - -bool Actor::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) -{ - (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(m_pLabor->GetDispatcher()->SendTo(iCmd, uiSeq, oMsgBody)); + ChannelOption stOption; + stOption.bPipeline = bPipeline; + stOption.bWithSsl = bWithSsl; + return(IO::SendRoundRobin(this, strIdentify, stOption, oRedisMsg)); } bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { + ChannelOption stOption; + stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(IO::SendRoundRobin(this, strNodeType, false, true, iCmd, uiSeq, oMsgBody)); + return(IO::SendRoundRobin(this, strNodeType, stOption, iCmd, uiSeq, oMsgBody)); } bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { + ChannelOption stOption; + stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(GetTraceId()); - return(IO::SendOriented(this, strNodeType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); + return(IO::SendOriented(this, strNodeType, uiFactor, stOption, iCmd, uiSeq, oMsgBody)); } bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { + ChannelOption stOption; + stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(GetTraceId()); if (oMsgBody.has_req_target()) { @@ -280,7 +287,7 @@ bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSe } else if (oMsgBody.req_target().route().length() > 0) { - return(IO::SendOriented(this, strNodeType, false, true, oMsgBody.req_target().route(), iCmd, uiSeq, oMsgBody)); + return(IO::SendOriented(this, strNodeType, oMsgBody.req_target().route(), stOption, iCmd, uiSeq, oMsgBody)); } else { diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp index bedb0808..2cea8266 100644 --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -260,15 +260,6 @@ class Actor: public std::enable_shared_from_this */ virtual bool SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = false); - /** - * @brief 从worker发送到loader或从loader发送到worker - * @param iCmd 发送的命令字 - * @param uiSeq 发送的数据包seq - * @param oMsgBody 数据包体 - * @return 是否发送成功 - */ - virtual bool SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - /** * @brief 发送到下一个同一类型的节点 * @note 发送到下一个同一类型的节点,适用于对同一类型节点做轮询方式发送以达到简单的负载均衡。 diff --git a/src/actor/ActorSender.cpp b/src/actor/ActorSender.cpp index 08fab6f4..ec148f23 100644 --- a/src/actor/ActorSender.cpp +++ b/src/actor/ActorSender.cpp @@ -28,11 +28,6 @@ ActorSender::~ActorSender() { } -void ActorSender::SetAuth(Actor* pActor, const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword) -{ - pActor->m_pLabor->GetDispatcher()->SetAuth(strNodeType, strAuth, strPassword); -} - bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel) { return(IO::Send(pChannel)); @@ -62,41 +57,45 @@ bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { + ChannelOption stOption; + stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); - return(IO::SendTo(pActor, strIdentify, SOCKET_STREAM, false, true, iCmd, uiSeq, oMsgBody)); + return(IO::SendTo(pActor, strIdentify, stOption, iCmd, uiSeq, oMsgBody)); } bool ActorSender::SendTo(Actor* pActor, const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq) { - bool bWithSsl = false; - bool bPipeline = false; + ChannelOption stOption; if (oHttpMsg.headers().find("x-trace-id") == oHttpMsg.headers().end()) { (const_cast(oHttpMsg)).mutable_headers()->insert({"x-trace-id", pActor->GetTraceId()}); } if (oHttpMsg.http_major() >= 2) { - bPipeline = true; + stOption.bPipeline = true; } std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(":")); std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c)->unsigned char {return std::tolower(c);}); if (strSchema == std::string("https")) { - bWithSsl = true; + stOption.bWithSsl = true; } if (oHttpMsg.http_major() == 2) { - return(IO::SendTo(pActor, strHost, iPort, bWithSsl, bPipeline, oHttpMsg)); + return(IO::SendTo(pActor, strHost, iPort, stOption, oHttpMsg)); } else { - return(IO::SendTo(pActor, strHost, iPort, bWithSsl, bPipeline, oHttpMsg)); + return(IO::SendTo(pActor, strHost, iPort, stOption, oHttpMsg)); } } bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(IO::SendTo(pActor, strIdentify, SOCKET_STREAM, bWithSsl, bPipeline, oRedisMsg)); + ChannelOption stOption; + stOption.bPipeline = bPipeline; + stOption.bPipeline = bWithSsl; + return(IO::SendTo(pActor, strIdentify, SOCKET_STREAM, stOption, oRedisMsg)); } bool ActorSender::SendToCluster(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) @@ -106,45 +105,51 @@ bool ActorSender::SendToCluster(Actor* pActor, const std::string& strIdentify, c bool ActorSender::SendRoundRobin(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline) { - return(IO::SendRoundRobin(pActor, strIdentify, bWithSsl, bPipeline, oRedisMsg)); + ChannelOption stOption; + stOption.bPipeline = bPipeline; + stOption.bPipeline = bWithSsl; + return(IO::SendRoundRobin(pActor, strIdentify, stOption, oRedisMsg)); } bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, const char* pRawData, uint32 uiRawDataSize, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { - return(IO::SendTo(pActor, strIdentify, SOCKET_STREAM, bWithSsl, bPipeline, pRawData, uiRawDataSize)); -} - -bool ActorSender::SendTo(Actor* pActor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) -{ - (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); - return(pActor->m_pLabor->GetDispatcher()->SendTo(iCmd, uiSeq, oMsgBody)); + ChannelOption stOption; + stOption.bPipeline = bPipeline; + stOption.bPipeline = bWithSsl; + return(IO::SendTo(pActor, strIdentify, stOption, pRawData, uiRawDataSize)); } bool ActorSender::SendRoundRobin(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { + ChannelOption stOption; + stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); - return(IO::SendRoundRobin(pActor, strNodeType, false, true, iCmd, uiSeq, oMsgBody)); + return(IO::SendRoundRobin(pActor, strNodeType, stOption, iCmd, uiSeq, oMsgBody)); } bool ActorSender::SendOriented(Actor* pActor, const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { + ChannelOption stOption; + stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); if (eCodecType == CODEC_NEBULA) { - return(IO::SendOriented(pActor, strNodeType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); + return(IO::SendOriented(pActor, strNodeType, uiFactor, stOption, iCmd, uiSeq, oMsgBody)); } else if (eCodecType == CODEC_NEBULA_IN_NODE) { - return(IO::SendOriented(pActor, strNodeType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); + return(IO::SendOriented(pActor, strNodeType, uiFactor, stOption, iCmd, uiSeq, oMsgBody)); } else { - return(IO::SendOriented(pActor, strNodeType, false, true, uiFactor, iCmd, uiSeq, oMsgBody)); + return(IO::SendOriented(pActor, strNodeType, uiFactor, stOption, iCmd, uiSeq, oMsgBody)); } } bool ActorSender::SendOriented(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { + ChannelOption stOption; + stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(pActor->GetTraceId()); if (oMsgBody.has_req_target()) { @@ -156,15 +161,15 @@ bool ActorSender::SendOriented(Actor* pActor, const std::string& strNodeType, in { if (eCodecType == CODEC_NEBULA) { - return(IO::SendOriented(pActor, strNodeType, false, true, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody)); + return(IO::SendOriented(pActor, strNodeType, oMsgBody.req_target().route_id(), stOption, iCmd, uiSeq, oMsgBody)); } else if (eCodecType == CODEC_NEBULA_IN_NODE) { - return(IO::SendOriented(pActor, strNodeType, false, true, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody)); + return(IO::SendOriented(pActor, strNodeType, oMsgBody.req_target().route_id(), stOption, iCmd, uiSeq, oMsgBody)); } else { - return(IO::SendOriented(pActor, strNodeType, false, true, oMsgBody.req_target().route_id(), iCmd, uiSeq, oMsgBody)); + return(IO::SendOriented(pActor, strNodeType, oMsgBody.req_target().route_id(), stOption, iCmd, uiSeq, oMsgBody)); } } else @@ -332,7 +337,10 @@ bool ActorSender::SendTo(Actor* pActor, const std::string& strUrl, const std::st bool ActorSender::SendRoundRobin(Actor* pActor, const std::string& strIdentify, const CassMessage& oCassMsg, bool bWithSsl, bool bPipeline) { - return(IO::SendRoundRobin(pActor, strIdentify, bWithSsl, bPipeline, oCassMsg)); + ChannelOption stOption; + stOption.bPipeline = bPipeline; + stOption.bWithSsl = bWithSsl; + return(IO::SendRoundRobin(pActor, strIdentify, stOption, oCassMsg)); } } /* namespace neb */ diff --git a/src/actor/ActorSender.hpp b/src/actor/ActorSender.hpp index f8f0bbe5..f7967ce0 100644 --- a/src/actor/ActorSender.hpp +++ b/src/actor/ActorSender.hpp @@ -40,14 +40,11 @@ class ActorSender ActorSender(); virtual ~ActorSender(); - static void SetAuth(Actor* pActor, const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword); - static bool SendTo(Actor* pActor, std::shared_ptr pChannel); // send pb message static bool SendTo(Actor* pActor, std::shared_ptr pChannel, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); static bool SendTo(Actor* pActor, const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); - static bool SendTo(Actor* pActor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); static bool SendRoundRobin(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); static bool SendOriented(Actor* pActor, const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); static bool SendOriented(Actor* pActor, const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); diff --git a/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp b/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp index bc2fc2ec..57590a41 100644 --- a/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp +++ b/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp @@ -59,6 +59,7 @@ bool CmdFdTransfer::AnyMessage( if (CODEC_NEBULA == oFdTransferInfo.codec_type()) { GetLabor(this)->GetDispatcher()->AddIoTimeout(pNewChannel, GetNodeInfo().dIoTimeout); + pNewChannel->SetKeepAlive(GetNodeInfo().dIoTimeout); std::shared_ptr pStepTellWorker = GetLabor(this)->GetActorBuilder()->MakeSharedStep(nullptr, "neb::StepTellWorker", pNewChannel); if (nullptr == pStepTellWorker) @@ -73,6 +74,7 @@ bool CmdFdTransfer::AnyMessage( ev_tstamp dIoTimeout = (GetNodeInfo().dConnectionProtection > 0) ? GetNodeInfo().dConnectionProtection : GetNodeInfo().dIoTimeout; GetLabor(this)->GetDispatcher()->AddIoTimeout(pNewChannel, dIoTimeout); + pNewChannel->SetKeepAlive(dIoTimeout); } GetLabor(this)->IoStatAddConnection(IO_STAT_DOWNSTREAM_NEW_CONNECTION); return(true); diff --git a/src/actor/step/sys_step/StepIoTimeout.cpp b/src/actor/step/sys_step/StepNebualChannelPing.cpp similarity index 69% rename from src/actor/step/sys_step/StepIoTimeout.cpp rename to src/actor/step/sys_step/StepNebualChannelPing.cpp index c83cad54..d725591e 100644 --- a/src/actor/step/sys_step/StepIoTimeout.cpp +++ b/src/actor/step/sys_step/StepNebualChannelPing.cpp @@ -1,28 +1,28 @@ /******************************************************************************* * Project: Nebula - * @file StepIoTimeout2.cpp + * @file StepNebualChannelPing.cpp * @brief * @author Bwar * @date: 2016年8月30日 * @note * Modify history: ******************************************************************************/ -#include "actor/step/sys_step/StepIoTimeout.hpp" +#include "actor/step/sys_step/StepNebualChannelPing.hpp" #include "ios/Dispatcher.hpp" namespace neb { -StepIoTimeout::StepIoTimeout(std::shared_ptr pChannel) +StepNebualChannelPing::StepNebualChannelPing(std::shared_ptr pChannel) : m_pChannel(pChannel) { } -StepIoTimeout::~StepIoTimeout() +StepNebualChannelPing::~StepNebualChannelPing() { } -E_CMD_STATUS StepIoTimeout::Emit(int iErrno, const std::string& strErrMsg, +E_CMD_STATUS StepNebualChannelPing::Emit(int iErrno, const std::string& strErrMsg, void* data) { MsgBody oOutMsgBody; @@ -37,14 +37,14 @@ E_CMD_STATUS StepIoTimeout::Emit(int iErrno, const std::string& strErrMsg, } } -E_CMD_STATUS StepIoTimeout::Callback(std::shared_ptr pChannel, +E_CMD_STATUS StepNebualChannelPing::Callback(std::shared_ptr pChannel, const MsgHead& oInMsgHead, const MsgBody& oInMsgBody, void* data) { GetLabor(this)->GetDispatcher()->AddIoTimeout(pChannel); return(CMD_STATUS_COMPLETED); } -E_CMD_STATUS StepIoTimeout::Timeout() +E_CMD_STATUS StepNebualChannelPing::Timeout() { GetLabor(this)->GetDispatcher()->Disconnect(m_pChannel); return(CMD_STATUS_FAULT); diff --git a/src/actor/step/sys_step/StepIoTimeout.hpp b/src/actor/step/sys_step/StepNebualChannelPing.hpp similarity index 69% rename from src/actor/step/sys_step/StepIoTimeout.hpp rename to src/actor/step/sys_step/StepNebualChannelPing.hpp index dd66dfae..0969940b 100644 --- a/src/actor/step/sys_step/StepIoTimeout.hpp +++ b/src/actor/step/sys_step/StepNebualChannelPing.hpp @@ -1,14 +1,14 @@ /******************************************************************************* * Project: Nebula - * @file StepIoTimeout2.hpp + * @file StepNebualChannelPing.hpp * @brief * @author Bwar * @date: 2016年8月30日 * @note * Modify history: ******************************************************************************/ -#ifndef SRC_ACTOR_STEP_SYS_STEP_STEPIOTIMEOUT_HPP_ -#define SRC_ACTOR_STEP_SYS_STEP_STEPIOTIMEOUT_HPP_ +#ifndef SRC_ACTOR_STEP_SYS_STEP_STEPNEBULACHANNELPING_HPP_ +#define SRC_ACTOR_STEP_SYS_STEP_STEPNEBULACHANNELPING_HPP_ #include "actor/ActorSys.hpp" #include "actor/step/PbStep.hpp" @@ -20,17 +20,17 @@ namespace neb /** * @brief IO超时回调步骤 - * @note 当发生正常连接(连接成功后曾经发送过合法数据包)IO超时事件时,创建一个StepIoTimeout, + * @note 当发生正常连接(连接成功后曾经发送过合法数据包)IO超时事件时,创建一个StepNebualChannelPing, * 若该步骤正常回调,则重置连接超时,若该步骤超时,则关闭连接,销毁连接资源。该步骤实现的是服务 * 端发起的心跳机制,心跳时间间隔就是IO超时时间。 */ -class StepIoTimeout: public PbStep, - public DynamicCreator& >, +class StepNebualChannelPing: public PbStep, + public DynamicCreator& >, public ActorSys { public: - StepIoTimeout(std::shared_ptr pChannel); - virtual ~StepIoTimeout(); + StepNebualChannelPing(std::shared_ptr pChannel); + virtual ~StepNebualChannelPing(); virtual E_CMD_STATUS Emit( int iErrno = 0, @@ -51,4 +51,4 @@ class StepIoTimeout: public PbStep, } /* namespace neb */ -#endif /* SRC_ACTOR_STEP_SYS_STEP_STEPIOTIMEOUT_HPP_ */ +#endif /* SRC_ACTOR_STEP_SYS_STEP_STEPNEBULACHANNELPING_HPP_ */ diff --git a/src/actor/step/sys_step/StepRedisChannelPing.cpp b/src/actor/step/sys_step/StepRedisChannelPing.cpp new file mode 100644 index 00000000..bb0cb69b --- /dev/null +++ b/src/actor/step/sys_step/StepRedisChannelPing.cpp @@ -0,0 +1,55 @@ +/******************************************************************************* + * Project: Nebula + * @file StepRedisChannelPing.cpp + * @brief + * @author Bwar + * @date: 2023-01-24 + * @note + * Modify history: + ******************************************************************************/ +#include "StepRedisChannelPing.hpp" +#include "ios/IO.hpp" + +namespace neb +{ + +StepRedisChannelPing::StepRedisChannelPing(std::shared_ptr pChannel) + : m_pChannel(pChannel) +{ +} + +StepRedisChannelPing::~StepRedisChannelPing() +{ +} + +E_CMD_STATUS StepRedisChannelPing::Emit(int iErrno, const std::string& strErrMsg, + void* data) +{ + SetCmd("PING"); + LOG4_TRACE("send 'PING' to channel[%d] %s", m_pChannel->GetFd(), m_pChannel->GetIdentify().c_str()); + if (IO::SendRequest(this, m_pChannel, GenerateRedisRequest())) + { + return(CMD_STATUS_RUNNING); + } + else // SendTo错误会触发断开连接和回收资源 + { + return(CMD_STATUS_FAULT); + } +} + +neb::E_CMD_STATUS StepRedisChannelPing::Callback( + std::shared_ptr pChannel, + const neb::RedisReply& oRedisReply) +{ + GetLabor(this)->GetDispatcher()->AddIoTimeout(pChannel, pChannel->GetKeepAlive()); + return(neb::CMD_STATUS_COMPLETED); +} + +E_CMD_STATUS StepRedisChannelPing::Timeout() +{ + GetLabor(this)->GetDispatcher()->Disconnect(m_pChannel); + return(CMD_STATUS_FAULT); +} + +} /* namespace neb */ + diff --git a/src/actor/step/sys_step/StepRedisChannelPing.hpp b/src/actor/step/sys_step/StepRedisChannelPing.hpp new file mode 100644 index 00000000..213095ea --- /dev/null +++ b/src/actor/step/sys_step/StepRedisChannelPing.hpp @@ -0,0 +1,46 @@ +/******************************************************************************* + * Project: Nebula + * @file StepRedisChannelPing.hpp + * @brief + * @author Bwar + * @date: 2023-01-24 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_STEP_SYS_STEP_STEPREDISCHANNELBEAT_HPP_ +#define SRC_ACTOR_STEP_SYS_STEP_STEPREDISCHANNELBEAT_HPP_ + +#include "actor/step/RedisStep.hpp" +#include "actor/ActorSys.hpp" + +namespace neb +{ + +class StepRedisChannelPing: public RedisStep, + public DynamicCreator& >, + public ActorSys +{ +public: + StepRedisChannelPing(std::shared_ptr pChannel); + virtual ~StepRedisChannelPing(); + + virtual E_CMD_STATUS Emit( + int iErrno = 0, + const std::string& strErrMsg = "", + void* data = NULL); + + + virtual E_CMD_STATUS Callback( + std::shared_ptr pChannel, + const RedisReply& oRedisReply); + + virtual E_CMD_STATUS Timeout(); + +private: + std::shared_ptr m_pChannel; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_STEP_SYS_STEP_STEPREDISCHANNELBEAT_HPP_ */ + diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp index c1fda1df..f46a3aa6 100644 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -472,6 +472,11 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, const RedisMsg& oR } else { + ChannelOption stOption; + stOption.bWithSsl = bWithSsl; + stOption.bPipeline = bPipeline; + stOption.dKeepAlive = GetTimeout(); + GetLabor(this)->GetDispatcher()->SetChannelOption(strIdentify, stOption); m_vecWaittingRequest.push_back(std::make_pair(uiStepSeq, oRedisMsg)); return(SendCmdClusterSlots()); } @@ -479,8 +484,7 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, const RedisMsg& oR bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptr pRedisMsg) { - bool bResult = IO::SendTo(this, strIdentify, - SOCKET_STREAM, m_bWithSsl, m_bPipeline, (*pRedisMsg.get())); + bool bResult = IO::SendWithoutOption(this, strIdentify, (*pRedisMsg.get())); if (bResult) { auto pChannel = GetLastActivityChannel(); @@ -1020,7 +1024,7 @@ bool StepRedisCluster::Auth(const std::string& strIdentify, std::shared_ptrGetDispatcher()->GetAuth(strIdentify, strAuth, strPassword); + GetLabor(this)->GetDispatcher()->GetAuth(m_strIdentify, strAuth, strPassword); SetCmd("AUTH"); Append(strPassword); auto pRedisRequest = MutableRedisRequest(); diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp index d3a3b5ba..8310e7eb 100644 --- a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp +++ b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp @@ -47,7 +47,9 @@ E_CMD_STATUS StepReportToBeacon::Emit( m_pSessionManager->MakeReportData(oReportData); oMsgBody.set_data(oReportData.ToString()); LOG4_TRACE("report to beacon, cmd %d", CMD_REQ_NODE_STATUS_REPORT); - IO::Broadcast(this, "BEACON", false, true, + ChannelOption stOption; + stOption.bPipeline = true; + IO::Broadcast(this, "BEACON", stOption, (int32)CMD_REQ_NODE_STATUS_REPORT, GetSequence(), oMsgBody); return(CMD_STATUS_RUNNING); } diff --git a/src/channel/Channel.hpp b/src/channel/Channel.hpp index 756192f4..a8191908 100644 --- a/src/channel/Channel.hpp +++ b/src/channel/Channel.hpp @@ -10,9 +10,61 @@ #ifndef SRC_CHANNEL_CHANNEL_HPP_ #define SRC_CHANNEL_CHANNEL_HPP_ +#include "Definition.hpp" + namespace neb { +struct ChannelOption +{ + bool bPipeline = false; + bool bWithSsl = false; + int iSocketType = SOCKET_STREAM; + ev_tstamp dKeepAlive = 7.0; + std::string strAuth; + std::string strPassword; + + ChannelOption(){} + ChannelOption(const ChannelOption& stOption) + { + bPipeline = stOption.bPipeline; + bWithSsl = stOption.bWithSsl; + iSocketType = stOption.iSocketType; + dKeepAlive = stOption.dKeepAlive; + strAuth = stOption.strAuth; + strPassword = stOption.strPassword; + } + ChannelOption(ChannelOption&& stOption) + { + bPipeline = stOption.bPipeline; + bWithSsl = stOption.bWithSsl; + iSocketType = stOption.iSocketType; + dKeepAlive = stOption.dKeepAlive; + strAuth = std::move(stOption.strAuth); + strPassword = std::move(stOption.strPassword); + } + ChannelOption& operator=(const ChannelOption& stOption) + { + bPipeline = stOption.bPipeline; + bWithSsl = stOption.bWithSsl; + iSocketType = stOption.iSocketType; + dKeepAlive = stOption.dKeepAlive; + strAuth = stOption.strAuth; + strPassword = stOption.strPassword; + return(*this); + } + ChannelOption& operator=(ChannelOption&& stOption) + { + bPipeline = stOption.bPipeline; + bWithSsl = stOption.bWithSsl; + iSocketType = stOption.iSocketType; + dKeepAlive = stOption.dKeepAlive; + strAuth = std::move(stOption.strAuth); + strPassword = std::move(stOption.strPassword); + return(*this); + } +}; + class Channel { public: diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp index a54ff13f..f25b0e4d 100644 --- a/src/channel/SocketChannel.cpp +++ b/src/channel/SocketChannel.cpp @@ -319,6 +319,14 @@ void SocketChannel::SetRemoteAddr(const std::string& strRemoteAddr) return(m_pImpl->SetRemoteAddr(strRemoteAddr)); } +void SocketChannel::SetKeepAlive(ev_tstamp dTime) +{ + if (m_pImpl != nullptr) + { + m_pImpl->SetKeepAlive(dTime); + } +} + bool SocketChannel::Close() { if (m_pImpl == nullptr) diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp index d07406e1..33f18e7f 100644 --- a/src/channel/SocketChannel.hpp +++ b/src/channel/SocketChannel.hpp @@ -72,6 +72,7 @@ class SocketChannel: public Channel virtual void SetClientData(const std::string& strClientData); virtual void SetIdentify(const std::string& strIdentify); virtual void SetRemoteAddr(const std::string& strRemoteAddr); + virtual void SetKeepAlive(ev_tstamp dTime); bool IsMigrated() const { diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 6e445c54..12bd63e4 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -190,7 +190,7 @@ class SocketChannelImpl: public SocketChannel } */ - void SetKeepAlive(ev_tstamp dTime) + virtual void SetKeepAlive(ev_tstamp dTime) { m_dKeepAlive = dTime; } @@ -377,8 +377,7 @@ template bool SocketChannelImpl::NeedAliveCheck() const { if (CODEC_HTTP == m_pCodec->GetCodecType() - || CODEC_NEBULA == m_pCodec->GetCodecType() - || CODEC_NEBULA_IN_NODE == m_pCodec->GetCodecType()) + || CODEC_HTTP2 == m_pCodec->GetCodecType()) { return(false); } @@ -451,7 +450,7 @@ E_CODEC_STATUS SocketChannelImpl::Send() } } - if (m_uiMsgNum < 1) + if (m_uiMsgNum < 1 && !IsClient()) { m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; } @@ -836,7 +835,10 @@ E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) ++m_uiMsgNum; if (m_uiMsgNum == 1) { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + if (!IsClient()) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + } m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; } } @@ -851,8 +853,8 @@ E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) if (!IsClient()) { m_pSendBuff->Clear(); + return(CODEC_STATUS_INVALID); } - return(CODEC_STATUS_INVALID); } } return(eCodecStatus); @@ -893,7 +895,10 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(Targs&&... args) ++m_uiMsgNum; if (m_uiMsgNum == 1) { - m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + if (!IsClient()) + { + m_dKeepAlive = m_pLabor->GetNodeInfo().dIoTimeout; + } m_ucChannelStatus = CHANNEL_STATUS_ESTABLISHED; } } @@ -903,7 +908,7 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(Targs&&... args) { m_pRecvBuff->SetReadIndex(uiReadIndex); } - if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus) + if (0 == m_uiMsgNum && CODEC_STATUS_PAUSE != eCodecStatus && !IsClient()) { return(CODEC_STATUS_INVALID); } diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 10bd14ae..58581d1c 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -45,11 +45,12 @@ enum E_CODEC_TYPE CODEC_NEBULA_IN_NODE = 8, ///< 节点各进程间通信协议,与CODEC_NEBULA协议相同,使用的编解码类也相同,只为区别节点内部连接与外部连接 CODEC_RESP = 9, ///< redis数据传输协议resp CODEC_HTTP2 = 10, ///< http2编解码 - CODEC_DIRECT = 11, ///< 虚拟编解码类型,用于SelfChannel,以参数方式直接传递数据包 - CODEC_CASS = 12, ///< cassandra scylladb - CODEC_RAW = 13, ///< 裸数据传输 - CODEC_TRANSFER = 14, ///< 虚拟编解码类型,用于SpecChannel - CODEC_CHANNEL_MIGRATE = 15, ///< 虚拟编解码类型,用于SocketChannel迁移 + CODEC_CASS = 11, ///< cassandra scylladb + CODEC_RAW = 12, ///< 裸数据传输 + + CODEC_DIRECT = 51, ///< 虚拟编解码类型,用于SelfChannel,以参数方式直接传递数据包 + CODEC_TRANSFER = 52, ///< 虚拟编解码类型,用于SpecChannel + CODEC_CHANNEL_MIGRATE = 53, ///< 虚拟编解码类型,用于SocketChannel迁移 CODEC_MAX }; diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 22ce28ec..0d20a79b 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -91,6 +91,59 @@ CodecHttp::~CodecHttp() { } +// request +int CodecHttp::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg) +{ + if (uiFromLabor == uiToLabor) + { + return(ERR_SPEC_CHANNEL_TARGET); + } + std::shared_ptr> pSpecChannel = nullptr; + auto pLaborShared = LaborShared::Instance(); + auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); + if (pChannel == nullptr) + { + pSpecChannel = std::make_shared>( + uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CREATE); + } + pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, Type()); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oHttpMsg))); + if (iResult == ERR_OK) + { + return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); + } + return(iResult); + } + else + { + pSpecChannel = std::static_pointer_cast>(pChannel); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CAST); + } + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oHttpMsg))); + if (iResult == ERR_OK) + { + pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + return(iResult); + } +} + +// response +int CodecHttp::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg) +{ + uint32 uiFrom; + uint32 uiTo; + std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); + return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, oHttpMsg)); +} + E_CODEC_STATUS CodecHttp::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); diff --git a/src/codec/CodecHttp.hpp b/src/codec/CodecHttp.hpp index 6245bbb0..e5c459bf 100644 --- a/src/codec/CodecHttp.hpp +++ b/src/codec/CodecHttp.hpp @@ -32,11 +32,9 @@ class CodecHttp: public Codec } // request - template static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg); // response - template static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg); E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); @@ -90,61 +88,6 @@ class CodecHttp: public Codec std::unordered_map m_mapAddingHttpHeader; ///< encode前添加的http头,encode之后要清空 }; -// request -template -int CodecHttp::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg) -{ - if (uiFromLabor == uiToLabor) - { - return(ERR_SPEC_CHANNEL_TARGET); - } - std::shared_ptr> pSpecChannel = nullptr; - auto pLaborShared = LaborShared::Instance(); - auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); - if (pChannel == nullptr) - { - pSpecChannel = std::make_shared>( - uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); - if (pSpecChannel == nullptr) - { - return(ERR_SPEC_CHANNEL_CREATE); - } - pChannel = std::dynamic_pointer_cast(pSpecChannel); - auto pWatcher = pSpecChannel->MutableWatcher(); - pWatcher->Set(pChannel, Type()); - int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oHttpMsg))); - if (iResult == ERR_OK) - { - return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); - } - return(iResult); - } - else - { - pSpecChannel = std::static_pointer_cast>(pChannel); - if (pSpecChannel == nullptr) - { - return(ERR_SPEC_CHANNEL_CAST); - } - int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oHttpMsg))); - if (iResult == ERR_OK) - { - pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); - } - return(iResult); - } -} - -// response -template -int CodecHttp::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg) -{ - uint32 uiFrom; - uint32 uiTo; - std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); - return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, oHttpMsg)); -} - } /* namespace neb */ #endif /* SRC_CODEC_CODECHTTP_HPP_ */ diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp index 2c58b729..20c94490 100644 --- a/src/codec/CodecProto.cpp +++ b/src/codec/CodecProto.cpp @@ -26,6 +26,61 @@ CodecProto::~CodecProto() { } +int CodecProto::Write(uint32 uiCodecType, uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, MsgHead&& oMsgHead, MsgBody&& oMsgBody) +{ + if (uiFromLabor == uiToLabor) + { + return(ERR_SPEC_CHANNEL_TARGET); + } + std::shared_ptr> pSpecChannel = nullptr; + auto pLaborShared = LaborShared::Instance(); + auto pChannel = pLaborShared->GetSpecChannel(uiCodecType, uiFromLabor, uiToLabor); + if (pChannel == nullptr) + { + pSpecChannel = std::make_shared>( + uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CREATE); + } + pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, uiCodecType); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::forward(oMsgHead), std::forward(oMsgBody)); + if (iResult == ERR_OK) + { + return(pLaborShared->AddSpecChannel(uiCodecType, uiFromLabor, uiToLabor, pChannel)); + } + return(iResult); + } + else + { + pSpecChannel = std::static_pointer_cast>(pChannel); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CAST); + } + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::forward(oMsgHead), std::forward(oMsgBody)); + if (iResult == ERR_OK) + { + pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + return(iResult); + } +} + +int CodecProto::Write(uint32 uiCodecType, std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, int32 iCmd, uint32 uiSeq, MsgBody&& oMsgBody) +{ + uint32 uiFrom; + uint32 uiTo; + MsgHead oMsgHead; + oMsgHead.set_cmd(iCmd); + oMsgHead.set_seq(uiSeq); + std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); + return(Write(uiCodecType, uiTo, uiFrom, uiFlags, uiStepSeq, std::forward(oMsgHead), std::forward(oMsgBody))); +} + + E_CODEC_STATUS CodecProto::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); diff --git a/src/codec/CodecProto.hpp b/src/codec/CodecProto.hpp index b3f37726..62ba5305 100644 --- a/src/codec/CodecProto.hpp +++ b/src/codec/CodecProto.hpp @@ -31,7 +31,6 @@ class CodecProto: public Codec } // request - template static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const MsgHead& oMsgHead, const MsgBody& oMsgBody) { return(CodecProto::Write(Type(), uiFromLabor, uiToLabor, uiFlags, uiStepSeq, @@ -39,7 +38,6 @@ class CodecProto: public Codec } // response - template static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { return(CodecProto::Write(Type(), pChannel, uiFlags, uiStepSeq, iCmd, uiSeq, std::move(const_cast(oMsgBody)))); @@ -55,10 +53,8 @@ class CodecProto: public Codec E_CODEC_STATUS ChannelSticky(const MsgHead& oMsgHead, const MsgBody& oMsgBody); // request - template static int Write(uint32 uiCodecType, uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, MsgHead&& oMsgHead, MsgBody&& oMsgBody); // response - template static int Write(uint32 uiCodecType, std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, int32 iCmd, uint32 uiSeq, MsgBody&& oMsgBody); private: @@ -83,7 +79,6 @@ class CodecNebula: public CodecProto } // request - template static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const MsgHead& oMsgHead, const MsgBody& oMsgBody) { return(CodecProto::Write(Type(), uiFromLabor, uiToLabor, uiFlags, uiStepSeq, @@ -91,7 +86,6 @@ class CodecNebula: public CodecProto } // response - template static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { return(CodecProto::Write(Type(), pChannel, uiFlags, uiStepSeq, iCmd, uiSeq, std::move(const_cast(oMsgBody)))); @@ -116,7 +110,6 @@ class CodecNebulaInNode: public CodecProto } // request - template static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const MsgHead& oMsgHead, const MsgBody& oMsgBody) { return(CodecProto::Write(Type(), uiFromLabor, uiToLabor, uiFlags, uiStepSeq, @@ -124,70 +117,12 @@ class CodecNebulaInNode: public CodecProto } // response - template static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { return(CodecProto::Write(Type(), pChannel, uiFlags, uiStepSeq, iCmd, uiSeq, std::move(const_cast(oMsgBody)))); } }; -template -int CodecProto::Write(uint32 uiCodecType, uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, MsgHead&& oMsgHead, MsgBody&& oMsgBody) -{ - if (uiFromLabor == uiToLabor) - { - return(ERR_SPEC_CHANNEL_TARGET); - } - std::shared_ptr> pSpecChannel = nullptr; - auto pLaborShared = LaborShared::Instance(); - auto pChannel = pLaborShared->GetSpecChannel(uiCodecType, uiFromLabor, uiToLabor); - if (pChannel == nullptr) - { - pSpecChannel = std::make_shared>( - uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); - if (pSpecChannel == nullptr) - { - return(ERR_SPEC_CHANNEL_CREATE); - } - pChannel = std::dynamic_pointer_cast(pSpecChannel); - auto pWatcher = pSpecChannel->MutableWatcher(); - pWatcher->Set(pChannel, uiCodecType); - int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::forward(oMsgHead), std::forward(oMsgBody)); - if (iResult == ERR_OK) - { - return(pLaborShared->AddSpecChannel(uiCodecType, uiFromLabor, uiToLabor, pChannel)); - } - return(iResult); - } - else - { - pSpecChannel = std::static_pointer_cast>(pChannel); - if (pSpecChannel == nullptr) - { - return(ERR_SPEC_CHANNEL_CAST); - } - int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::forward(oMsgHead), std::forward(oMsgBody)); - if (iResult == ERR_OK) - { - pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); - } - return(iResult); - } -} - -template -int CodecProto::Write(uint32 uiCodecType, std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, int32 iCmd, uint32 uiSeq, MsgBody&& oMsgBody) -{ - uint32 uiFrom; - uint32 uiTo; - MsgHead oMsgHead; - oMsgHead.set_cmd(iCmd); - oMsgHead.set_seq(uiSeq); - std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); - return(Write(uiCodecType, uiTo, uiFrom, uiFlags, uiStepSeq, std::forward(oMsgHead), std::forward(oMsgBody))); -} - - } /* namespace neb */ #endif /* SRC_CODEC_CODECPROTO_HPP_ */ diff --git a/src/codec/CodecRaw.cpp b/src/codec/CodecRaw.cpp index 991f1a64..83c63582 100644 --- a/src/codec/CodecRaw.cpp +++ b/src/codec/CodecRaw.cpp @@ -25,6 +25,63 @@ CodecRaw::~CodecRaw() { } +// request +int CodecRaw::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const char* pRaw, uint32 uiRawSize) +{ + if (uiFromLabor == uiToLabor) + { + return(ERR_SPEC_CHANNEL_TARGET); + } + std::shared_ptr> pSpecChannel = nullptr; + auto pLaborShared = LaborShared::Instance(); + auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); + if (pChannel == nullptr) + { + pSpecChannel = std::make_shared>( + uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CREATE); + } + pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, Type()); + Bytes oBytes; + oBytes.Assign(pRaw, uiRawSize); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(oBytes)); + if (iResult == ERR_OK) + { + return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); + } + return(iResult); + } + else + { + pSpecChannel = std::static_pointer_cast>(pChannel); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CAST); + } + Bytes oBytes; + oBytes.Assign(pRaw, uiRawSize); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(oBytes)); + if (iResult == ERR_OK) + { + pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + return(iResult); + } +} + +// response +int CodecRaw::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const char* pRaw, uint32 uiRawSize) +{ + uint32 uiFrom; + uint32 uiTo; + std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); + return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, pRaw, uiRawSize)); +} + E_CODEC_STATUS CodecRaw::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); diff --git a/src/codec/CodecRaw.hpp b/src/codec/CodecRaw.hpp index d87dd6f0..b96ed3c2 100644 --- a/src/codec/CodecRaw.hpp +++ b/src/codec/CodecRaw.hpp @@ -31,11 +31,9 @@ class CodecRaw: public Codec } // request - template static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const char* pRaw, uint32 uiRawSize); // response - template static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const char* pRaw, uint32 uiRawSize); E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); @@ -45,65 +43,6 @@ class CodecRaw: public Codec E_CODEC_STATUS Decode(CBuffer* pBuff, CBuffer& oBuff, CBuffer* pReactBuff); }; -// request -template -int CodecRaw::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const char* pRaw, uint32 uiRawSize) -{ - if (uiFromLabor == uiToLabor) - { - return(ERR_SPEC_CHANNEL_TARGET); - } - std::shared_ptr> pSpecChannel = nullptr; - auto pLaborShared = LaborShared::Instance(); - auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); - if (pChannel == nullptr) - { - pSpecChannel = std::make_shared>( - uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); - if (pSpecChannel == nullptr) - { - return(ERR_SPEC_CHANNEL_CREATE); - } - pChannel = std::dynamic_pointer_cast(pSpecChannel); - auto pWatcher = pSpecChannel->MutableWatcher(); - pWatcher->Set(pChannel, Type()); - Bytes oBytes; - oBytes.Assign(pRaw, uiRawSize); - int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(oBytes)); - if (iResult == ERR_OK) - { - return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); - } - return(iResult); - } - else - { - pSpecChannel = std::static_pointer_cast>(pChannel); - if (pSpecChannel == nullptr) - { - return(ERR_SPEC_CHANNEL_CAST); - } - Bytes oBytes; - oBytes.Assign(pRaw, uiRawSize); - int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(oBytes)); - if (iResult == ERR_OK) - { - pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); - } - return(iResult); - } -} - -// response -template -int CodecRaw::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const char* pRaw, uint32 uiRawSize) -{ - uint32 uiFrom; - uint32 uiTo; - std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); - return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, pRaw, uiRawSize)); -} - } /* namespace neb */ #endif /* SRC_CODEC_CODECRAW_HPP_ */ diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp index 14d520e6..ddc41357 100644 --- a/src/codec/CodecResp.cpp +++ b/src/codec/CodecResp.cpp @@ -29,6 +29,59 @@ CodecResp::~CodecResp() { } +// request +int CodecResp::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const RedisReply& oReply) +{ + if (uiFromLabor == uiToLabor) + { + return(ERR_SPEC_CHANNEL_TARGET); + } + std::shared_ptr> pSpecChannel = nullptr; + auto pLaborShared = LaborShared::Instance(); + auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); + if (pChannel == nullptr) + { + pSpecChannel = std::make_shared>( + uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CREATE); + } + pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, Type()); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oReply))); + if (iResult == ERR_OK) + { + return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); + } + return(iResult); + } + else + { + pSpecChannel = std::static_pointer_cast>(pChannel); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CAST); + } + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oReply))); + if (iResult == ERR_OK) + { + pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + return(iResult); + } +} + +// response +int CodecResp::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const RedisReply& oReply) +{ + uint32 uiFrom; + uint32 uiTo; + std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); + return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, oReply)); +} + E_CODEC_STATUS CodecResp::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) { return(CODEC_STATUS_OK); diff --git a/src/codec/CodecResp.hpp b/src/codec/CodecResp.hpp index 036e4ced..5f61d3af 100644 --- a/src/codec/CodecResp.hpp +++ b/src/codec/CodecResp.hpp @@ -57,11 +57,9 @@ class CodecResp: public neb::Codec } // request - template static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const RedisReply& oReply); // response - template static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const RedisReply& oReply); virtual bool DecodeWithStack() const @@ -115,61 +113,6 @@ class CodecResp: public neb::Codec static const char RESP_ARRAY; }; -// request -template -int CodecResp::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const RedisReply& oReply) -{ - if (uiFromLabor == uiToLabor) - { - return(ERR_SPEC_CHANNEL_TARGET); - } - std::shared_ptr> pSpecChannel = nullptr; - auto pLaborShared = LaborShared::Instance(); - auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); - if (pChannel == nullptr) - { - pSpecChannel = std::make_shared>( - uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); - if (pSpecChannel == nullptr) - { - return(ERR_SPEC_CHANNEL_CREATE); - } - pChannel = std::dynamic_pointer_cast(pSpecChannel); - auto pWatcher = pSpecChannel->MutableWatcher(); - pWatcher->Set(pChannel, Type()); - int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oReply))); - if (iResult == ERR_OK) - { - return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); - } - return(iResult); - } - else - { - pSpecChannel = std::static_pointer_cast>(pChannel); - if (pSpecChannel == nullptr) - { - return(ERR_SPEC_CHANNEL_CAST); - } - int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oReply))); - if (iResult == ERR_OK) - { - pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); - } - return(iResult); - } -} - -// response -template -int CodecResp::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const RedisReply& oReply) -{ - uint32 uiFrom; - uint32 uiTo; - std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); - return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, oReply)); -} - } /* namespace neb */ #endif /* SRC_CODEC_CODECRESP_HPP_ */ diff --git a/src/codec/virtual/CodecDeliver.cpp b/src/codec/virtual/CodecDeliver.cpp new file mode 100644 index 00000000..35bf790e --- /dev/null +++ b/src/codec/virtual/CodecDeliver.cpp @@ -0,0 +1,77 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecDeliver.cpp + * @brief + * @author Bwar + * @date: 2023-01-25 + * @note + * Modify history: + ******************************************************************************/ +#include "CodecDeliver.hpp" + +namespace neb +{ + +CodecDeliver::CodecDeliver() +{ +} + +CodecDeliver::~CodecDeliver() +{ +} + +// request +int CodecDeliver::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, Package&& oPackage) +{ + if (uiFromLabor == uiToLabor) + { + return(ERR_SPEC_CHANNEL_TARGET); + } + std::shared_ptr> pSpecChannel = nullptr; + auto pLaborShared = LaborShared::Instance(); + auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); + if (pChannel == nullptr) + { + pSpecChannel = std::make_shared>( + uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CREATE); + } + pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, Type()); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::forward(oPackage)); + if (iResult == ERR_OK) + { + return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); + } + return(iResult); + } + else + { + pSpecChannel = std::static_pointer_cast>(pChannel); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CAST); + } + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::forward(oPackage)); + if (iResult == ERR_OK) + { + pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + return(iResult); + } +} + +// response +int CodecDeliver::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, Package&& oPackage) +{ + uint32 uiFrom; + uint32 uiTo; + std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); + return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, std::forward(oPackage))); +} + +} /* namespace neb */ + diff --git a/src/codec/virtual/CodecDeliver.hpp b/src/codec/virtual/CodecDeliver.hpp new file mode 100644 index 00000000..2e19cfe4 --- /dev/null +++ b/src/codec/virtual/CodecDeliver.hpp @@ -0,0 +1,42 @@ +/******************************************************************************* + * Project: Nebula + * @file CodecDeliver.hpp + * @brief + * @author Bwar + * @date: 2023-01-25 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_CODEC_VIRTUAL_CODECDELIVER_HPP_ +#define SRC_CODEC_VIRTUAL_CODECDELIVER_HPP_ + +#include +#include "channel/SpecChannel.hpp" +#include "labor/LaborShared.hpp" +#include "type/Package.hpp" + +namespace neb +{ + +class CodecDeliver +{ +public: + CodecDeliver(); + virtual ~CodecDeliver(); + + static E_CODEC_TYPE Type() + { + return(CODEC_RESP); + } + + // request + static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, Package&& oPackage); + + // response + static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, Package&& oPackage); +}; + +} /* namespace neb */ + +#endif /* SRC_CODEC_VIRTUAL_CODECDELIVER_HPP_ */ + diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index aa52f812..e0710e22 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -304,7 +304,7 @@ bool Dispatcher::OnIoError(std::shared_ptr pChannel) bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) { - ev_tstamp after = pChannel->GetActiveTime() - ev_now(m_loop) + pChannel->GetKeepAlive(); + ev_tstamp after = pChannel->GetLastRecvTime() - ev_now(m_loop) + pChannel->GetKeepAlive(); if (after > 0) // IO在定时时间内被重新刷新过,重新设置定时器 { ev_timer_stop (m_loop, pChannel->MutableWatcher()->MutableTimerWatcher()); @@ -313,34 +313,21 @@ bool Dispatcher::OnIoTimeout(std::shared_ptr pChannel) return(true); } - LOG4_TRACE("fd %d, seq %u, active time %f, now time %f, keep alive %f", - pChannel->GetFd(), pChannel->GetSequence(), pChannel->GetActiveTime(), + LOG4_TRACE("fd %d, seq %u, last recv time %f, now time %f, keep alive %f", + pChannel->GetFd(), pChannel->GetSequence(), pChannel->GetLastRecvTime(), ev_now(m_loop), pChannel->GetKeepAlive()); - if (CODEC_PROTO == pChannel->GetCodecType() && pChannel->NeedAliveCheck()) // 需要发送心跳检查 + if (pChannel->IsClient() && pChannel->NeedAliveCheck()) // 需要发送心跳检查 { - std::shared_ptr pStepIoTimeout = m_pLabor->GetActorBuilder()->MakeSharedStep( - nullptr, "neb::StepIoTimeout", pChannel); - if (nullptr == pStepIoTimeout) + if (!PingChannel(pChannel)) { - LOG4_ERROR("new StepIoTimeout error!"); + LOG4_TRACE("channel timeout!"); DiscardSocketChannel(pChannel); } - E_CMD_STATUS eStatus = pStepIoTimeout->Emit(); - if (CMD_STATUS_RUNNING != eStatus) - { - // 若返回非running状态,则表明发包时已出错, - // 销毁连接过程在SendTo里已经完成,这里不需要再销毁连接 - m_pLabor->GetActorBuilder()->RemoveStep(pStepIoTimeout); - } } else // 关闭文件描述符并清理相关资源 { - if ((CODEC_NEBULA != pChannel->GetCodecType()) - && (CODEC_NEBULA_IN_NODE != pChannel->GetCodecType())) // 非内部服务器间的连接才会在超时中关闭 - { - LOG4_TRACE("io timeout!"); - DiscardSocketChannel(pChannel); - } + LOG4_TRACE("io timeout!"); + DiscardSocketChannel(pChannel); } return(true); } @@ -409,7 +396,9 @@ bool Dispatcher::SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBod { if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) { - return(IO::Broadcast(this, 0, "BEACON", false, true, iCmd, uiSeq, oMsgBody)); + ChannelOption stOption; + stOption.bPipeline = true; + return(IO::Broadcast(this, 0, "BEACON", stOption, iCmd, uiSeq, oMsgBody)); } else { @@ -520,54 +509,6 @@ std::shared_ptr Dispatcher::StressSend(const std::string& strIden } } -bool Dispatcher::SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) -{ - if (m_pLabor->GetLaborType() == Labor::LABOR_MANAGER) - { - LOG4_ERROR("this function can not be called by manager process!"); - return(false); - } - if (m_iterLoaderAndWorkerChannel == m_mapLoaderAndWorkerChannel.end()) - { - m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); - if (m_iterLoaderAndWorkerChannel == m_mapLoaderAndWorkerChannel.end()) - { - LOG4_ERROR("no channel for loader!"); - return(false); - } - } - bool bRes = false; - if (gc_uiCmdReq & iCmd) - { - bRes = IO::SendRequest(this, 0, m_iterLoaderAndWorkerChannel->second, iCmd, uiSeq, oMsgBody); - } - else - { - bRes = IO::SendResponse(this, m_iterLoaderAndWorkerChannel->second, iCmd, uiSeq, oMsgBody); - } - m_iterLoaderAndWorkerChannel++; - return(bRes); -} - -bool Dispatcher::SendTo(int iFd, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) -{ - auto iter = m_mapSocketChannel.find(iFd); - if (iter != m_mapSocketChannel.end()) - { - bool bRes = false; - if (gc_uiCmdReq & iCmd) - { - bRes = IO::SendRequest(this, 0, iter->second, iCmd, uiSeq, oMsgBody); - } - else - { - bRes = IO::SendResponse(this, iter->second, iCmd, uiSeq, oMsgBody); - } - return(bRes); - } - return(false); -} - std::shared_ptr Dispatcher::GetLastActivityChannel() { return(m_pLastActivityChannel); @@ -691,6 +632,15 @@ void Dispatcher::CircuitBreak(const std::string& strIdentify) m_pSessionNode->NodeFailed(strIdentify); } +void Dispatcher::SetChannelPingStep(int iCodec, const std::string& strStepName) +{ + auto iter = m_mapChannelPingStepName.find(iCodec); + if (iter == m_mapChannelPingStepName.end()) + { + m_mapChannelPingStepName.insert(std::make_pair(iCodec, strStepName)); + } +} + void Dispatcher::SetClientData(std::shared_ptr pChannel, const std::string& strClientData) { pChannel->SetClientData(strClientData); @@ -701,14 +651,19 @@ bool Dispatcher::IsNodeType(const std::string& strNodeIdentify, const std::strin return(m_pSessionNode->IsNodeType(strNodeIdentify, strNodeType)); } -bool Dispatcher::GetAuth(const std::string& strNodeType, std::string& strAuth, std::string& strPassword) +bool Dispatcher::GetAuth(const std::string& strIdentify, std::string& strAuth, std::string& strPassword) +{ + return(m_pSessionNode->GetAuth(strIdentify, strAuth, strPassword)); +} + +std::shared_ptr Dispatcher::GetChannelOption(const std::string& strIdentify) { - return(m_pSessionNode->GetAuth(strNodeType, strAuth, strPassword)); + return(m_pSessionNode->GetChannelOption(strIdentify)); } -void Dispatcher::SetAuth(const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword) +void Dispatcher::SetChannelOption(const std::string& strIdentify, const ChannelOption& stOption) { - m_pSessionNode->SetAuth(strNodeType, strAuth, strPassword); + m_pSessionNode->SetChannelOption(strIdentify, stOption); } bool Dispatcher::AddEvent(ev_signal* signal_watcher, signal_callback pFunc, int iSignum) @@ -900,6 +855,9 @@ bool Dispatcher::Init() #else m_pSessionNode = std::unique_ptr(new Nodes()); #endif + SetChannelPingStep(CODEC_PROTO, "neb::StepNebulaChannelPing"); + SetChannelPingStep(CODEC_NEBULA, "neb::StepNebulaChannelPing"); + SetChannelPingStep(CODEC_RESP, "neb::StepRedisChannelPing"); return(true); } @@ -1012,16 +970,6 @@ bool Dispatcher::DiscardSocketChannel(std::shared_ptr pChannel, b } pChannel->MutableWatcher()->Reset(); - if (CODEC_NEBULA_IN_NODE == pChannel->GetCodecType()) - { - auto inner_channel_iter = m_mapLoaderAndWorkerChannel.find(pChannel->GetFd()); - if (inner_channel_iter != m_mapLoaderAndWorkerChannel.end()) - { - m_mapLoaderAndWorkerChannel.erase(inner_channel_iter); - } - m_iterLoaderAndWorkerChannel = m_mapLoaderAndWorkerChannel.begin(); - } - auto channel_iter = m_mapSocketChannel.find(pChannel->GetFd()); if (channel_iter != m_mapSocketChannel.end()) { @@ -1403,6 +1351,34 @@ bool Dispatcher::AcceptServerConn(int iFd) return(false); } +bool Dispatcher::PingChannel(std::shared_ptr pChannel) +{ + auto iter = m_mapChannelPingStepName.find(pChannel->GetCodecType()); + if (iter == m_mapChannelPingStepName.end()) + { + return(false); + } + else + { + auto pStep = m_pLabor->GetActorBuilder()->MakeSharedStep(nullptr, iter->second, pChannel); + if (nullptr == pStep) + { + LOG4_ERROR("failed to ping channel."); + return(false); + } + else + { + E_CMD_STATUS eStatus = pStep->Emit(); + if (CMD_STATUS_RUNNING != eStatus) + { + m_pLabor->GetActorBuilder()->RemoveStep(pStep); + return(false); + } + return(true); + } + } +} + void Dispatcher::CheckFailedNode() { if (GetNowTime() - m_lLastCheckNodeTime >= 60) diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index ddb7dc22..8674fd67 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -129,10 +129,6 @@ class Dispatcher bool SendDataReport(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); std::shared_ptr StressSend(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType = CODEC_NEBULA); - // SendTo() for unix domain socket - bool SendTo(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - bool SendTo(int iFd, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); - std::shared_ptr GetLastActivityChannel(); bool Disconnect(std::shared_ptr pChannel, bool bChannelNotice = true); bool Disconnect(const std::string& strIdentify, bool bChannelNotice = true); @@ -145,10 +141,12 @@ class Dispatcher void AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); void DelNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); void CircuitBreak(const std::string& strIdentify); + void SetChannelPingStep(int iCodec, const std::string& strStepName); void SetClientData(std::shared_ptr pChannel, const std::string& strClientData); bool IsNodeType(const std::string& strNodeIdentify, const std::string& strNodeType); - bool GetAuth(const std::string& strNodeType, std::string& strAuth, std::string& strPassword); - void SetAuth(const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword); + bool GetAuth(const std::string& strIdentify, std::string& strAuth, std::string& strPassword); + std::shared_ptr GetChannelOption(const std::string& strIdentify); + void SetChannelOption(const std::string& strIdentify, const ChannelOption& stOption); time_t GetNowTime() const { @@ -194,6 +192,7 @@ class Dispatcher bool AddClientConnFrequencyTimeout(const char* pAddr, ev_tstamp dTimeout = 60.0); bool AcceptFdAndTransfer(int iFd, int iFamily = AF_INET, int iBonding = 0); bool AcceptServerConn(int iFd); + bool PingChannel(std::shared_ptr pChannel); void CheckFailedNode(); void EvBreak(); @@ -211,8 +210,7 @@ class Dispatcher /* named Channel */ std::unordered_map > > m_mapNamedSocketChannel; ///< key为Identify,连接存在时,if(http连接)set.size()>=1;else set.size()==1; - std::unordered_map > m_mapLoaderAndWorkerChannel; ///< Loader和Worker之间通信通道 - std::unordered_map >::iterator m_iterLoaderAndWorkerChannel; + std::unordered_map m_mapChannelPingStepName; // CODEC_TYPE as key std::unordered_map m_mapClientConnFrequency; ///< 客户端连接频率 diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index 7765e1e1..23109371 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -51,49 +51,100 @@ class IO template static bool SendRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, Targs&&... args); + static std::shared_ptr ApplySocketChannel(Actor* pActor, const ChannelOption& stOption, const std::string& strIdentify); + + static std::shared_ptr ApplySocketChannel(Actor* pActor, const ChannelOption& stOption, const std::string& strHost, int iPort); + // for spec channel - template + template static bool TransmitTo(Actor* pActor, uint32 uiTargetLaborId, uint32 uiCallbackStepSeq, Targs&&... args); - + + // for spec channel, package deliver + template + static bool DeliverTo(Actor* pActor, uint32 uiTargetLaborId, uint32 uiCallbackStepSeq, Targs&&... args); + + template + static bool DeliverReply(Actor* pActor, std::shared_ptr pChannel, uint32 uiPeerStepSeq, Targs&&... args); + + template + static bool SendWithoutOption(Actor* pActor, const std::string& strIdentify, Targs&&... args); + template - static bool SendTo(Actor* pActor, const std::string& strIdentify, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); + static bool SendTo(Actor* pActor, const std::string& strIdentify, const ChannelOption& stOption, Targs&&... args); template - static bool SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); + static bool SendWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, Targs&&... args); template - static bool SendTo(Actor* pActor, const std::string& strHost, int iPort, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); + static bool SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, const ChannelOption& stOption, Targs&&... args); template - static bool SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strHost, int iPort, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); + static bool SendWithoutOption(Actor* pActor, const std::string& strHost, int iPort, Targs&&... args); template - static bool SendRoundRobin(Actor* pActor, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args); + static bool SendTo(Actor* pActor, const std::string& strHost, int iPort, const ChannelOption& stOption, Targs&&... args); template - static bool SendRoundRobin(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args); + static bool SendWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strHost, int iPort, Targs&&... args); + + template + static bool SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strHost, int iPort, const ChannelOption& stOption, Targs&&... args); + + template + static bool SendRoundRobinWithoutOption(Actor* pActor, const std::string& strNodeType, Targs&&... args); + + template + static bool SendRoundRobin(Actor* pActor, const std::string& strNodeType, const ChannelOption& stOption, Targs&&... args); + + template + static bool SendRoundRobinWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, Targs&&... args); + + template + static bool SendRoundRobin(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, const ChannelOption& stOption, Targs&&... args); + + template + static bool SendOrientedWithoutOption(Actor* pActor, const std::string& strNodeType, + uint32 uiFactor, Targs&&... args); template static bool SendOriented(Actor* pActor, const std::string& strNodeType, - bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args); + uint32 uiFactor, const ChannelOption& stOption, Targs&&... args); + + template + static bool SendOrientedWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, + const std::string& strNodeType, uint32 uiFactor, Targs&&... args); template static bool SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, - bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args); + uint32 uiFactor, const ChannelOption& stOption, Targs&&... args); + + template + static bool SendOrientedWithoutOption(Actor* pActor, const std::string& strNodeType, + const std::string& strFactor, Targs&&... args); template static bool SendOriented(Actor* pActor, const std::string& strNodeType, - bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args); + const std::string& strFactor, const ChannelOption& stOption, Targs&&... args); + + template + static bool SendOrientedWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, + const std::string& strFactor, Targs&&... args); template static bool SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, - bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args); + const std::string& strFactor, const ChannelOption& stOption, Targs&&... args); + + template + static bool BroadcastWithoutOption(Actor* pActor, const std::string& strNodeType, Targs&&... args); template - static bool Broadcast(Actor* pActor, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args); + static bool Broadcast(Actor* pActor, const std::string& strNodeType, const ChannelOption& stOption, Targs&&... args); template - static bool Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args); + static bool BroadcastWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, Targs&&... args); + + template + static bool Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, const ChannelOption& stOption, Targs&&... args); template static uint32 SendToSelf(Actor* pActor, Targs&&... args); @@ -116,7 +167,7 @@ class IO template static bool OnResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, uint32 uiStreamId, E_CODEC_STATUS eCodecStatus, Targs&&... args); - // SelfChannel response, SpecChannel response + // SelfChannel response, SpecChannel response template static bool OnResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, uint32 uiStepSeq, Targs&&... args); @@ -141,8 +192,20 @@ class IO protected: template - static bool AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, const std::string& strHost, - int iPort, int iRemoteWorkerIndex, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args); + static bool AutoSendWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, Targs&&... args); + template + static bool AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, + const std::string& strHost, int iPort, const ChannelOption& stOption, Targs&&... args); + template + static bool AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, + const std::string& strIdentify, const ChannelOption& stOption, Targs&&... args); + template + static bool AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, + const std::string& strIdentify, const std::string& strHost, int iPort, + int iRemoteWorkerIndex, const ChannelOption& stOption, Targs&&... args); + static std::shared_ptr NewSocketChannel(Dispatcher* pDispatcher, const ChannelOption& stOption, + const std::string& strIdentify, const std::string& strHost, int iPort, int iRemoteWorkerIndex); + static bool SplitIdentify(const std::string& strIdentify, std::string& strHost, int& iPort, int& iWorkerIndex, std::string& strError); static in_addr_t inet_addr(const char* text, uint32 len); }; @@ -215,7 +278,7 @@ bool IO::SendResponse(Dispatcher* pDispatcher, std::shared_ptr LOG4_TRACE_DISPATCH("CODEC_UNKNOW is invalid, channel had not been init?"); return(false); } - if (pChannel->GetCodecType() == CODEC_DIRECT) // self channel + if (pChannel->GetCodecType() == CODEC_DIRECT) // self channel { auto pSelfChannel = std::dynamic_pointer_cast(pChannel); if (pSelfChannel == nullptr) @@ -396,6 +459,87 @@ bool IO::SendRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_p } } +template +std::shared_ptr IO::ApplySocketChannel(Actor* pActor, const ChannelOption& stOption, const std::string& strIdentify) +{ + if (pActor == nullptr) + { + return(nullptr); + } + else + { + std::string strError; + std::string strHost; + int iPort = 0; + int iRemoteWorkerIndex = -1; + if (!SplitIdentify(strIdentify, strHost, iPort, iRemoteWorkerIndex, strError)) + { + pActor->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "%s", strError.c_str()); + return(nullptr); + } + auto pDispatcher = pActor->m_pLabor->GetDispatcher(); + auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(strIdentify); + if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + { + return(NewSocketChannel(pDispatcher, stOption, strIdentify, strHost, iPort, iRemoteWorkerIndex)); + } + else + { + if (named_iter->second.empty()) + { + return(NewSocketChannel(pDispatcher, stOption, strIdentify, strHost, iPort, iRemoteWorkerIndex)); + } + else + { + auto channel_iter = named_iter->second.begin(); + auto pChannel = *channel_iter; + if (!pChannel->IsPipeline()) + { + named_iter->second.erase(channel_iter); + } + return(pChannel); + } + } + } +} + +template +std::shared_ptr IO::ApplySocketChannel(Actor* pActor, const ChannelOption& stOption, const std::string& strHost, int iPort) +{ + if (pActor == nullptr) + { + return(nullptr); + } + else + { + std::ostringstream ossIdentify; + ossIdentify << strHost << ":" << iPort; + auto pDispatcher = pActor->m_pLabor->GetDispatcher(); + auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(ossIdentify.str()); + if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + { + return(NewSocketChannel(pDispatcher, stOption, ossIdentify.str(), strHost, iPort, -1)); + } + else + { + if (named_iter->second.empty()) + { + return(NewSocketChannel(pDispatcher, stOption, ossIdentify.str(), strHost, iPort, -1)); + } + else + { + auto channel_iter = named_iter->second.begin(); + auto pChannel = *channel_iter; + if (!pChannel->IsPipeline()) + { + named_iter->second.erase(channel_iter); + } + return(pChannel); + } + } + } +} + template template bool IO::TransmitTo(Actor* pActor, uint32 uiTargetLaborId, uint32 uiCallbackStepSeq, Targs&&... args) @@ -412,7 +556,57 @@ bool IO::TransmitTo(Actor* pActor, uint32 uiTargetLaborId, uint32 uiCallbackS template template -bool IO::SendTo(Actor* pActor, const std::string& strIdentify, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool IO::DeliverTo(Actor* pActor, uint32 uiTargetLaborId, uint32 uiCallbackStepSeq, Targs&&... args) +{ + return(TransmitTo(pActor, uiTargetLaborId, uiCallbackStepSeq, std::forward(args)...)); +} + +template +template +bool IO::DeliverReply(Actor* pActor, std::shared_ptr pChannel, uint32 uiPeerStepSeq, Targs&&... args) +{ + if (uiPeerStepSeq == 0) + { + pActor->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "no peer step seq for response"); + return(false); + } + int iResult = T::Write(pChannel, 0, uiPeerStepSeq, std::forward(args)...); + if (ERR_OK == iResult) + { + return(true); + } + pActor->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "spec channel error %d", iResult); + return(false); +} + +template +template +bool IO::SendWithoutOption(Actor* pActor, const std::string& strIdentify, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendWithoutOption(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strIdentify, std::forward(args)...)); + } + else + { + return(SendWithoutOption(pActor->m_pLabor->GetDispatcher(), 0, + strIdentify, std::forward(args)...)); + } + } +} + +template +template +bool IO::SendTo(Actor* pActor, const std::string& strIdentify, const ChannelOption& stOption, Targs&&... args) { if (pActor == nullptr) { @@ -423,100 +617,72 @@ bool IO::SendTo(Actor* pActor, const std::string& strIdentify, int iSocketTyp if (pActor->WantResponse()) { return(SendTo(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), - strIdentify, iSocketType, bWithSsl, bPipeline, - std::forward(args)...)); + strIdentify, stOption, std::forward(args)...)); } else { return(SendTo(pActor->m_pLabor->GetDispatcher(), 0, - strIdentify, iSocketType, bWithSsl, bPipeline, - std::forward(args)...)); + strIdentify, stOption, std::forward(args)...)); } } } template template -bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool IO::SendWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, Targs&&... args) { LOG4_TRACE_DISPATCH("identify: %s", strIdentify.c_str()); - // 将strIdentify分割的功能只在此SendTo()函数内两处调用,定义为Dispatcher的成员函数语义上不太合适,故定义lambda表达式 - auto split = [](const std::string& strIdentify, std::string& strHost, int& iPort, int& iWorkerIndex, std::string& strError)->bool + + auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(strIdentify); + if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) { - size_t iPosIpPortSeparator = strIdentify.rfind(':'); - size_t iPosPortWorkerIndexSeparator = strIdentify.rfind('.'); - if (iPosIpPortSeparator == std::string::npos) - { - return(false); - } - strHost = strIdentify.substr(0, iPosIpPortSeparator); - std::string strPort; - if (iPosPortWorkerIndexSeparator != std::string::npos && iPosPortWorkerIndexSeparator > iPosIpPortSeparator) + LOG4_TRACE_DISPATCH("no channel match %s.", strIdentify.c_str()); + return(AutoSendWithoutOption(pDispatcher, uiStepSeq, strIdentify, std::forward(args)...)); + } + else + { + if (named_iter->second.empty()) { - strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); - iPort = atoi(strPort.c_str()); - if (iPort == 0) - { - return(false); - } - std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); - if (strWorkerIndex.size() > 0) - { - iWorkerIndex = atoi(strWorkerIndex.c_str()); - if (iWorkerIndex > 200) - { - strError = "worker index must smaller than 200"; - return(false); - } - } + return(AutoSendWithoutOption(pDispatcher, uiStepSeq, strIdentify, std::forward(args)...)); } else { - strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); - iPort = atoi(strPort.c_str()); - if (iPort == 0) + auto channel_iter = named_iter->second.begin(); + auto pChannel = *channel_iter; + bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); + if (!pChannel->IsPipeline() && bResult) { - return(false); + named_iter->second.erase(channel_iter); } + return(bResult); } - return(true); - }; + } +} + +template +template +bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, const ChannelOption& stOption, Targs&&... args) +{ + LOG4_TRACE_DISPATCH("identify: %s", strIdentify.c_str()); auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(strIdentify); if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) { LOG4_TRACE_DISPATCH("no channel match %s.", strIdentify.c_str()); - std::string strError; - std::string strHost; - int iPort = 0; - int iWorkerIndex = -1; - if (!split(strIdentify, strHost, iPort, iWorkerIndex, strError)) - { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "%s", strError.c_str()); - return(false); - } - return(AutoSend(pDispatcher, uiStepSeq, strIdentify, strHost, iPort, iWorkerIndex, iSocketType, bWithSsl, bPipeline, std::forward(args)...)); + return(AutoSend(pDispatcher, uiStepSeq, strIdentify, stOption, std::forward(args)...)); } else { if (named_iter->second.empty()) { - std::string strError; - std::string strHost; - int iPort = 0; - int iWorkerIndex = -1; - if (!split(strIdentify, strHost, iPort, iWorkerIndex, strError)) - { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "%s", strError.c_str()); - return(false); - } - return(AutoSend(pDispatcher, uiStepSeq, strIdentify, strHost, iPort, iWorkerIndex, iSocketType, bWithSsl, bPipeline, std::forward(args)...)); + return(AutoSend(pDispatcher, uiStepSeq, strIdentify, stOption, std::forward(args)...)); } else { auto channel_iter = named_iter->second.begin(); - bool bResult = SendRequest(pDispatcher, uiStepSeq, (*channel_iter), std::forward(args)...); - if (!bPipeline && bResult) + auto pChannel = *channel_iter; + bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); + if (!pChannel->IsPipeline() && bResult) { named_iter->second.erase(channel_iter); } @@ -527,7 +693,7 @@ bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& template template -bool IO::SendTo(Actor* pActor, const std::string& strHost, int iPort, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool IO::SendWithoutOption(Actor* pActor, const std::string& strHost, int iPort, Targs&&... args) { if (pActor == nullptr) { @@ -537,51 +703,372 @@ bool IO::SendTo(Actor* pActor, const std::string& strHost, int iPort, int iSo { if (pActor->WantResponse()) { - return(SendTo(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), - strHost, iPort, iSocketType, bWithSsl, bPipeline, + return(SendWithoutOption(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), strHost, iPort, + std::forward(args)...)); + } + else + { + return(SendWithoutOption(pActor->m_pLabor->GetDispatcher(), 0, strHost, iPort, std::forward(args)...)); } + } +} + +template +template +bool IO::SendTo(Actor* pActor, const std::string& strHost, int iPort, const ChannelOption& stOption, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendTo(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strHost, iPort, stOption, std::forward(args)...)); + } else { - return(SendTo(pActor->m_pLabor->GetDispatcher(), 0, - strHost, iPort, iSocketType, bWithSsl, bPipeline, - std::forward(args)...)); + return(SendTo(pActor->m_pLabor->GetDispatcher(), 0, + strHost, iPort, stOption, std::forward(args)...)); + } + } +} + +template +template +bool IO::SendWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strHost, int iPort, Targs&&... args) +{ + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "host %s port %d", strHost.c_str(), iPort); + std::ostringstream ossIdentify; + ossIdentify << strHost << ":" << iPort; + auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(ossIdentify.str()); + if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + { + LOG4_TRACE_DISPATCH("no channel match %s.", ossIdentify.str().c_str()); + return(AutoSendWithoutOption(pDispatcher, uiStepSeq, strHost, iPort, std::forward(args)...)); + } + else + { + auto channel_iter = named_iter->second.begin(); + if (channel_iter == named_iter->second.end()) + { + return(AutoSendWithoutOption(pDispatcher, uiStepSeq, strHost, iPort, std::forward(args)...)); + } + auto pChannel = *channel_iter; + bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); + if (!pChannel->IsPipeline() && bResult) + { + named_iter->second.erase(channel_iter); + } + return(bResult); + } +} + +template +template +bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strHost, int iPort, const ChannelOption& stOption, Targs&&... args) +{ + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "host %s port %d", strHost.c_str(), iPort); + std::ostringstream ossIdentify; + ossIdentify << strHost << ":" << iPort; + auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(ossIdentify.str()); + if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + { + LOG4_TRACE_DISPATCH("no channel match %s.", ossIdentify.str().c_str()); + return(AutoSend(pDispatcher, uiStepSeq, strHost, iPort, stOption, std::forward(args)...)); + } + else + { + auto channel_iter = named_iter->second.begin(); + if (channel_iter == named_iter->second.end()) + { + return(AutoSend(pDispatcher, uiStepSeq, strHost, iPort, stOption, std::forward(args)...)); + } + auto pChannel = *channel_iter; + bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); + if (!pChannel->IsPipeline() && bResult) + { + named_iter->second.erase(channel_iter); + } + return(bResult); + } +} + +template +template +bool IO::SendRoundRobinWithoutOption(Actor* pActor, const std::string& strNodeType, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendRoundRobinWithoutOption(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strNodeType, std::forward(args)...)); + } + else + { + return(SendRoundRobinWithoutOption(pActor->m_pLabor->GetDispatcher(), 0, + strNodeType, std::forward(args)...)); + } + } +} + +template +template +bool IO::SendRoundRobin(Actor* pActor, const std::string& strNodeType, const ChannelOption& stOption, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendRoundRobin(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strNodeType, stOption, std::forward(args)...)); + } + else + { + return(SendRoundRobin(pActor->m_pLabor->GetDispatcher(), 0, + strNodeType, stOption, std::forward(args)...)); + } + } +} + +template +template +bool IO::SendRoundRobinWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, Targs&&... args) +{ + LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); + std::string strOnlineNode; + if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) + { + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); + SendWithoutOption(pDispatcher, 0, strOnlineNode); + } + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, strOnlineNode)) + { + auto pChannelOption = pDispatcher->GetChannelOption(strNodeType); + if (pChannelOption == nullptr) + { + ChannelOption stOption; + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); + } + else + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, *(pChannelOption.get()), std::forward(args)...)); + } + } + else + { + LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + auto pChannelOption = pDispatcher->GetChannelOption(strNodeType); + if (pChannelOption == nullptr) + { + ChannelOption stOption; + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); + } + else + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, *(pChannelOption.get()), std::forward(args)...)); + } + } + LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } +} + +template +template +bool IO::SendRoundRobin(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, const ChannelOption& stOption, Targs&&... args) +{ + LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); + std::string strOnlineNode; + if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) + { + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); + SendWithoutOption(pDispatcher, 0, strOnlineNode); + } + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, strOnlineNode)) + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); + } + else + { + LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); + } + LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } +} + +template +template +bool IO::SendOrientedWithoutOption(Actor* pActor, const std::string& strNodeType, + uint32 uiFactor, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendOrientedWithoutOption(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strNodeType, uiFactor, std::forward(args)...)); + } + else + { + return(SendOrientedWithoutOption(pActor->m_pLabor->GetDispatcher(), 0, + strNodeType, uiFactor, std::forward(args)...)); + } + } +} + +template +template +bool IO::SendOriented(Actor* pActor, const std::string& strNodeType, + uint32 uiFactor, const ChannelOption& stOption, Targs&&... args) +{ + if (pActor == nullptr) + { + return(false); + } + else + { + if (pActor->WantResponse()) + { + return(SendOriented(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strNodeType, uiFactor, stOption, std::forward(args)...)); + } + else + { + return(SendOriented(pActor->m_pLabor->GetDispatcher(), 0, + strNodeType, uiFactor, stOption, std::forward(args)...)); + } + } +} + +template +template +bool IO::SendOrientedWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, + uint32 uiFactor, Targs&&... args) +{ + LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); + std::string strOnlineNode; + if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) + { + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); + SendWithoutOption(pDispatcher, 0, strOnlineNode); + } + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) + { + auto pChannelOption = pDispatcher->GetChannelOption(strNodeType); + if (pChannelOption == nullptr) + { + ChannelOption stOption; + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); + } + else + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, *(pChannelOption.get()), std::forward(args)...)); + } + } + else + { + LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + auto pChannelOption = pDispatcher->GetChannelOption(strNodeType); + if (pChannelOption == nullptr) + { + ChannelOption stOption; + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); + } + else + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, *(pChannelOption.get()), std::forward(args)...)); + } + } + LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); + } +} + +template +template +bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, + const std::string& strNodeType, uint32 uiFactor, const ChannelOption& stOption, Targs&&... args) +{ + LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); + std::string strOnlineNode; + if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) + { + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); + SendWithoutOption(pDispatcher, 0, strOnlineNode); + } + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); + } + else + { + LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); } + LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); + return(false); } } template template -bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strHost, int iPort, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool IO::SendOrientedWithoutOption(Actor* pActor, const std::string& strNodeType, + const std::string& strFactor, Targs&&... args) { - pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "host %s port %d", strHost.c_str(), iPort); - std::ostringstream ossIdentify; - ossIdentify << strHost << ":" << iPort; - auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(ossIdentify.str()); - if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + if (pActor == nullptr) { - LOG4_TRACE_DISPATCH("no channel match %s.", ossIdentify.str().c_str()); - return(AutoSend(pDispatcher, uiStepSeq, ossIdentify.str(), strHost, iPort, 0, iSocketType, bWithSsl, bPipeline, std::forward(args)...)); + return(false); } else { - auto channel_iter = named_iter->second.begin(); - if (channel_iter == named_iter->second.end()) + if (pActor->WantResponse()) { - return(AutoSend(pDispatcher, uiStepSeq, ossIdentify.str(), strHost, iPort, 0, iSocketType, bWithSsl, bPipeline, std::forward(args)...)); + return(SendOrientedWithoutOption(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strNodeType, strFactor, std::forward(args)...)); } - bool bResult = SendRequest(pDispatcher, uiStepSeq, (*channel_iter), std::forward(args)...); - if (!bPipeline && bResult) + else { - named_iter->second.erase(channel_iter); + return(SendOrientedWithoutOption(pActor->m_pLabor->GetDispatcher(), 0, + strNodeType, strFactor, std::forward(args)...)); } - return(bResult); } } template template -bool IO::SendRoundRobin(Actor* pActor, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool IO::SendOriented(Actor* pActor, const std::string& strNodeType, + const std::string& strFactor, const ChannelOption& stOption, Targs&&... args) { if (pActor == nullptr) { @@ -591,41 +1078,58 @@ bool IO::SendRoundRobin(Actor* pActor, const std::string& strNodeType, bool b { if (pActor->WantResponse()) { - return(SendRoundRobin(pActor->m_pLabor->GetDispatcher(), - pActor->GetSequence(), strNodeType, bWithSsl, bPipeline, - std::forward(args)...)); + return(SendOriented(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strNodeType, strFactor, stOption, std::forward(args)...)); } else { - return(SendRoundRobin(pActor->m_pLabor->GetDispatcher(), - 0, strNodeType, bWithSsl, bPipeline, - std::forward(args)...)); + return(SendOriented(pActor->m_pLabor->GetDispatcher(), 0, + strNodeType, strFactor, stOption, std::forward(args)...)); } } } template template -bool IO::SendRoundRobin(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool IO::SendOrientedWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, + const std::string& strFactor, Targs&&... args) { LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) { pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, - "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); - SendTo(pDispatcher, 0, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); + "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); + SendWithoutOption(pDispatcher, 0, strOnlineNode); } - if (pDispatcher->m_pSessionNode->GetNode(strNodeType, strOnlineNode)) + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, strFactor, strOnlineNode)) { - return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + auto pChannelOption = pDispatcher->GetChannelOption(strNodeType); + if (pChannelOption == nullptr) + { + ChannelOption stOption; + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); + } + else + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, *(pChannelOption.get()), std::forward(args)...)); + } } else { LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) { - return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + auto pChannelOption = pDispatcher->GetChannelOption(strNodeType); + if (pChannelOption == nullptr) + { + ChannelOption stOption; + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); + } + else + { + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, *(pChannelOption.get()), std::forward(args)...)); + } } LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); return(false); @@ -634,53 +1138,27 @@ bool IO::SendRoundRobin(Dispatcher* pDispatcher, uint32 uiStepSeq, const std: template template -bool IO::SendOriented(Actor* pActor, const std::string& strNodeType, - bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args) -{ - if (pActor == nullptr) - { - return(false); - } - else - { - if (pActor->WantResponse()) - { - return(SendOriented(pActor->m_pLabor->GetDispatcher(), - pActor->GetSequence(), strNodeType, bWithSsl, bPipeline, uiFactor, - std::forward(args)...)); - } - else - { - return(SendOriented(pActor->m_pLabor->GetDispatcher(), - 0, strNodeType, bWithSsl, bPipeline, uiFactor, - std::forward(args)...)); - } - } -} - -template -template -bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, - bool bWithSsl, bool bPipeline, uint32 uiFactor, Targs&&... args) +bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, + const std::string& strNodeType, const std::string& strFactor, const ChannelOption& stOption, Targs&&... args) { LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); std::string strOnlineNode; if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) { pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, - "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); - SendTo(pDispatcher, 0, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); + "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); + SendWithoutOption(pDispatcher, 0, strOnlineNode); } - if (pDispatcher->m_pSessionNode->GetNode(strNodeType, uiFactor, strOnlineNode)) + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, strFactor, strOnlineNode)) { - return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); } else { LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) { - return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, stOption, std::forward(args)...)); } LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); return(false); @@ -689,8 +1167,7 @@ bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::s template template -bool IO::SendOriented(Actor* pActor, const std::string& strNodeType, - bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args) +bool IO::BroadcastWithoutOption(Actor* pActor, const std::string& strNodeType, Targs&&... args) { if (pActor == nullptr) { @@ -700,76 +1177,110 @@ bool IO::SendOriented(Actor* pActor, const std::string& strNodeType, { if (pActor->WantResponse()) { - return(SendOriented(pActor->m_pLabor->GetDispatcher(), - pActor->GetSequence(), strNodeType, bWithSsl, bPipeline, strFactor, - std::forward(args)...)); + return(BroadcastWithoutOption(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strNodeType, std::forward(args)...)); } else { - return(SendOriented(pActor->m_pLabor->GetDispatcher(), - 0, strNodeType, bWithSsl, bPipeline, strFactor, - std::forward(args)...)); + return(BroadcastWithoutOption(pActor->m_pLabor->GetDispatcher(), 0, + strNodeType, std::forward(args)...)); } } } template template -bool IO::SendOriented(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, - bool bWithSsl, bool bPipeline, const std::string& strFactor, Targs&&... args) +bool IO::Broadcast(Actor* pActor, const std::string& strNodeType, const ChannelOption& stOption, Targs&&... args) { - LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); - std::string strOnlineNode; - if (pDispatcher->m_pSessionNode->NodeDetect(strNodeType, strOnlineNode)) - { - pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, - "NodeDetect(%s, %s)", strNodeType.c_str(), strOnlineNode.c_str()); - SendTo(pDispatcher, 0, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline); - } - if (pDispatcher->m_pSessionNode->GetNode(strNodeType, strFactor, strOnlineNode)) + if (pActor == nullptr) { - return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + return(false); } else { - LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); - if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + if (pActor->WantResponse()) { - return(SendTo(pDispatcher, uiStepSeq, strOnlineNode, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...)); + return(Broadcast(pActor->m_pLabor->GetDispatcher(), pActor->GetSequence(), + strNodeType, stOption, std::forward(args)...)); + } + else + { + return(Broadcast(pActor->m_pLabor->GetDispatcher(), 0, + strNodeType, stOption, std::forward(args)...)); } - LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); - return(false); } } template template -bool IO::Broadcast(Actor* pActor, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool IO::BroadcastWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, Targs&&... args) { - if (pActor == nullptr) + LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); + std::set setOnlineNodes; + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) { - return(false); + bool bSendResult = false; + auto pChannelOption = pDispatcher->GetChannelOption(strNodeType); + if (pChannelOption == nullptr) + { + ChannelOption stOption; + for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + { + bSendResult |= SendTo(pDispatcher, uiStepSeq, *node_iter, stOption, std::forward(args)...); + } + } + else + { + for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + { + bSendResult |= SendTo(pDispatcher, uiStepSeq, *node_iter, *(pChannelOption.get()), std::forward(args)...); + } + } + return(bSendResult); } else { - if (pActor->WantResponse()) + if ("BEACON" == strNodeType) { - return(Broadcast(pActor->m_pLabor->GetDispatcher(), - pActor->GetSequence(), strNodeType, bWithSsl, bPipeline, - std::forward(args)...)); + pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "no beacon config."); } else { - return(Broadcast(pActor->m_pLabor->GetDispatcher(), - 0, strNodeType, bWithSsl, bPipeline, - std::forward(args)...)); + LOG4_TRACE_DISPATCH("node type \"%s\" not found, go to SplitAddAndGetNode.", strNodeType.c_str()); + std::string strOnlineNode; + if (pDispatcher->m_pSessionNode->SplitAddAndGetNode(strNodeType, strOnlineNode)) + { + if (pDispatcher->m_pSessionNode->GetNode(strNodeType, setOnlineNodes)) + { + bool bSendResult = false; + auto pChannelOption = pDispatcher->GetChannelOption(strNodeType); + if (pChannelOption == nullptr) + { + ChannelOption stOption; + for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + { + bSendResult |= SendTo(pDispatcher, uiStepSeq, *node_iter, stOption, std::forward(args)...); + } + } + else + { + for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) + { + bSendResult |= SendTo(pDispatcher, uiStepSeq, *node_iter, *(pChannelOption.get()), std::forward(args)...); + } + } + return(bSendResult); + } + } + LOG4_TRACE_DISPATCH("no online node match node_type \"%s\"", strNodeType.c_str()); } + return(false); } } template template -bool IO::Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool IO::Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strNodeType, const ChannelOption& stOption, Targs&&... args) { LOG4_TRACE_DISPATCH("node_type: %s", strNodeType.c_str()); std::set setOnlineNodes; @@ -778,7 +1289,7 @@ bool IO::Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::stri bool bSendResult = false; for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) { - bSendResult |= SendTo(pDispatcher, uiStepSeq, *node_iter, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...); + bSendResult |= SendTo(pDispatcher, uiStepSeq, *node_iter, stOption, std::forward(args)...); } return(bSendResult); } @@ -799,7 +1310,7 @@ bool IO::Broadcast(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::stri bool bSendResult = false; for (auto node_iter = setOnlineNodes.begin(); node_iter != setOnlineNodes.end(); ++node_iter) { - bSendResult |= SendTo(pDispatcher, uiStepSeq, *node_iter, SOCKET_STREAM, bWithSsl, bPipeline, std::forward(args)...); + bSendResult |= SendTo(pDispatcher, uiStepSeq, *node_iter, stOption, std::forward(args)...); } return(bSendResult); } @@ -825,15 +1336,120 @@ uint32 IO::SendToSelf(Actor* pActor, Targs&&... args) template template -bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, const std::string& strHost, - int iPort, int iRemoteWorkerIndex, int iSocketType, bool bWithSsl, bool bPipeline, Targs&&... args) +bool IO::AutoSendWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, Targs&&... args) +{ + auto pChannelOption = pDispatcher->GetChannelOption(strIdentify); + if (pChannelOption == nullptr) + { + ChannelOption stOption; + return(AutoSend(pDispatcher, uiStepSeq, strIdentify, stOption, std::forward(args)...)); + } + else + { + return(AutoSend(pDispatcher, uiStepSeq, strIdentify, (*pChannelOption.get()), std::forward(args)...)); + } +} + +template +template +bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, + const std::string& strHost, int iPort, const ChannelOption& stOption, Targs&&... args) { - LOG4_TRACE_DISPATCH("identify %s, host %s port %d, remote_worker_index %d", - strIdentify.c_str(), strHost.c_str(), iPort, iRemoteWorkerIndex); + std::ostringstream ossIdentify; + ossIdentify << strHost << ":" << iPort; + auto pChannelOption = pDispatcher->GetChannelOption(ossIdentify.str()); + if (pChannelOption == nullptr) + { + ChannelOption stOption; + return(AutoSend(pDispatcher, uiStepSeq, ossIdentify.str(), + strHost, iPort, -1, stOption, std::forward(args)...)); + } + else + { + return(AutoSend(pDispatcher, uiStepSeq, ossIdentify.str(), + strHost, iPort, -1, (*pChannelOption.get()), std::forward(args)...)); + } +} + +template +template +bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, + const std::string& strIdentify, const ChannelOption& stOption, Targs&&... args) +{ + std::string strError; + std::string strHost; + int iPort = 0; + int iRemoteWorkerIndex = -1; + if (!SplitIdentify(strIdentify, strHost, iPort, iRemoteWorkerIndex, strError)) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "%s", strError.c_str()); + return(false); + } + return(AutoSend(pDispatcher, uiStepSeq, strIdentify, + strHost, iPort, iRemoteWorkerIndex, stOption, std::forward(args)...)); +} + +template +template +bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, + const std::string& strIdentify, const std::string& strHost, int iPort, + int iRemoteWorkerIndex, const ChannelOption& stOption, Targs&&... args) +{ + std::shared_ptr pChannel = NewSocketChannel(pDispatcher, stOption, strIdentify, strHost, iPort, iRemoteWorkerIndex); + if (nullptr == pChannel) + { + return(false); + } + else + { + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + if (pChannel->WithSsl()) + { +#ifdef WITH_OPENSSL + eCodecStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); +#else + eCodecStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); +#endif + } + else + { + eCodecStatus = std::static_pointer_cast>( + pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); + } + if (pChannel->IsClient()) + { + pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd(), IO_STAT_UPSTREAM_SEND_NUM); + } + else + { + pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd(), IO_STAT_DOWNSTREAM_SEND_NUM); + } + pDispatcher->m_pLastActivityChannel = pChannel; + if (CODEC_STATUS_OK != eCodecStatus + && CODEC_STATUS_PAUSE != eCodecStatus + && CODEC_STATUS_WANT_WRITE != eCodecStatus + && CODEC_STATUS_WANT_READ != eCodecStatus) + { + pDispatcher->DiscardSocketChannel(pChannel); + } + return(true); + } +} + +template +std::shared_ptr IO::NewSocketChannel(Dispatcher* pDispatcher, const ChannelOption& stOption, + const std::string& strIdentify, const std::string& strHost, int iPort, int iRemoteWorkerIndex) +{ + if (pDispatcher->GetChannelOption(strIdentify) == nullptr) + { + pDispatcher->SetChannelOption(strIdentify, stOption); + } struct addrinfo stAddrHints; struct addrinfo* pAddrResult = nullptr; struct addrinfo* pAddrCurrent = nullptr; - auto pSessionAddr = std::static_pointer_cast(pDispatcher->m_pLabor->GetActorBuilder()->GetSession(strIdentify)); + auto pSessionAddr = std::dynamic_pointer_cast(pDispatcher->m_pLabor->GetActorBuilder()->GetSession(strIdentify)); if (pSessionAddr != nullptr) { pAddrResult = pSessionAddr->GetAddrinfo(); @@ -842,23 +1458,24 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::strin { memset(&stAddrHints, 0, sizeof(struct addrinfo)); stAddrHints.ai_family = AF_UNSPEC; - stAddrHints.ai_socktype = iSocketType; + stAddrHints.ai_socktype = stOption.iSocketType; stAddrHints.ai_protocol = IPPROTO_IP; if (inet_addr(strHost.data(), strHost.length()) != 0) { stAddrHints.ai_flags |= (AI_NUMERICHOST | AI_NUMERICSERV); } - LOG4_TRACE_DISPATCH("get addrinfo for %s", strIdentify.c_str()); + + LOG4_TRACE_DISPATCH("getaddrinfo for %s", strIdentify.c_str()); int iCode = getaddrinfo(strHost.c_str(), std::to_string(iPort).c_str(), &stAddrHints, &pAddrResult); if (0 != iCode) { LOG4_TRACE_DISPATCH("getaddrinfo(\"%s\", \"%d\") error %d: %s", strHost.c_str(), iPort, iCode, gai_strerror(iCode)); - return(false); + return(nullptr); } if (pSessionAddr == nullptr) { - pSessionAddr = std::static_pointer_cast( + pSessionAddr = std::dynamic_pointer_cast( pDispatcher->m_pLabor->GetActorBuilder()->MakeSharedSession(nullptr, "neb::TimerAddrinfo", strIdentify)); } if (pSessionAddr != nullptr) @@ -880,6 +1497,14 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::strin break; } + /* No address succeeded */ +// if (pAddrCurrent == NULL) +// { +// LOG4_TRACE_DISPATCH("Could not connect to \"%s:%d\"", strHost.c_str(), iPort); +// freeaddrinfo(pAddrResult); /* No longer needed */ +// return(false); +// } + x_sock_set_block(iFd, 0); int nREUSEADDR = 1; int iKeepAlive = 1; @@ -895,69 +1520,38 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::strin setsockopt(iFd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&iKeepCount, sizeof (iKeepCount)); setsockopt(iFd, IPPROTO_TCP, TCP_NODELAY, (void*)&iTcpNoDelay, sizeof(iTcpNoDelay)); setsockopt(iFd, IPPROTO_TCP, TCP_QUICKACK, (void*)&iTcpQuickAck, sizeof(iTcpQuickAck)); - std::shared_ptr pChannel = CreateSocketChannel(pDispatcher, iFd, true, bWithSsl); + std::shared_ptr pChannel = CreateSocketChannel(pDispatcher, iFd, true, stOption.bWithSsl); if (nullptr != pChannel) { connect(iFd, pAddrCurrent->ai_addr, pAddrCurrent->ai_addrlen); if (pSessionAddr != nullptr && pAddrResult != pSessionAddr->GetAddrinfo()) { - freeaddrinfo(pAddrResult); + freeaddrinfo(pAddrResult); /* No longer needed */ } pDispatcher->m_pLabor->IoStatAddConnection(IO_STAT_UPSTREAM_NEW_CONNECTION); - ev_tstamp dIoTimeout = (pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection > 0) - ? pDispatcher->m_pLabor->GetNodeInfo().dConnectionProtection : pDispatcher->m_pLabor->GetNodeInfo().dIoTimeout; + ev_tstamp dIoTimeout = stOption.dKeepAlive; + pChannel->SetKeepAlive(dIoTimeout); pDispatcher->AddIoTimeout(pChannel, dIoTimeout); pDispatcher->AddIoReadEvent(pChannel); pDispatcher->AddIoWriteEvent(pChannel); std::static_pointer_cast>(pChannel->m_pImpl)->SetIdentify(strIdentify); std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteAddr(strHost); - std::static_pointer_cast>(pChannel->m_pImpl)->SetPipeline(bPipeline); - E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; - if (pChannel->WithSsl()) - { -#ifdef WITH_OPENSSL - eCodecStatus = std::static_pointer_cast>( - pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); -#else - eCodecStatus = std::static_pointer_cast>( - pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); -#endif - } - else - { - eCodecStatus = std::static_pointer_cast>( - pChannel->m_pImpl)->SendRequest(uiStepSeq, std::forward(args)...); - } - if (pChannel->IsClient()) - { - pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd(), IO_STAT_UPSTREAM_SEND_NUM); - } - else - { - pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd(), IO_STAT_DOWNSTREAM_SEND_NUM); - } + std::static_pointer_cast>(pChannel->m_pImpl)->SetPipeline(stOption.bPipeline); pDispatcher->m_pLastActivityChannel = pChannel; - if (CODEC_STATUS_OK != eCodecStatus - && CODEC_STATUS_PAUSE != eCodecStatus - && CODEC_STATUS_WANT_WRITE != eCodecStatus - && CODEC_STATUS_WANT_READ != eCodecStatus) - { - pDispatcher->DiscardSocketChannel(pChannel); - } std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteWorkerIndex(iRemoteWorkerIndex); - if (bPipeline) + if (stOption.bPipeline) { pDispatcher->AddNamedSocketChannel(strIdentify, pChannel); } - return(true); + return(pChannel); } else // 没有足够资源分配给新连接,直接close掉 { freeaddrinfo(pAddrResult); /* No longer needed */ close(iFd); - return(false); + return(nullptr); } } @@ -1172,7 +1766,9 @@ template template bool IO::OnResponse(ActorBuilder* pBuilder, std::shared_ptr pChannel, uint32 uiStreamId, E_CODEC_STATUS eCodecStatus, Targs&&... args) { + LOG4_TRACE_BUILDER("stream id = %u, eCodecStatus = %d", uiStreamId, eCodecStatus); auto uiStepSeq = pChannel->PopStepSeq(uiStreamId, eCodecStatus); + LOG4_TRACE_BUILDER("stream id = %u, step seq = %u", uiStreamId, uiStepSeq); auto step_iter = pBuilder->m_mapCallbackStep.find(uiStepSeq); if (!pChannel->IsPipeline() && pChannel->PipelineIsEmpty()) { @@ -1180,9 +1776,7 @@ bool IO::OnResponse(ActorBuilder* pBuilder, std::shared_ptr pC } if (step_iter == pBuilder->m_mapCallbackStep.end()) { - pBuilder->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, - "no callback for reply from %s!", pChannel->GetIdentify().c_str()); - LOG4_TRACE_BUILDER("no callback for reply from %s, stream id %u, step seq %u", + LOG4_TRACE_BUILDER("no callback for reply from %s, stream id %u, step seq %u!", pChannel->GetIdentify().c_str(), uiStreamId, uiStepSeq); return(false); } @@ -1274,8 +1868,7 @@ std::shared_ptr IO::CreateSocketChannel(Dispatcher* pDispatche if (pChannel != nullptr) { pDispatcher->m_mapSocketChannel.insert(std::make_pair(iFd, pChannel)); - LOG4_TRACE_DISPATCH( - "new channel for fd %d with codec type %d", pChannel->GetFd(), pChannel->GetCodecType()); + LOG4_TRACE_DISPATCH("new channel[%d] with codec type %d", pChannel->GetFd(), pChannel->GetCodecType()); } return(pChannel); } @@ -1286,6 +1879,48 @@ std::shared_ptr IO::CreateSocketChannel(Dispatcher* pDispatche } } +template +bool IO::SplitIdentify(const std::string& strIdentify, std::string& strHost, int& iPort, int& iWorkerIndex, std::string& strError) +{ + size_t iPosIpPortSeparator = strIdentify.rfind(':'); + size_t iPosPortWorkerIndexSeparator = strIdentify.rfind('.'); + if (iPosIpPortSeparator == std::string::npos) + { + return(false); + } + strHost = strIdentify.substr(0, iPosIpPortSeparator); + std::string strPort; + if (iPosPortWorkerIndexSeparator != std::string::npos && iPosPortWorkerIndexSeparator > iPosIpPortSeparator) + { + strPort = strIdentify.substr(iPosIpPortSeparator + 1, iPosPortWorkerIndexSeparator - (iPosIpPortSeparator + 1)); + iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + return(false); + } + std::string strWorkerIndex = strIdentify.substr(iPosPortWorkerIndexSeparator + 1, std::string::npos); + if (strWorkerIndex.size() > 0) + { + iWorkerIndex = atoi(strWorkerIndex.c_str()); + if (iWorkerIndex > 200) + { + strError = "worker index must smaller than 200"; + return(false); + } + } + } + else + { + strPort = strIdentify.substr(iPosIpPortSeparator + 1, std::string::npos); + iPort = atoi(strPort.c_str()); + if (iPort == 0) + { + return(false); + } + } + return(true); +}; + } /* namespace neb */ #endif /* SRC_IOS_IO_HPP_ */ diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index 78977d19..4d54fcad 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -404,36 +404,45 @@ bool Nodes::SplitAddAndGetNode(const std::string& strNodeType, std::string& strN return(GetNode(strNodeType, strNodeIdentify)); } -bool Nodes::GetAuth(const std::string& strNodeType, std::string& strAuth, std::string& strPassword) +bool Nodes::GetAuth(const std::string& strIdentify, std::string& strAuth, std::string& strPassword) { - auto node_type_iter = m_mapNode.find(strNodeType); - if (node_type_iter == m_mapNode.end()) + auto iter = m_mapChannelOption.find(strIdentify); + if (iter == m_mapChannelOption.end()) { return(false); } else { - strAuth = node_type_iter->second->strAuth; - strPassword = node_type_iter->second->strPassword; + strAuth = iter->second->strAuth; + strPassword = iter->second->strPassword; return(true); } } -void Nodes::SetAuth(const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword) +std::shared_ptr Nodes::GetChannelOption(const std::string& strIdentify) { - auto node_type_iter = m_mapNode.find(strNodeType); - if (node_type_iter == m_mapNode.end()) + auto iter = m_mapChannelOption.find(strIdentify); + if (iter == m_mapChannelOption.end()) { - std::shared_ptr pNode = std::make_shared(); - m_mapNode.insert(std::make_pair(strNodeType, pNode)); - pNode->strNodeType = strNodeType; - pNode->strAuth = strAuth; - pNode->strPassword = strPassword; + return(nullptr); + } + else + { + return(iter->second); + } +} + +void Nodes::SetChannelOption(const std::string& strIdentify, const ChannelOption& stOption) +{ + auto pOption = std::make_shared(stOption); + auto iter = m_mapChannelOption.find(strIdentify); + if (iter == m_mapChannelOption.end()) + { + m_mapChannelOption.insert(std::make_pair(strIdentify, pOption)); } else { - node_type_iter->second->strAuth = strAuth; - node_type_iter->second->strPassword = strPassword; + iter->second = pOption; } } diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index 91e7105d..16169b89 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -18,6 +18,7 @@ #include #include #include "Definition.hpp" +#include "channel/Channel.hpp" //#define ROT32(x, y) ((x << y) | (x >> (32 - y))) // avoid effort @@ -58,8 +59,6 @@ class Nodes { bool bCheckFailedNode = false; std::string strNodeType; - std::string strAuth; - std::string strPassword; T_NODE2HASH_MAP mapNode2Hash; T_NODE2HASH_MAP::iterator itPollingNode; std::map mapHash2Node; @@ -114,9 +113,11 @@ class Nodes */ bool SplitAddAndGetNode(const std::string& strNodeType, std::string& strNodeIdentify); - bool GetAuth(const std::string& strNodeType, std::string& strAuth, std::string& strPassword); + bool GetAuth(const std::string& strIdentify, std::string& strAuth, std::string& strPassword); - void SetAuth(const std::string& strNodeType, const std::string& strAuth, const std::string& strPassword); + std::shared_ptr GetChannelOption(const std::string& strIdentify); + + void SetChannelOption(const std::string& strIdentify, const ChannelOption& stOption); /** * @brief 删除节点 @@ -154,6 +155,7 @@ class Nodes std::unordered_map > m_mapNode; std::unordered_map> m_mapNodeType; // key为节点标识 + std::unordered_map> m_mapChannelOption; // key为节点标识 }; } /* namespace neb */ diff --git a/src/type/Package.cpp b/src/type/Package.cpp new file mode 100644 index 00000000..09ff0058 --- /dev/null +++ b/src/type/Package.cpp @@ -0,0 +1,75 @@ +/******************************************************************************* + * Project: Nebula + * @file Package.cpp + * @brief + * @author Bwar + * @date: 2023-01-25 + * @note + * Modify history: + ******************************************************************************/ +#include "Package.hpp" + +namespace neb +{ + +Package::Package() +{ +} + +Package::Package(Package&& oPackage) +{ + m_iType = oPackage.m_iType; + m_pPayload = oPackage.m_pPayload; + oPackage.m_iType = 0; + oPackage.m_pPayload = nullptr; +} + +Package::~Package() +{ +} + +Package& Package::operator=(Package&& oPackage) +{ + m_iType = oPackage.m_iType; + m_pPayload = oPackage.m_pPayload; + oPackage.m_iType = 0; + oPackage.m_pPayload = nullptr; + return(*this); +} + +/* +// example +struct ChannelOption +{ + bool bPipeline = false; + bool bWithSsl = false; + std::string strAuth = "redis"; + std::string strPassword; + static int Type() + { + return(1); + } +}; + +int main(int argc, char const *argv[]) +{ + ChannelOption* pOption = new ChannelOption(); + Package oPackage; + std::cout << pOption->strAuth << std::endl; + std::cout << "pOption = " << pOption << ", &pOption = " << &pOption << std::endl; + oPackage.Pack(&pOption); + std::cout << "pOption = " << pOption << ", &pOption = " << &pOption << std::endl; + + ChannelOption* pMigrateOption = nullptr; + std::cout << "pMigrateOption = " << pMigrateOption << ", &pMigrateOption = " << &pMigrateOption << std::endl; + std::cout << "unpack result " << oPackage.Unpack(&pMigrateOption) << std::endl; + std::cout << "pMigrateOption = " << pMigrateOption << ", &pMigrateOption = " << &pMigrateOption << std::endl; + std::cout << pMigrateOption->strAuth << std::endl; + + return 0; +} +*/ + + +} /* namespace neb */ + diff --git a/src/type/Package.hpp b/src/type/Package.hpp new file mode 100644 index 00000000..f9aee891 --- /dev/null +++ b/src/type/Package.hpp @@ -0,0 +1,109 @@ +/******************************************************************************* + * Project: Nebula + * @file Package.hpp + * @brief + * @author Bwar + * @date: 2023-01-25 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_TYPE_PACKAGE_HPP_ +#define SRC_TYPE_PACKAGE_HPP_ + +namespace neb +{ + +struct Item +{ + static int Type() + { + return(0); + } + + // data +}; + +class Package +{ +public: + Package(); + Package(const Package&) = delete; + Package(Package&& oPackage); + virtual ~Package(); + + Package& operator=(const Package& oPackage) = delete; + Package& operator=(Package&& oPackage); + + /** + * @brief put item into package + * @note put item into package, and clear source address of the item. + * The item class must implement a static Type() method. + */ + template + void Pack(T** item); + + template + void Pack(int type, T** item); + + /** + * @brief take out item from package + * @note take out item from package, and clear package. + * The item class must implement a static Type() method. + */ + template + bool Unpack(T** item); + + template + bool Unpack(int type, T** item); + +private: + int m_iType = 0; + void* m_pPayload = nullptr; +}; + +template +void Package::Pack(T** item) +{ + m_iType = T::Type(); + m_pPayload = (void*)(*item); + *item = nullptr; +} + +template +void Package::Pack(int type, T** item) +{ + m_iType = type; + m_pPayload = (void*)(*item); + *item = nullptr; +} + +template +bool Package::Unpack(T** item) +{ + if (T::Type() == m_iType) + { + *item = (T*)m_pPayload; + m_pPayload = nullptr; + m_iType = 0; + return(true); + } + return(false); +} + +template +bool Package::Unpack(int type, T** item) +{ + if (type == m_iType) + { + *item = (T*)m_pPayload; + m_pPayload = nullptr; + m_iType = 0; + return(true); + } + return(false); +} + +} /* namespace neb */ + +#endif /* SRC_TYPE_PACKAGE_HPP_ */ + diff --git a/src/util/StringCoder.cpp b/src/util/StringCoder.cpp index 8349341d..6f0fab93 100644 --- a/src/util/StringCoder.cpp +++ b/src/util/StringCoder.cpp @@ -189,7 +189,6 @@ void Split(const std::string& strSrc, const std::string& strPattern, std::vector p = strsep(&buff, strPattern.data()); while (p != NULL) { - printf("%s\n", p); vecDest.push_back(p); p = strsep(&buff, strPattern.data()); } From 22e9e3b8db0d40c73a8a30461aa707b9495247fc Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 18 Feb 2023 19:57:02 +0800 Subject: [PATCH 172/176] add DeliverCmd and DeliverStep; console log bug fixed. --- src/actor/cmd/CW.hpp | 2 ++ src/actor/cmd/DeliverCmd.hpp | 53 ++++++++++++++++++++++++++++++ src/actor/step/DeliverStep.cpp | 25 ++++++++++++++ src/actor/step/DeliverStep.hpp | 40 ++++++++++++++++++++++ src/codec/Codec.hpp | 1 + src/codec/CodecFactory.cpp | 19 +++++++++++ src/codec/CodecFactory.hpp | 2 ++ src/codec/virtual/CodecDeliver.hpp | 2 +- src/ios/IO.hpp | 22 ++++++------- src/labor/Manager.cpp | 2 +- src/logger/NetLogger.cpp | 4 +-- src/type/Package.cpp | 7 ++++ src/type/Package.hpp | 5 +++ 13 files changed, 169 insertions(+), 15 deletions(-) create mode 100644 src/actor/cmd/DeliverCmd.hpp create mode 100644 src/actor/step/DeliverStep.cpp create mode 100644 src/actor/step/DeliverStep.hpp diff --git a/src/actor/cmd/CW.hpp b/src/actor/cmd/CW.hpp index 78a6e5d6..9d1dad00 100644 --- a/src/actor/cmd/CW.hpp +++ b/src/actor/cmd/CW.hpp @@ -46,6 +46,8 @@ enum E_CMD CMD_RSP_FD_TRANSFER = 20, ///< 通过SpecChannel传送文件描述符响应(无须响应) CMD_REQ_CHANNEL_MIGRATE = 21, ///< 通过SpecChannel迁移SocketChannel请求 CMD_RSP_CHANNEL_MIGRATE = 22, ///< 通过SpecChannel迁移SocketChannel响应(无须响应) + CMD_REQ_DELIVER = 23, ///< 通过SpecChannel直传Package请求 + CMD_RSP_DELIVER = 24, ///< 通过SpecChannel直传Package响应 CMD_REQ_NODE_STATUS_REPORT = 101, ///< 节点Server状态上报请求(各节点向控制中心上报自身状态信息) CMD_RSP_NODE_STATUS_REPORT = 102, ///< 节点Server状态上报应答 diff --git a/src/actor/cmd/DeliverCmd.hpp b/src/actor/cmd/DeliverCmd.hpp new file mode 100644 index 00000000..7234d782 --- /dev/null +++ b/src/actor/cmd/DeliverCmd.hpp @@ -0,0 +1,53 @@ +/******************************************************************************* + * Project: Nebula + * @file DeliverCmd.hpp + * @brief 线程间数据包传送 + * @author Bwar + * @date: 2023-02-11 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_CMD_DELIVERCMD_HPP_ +#define SRC_ACTOR_CMD_DELIVERCMD_HPP_ + +#include "Cmd.hpp" +#include "type/Package.hpp" + +namespace neb +{ + +/** + * @brief 数据包直传 + */ +class DeliverCmd: public Cmd +{ +public: + DeliverCmd(int32 iCmd) + : Cmd(iCmd) + { + } + DeliverCmd(const DeliverCmd&) = delete; + DeliverCmd& operator=(const DeliverCmd&) = delete; + virtual ~DeliverCmd(){}; + + virtual bool Init() + { + return(true); + } + + virtual bool AnyMessage( + std::shared_ptr pChannel, + const MsgHead& oMsgHead, const MsgBody& oMsgBody) + { + return(false); + } + + virtual bool AnyMessage( + std::shared_ptr pChannel, + Package&& oPackage) = 0; +}; + +} + +#endif /* SRC_ACTOR_CMD_DELIVERCMD_HPP_ */ + diff --git a/src/actor/step/DeliverStep.cpp b/src/actor/step/DeliverStep.cpp new file mode 100644 index 00000000..58106e0b --- /dev/null +++ b/src/actor/step/DeliverStep.cpp @@ -0,0 +1,25 @@ +/******************************************************************************* + * Project: Nebula + * @file DeliverStep.cpp + * @brief + * @author Bwar + * @date: 2023-02-11 + * @note + * Modify history: + ******************************************************************************/ +#include "DeliverStep.hpp" + +namespace neb +{ + +DeliverStep::DeliverStep(ev_tstamp dTimeout) + : Step(ACT_REDIS_STEP, dTimeout) +{ +} + +DeliverStep::~DeliverStep() +{ +} + +} /* namespace neb */ + diff --git a/src/actor/step/DeliverStep.hpp b/src/actor/step/DeliverStep.hpp new file mode 100644 index 00000000..9dd3c774 --- /dev/null +++ b/src/actor/step/DeliverStep.hpp @@ -0,0 +1,40 @@ +/******************************************************************************* + * Project: Nebula + * @file DeliverStep.hpp + * @brief + * @author Bwar + * @date: 2023-02-11 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_ACTOR_STEP_DELIVERSTEP_HPP_ +#define SRC_ACTOR_STEP_DELIVERSTEP_HPP_ + +#include "Step.hpp" +#include "type/Package.hpp" + +namespace neb +{ + +class DeliverStep: public Step +{ +public: + DeliverStep(ev_tstamp dTimeout = gc_dConfigTimeout); + DeliverStep(const DeliverStep&) = delete; + DeliverStep& operator=(const DeliverStep&) = delete; + virtual ~DeliverStep(); + + /** + * @brief redis步骤回调 + * @param pChannel 数据来源通道 + * @param oRedisReply redis响应 + */ + virtual E_CMD_STATUS Callback( + std::shared_ptr pChannel, + Package&& oPackage) = 0; +}; + +} /* namespace neb */ + +#endif /* SRC_ACTOR_STEP_DELIVERSTEP_HPP_ */ + diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 58581d1c..188f6354 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -51,6 +51,7 @@ enum E_CODEC_TYPE CODEC_DIRECT = 51, ///< 虚拟编解码类型,用于SelfChannel,以参数方式直接传递数据包 CODEC_TRANSFER = 52, ///< 虚拟编解码类型,用于SpecChannel CODEC_CHANNEL_MIGRATE = 53, ///< 虚拟编解码类型,用于SocketChannel迁移 + CODEC_DELIVER = 54, ///< 虚拟编解码类型,用于SpecChannel直传数据包 CODEC_MAX }; diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp index 761e5fd3..7c4ee38b 100644 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -276,6 +276,25 @@ E_CODEC_STATUS CodecFactory::OnEvent(SpecChannelWatcher* pAsyncWatcher, std::sha } } break; + case CODEC_DELIVER: + { + Package oPackage; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->m_pLastActivityChannel = pChannel; + while (pSpecChannel->Read(uiFlags, uiStepSeq, oPackage)) + { + if (gc_uiCmdReq & uiFlags) + { + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_DELIVER, std::move(oPackage)); + } + else + { + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, std::move(oPackage)); + } + } + } + break; case CODEC_UNKNOW: break; default: diff --git a/src/codec/CodecFactory.hpp b/src/codec/CodecFactory.hpp index 5b7d4aa5..a48ab2c3 100644 --- a/src/codec/CodecFactory.hpp +++ b/src/codec/CodecFactory.hpp @@ -24,11 +24,13 @@ #include "actor/cmd/Module.hpp" #include "actor/cmd/RedisCmd.hpp" #include "actor/cmd/RawCmd.hpp" +#include "actor/cmd/DeliverCmd.hpp" #include "actor/step/PbStep.hpp" #include "actor/step/HttpStep.hpp" #include "actor/step/RedisStep.hpp" #include "actor/step/RawStep.hpp" #include "actor/step/CassStep.hpp" +#include "actor/step/DeliverStep.hpp" namespace neb { diff --git a/src/codec/virtual/CodecDeliver.hpp b/src/codec/virtual/CodecDeliver.hpp index 2e19cfe4..69cd2d26 100644 --- a/src/codec/virtual/CodecDeliver.hpp +++ b/src/codec/virtual/CodecDeliver.hpp @@ -26,7 +26,7 @@ class CodecDeliver static E_CODEC_TYPE Type() { - return(CODEC_RESP); + return(CODEC_DELIVER); } // request diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index 23109371..a239188f 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -267,17 +267,6 @@ template template bool IO::SendResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, Targs&&... args) { - if (pChannel->m_pImpl == nullptr) - { - pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, - "no channel impl"); - return(false); - } - if (CODEC_UNKNOW == pChannel->GetCodecType()) - { - LOG4_TRACE_DISPATCH("CODEC_UNKNOW is invalid, channel had not been init?"); - return(false); - } if (pChannel->GetCodecType() == CODEC_DIRECT) // self channel { auto pSelfChannel = std::dynamic_pointer_cast(pChannel); @@ -289,6 +278,17 @@ bool IO::SendResponse(Dispatcher* pDispatcher, std::shared_ptr pSelfChannel->SetResponse(); return(CodecFactory::OnSelfResponse(pDispatcher, pSelfChannel, std::forward(args)...)); } + if (pChannel->m_pImpl == nullptr) + { + pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, + "no channel impl"); + return(false); + } + if (CODEC_UNKNOW == pChannel->GetCodecType()) + { + LOG4_TRACE_DISPATCH("CODEC_UNKNOW is invalid, channel had not been init?"); + return(false); + } if (T::Type() != pChannel->GetCodecType()) { E_CODEC_TYPE eOriginCodecType = pChannel->GetCodecType(); diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 77baf6c9..97ea0a51 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -149,7 +149,7 @@ bool Manager::InitLogger(const CJsonObject& oJsonConf) { bool bConsoleLog = false; m_oCurrentConf.Get("console_log", bConsoleLog); - m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, bAlwaysFlushLog, this); + m_pLogger = std::make_shared(strLogname, iLogLevel, iMaxLogFileSize, iMaxLogFileNum, bAlwaysFlushLog, bConsoleLog, this); } m_pLogger->SetNetLogLevel(iNetLogLevel); LOG4_NOTICE("%s program begin, and work path %s...", oJsonConf("server_name").c_str(), m_stNodeInfo.strWorkPath.c_str()); diff --git a/src/logger/NetLogger.cpp b/src/logger/NetLogger.cpp index ad7af36c..303ea660 100644 --- a/src/logger/NetLogger.cpp +++ b/src/logger/NetLogger.cpp @@ -27,9 +27,9 @@ NetLogger::NetLogger(const std::string& strLogFile, int iLogLev, unsigned int ui m_bEnableNetLogger(false), m_pLabor(pLabor), m_pLog(nullptr) { #if __cplusplus >= 201401L - m_pLog = std::make_unique(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex, bConsoleLog, bAlwaysFlush); + m_pLog = std::make_unique(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex, bAlwaysFlush, bConsoleLog); #else - m_pLog = std::unique_ptr(new FileLogger(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex, bConsoleLog, bAlwaysFlush)); + m_pLog = std::unique_ptr(new FileLogger(strLogFile, iLogLev, uiMaxFileSize, uiMaxRollFileIndex, bAlwaysFlush, bConsoleLog)); #endif } diff --git a/src/type/Package.cpp b/src/type/Package.cpp index 09ff0058..77ffda49 100644 --- a/src/type/Package.cpp +++ b/src/type/Package.cpp @@ -8,6 +8,7 @@ * Modify history: ******************************************************************************/ #include "Package.hpp" +#include namespace neb { @@ -26,6 +27,12 @@ Package::Package(Package&& oPackage) Package::~Package() { + if (m_pPayload != nullptr) + { + std::cerr << "there may be payload memory leak!" << std::endl; + free(m_pPayload); // There may be destructors that have not been called, it should be avoided. + m_pPayload = nullptr; + } } Package& Package::operator=(Package&& oPackage) diff --git a/src/type/Package.hpp b/src/type/Package.hpp index f9aee891..e4a3cf33 100644 --- a/src/type/Package.hpp +++ b/src/type/Package.hpp @@ -34,6 +34,11 @@ class Package Package& operator=(const Package& oPackage) = delete; Package& operator=(Package&& oPackage); + int GetPayloadType() const + { + return(m_iType); + } + /** * @brief put item into package * @note put item into package, and clear source address of the item. From 674e18b7d456143e62919564410a30b2e99cf5f0 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sun, 9 Apr 2023 18:12:34 +0800 Subject: [PATCH 173/176] 1. Added custom delayed startup. 2. package passes the data in shared_ptr. 3. Fixed http2 receiving and sending after V1.7 --- conf/nebula.json | 8 +- src/actor/Actor.cpp | 2 +- src/actor/ActorBuilder.cpp | 25 +++- src/actor/ActorBuilder.hpp | 6 +- src/actor/ActorSender.cpp | 4 +- src/actor/cmd/sys_cmd/CmdNodeNotice.cpp | 4 + .../sys_session/manager/SessionManager.cpp | 2 +- src/channel/Channel.hpp | 1 + src/codec/CodecFactory.cpp | 35 +++++ src/codec/CodecFactory.hpp | 3 + src/codec/http2/CodecHttp2.cpp | 54 ++++++++ src/codec/http2/CodecHttp2.hpp | 7 +- src/codec/virtual/CodecDeliver.cpp | 35 ++++- src/codec/virtual/CodecDeliver.hpp | 12 +- src/ios/Dispatcher.hpp | 2 +- src/ios/IO.hpp | 8 +- src/ios/Nodes.hpp | 8 +- src/labor/Loader.cpp | 4 +- src/labor/Manager.cpp | 7 +- src/labor/Worker.cpp | 24 +++- src/labor/Worker.hpp | 3 +- src/type/Package.cpp | 7 +- src/type/Package.hpp | 36 +++-- src/util/StringCoder.cpp | 12 ++ src/util/StringCoder.hpp | 2 + src/util/StringConverter.hpp | 129 ++++++++++++++++++ 26 files changed, 381 insertions(+), 59 deletions(-) diff --git a/conf/nebula.json b/conf/nebula.json index 84a3927f..51f7bf1a 100644 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -6,7 +6,7 @@ "//access_port": "对系统外提供服务监听的端口", "access_port": 9988, "access_ports":[], - "//access_codec": "接入端编解码器,见codec/Codec.hpp中E_CODEC_TYPE枚举定义" + "//access_codec": "接入端编解码器,见codec/Codec.hpp中E_CODEC_TYPE枚举定义", "access_codec": 4, "access_socket_type":"TCP", "//need_channel_verify":"是否需要连接验证,如设置为true,第一个消息必须是验证消息,未经验证的连接收到第二个消息会被立即关闭", @@ -31,13 +31,15 @@ "connection_dispatcher":{"round_robin":0, "min_load":1, "client_addr_hash":2}, "connection_dispatch":0, "//access_port_to_worker":"端口到worker映射关系", - "access_port_to_worker":[ + "access_port_to_worker":{ "9919":[1,3,5,7,9,11,13,15], "9921":[2,4,6,8,10,12,14,16] - ], + }, "daemonize":true, "//worker_capacity": "子进程最大工作负荷", "worker_capacity": 1000000, + "//loader_custom_start_service":"是否由loader线程自定义开启端口", + "loader_custom_start_service":false, "//config_path": "配置文件路径(相对路径)", "config_path": "conf/", "//log_path": "日志文件路径(相对路径)", diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp index 116d953a..e092c961 100644 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -134,7 +134,7 @@ std::shared_ptr Actor::GetSession(uint32 uiSessionId) std::shared_ptr Actor::GetSession(const std::string& strSessionId) { auto pSession = m_pLabor->GetActorBuilder()->GetSession(strSessionId); - if (pSession == nullptr) + if (pSession == nullptr && GetWorkerIndex() != 0) { auto pActorBuilder = m_pLabor->GetLoaderActorBuilder(); if (pActorBuilder != nullptr) diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp index c1de301b..ad4492ad 100644 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -55,9 +55,14 @@ ActorBuilder::~ActorBuilder() } } -bool ActorBuilder::Init(CJsonObject& oBootLoadConf, CJsonObject& oDynamicLoadConf) +bool ActorBuilder::Init() { LoadSysCmd(); + return(true); +} + +bool ActorBuilder::LoadCmd(CJsonObject& oBootLoadConf, CJsonObject& oDynamicLoadConf) +{ BootLoadCmd(oBootLoadConf); DynamicLoad(oDynamicLoadConf); std::string strReportSessionId = "neb::SessionDataReport"; @@ -70,7 +75,7 @@ bool ActorBuilder::Init(CJsonObject& oBootLoadConf, CJsonObject& oDynamicLoadCon return(true); } -bool ActorBuilder::Init(CJsonObject& oDynamicLoadConf) +bool ActorBuilder::LoadCmd(CJsonObject& oDynamicLoadConf) { DynamicLoad(oDynamicLoadConf); return(true); @@ -204,11 +209,17 @@ bool ActorBuilder::OnMessage(std::shared_ptr pChannel, const MsgH if (CMD_REQ_SET_LOG_LEVEL == oMsgHead.cmd()) { LogLevel oLogLevel; - oLogLevel.ParseFromString(oMsgBody.data()); - LOG4_INFO("log level set to %d, net_log_level set to %d", - oLogLevel.log_level(), oLogLevel.net_log_level()); - m_pLogger->SetLogLevel(oLogLevel.log_level()); - m_pLogger->SetNetLogLevel(oLogLevel.net_log_level()); + if (oLogLevel.ParseFromString(oMsgBody.data())) + { + LOG4_INFO("log level set to %d, net_log_level set to %d", + oLogLevel.log_level(), oLogLevel.net_log_level()); + m_pLogger->SetLogLevel(oLogLevel.log_level()); + m_pLogger->SetNetLogLevel(oLogLevel.net_log_level()); + } + else + { + LOG4_ERROR("LogLevel parse failed."); + } } else if (CMD_REQ_RELOAD_SO == oMsgHead.cmd()) { diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp index 83bbe553..c5cb7668 100644 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -85,8 +85,9 @@ class ActorBuilder public: ActorBuilder(Labor* pLabor, std::shared_ptr pLogger); virtual ~ActorBuilder(); - bool Init(CJsonObject& oBootLoadConf, CJsonObject& oDynamicLoadConf); - bool Init(CJsonObject& oDynamicLoadConf); + bool Init(); + bool LoadCmd(CJsonObject& oBootLoadConf, CJsonObject& oDynamicLoadConf); + bool LoadCmd(CJsonObject& oDynamicLoadConf); static void StepTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); static void SessionTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); @@ -149,6 +150,7 @@ class ActorBuilder bool ReloadCmdConf(); bool AddNetLogMsg(const MsgBody& oMsgBody); void AddChainConf(const std::string& strChainKey, std::queue >&& queChainBlocks); + // The application layer should call this function carefully to prevent the worker from deleting ther loader's session void RemoveSession(std::shared_ptr pSession); protected: diff --git a/src/actor/ActorSender.cpp b/src/actor/ActorSender.cpp index ec148f23..38d9a7c1 100644 --- a/src/actor/ActorSender.cpp +++ b/src/actor/ActorSender.cpp @@ -197,7 +197,7 @@ bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, oHttpMsg.mutable_headers()->insert({"content-type", "application/grpc"}); oHttpMsg.add_adding_never_index_headers("grpc-status"); oHttpMsg.add_adding_never_index_headers("grpc-message"); - oHttpMsg.add_adding_never_index_headers("x-trace-id"); + //oHttpMsg.add_adding_never_index_headers("x-trace-id"); if (eStatus == GRPC_OK) { uint8 ucCompressedFlag = 0; @@ -245,7 +245,7 @@ bool ActorSender::SendTo(Actor* pActor, std::shared_ptr pChannel, pHeader = oHttpMsg.add_trailer_header(); pHeader->set_name("grpc-message"); pHeader->set_value(strStatusMessage); - return(SendTo(pActor, pChannel, oHttpMsg)); + return(IO::SendResponse(pActor, pChannel, oHttpMsg)); } bool ActorSender::SendTo(Actor* pActor, const std::string& strUrl, const std::string& strGrpcRequest, E_COMPRESSION eCompression) diff --git a/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp b/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp index 50eb5442..6fe92569 100644 --- a/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp +++ b/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp @@ -35,6 +35,10 @@ bool CmdNodeNotice::AnyMessage( MsgBody oOutMsgBody; CJsonObject oJson; + if (oInMsgBody.data().empty()) + { + return(false); + } if (oJson.Parse(oInMsgBody.data())) { LOG4_DEBUG("CmdNodeNotice seq[%llu] jsonbuf[%s] Parse is ok", diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index 799b1de6..fdc46e7c 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -199,7 +199,7 @@ bool SessionManager::SendToChild(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBo MsgBody oOutMsgBody(oMsgBody); oMsgHead.set_cmd(iCmd); oMsgHead.set_seq(uiSeq); - IO::TransmitTo(this, m_vecWorkerInfo[i]->iWorkerIndex, uiSeq, oMsgHead, oMsgBody); + IO::TransmitTo(this, m_vecWorkerInfo[i]->iWorkerIndex, uiSeq, oMsgHead, oOutMsgBody); } return(true); } diff --git a/src/channel/Channel.hpp b/src/channel/Channel.hpp index a8191908..d9ddc8c9 100644 --- a/src/channel/Channel.hpp +++ b/src/channel/Channel.hpp @@ -20,6 +20,7 @@ struct ChannelOption bool bPipeline = false; bool bWithSsl = false; int iSocketType = SOCKET_STREAM; + uint32 uiMaxSendBuffSize = gc_iMaxBuffLen; ev_tstamp dKeepAlive = 7.0; std::string strAuth; std::string strPassword; diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp index 7c4ee38b..174cd745 100644 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -435,6 +435,26 @@ bool CodecFactory::OnSpecChannelCreated(uint32 uiCodecType, uint32 uiFromLabor, } } break; + case CODEC_DELIVER: + { + Package oPackage; + auto pSpecChannel = std::static_pointer_cast>(pChannel); + auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + pDispatcher->AddEvent(pSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); + pDispatcher->m_pLastActivityChannel = pChannel; + while (pSpecChannel->Read(uiFlags, uiStepSeq, oPackage)) + { + if (gc_uiCmdReq & uiFlags) + { + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_DELIVER, std::move(oPackage)); + } + else + { + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, std::move(oPackage)); + } + } + } + break; case CODEC_PRIVATE: break; case CODEC_CHANNEL_MIGRATE: @@ -532,6 +552,21 @@ bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr::OnResponse(pDispatcher, pSocketChannel, pChannel->GetStepSeq(), pRaw, uiRawSize)); } +bool CodecFactory::OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, int32 iCmd, Package&& oPackage) +{ + pChannel->SetStepSeq(uiStepSeq); + auto pSocketChannel = std::static_pointer_cast(pChannel); + pDispatcher->m_pLastActivityChannel = pSocketChannel; + return(IO::OnRequest(pDispatcher, pSocketChannel, iCmd, std::move(oPackage))); +} + +bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, Package&& oPackage) +{ + auto pSocketChannel = std::static_pointer_cast(pChannel); + pDispatcher->m_pLastActivityChannel = pSocketChannel; + return(IO::OnResponse(pDispatcher, pSocketChannel, pChannel->GetStepSeq(), std::move(oPackage))); +} + bool CodecFactory::OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const CassMessage& oCassMsg) { auto pSocketChannel = std::static_pointer_cast(pChannel); diff --git a/src/codec/CodecFactory.hpp b/src/codec/CodecFactory.hpp index a48ab2c3..9bdf43ea 100644 --- a/src/codec/CodecFactory.hpp +++ b/src/codec/CodecFactory.hpp @@ -65,6 +65,9 @@ class CodecFactory static bool OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const char* pRaw, uint32 uiRawSize); static bool OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const char* pRaw, uint32 uiRawSize); + static bool OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, int32 iCmd, Package&& oPackage); + static bool OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, Package&& oPackage); + //static bool OnSelfRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, const CassMessage& oCassMsg); static bool OnSelfResponse(Dispatcher* pDispatcher, std::shared_ptr pChannel, const CassMessage& oCassMsg); diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index 22992661..9e4eb41c 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -60,6 +60,60 @@ CodecHttp2::~CodecHttp2() LOG4_TRACE("codec type %d", GetCodecType()); } +// request +int CodecHttp2::Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg) +{ + if (uiFromLabor == uiToLabor) + { + return(ERR_SPEC_CHANNEL_TARGET); + } + std::shared_ptr> pSpecChannel = nullptr; + auto pLaborShared = LaborShared::Instance(); + auto pChannel = pLaborShared->GetSpecChannel(Type(), uiFromLabor, uiToLabor); + if (pChannel == nullptr) + { + pSpecChannel = std::make_shared>( + uiFromLabor, uiToLabor, pLaborShared->GetSpecChannelQueueSize(), true); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CREATE); + } + pChannel = std::dynamic_pointer_cast(pSpecChannel); + auto pWatcher = pSpecChannel->MutableWatcher(); + pWatcher->Set(pChannel, Type()); + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oHttpMsg))); + if (iResult == ERR_OK) + { + return(pLaborShared->AddSpecChannel(Type(), uiFromLabor, uiToLabor, pChannel)); + } + return(iResult); + } + else + { + pSpecChannel = std::static_pointer_cast>(pChannel); + if (pSpecChannel == nullptr) + { + return(ERR_SPEC_CHANNEL_CAST); + } + int iResult = pSpecChannel->Write(uiFlags, uiStepSeq, std::move(const_cast(oHttpMsg))); + if (iResult == ERR_OK) + { + pLaborShared->GetDispatcher(uiToLabor)->AsyncSend(pSpecChannel->MutableWatcher()->MutableAsyncWatcher()); + } + return(iResult); + } +} + +// response +int CodecHttp2::Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg) +{ + uint32 uiFrom; + uint32 uiTo; + std::static_pointer_cast>(pChannel)->GetEnds(uiFrom, uiTo); + return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, oHttpMsg)); +} + + void CodecHttp2::ConnectionSetting(CBuffer* pBuff) { std::vector vecSetting; diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index ec4ef621..bffc6d51 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -17,6 +17,8 @@ #include "H2Comm.hpp" #include "Tree.hpp" #include "Http2Header.hpp" +#include "channel/SpecChannel.hpp" +#include "labor/LaborShared.hpp" namespace neb { @@ -44,7 +46,10 @@ class CodecHttp2: public Codec return(CODEC_HTTP2); } - virtual bool DecodeWithReator() const + static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg); + static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg); + + virtual bool DecodeWithReactor() const { return(true); } diff --git a/src/codec/virtual/CodecDeliver.cpp b/src/codec/virtual/CodecDeliver.cpp index 35bf790e..afd1a965 100644 --- a/src/codec/virtual/CodecDeliver.cpp +++ b/src/codec/virtual/CodecDeliver.cpp @@ -12,7 +12,9 @@ namespace neb { -CodecDeliver::CodecDeliver() +CodecDeliver::CodecDeliver(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel) + : Codec(pLogger, eCodecType, pBindChannel) { } @@ -73,5 +75,36 @@ int CodecDeliver::Write(std::shared_ptr pChannel, uint32 uiFlags, return(Write(uiTo, uiFrom, uiFlags, uiStepSeq, std::forward(oPackage))); } +E_CODEC_STATUS CodecDeliver::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + +E_CODEC_STATUS CodecDeliver::Encode(Package&& oPackage, CBuffer* pBuff) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + +E_CODEC_STATUS CodecDeliver::Encode(Package&& oPackage, CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + +E_CODEC_STATUS CodecDeliver::Decode(CBuffer* pBuff, Package&& oPackage) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + +E_CODEC_STATUS CodecDeliver::Decode(CBuffer* pBuff, Package&& oPackage, CBuffer* pReactBuff) +{ + LOG4_ERROR("invalid"); + return(CODEC_STATUS_ERR); +} + + } /* namespace neb */ diff --git a/src/codec/virtual/CodecDeliver.hpp b/src/codec/virtual/CodecDeliver.hpp index 69cd2d26..461c2107 100644 --- a/src/codec/virtual/CodecDeliver.hpp +++ b/src/codec/virtual/CodecDeliver.hpp @@ -11,6 +11,7 @@ #define SRC_CODEC_VIRTUAL_CODECDELIVER_HPP_ #include +#include "codec/Codec.hpp" #include "channel/SpecChannel.hpp" #include "labor/LaborShared.hpp" #include "type/Package.hpp" @@ -18,10 +19,11 @@ namespace neb { -class CodecDeliver +class CodecDeliver: public Codec { public: - CodecDeliver(); + CodecDeliver(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, + std::shared_ptr pBindChannel); virtual ~CodecDeliver(); static E_CODEC_TYPE Type() @@ -34,6 +36,12 @@ class CodecDeliver // response static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, Package&& oPackage); + + E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); + E_CODEC_STATUS Encode(Package&& oPackage, CBuffer* pBuff); + E_CODEC_STATUS Encode(Package&& oPackage, CBuffer* pBuff, CBuffer* pSecondlyBuff); + E_CODEC_STATUS Decode(CBuffer* pBuff, Package&& oPackage); + E_CODEC_STATUS Decode(CBuffer* pBuff, Package&& oPackage, CBuffer* pReactBuff); }; } /* namespace neb */ diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index 8674fd67..f03747f6 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -154,7 +154,7 @@ class Dispatcher } long GetNowTimeMs() const { - return((long)ev_now(m_loop) * 1000); + return((long)(ev_now(m_loop) * 1000)); } std::shared_ptr GetLogger() const { diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index a239188f..7682ae8c 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -212,7 +212,7 @@ class IO template bool IO::Send(std::shared_ptr pChannel) { - if (pChannel->m_pImpl == nullptr) + if (pChannel == nullptr || pChannel->m_pImpl == nullptr) { return(false); } @@ -235,7 +235,7 @@ template template bool IO::SendResponse(Actor* pActor, std::shared_ptr pChannel, Targs&&... args) { - if (pActor == nullptr) + if (pActor == nullptr || pChannel == nullptr) { return(false); } @@ -359,7 +359,7 @@ template template bool IO::SendRequest(Actor* pActor, std::shared_ptr pChannel, Targs&&... args) { - if (pActor == nullptr) + if (pActor == nullptr || pChannel == nullptr) { return(false); } @@ -382,7 +382,7 @@ template template bool IO::SendRequest(Dispatcher* pDispatcher, uint32 uiStepSeq, std::shared_ptr pChannel, Targs&&... args) { - if (pChannel->m_pImpl == nullptr) + if (pChannel == nullptr || pChannel->m_pImpl == nullptr) { pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "no channel impl"); diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index 16169b89..b77d3eea 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -144,10 +144,10 @@ class Nodes void CheckFailedNode(); -protected: - uint32 hash_fnv1_64(const char *key, size_t key_length); - uint32 hash_fnv1a_64(const char *key, size_t key_length); - uint32_t murmur3_32(const char *key, uint32_t len, uint32_t seed); +public: + static uint32 hash_fnv1_64(const char *key, size_t key_length); + static uint32 hash_fnv1a_64(const char *key, size_t key_length); + static uint32_t murmur3_32(const char *key, uint32_t len, uint32_t seed); private: const int m_iHashAlgorithm; diff --git a/src/labor/Loader.cpp b/src/labor/Loader.cpp index ef7a66b2..896774f1 100644 --- a/src/labor/Loader.cpp +++ b/src/labor/Loader.cpp @@ -29,9 +29,7 @@ bool Loader::InitActorBuilder() LOG4_TRACE(""); if (NewActorBuilder()) { - return(GetActorBuilder()->Init( - (const_cast(GetNodeConf()))["load_config"]["loader"]["boot_load"], - (const_cast(GetNodeConf()))["load_config"]["loader"]["dynamic_loading"])); + return(GetActorBuilder()->Init()); } return(false); } diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp index 97ea0a51..eae3179d 100644 --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -183,7 +183,8 @@ bool Manager::InitActorBuilder() LOG4_ERROR("new ActorBuilder error: %s", e.what()); return(false); } - if (!m_pActorBuilder->Init( + m_pActorBuilder->Init(); + if (!m_pActorBuilder->LoadCmd( m_oCurrentConf["load_config"]["manager"]["boot_load"], m_oCurrentConf["load_config"]["manager"]["dynamic_loading"])) { @@ -192,8 +193,8 @@ bool Manager::InitActorBuilder() } if (m_stNodeInfo.bThreadMode) { - m_pActorBuilder->Init(m_oCurrentConf["load_config"]["loader"]["dynamic_loading"]); - m_pActorBuilder->Init(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"]); + m_pActorBuilder->LoadCmd(m_oCurrentConf["load_config"]["loader"]["dynamic_loading"]); + m_pActorBuilder->LoadCmd(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"]); } return(true); } diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp index f1f089aa..52801137 100644 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -79,7 +79,25 @@ void Worker::Run() } #endif - StartService(); + if (Labor::LABOR_LOADER == GetLaborType()) + { + m_pActorBuilder->LoadCmd( + m_oNodeConf["load_config"]["loader"]["boot_load"], + m_oNodeConf["load_config"]["loader"]["dynamic_loading"]); + bool bLoaderCustomStartService = false; + m_oNodeConf.Get("loader_custom_start_service", bLoaderCustomStartService); + if (!bLoaderCustomStartService) + { + StartService(); + } + } + else + { + m_pActorBuilder->LoadCmd( + m_oNodeConf["load_config"]["worker"]["boot_load"], + m_oNodeConf["load_config"]["worker"]["dynamic_loading"]); + StartService(); + } m_pDispatcher->EventRun(); } @@ -378,9 +396,7 @@ bool Worker::InitActorBuilder() { if (NewActorBuilder()) { - return(m_pActorBuilder->Init( - m_oNodeConf["load_config"]["worker"]["boot_load"], - m_oNodeConf["load_config"]["worker"]["dynamic_loading"])); + return(m_pActorBuilder->Init()); } return(false); } diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 9b675a58..4e01ab28 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -168,6 +168,8 @@ class Worker: public Labor } } + void StartService(); + template void Logger(int iLogLevel, const char* szFileName, unsigned int uiFileLine, const char* szFunction, Targs&&... args); @@ -178,7 +180,6 @@ class Worker: public Labor bool NewDispatcher(); bool NewActorBuilder(); bool CreateEvents(); - void StartService(); void Destroy(); bool AddPeriodicTaskEvent(); diff --git a/src/type/Package.cpp b/src/type/Package.cpp index 77ffda49..ec02eae7 100644 --- a/src/type/Package.cpp +++ b/src/type/Package.cpp @@ -27,12 +27,7 @@ Package::Package(Package&& oPackage) Package::~Package() { - if (m_pPayload != nullptr) - { - std::cerr << "there may be payload memory leak!" << std::endl; - free(m_pPayload); // There may be destructors that have not been called, it should be avoided. - m_pPayload = nullptr; - } + m_pPayload = nullptr; } Package& Package::operator=(Package&& oPackage) diff --git a/src/type/Package.hpp b/src/type/Package.hpp index e4a3cf33..5fb90725 100644 --- a/src/type/Package.hpp +++ b/src/type/Package.hpp @@ -10,6 +10,8 @@ #ifndef SRC_TYPE_PACKAGE_HPP_ #define SRC_TYPE_PACKAGE_HPP_ +#include + namespace neb { @@ -45,10 +47,10 @@ class Package * The item class must implement a static Type() method. */ template - void Pack(T** item); + void Pack(std::shared_ptr* item); template - void Pack(int type, T** item); + void Pack(int type, std::shared_ptr* item); /** * @brief take out item from package @@ -56,38 +58,42 @@ class Package * The item class must implement a static Type() method. */ template - bool Unpack(T** item); + bool Unpack(std::shared_ptr* item); template - bool Unpack(int type, T** item); + bool Unpack(int type, std::shared_ptr* item); private: int m_iType = 0; - void* m_pPayload = nullptr; + std::shared_ptr m_pPayload = nullptr; }; template -void Package::Pack(T** item) +void Package::Pack(std::shared_ptr* item) { m_iType = T::Type(); - m_pPayload = (void*)(*item); + m_pPayload = *item; *item = nullptr; } template -void Package::Pack(int type, T** item) +void Package::Pack(int type, std::shared_ptr* item) { m_iType = type; - m_pPayload = (void*)(*item); + m_pPayload = *item; *item = nullptr; } template -bool Package::Unpack(T** item) +bool Package::Unpack(std::shared_ptr* item) { if (T::Type() == m_iType) { - *item = (T*)m_pPayload; + *item = std::static_pointer_cast(m_pPayload); + if (*item == nullptr) + { + return(false); + } m_pPayload = nullptr; m_iType = 0; return(true); @@ -96,11 +102,15 @@ bool Package::Unpack(T** item) } template -bool Package::Unpack(int type, T** item) +bool Package::Unpack(int type, std::shared_ptr* item) { if (type == m_iType) { - *item = (T*)m_pPayload; + *item = std::static_pointer_cast(m_pPayload); + if (*item == nullptr) + { + return(false); + } m_pPayload = nullptr; m_iType = 0; return(true); diff --git a/src/util/StringCoder.cpp b/src/util/StringCoder.cpp index 6f0fab93..b4399c2c 100644 --- a/src/util/StringCoder.cpp +++ b/src/util/StringCoder.cpp @@ -194,4 +194,16 @@ void Split(const std::string& strSrc, const std::string& strPattern, std::vector } } +std::string& Trim(std::string &s) +{ + if (s.empty()) + { + return s; + } + + s.erase(0,s.find_first_not_of(" ")); + s.erase(s.find_last_not_of(" ") + 1); + return s; +} + } /* namespace neb */ diff --git a/src/util/StringCoder.hpp b/src/util/StringCoder.hpp index 7b18bdd8..cd902868 100644 --- a/src/util/StringCoder.hpp +++ b/src/util/StringCoder.hpp @@ -40,6 +40,8 @@ void DecodeParameter(const std::string& strParameter, std::map& vecDest); +std::string& Trim(std::string &s); + } /* namespace neb */ #endif /* STRINGCODER_HPP_ */ diff --git a/src/util/StringConverter.hpp b/src/util/StringConverter.hpp index 231c0cf3..2be28923 100644 --- a/src/util/StringConverter.hpp +++ b/src/util/StringConverter.hpp @@ -10,12 +10,21 @@ #ifndef SRC_UTIL_STRINGCONVERTER_HPP_ #define SRC_UTIL_STRINGCONVERTER_HPP_ +#include "Definition.hpp" + #define _TO_NUM(v) ((v) ^ '0') #define _IS_NUM(v) (v >= '0' && v <= '9') namespace neb { +static const char gc_cDigitForConvertor[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + class StringConverter { public: @@ -60,8 +69,128 @@ class StringConverter } return(res * sign); } + + template + static inline TInt RapidAtoi(const char* str, int iDefaultValue) + { + int sign = 1; + TInt res = 0; + int k = 0; + if (str[0] == '-') + { + sign = -1; + k = 1; + } + for (; ; k += 4) + { + if (not _IS_NUM(str[k])) + { + if (str[k] == '\0') + { + return(res * sign); + } + return(iDefaultValue); + } + else if (not _IS_NUM(str[k + 1])) + { + if (str[k + 1] == '\0') + { + return((res * 10 + _TO_NUM(str[k])) * sign); + } + return(iDefaultValue); + } + else if (not _IS_NUM(str[k + 2])) + { + if (str[k + 2] == '\0') + { + return((res * 100 + _TO_NUM(str[k]) * 10 + _TO_NUM(str[k + 1])) * sign); + } + return(iDefaultValue); + } + else if (not _IS_NUM(str[k + 3])) + { + if (str[k + 3] == '\0') + { + return((res * 1000 + _TO_NUM(str[k]) * 100 + + _TO_NUM(str[k + 1]) * 10 + _TO_NUM(str[k + 2])) * sign); + } + return(iDefaultValue); + } + else + { + res = res * 10000 + _TO_NUM(str[k]) * 1000 + _TO_NUM(str[k + 1]) + * 100 + _TO_NUM(str[k + 2]) * 10 + _TO_NUM(str[k + 3]); + } + } + return(res * sign); + } + + static uint32 Digits10(uint64 v) { + if (v < 10) return 1; + if (v < 100) return 2; + if (v < 1000) return 3; + if (v < 1000000000000) { // 10^12 + if (v < 100000000) { // 10^7 + if (v < 1000000) { // 10^6 + if (v < 10000) return 4; + return 5 + (v >= 100000); // 10^5 + } + return 7 + (v >= 10000000); // 10^7 + } + if (v < 10000000000) { // 10^10 + return 9 + (v >= 1000000000); // 10^9 + } + return 11 + (v >= 100000000000); // 10^11 + } + return 12 + Digits10(v / 1000000000000); // 10^12 + } + + static uint32 uint64ToAscii(uint64 value, char* dst) { + + const uint32 length = Digits10(value); + uint32 next = length - 1; + + while (value >= 100) { + const uint32 i = (value % 100) * 2; + value /= 100; + dst[next - 1] = gc_cDigitForConvertor[i]; + dst[next] = gc_cDigitForConvertor[i + 1]; + next -= 2; + } + // Handle last 1-2 digits + if (value < 10) { + dst[next] = '0' + uint32(value); + } else { + uint32 i = uint32(value) * 2; + dst[next - 1] = gc_cDigitForConvertor[i]; + dst[next] = gc_cDigitForConvertor[i + 1]; + } + return length; + } + + static void uint64ToAscii(uint64_t value, uint32 value_len, char* dst) + { + uint32 next = value_len - 1; + + while (value >= 100) { + const uint32 i = (value % 100) * 2; + value /= 100; + dst[next - 1] = gc_cDigitForConvertor[i]; + dst[next] = gc_cDigitForConvertor[i + 1]; + next -= 2; + } + // Handle last 1-2 digits + if (value < 10) { + dst[next] = '0' + uint32(value); + } else { + uint32 i = uint32(value) * 2; + dst[next - 1] = gc_cDigitForConvertor[i]; + dst[next] = gc_cDigitForConvertor[i + 1]; + } + } }; + } /* namespace neb */ #endif /* SRC_UTIL_STRINGCONVERTER_HPP_ */ From a71b61bbaf805601c43d6c6d3edd9820c0ca4122 Mon Sep 17 00:00:00 2001 From: Bwar Date: Mon, 15 May 2023 00:22:39 +0800 Subject: [PATCH 174/176] http2 bug fixed --- src/Definition.hpp | 0 src/Error.hpp | 0 src/Makefile | 0 src/Makefile_ssl | 0 .../sys_session/manager/SessionManager.cpp | 2 +- src/channel/SocketChannelImpl.hpp | 33 +-- src/channel/SpecChannel.hpp | 8 +- src/codec/Codec.hpp | 7 - src/codec/CodecFactory.cpp | 41 ++- src/codec/CodecFactory.hpp | 2 +- src/codec/CodecHttp.cpp | 77 +++--- src/codec/CodecProto.cpp | 4 + src/codec/http2/CodecHttp2.cpp | 254 ++++++++++++------ src/codec/http2/CodecHttp2.hpp | 29 +- src/codec/http2/H2Comm.hpp | 5 +- src/codec/http2/Http2Frame.cpp | 249 +++++++++++------ src/codec/http2/Http2Frame.hpp | 8 +- src/codec/http2/Http2Stream.cpp | 110 +++++--- src/codec/http2/Http2Stream.hpp | 6 +- src/ios/Dispatcher.cpp | 34 ++- src/ios/Dispatcher.hpp | 1 + src/ios/IO.hpp | 2 + src/ios/Nodes.cpp | 20 ++ src/ios/Nodes.hpp | 2 + src/labor/Worker.hpp | 8 +- src/pb/http.pb.cc | 0 src/pb/mydis.pb.cc | 0 src/pb/mydis.pb.h | 0 src/pb/neb_sys.pb.cc | 0 src/util/StringCoder.cpp | 8 +- src/util/json/CJsonObject.cpp | 48 ++++ src/util/json/CJsonObject.hpp | 2 + 32 files changed, 653 insertions(+), 307 deletions(-) mode change 100644 => 100755 src/Definition.hpp mode change 100644 => 100755 src/Error.hpp mode change 100644 => 100755 src/Makefile mode change 100644 => 100755 src/Makefile_ssl mode change 100644 => 100755 src/pb/http.pb.cc mode change 100644 => 100755 src/pb/mydis.pb.cc mode change 100644 => 100755 src/pb/mydis.pb.h mode change 100644 => 100755 src/pb/neb_sys.pb.cc mode change 100644 => 100755 src/util/json/CJsonObject.cpp diff --git a/src/Definition.hpp b/src/Definition.hpp old mode 100644 new mode 100755 diff --git a/src/Error.hpp b/src/Error.hpp old mode 100644 new mode 100755 diff --git a/src/Makefile b/src/Makefile old mode 100644 new mode 100755 diff --git a/src/Makefile_ssl b/src/Makefile_ssl old mode 100644 new mode 100755 diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp index fdc46e7c..eafd558e 100644 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -326,7 +326,7 @@ bool SessionManager::SetWorkerLoad(uint32 uiWorkerIndex, const ReportRecord& oWo } if (oWorkerStatus.value_size() < 6) { - LOG4_ERROR("value size %d error", oWorkerStatus.value_size()); + LOG4_INFO("value size %d error", oWorkerStatus.value_size()); return(false); } m_vecWorkerInfo[uiWorkerIndex]->uiLoad = oWorkerStatus.value(0); diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp index 12bd63e4..5ce3d049 100644 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -467,7 +467,7 @@ E_CODEC_STATUS SocketChannelImpl::Send() } } while (iWrittenLen > 0 && iHadWrittenLen < iNeedWriteLen); - LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iHadWrittenLen); + LOG4_TRACE("iNeedWriteLen = %d, iWrittenLen = %d, iHadWrittenLen = %d", iNeedWriteLen, iWrittenLen, iHadWrittenLen); if (iHadWrittenLen >= 0) { if (m_bIsClient) @@ -487,10 +487,14 @@ E_CODEC_STATUS SocketChannelImpl::Send() m_dActiveTime = m_pLabor->GetNowTime(); if (iNeedWriteLen == iHadWrittenLen && 0 == m_pWaitForSendBuff->ReadableBytes()) { + LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d, m_pWaitForSendBuff->ReadableBytes() = %u", + iNeedWriteLen, iHadWrittenLen, m_pWaitForSendBuff->ReadableBytes()); return(CODEC_STATUS_OK); } else { + LOG4_TRACE("iNeedWriteLen = %d, iHadWrittenLen = %d, m_pWaitForSendBuff->ReadableBytes() = %u", + iNeedWriteLen, iHadWrittenLen, m_pWaitForSendBuff->ReadableBytes()); return(CODEC_STATUS_PAUSE); } } @@ -525,8 +529,8 @@ E_CODEC_STATUS SocketChannelImpl::SendRequest(uint32 uiStepSeq, Targs&&... ar switch (m_ucChannelStatus) { case CHANNEL_STATUS_ESTABLISHED: - eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pSendBuff); - if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) + eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pSendBuff, m_pWaitForSendBuff); + if (uiStepSeq > 0 && (CODEC_STATUS_OK == eCodecStatus || CODEC_STATUS_PART_OK == eCodecStatus)) { uint32 uiStreamId = (static_cast(m_pCodec))->GetLastStreamId(); LOG4_TRACE("uiStreamId = %u", uiStreamId); @@ -551,7 +555,7 @@ E_CODEC_STATUS SocketChannelImpl::SendRequest(uint32 uiStepSeq, Targs&&... ar case CHANNEL_STATUS_TRY_CONNECT: case CHANNEL_STATUS_INIT: eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pSendBuff, m_pWaitForSendBuff); - if (CODEC_STATUS_OK == eCodecStatus && uiStepSeq > 0) + if (uiStepSeq > 0 && (CODEC_STATUS_OK == eCodecStatus || CODEC_STATUS_PART_OK == eCodecStatus)) { eCodecStatus = CODEC_STATUS_PAUSE; uint32 uiStreamId = (static_cast(m_pCodec))->GetLastStreamId(); @@ -649,7 +653,7 @@ E_CODEC_STATUS SocketChannelImpl::SendResponse(Targs&&... args) switch (m_ucChannelStatus) { case CHANNEL_STATUS_ESTABLISHED: - eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pSendBuff); + eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pSendBuff, m_pWaitForSendBuff); break; case CHANNEL_STATUS_CLOSED: LOG4_WARNING("%s channel_fd[%d], channel_seq[%d], channel_status[%d] remote %s EOF.", @@ -661,7 +665,7 @@ E_CODEC_STATUS SocketChannelImpl::SendResponse(Targs&&... args) case CHANNEL_STATUS_CONNECTED: case CHANNEL_STATUS_TRY_CONNECT: case CHANNEL_STATUS_INIT: - eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pWaitForSendBuff); + eCodecStatus = (static_cast(m_pCodec))->Encode(std::forward(args)..., m_pSendBuff, m_pWaitForSendBuff); break; default: LOG4_ERROR("%s invalid connection status %d!", m_strIdentify.c_str(), (int)m_ucChannelStatus); @@ -817,19 +821,13 @@ E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; if (m_pCodec->DecodeWithReactor()) { - size_t uiSendBuffLen = m_pSendBuff->ReadableBytes(); eCodecStatus = (static_cast(m_pCodec))->Decode(m_pRecvBuff, std::forward(args)..., m_pSendBuff); - if (m_pSendBuff->ReadableBytes() > uiSendBuffLen - && (eCodecStatus == CODEC_STATUS_OK || eCodecStatus == CODEC_STATUS_PART_OK)) - { - Send(); - } } else { eCodecStatus = (static_cast(m_pCodec))->Decode(m_pRecvBuff, std::forward(args)...); } - if (CODEC_STATUS_OK == eCodecStatus || CODEC_STATUS_PART_OK == eCodecStatus) + if (CODEC_STATUS_OK == eCodecStatus || CODEC_STATUS_PART_OK == eCodecStatus || CODEC_STATUS_PART_ERR == eCodecStatus) { ++m_uiUnitTimeMsgNum; ++m_uiMsgNum; @@ -877,19 +875,13 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(Targs&&... args) E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; if (m_pCodec->DecodeWithReactor()) { - size_t uiSendBuffLen = m_pSendBuff->ReadableBytes(); eCodecStatus = (static_cast(m_pCodec))->Decode(m_pRecvBuff, std::forward(args)..., m_pSendBuff); - if (m_pSendBuff->ReadableBytes() > uiSendBuffLen - && (eCodecStatus == CODEC_STATUS_OK || eCodecStatus == CODEC_STATUS_PART_OK)) - { - Send(); - } } else { eCodecStatus = (static_cast(m_pCodec))->Decode(m_pRecvBuff, std::forward(args)...); } - if (CODEC_STATUS_OK == eCodecStatus || CODEC_STATUS_PART_OK == eCodecStatus) + if (CODEC_STATUS_OK == eCodecStatus || CODEC_STATUS_PART_OK == eCodecStatus || CODEC_STATUS_PART_ERR == eCodecStatus) { ++m_uiUnitTimeMsgNum; ++m_uiMsgNum; @@ -988,7 +980,6 @@ bool SocketChannelImpl::SetCodec(Codec* pCodec) { delete m_pCodec; } - pCodec->ConnectionSetting(m_pSendBuff); m_pCodec = pCodec; return(true); } diff --git a/src/channel/SpecChannel.hpp b/src/channel/SpecChannel.hpp index 4423d2d5..793a2cc7 100644 --- a/src/channel/SpecChannel.hpp +++ b/src/channel/SpecChannel.hpp @@ -142,7 +142,7 @@ SpecChannel::~SpecChannel() template int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Tdata&& oData) { - auto const uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); + auto uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); auto uiNextRecord = uiCurrentWrite + 1; uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; if (m_uiEffectiveSize < m_uiSize) @@ -173,7 +173,7 @@ int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Tdata&& o template int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Thead&& oHead, Tdata&& oData) { - auto const uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); + auto uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); auto uiNextRecord = uiCurrentWrite + 1; uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; if (m_uiEffectiveSize < m_uiSize) @@ -206,7 +206,7 @@ int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Thead&& o template bool SpecChannel::Read(uint32& uiFlags, uint32& uiStepSeq, Tdata& oData) { - auto const uiCurrentRead = m_uiReadIndex.load(std::memory_order_relaxed); + auto uiCurrentRead = m_uiReadIndex.load(std::memory_order_relaxed); if (uiCurrentRead == m_uiWriteIndex.load(std::memory_order_acquire)) // queue is empty { return(false); @@ -225,7 +225,7 @@ bool SpecChannel::Read(uint32& uiFlags, uint32& uiStepSeq, Tdata& template bool SpecChannel::Read(uint32& uiFlags, uint32& uiStepSeq, Thead& oHead, Tdata& oData) { - auto const uiCurrentRead = m_uiReadIndex.load(std::memory_order_relaxed); + auto uiCurrentRead = m_uiReadIndex.load(std::memory_order_relaxed); if (uiCurrentRead == m_uiWriteIndex.load(std::memory_order_acquire)) // queue is empty { return(false); diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp index 188f6354..a3dc4d47 100644 --- a/src/codec/Codec.hpp +++ b/src/codec/Codec.hpp @@ -110,13 +110,6 @@ class Codec return(false); } - /** - * @see CodecHttp2::ConnectionSetting() - */ - virtual void ConnectionSetting(CBuffer* pBuff) - { - } - void SetKey(const std::string& strKey) { m_strKey = strKey; diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp index 174cd745..9e032c87 100644 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -176,11 +176,11 @@ Codec* CodecFactory::CreateCodec(std::shared_ptr pLogger, E_CODEC_TYP return(pCodec); } -E_CODEC_STATUS CodecFactory::OnEvent(SpecChannelWatcher* pAsyncWatcher, std::shared_ptr pChannel) +E_CODEC_STATUS CodecFactory::OnEvent(uint32 uiSpecChannelCodecType, std::shared_ptr pChannel, Dispatcher* pDispatcher) { uint32 uiFlags = 0; uint32 uiStepSeq = 0; - switch (pAsyncWatcher->GetCodecType()) + switch (uiSpecChannelCodecType) { case CODEC_PROTO: case CODEC_NEBULA: @@ -189,7 +189,10 @@ E_CODEC_STATUS CodecFactory::OnEvent(SpecChannelWatcher* pAsyncWatcher, std::sha MsgHead oMsgHead; MsgBody oMsgBody; auto pSpecChannel = std::static_pointer_cast>(pChannel); - auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + if (pDispatcher == nullptr) + { + pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + } pDispatcher->m_pLastActivityChannel = pChannel; while (pSpecChannel->Read(uiFlags, uiStepSeq, oMsgHead, oMsgBody)) { @@ -209,7 +212,10 @@ E_CODEC_STATUS CodecFactory::OnEvent(SpecChannelWatcher* pAsyncWatcher, std::sha { HttpMsg oHttpMsg; auto pSpecChannel = std::static_pointer_cast>(pChannel); - auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + if (pDispatcher == nullptr) + { + pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + } pDispatcher->m_pLastActivityChannel = pChannel; while (pSpecChannel->Read(uiFlags, uiStepSeq, oHttpMsg)) { @@ -228,7 +234,10 @@ E_CODEC_STATUS CodecFactory::OnEvent(SpecChannelWatcher* pAsyncWatcher, std::sha { RedisMsg oRedisMsg; auto pSpecChannel = std::static_pointer_cast>(pChannel); - auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + if (pDispatcher == nullptr) + { + pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + } pDispatcher->m_pLastActivityChannel = pChannel; while (pSpecChannel->Read(uiFlags, uiStepSeq, oRedisMsg)) { @@ -247,7 +256,10 @@ E_CODEC_STATUS CodecFactory::OnEvent(SpecChannelWatcher* pAsyncWatcher, std::sha { CassResponse oCassResponse; auto pSpecChannel = std::static_pointer_cast>(pChannel); - auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + if (pDispatcher == nullptr) + { + pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + } pDispatcher->m_pLastActivityChannel = pChannel; while (pSpecChannel->Read(uiFlags, uiStepSeq, oCassResponse)) { @@ -261,7 +273,10 @@ E_CODEC_STATUS CodecFactory::OnEvent(SpecChannelWatcher* pAsyncWatcher, std::sha { SocketChannelPack oPack; auto pSpecChannel = std::static_pointer_cast>(pChannel); - auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + if (pDispatcher == nullptr) + { + pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + } pDispatcher->m_pLastActivityChannel = pChannel; while (pSpecChannel->Read(uiFlags, uiStepSeq, oPack)) { @@ -280,7 +295,10 @@ E_CODEC_STATUS CodecFactory::OnEvent(SpecChannelWatcher* pAsyncWatcher, std::sha { Package oPackage; auto pSpecChannel = std::static_pointer_cast>(pChannel); - auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + if (pDispatcher == nullptr) + { + pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); + } pDispatcher->m_pLastActivityChannel = pChannel; while (pSpecChannel->Read(uiFlags, uiStepSeq, oPackage)) { @@ -680,7 +698,8 @@ E_CODEC_STATUS CodecFactory::OnHttpEvent(Dispatcher* pDispatcher, std::shared_pt oOutHttpMsg.set_status_code(404); oOutHttpMsg.set_http_major(oHttpMsg.http_major()); oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); - IO::SendRequest(pDispatcher, 0, pChannel, oOutHttpMsg); + oOutHttpMsg.set_stream_id(oHttpMsg.stream_id()); + IO::SendResponse(pDispatcher, pChannel, oOutHttpMsg); } } else @@ -701,7 +720,7 @@ E_CODEC_STATUS CodecFactory::OnHttpEvent(Dispatcher* pDispatcher, std::shared_pt oOutHttpMsg.set_status_code(404); oOutHttpMsg.set_http_major(oHttpMsg.http_major()); oOutHttpMsg.set_http_minor(oHttpMsg.http_minor()); - IO::SendRequest(pDispatcher, 0, pChannel, oOutHttpMsg); + IO::SendResponse(pDispatcher, pChannel, oOutHttpMsg); } } else @@ -802,7 +821,7 @@ bool CodecFactory::AutoSwitchCodec(Dispatcher* pDispatcher, { for (uint32 i = uiLastCodecPos + 1; i < s_vecAutoSwitchCodec.size(); ++i) { - if (eOriginCodecType == s_vecAutoSwitchCodec[i]) + if (eOriginCodecType == s_vecAutoSwitchCodec[i] || CODEC_UNKNOW == s_vecAutoSwitchCodec[i]) { continue; } diff --git a/src/codec/CodecFactory.hpp b/src/codec/CodecFactory.hpp index 9bdf43ea..f707590d 100644 --- a/src/codec/CodecFactory.hpp +++ b/src/codec/CodecFactory.hpp @@ -49,7 +49,7 @@ class CodecFactory static std::shared_ptr CreateChannel(Labor* pLabor, std::shared_ptr pLogger, int iFd, E_CODEC_TYPE eCodecType, bool bIsClient, bool bWithSsl); static Codec* CreateCodec(std::shared_ptr pLogger, E_CODEC_TYPE eCodecType, std::shared_ptr pBindChannel); - static E_CODEC_STATUS OnEvent(SpecChannelWatcher* pAsyncWatcher, std::shared_ptr pChannel); + static E_CODEC_STATUS OnEvent(uint32 uiSpecChannelCodecType, std::shared_ptr pChannel, Dispatcher* pDispatcher); static E_CODEC_STATUS OnEvent(Dispatcher* pDispatcher, std::shared_ptr pChannel); static bool OnSpecChannelCreated(uint32 uiCodecType, uint32 uiFromLabor, uint32 uiToLabor); diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp index 0d20a79b..0233047c 100644 --- a/src/codec/CodecHttp.cpp +++ b/src/codec/CodecHttp.cpp @@ -174,52 +174,61 @@ E_CODEC_STATUS CodecHttp::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) std::string strHost; std::string strPath; struct http_parser_url stUrl; - if(0 == http_parser_parse_url(oHttpMsg.url().c_str(), oHttpMsg.url().length(), 0, &stUrl)) + if (oHttpMsg.url().size() > 0) { - if(stUrl.field_set & (1 << UF_PORT)) + if(0 == http_parser_parse_url(oHttpMsg.url().c_str(), oHttpMsg.url().length(), 0, &stUrl)) { - iPort = stUrl.port; - } - else - { - iPort = 80; - } + if(stUrl.field_set & (1 << UF_PORT)) + { + iPort = stUrl.port; + } + else + { + iPort = 80; + } - if(stUrl.field_set & (1 << UF_HOST) ) - { - strHost = oHttpMsg.url().substr(stUrl.field_data[UF_HOST].off, stUrl.field_data[UF_HOST].len); - } + if(stUrl.field_set & (1 << UF_HOST) ) + { + strHost = oHttpMsg.url().substr(stUrl.field_data[UF_HOST].off, stUrl.field_data[UF_HOST].len); + } - if(stUrl.field_set & (1 << UF_PATH)) - { - strPath = oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off, stUrl.field_data[UF_PATH].len); - } + if(stUrl.field_set & (1 << UF_PATH)) + { + strPath = oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off, stUrl.field_data[UF_PATH].len); + } - if (iPort == 80) - { - std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(':')); - std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c) -> unsigned char { return std::tolower(c); }); - if (strSchema == std::string("https")) + if (iPort == 80) { - iPort = 443; + std::string strSchema = oHttpMsg.url().substr(0, oHttpMsg.url().find_first_of(':')); + std::transform(strSchema.begin(), strSchema.end(), strSchema.begin(), [](unsigned char c) -> unsigned char { return std::tolower(c); }); + if (strSchema == std::string("https")) + { + iPort = 443; + } } } + else + { + LOG4_WARNING("http_parser_parse_url error!"); + m_mapAddingHttpHeader.clear(); + return(CODEC_STATUS_ERR); + } + if (stUrl.field_data[UF_PATH].off >= oHttpMsg.url().size()) + { + LOG4_WARNING("invalid url \"%s\"!", oHttpMsg.url().c_str()); + m_mapAddingHttpHeader.clear(); + return(CODEC_STATUS_ERR); + } + iWriteSize = pBuff->Printf("%s %s HTTP/%u.%u\r\n", http_method_str((http_method)oHttpMsg.method()), + oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off, std::string::npos).c_str(), + oHttpMsg.http_major(), oHttpMsg.http_minor()); } else { - LOG4_WARNING("http_parser_parse_url error!"); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); - } - if (stUrl.field_data[UF_PATH].off >= oHttpMsg.url().size()) - { - LOG4_WARNING("invalid url \"%s\"!", oHttpMsg.url().c_str()); - m_mapAddingHttpHeader.clear(); - return(CODEC_STATUS_ERR); + iWriteSize = pBuff->Printf("%s %s HTTP/%u.%u\r\n", http_method_str((http_method)oHttpMsg.method()), + oHttpMsg.path().c_str(), + oHttpMsg.http_major(), oHttpMsg.http_minor()); } - iWriteSize = pBuff->Printf("%s %s HTTP/%u.%u\r\n", http_method_str((http_method)oHttpMsg.method()), - oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off, std::string::npos).c_str(), - oHttpMsg.http_major(), oHttpMsg.http_minor()); if (iWriteSize < 0) { pBuff->SetWriteIndex(pBuff->GetWriteIndex() - iHadEncodedSize); diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp index 20c94490..5c05b9a2 100644 --- a/src/codec/CodecProto.cpp +++ b/src/codec/CodecProto.cpp @@ -128,6 +128,10 @@ E_CODEC_STATUS CodecProto::Encode(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgB E_CODEC_STATUS CodecProto::Encode(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, CBuffer* pBuff, CBuffer* pSecondlyBuff) { + if (CHANNEL_STATUS_ESTABLISHED == GetBindChannel()->GetChannelStatus()) + { + return(Encode(iCmd, uiSeq, oMsgBody, pBuff)); + } switch (iCmd) { case CMD_RSP_TELL_WORKER: diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index 9e4eb41c..cc650670 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -121,19 +121,23 @@ void CodecHttp2::ConnectionSetting(CBuffer* pBuff) if (GetBindChannel()->IsClient()) { pBuff->Write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24); - m_bWantMagic = false; + m_bClientPrefaceMagic = true; stSetting.unIdentifier = H2_SETTINGS_INITIAL_WINDOW_SIZE; - stSetting.uiValue = 1048576; - m_uiRecvWindowSize = 1048576; - m_uiSendWindowSize = 1048576; + stSetting.uiValue = 4194304; + m_iRecvWindowSize = 4194304; + m_iSettingsMaxRecvWindowSize = 4194304; + m_iSendWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; vecSetting.push_back(stSetting); stSetting.unIdentifier = H2_SETTINGS_MAX_FRAME_SIZE; - stSetting.uiValue = 4194304; - m_uiSettingsMaxDecodeFrameSize = 4194304; + stSetting.uiValue = DEFAULT_SETTINGS_MAX_FRAME_SIZE; + m_uiSettingsMaxDecodeFrameSize = DEFAULT_SETTINGS_MAX_FRAME_SIZE; vecSetting.push_back(stSetting); stSetting.unIdentifier = H2_SETTINGS_MAX_CONCURRENT_STREAMS; stSetting.uiValue = 100; vecSetting.push_back(stSetting); + stSetting.unIdentifier = H2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA; + stSetting.uiValue = 1; + vecSetting.push_back(stSetting); m_pFrame->EncodeSetting(this, vecSetting, pBuff); m_pFrame->EncodeWindowUpdate(this, 0, 4128769, pBuff); // In HTTP/2, 1 on client is reserved for Upgrade. @@ -143,20 +147,25 @@ void CodecHttp2::ConnectionSetting(CBuffer* pBuff) { stSetting.unIdentifier = H2_SETTINGS_INITIAL_WINDOW_SIZE; stSetting.uiValue = 4194304; - m_uiRecvWindowSize = 4194304; - m_uiSendWindowSize = 1048576; + m_iRecvWindowSize = 4194304; + m_iSettingsMaxRecvWindowSize = 4194304; + m_iSendWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; vecSetting.push_back(stSetting); stSetting.unIdentifier = H2_SETTINGS_MAX_FRAME_SIZE; - stSetting.uiValue = 4194304; - m_uiSettingsMaxDecodeFrameSize = 4194304; + stSetting.uiValue = DEFAULT_SETTINGS_MAX_FRAME_SIZE; + m_uiSettingsMaxDecodeFrameSize = DEFAULT_SETTINGS_MAX_FRAME_SIZE; vecSetting.push_back(stSetting); stSetting.unIdentifier = H2_SETTINGS_MAX_HEADER_LIST_SIZE; stSetting.uiValue = 8192; m_uiSettingsMaxHeaderListSize = 8192; vecSetting.push_back(stSetting); + stSetting.unIdentifier = H2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA; + stSetting.uiValue = 1; + vecSetting.push_back(stSetting); m_pFrame->EncodeSetting(this, vecSetting, pBuff); m_pFrame->EncodeWindowUpdate(this, 0, 4128769, pBuff); - m_pFrame->EncodePing(this, false, 0, 0, pBuff); + m_bServerPreface = true; + //m_pFrame->EncodePing(this, false, 0, 0, pBuff); } } @@ -167,11 +176,6 @@ E_CODEC_STATUS CodecHttp2::Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff) E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) { - if (m_bWantMagic && GetBindChannel()->IsClient()) - { - pBuff->Write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24); - m_bWantMagic = false; - } for (int i = 0; i < oHttpMsg.adding_without_index_headers_size(); ++i) { m_setEncodingWithoutIndexHeaders.insert(oHttpMsg.adding_without_index_headers(i)); @@ -226,7 +230,7 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) } } size_t uiReadIdx = pBuff->GetReadIndex(); - LOG4_TRACE("%s", oHttpMsg.DebugString().c_str()); + // LOG4_TRACE("%s", oHttpMsg.DebugString().c_str()); E_CODEC_STATUS eCodecStatus = m_pCodingStream->Encode(this, oHttpMsg, pBuff); if (CODEC_STATUS_PAUSE == eCodecStatus || CODEC_STATUS_ERR == eCodecStatus) @@ -248,7 +252,29 @@ E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff) E_CODEC_STATUS CodecHttp2::Encode(const HttpMsg& oHttpMsg, CBuffer* pBuff, CBuffer* pSecondlyBuff) { - return(Encode(oHttpMsg, pBuff)); + if (m_bClientPrefaceMagic) + { + if (m_bServerPreface) + { + return(Encode(oHttpMsg, pBuff)); + } + else + { + return(Encode(oHttpMsg, pSecondlyBuff)); + } + } + else + { + if (GetBindChannel()->IsClient()) + { + ConnectionSetting(pBuff); + return(Encode(oHttpMsg, pSecondlyBuff)); + } + else + { + return(Encode(oHttpMsg, pBuff)); + } + } } E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) @@ -260,23 +286,30 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg) E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) { LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); - if (m_bWantMagic && !GetBindChannel()->IsClient()) + if (m_bClientPrefaceMagic) + { + ; + } + else { - if (pBuff->ReadableBytes() >= 24) + if (!GetBindChannel()->IsClient()) { - LOG4_TRACE("%s", pBuff->GetRawReadBuffer()); - std::string strMagic; - strMagic.assign(pBuff->GetRawReadBuffer(), 24); - if (strMagic == "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") + if (pBuff->ReadableBytes() >= 24) { - m_bWantMagic = false; - pBuff->AdvanceReadIndex(24); - return(Decode(pBuff, oHttpMsg, pReactBuff)); + LOG4_TRACE("%s", pBuff->GetRawReadBuffer()); + std::string strMagic; + strMagic.assign(pBuff->GetRawReadBuffer(), 24); + if (strMagic == "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") + { + m_bClientPrefaceMagic = true; + pBuff->AdvanceReadIndex(24); + return(Decode(pBuff, oHttpMsg, pReactBuff)); + } + LOG4_ERROR("need upgrade magic."); + return(CODEC_STATUS_ERR); } - LOG4_ERROR("need upgrade magic."); - return(CODEC_STATUS_ERR); + return(CODEC_STATUS_PAUSE); } - return(CODEC_STATUS_PAUSE); } if (pBuff->ReadableBytes() < H2_FRAME_HEAD_SIZE) { @@ -318,6 +351,29 @@ E_CODEC_STATUS CodecHttp2::Decode(CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pR m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "invalid frame type.", pReactBuff); return(CODEC_STATUS_ERR); } + if (!m_bServerPreface) + { + if (GetBindChannel()->IsClient()) + { + if (H2_FRAME_SETTINGS == m_stDecodeFrameHead.ucType + && (H2_FRAME_FLAG_ACK & m_stDecodeFrameHead.ucFlag) == 0) + { + m_bServerPreface = true; + } + else + { + LOG4_ERROR("First received frame was not a SETTING frame: frame type %u, flag %u", + (uint32)m_stDecodeFrameHead.ucType, (uint32)m_stDecodeFrameHead.ucFlag); + SetErrno(H2_ERR_PROTOCOL_ERROR); + m_pFrame->EncodeGoaway(this, H2_ERR_PROTOCOL_ERROR, "First received frame was not a SETTING frame", pReactBuff); + return(CODEC_STATUS_ERR); + } + } + else + { + ConnectionSetting(pReactBuff); + } + } if (m_stDecodeFrameHead.uiLength > m_uiSettingsMaxDecodeFrameSize) { LOG4_TRACE("m_uiSettingsMaxDecodeFrameSize = %u, m_stDecodeFrameHead.uiLength = %u", @@ -475,40 +531,48 @@ void CodecHttp2::SetPriority(uint32 uiStreamId, const tagPriority& stPriority) return; } } - if (pCurrentStreamWeight->pParent != nullptr) + if (m_pStreamWeightRoot->pFirstChild == nullptr) { - if (pCurrentStreamWeight->pParent->pFirstChild == pCurrentStreamWeight) - { - pCurrentStreamWeight->pParent->pFirstChild = pCurrentStreamWeight->pRightBrother; - } - else + m_pStreamWeightRoot->pFirstChild = pCurrentStreamWeight; + pCurrentStreamWeight->pParent = m_pStreamWeightRoot; + } + else + { + if (pCurrentStreamWeight->pParent != nullptr) { - auto pLast = pCurrentStreamWeight->pParent->pFirstChild; - while (pLast->pRightBrother != pCurrentStreamWeight) + if (pCurrentStreamWeight->pParent->pFirstChild == pCurrentStreamWeight) + { + pCurrentStreamWeight->pParent->pFirstChild = pCurrentStreamWeight->pRightBrother; + } + else { - pLast = pLast->pRightBrother; + auto pLast = pCurrentStreamWeight->pParent->pFirstChild; + while (pLast->pRightBrother != pCurrentStreamWeight) + { + pLast = pLast->pRightBrother; + } + pLast->pRightBrother = pCurrentStreamWeight->pRightBrother; } - pLast->pRightBrother = pCurrentStreamWeight->pRightBrother; } - } - auto pDependencyStreamWeight = FindStreamWeight(stPriority.uiDependency, m_pStreamWeightRoot); - if (pDependencyStreamWeight == nullptr) - { - pCurrentStreamWeight->pRightBrother = m_pStreamWeightRoot->pRightBrother; - m_pStreamWeightRoot->pRightBrother = pCurrentStreamWeight; - } - else - { - if (stPriority.E) + auto pDependencyStreamWeight = FindStreamWeight(stPriority.uiDependency, m_pStreamWeightRoot); + if (pDependencyStreamWeight == nullptr) { - pCurrentStreamWeight->pFirstChild = pDependencyStreamWeight->pFirstChild; + pCurrentStreamWeight->pRightBrother = m_pStreamWeightRoot->pFirstChild; + m_pStreamWeightRoot->pFirstChild = pCurrentStreamWeight; } else { - pCurrentStreamWeight->pRightBrother = pDependencyStreamWeight->pFirstChild; + if (stPriority.E) + { + pCurrentStreamWeight->pFirstChild = pDependencyStreamWeight->pFirstChild; + } + else + { + pCurrentStreamWeight->pRightBrother = pDependencyStreamWeight->pFirstChild; + } + pDependencyStreamWeight->pFirstChild = pCurrentStreamWeight; + pCurrentStreamWeight->pParent = pDependencyStreamWeight; } - pDependencyStreamWeight->pFirstChild = pCurrentStreamWeight; - pCurrentStreamWeight->pParent = pDependencyStreamWeight; } } @@ -589,11 +653,15 @@ E_H2_ERR_CODE CodecHttp2::Setting(const std::vector& vecSetting, boo case H2_SETTINGS_INITIAL_WINDOW_SIZE: if (vecSetting[i].uiValue <= SETTINGS_MAX_INITIAL_WINDOW_SIZE) { - for (auto it = m_mapStream.begin(); it != m_mapStream.end(); ++it) + if (m_iSettingsMaxSendWindowSize != (int32)vecSetting[i].uiValue) { - it->second->WindowInit(vecSetting[i].uiValue - m_uiSettingsMaxWindowSize); + int32 iIncreaseSize = (int32)vecSetting[i].uiValue - m_iSettingsMaxSendWindowSize; + for (auto it = m_mapStream.begin(); it != m_mapStream.end(); ++it) + { + it->second->WindowUpdate(iIncreaseSize); + } + m_iSettingsMaxSendWindowSize = (int32)vecSetting[i].uiValue; } - m_uiSettingsMaxWindowSize = vecSetting[i].uiValue; } else { @@ -628,22 +696,33 @@ E_H2_ERR_CODE CodecHttp2::Setting(const std::vector& vecSetting, boo return(H2_ERR_NO_ERROR); } -void CodecHttp2::WindowUpdate(uint32 uiStreamId, uint32 uiIncrement) +bool CodecHttp2::WindowUpdate(uint32 uiStreamId, uint32 uiIncrement) { - m_uiSendWindowSize += uiIncrement; if (uiStreamId > 0) { auto iter = m_mapStream.find(uiStreamId); if (iter != m_mapStream.end()) { - iter->second->WindowUpdate((int32)uiIncrement); + return(iter->second->WindowUpdate((int32)uiIncrement)); + } + } + else + { + int32 iSendWindowSize = m_iSendWindowSize + uiIncrement; + if (iSendWindowSize > SETTINGS_MAX_INITIAL_WINDOW_SIZE) + { + LOG4_ERROR("m_iSettingMaxSendWindowSize = %d, m_iSendWindowSize = %d, uiIncrement = %u", + m_iSettingsMaxSendWindowSize, m_iSendWindowSize, uiIncrement); + return(false); } + m_iSendWindowSize = iSendWindowSize; } + return(true); } void CodecHttp2::UpdateSendWindow(uint32 uiStreamId, uint32 uiSendLength) { - m_uiSendWindowSize -= uiSendLength; + m_iSendWindowSize -= uiSendLength; if (uiStreamId > 0) { auto iter = m_mapStream.find(uiStreamId); @@ -656,19 +735,23 @@ void CodecHttp2::UpdateSendWindow(uint32 uiStreamId, uint32 uiSendLength) void CodecHttp2::UpdateRecvWindow(uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) { - m_uiRecvWindowSize -= uiRecvLength; - if (m_uiRecvWindowSize < DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE / 4) + m_iRecvWindowSize -= uiRecvLength; + if (m_iRecvWindowSize < m_iSettingsMaxRecvWindowSize / 4) { m_pFrame->EncodeWindowUpdate(this, 0, - SETTINGS_MAX_INITIAL_WINDOW_SIZE - uiRecvLength, pBuff); - m_uiRecvWindowSize = SETTINGS_MAX_INITIAL_WINDOW_SIZE; + m_iSettingsMaxRecvWindowSize - m_iRecvWindowSize, pBuff); + m_iRecvWindowSize = m_iSettingsMaxRecvWindowSize; } if (uiStreamId > 0) { auto iter = m_mapStream.find(uiStreamId); if (iter != m_mapStream.end()) { - iter->second->UpdateRecvWindow(this, uiStreamId, uiRecvLength, pBuff); + bool bUpdated = iter->second->UpdateRecvWindow(this, m_iSettingsMaxRecvWindowSize, uiStreamId, uiRecvLength, pBuff); + if (bUpdated) + { + m_iRecvWindowSize = m_iSettingsMaxRecvWindowSize; + } } } } @@ -838,6 +921,7 @@ E_CODEC_STATUS CodecHttp2::PromiseStream(uint32 uiStreamId, CBuffer* pReactBuff) if (uiStreamId <= m_uiStreamIdGenerate) { SetErrno(H2_ERR_REFUSED_STREAM); + LOG4_ERROR("reset stream for H2_ERR_REFUSED_STREAM"); m_pFrame->EncodeRstStream(this, uiStreamId, H2_ERR_REFUSED_STREAM, pReactBuff); return(CODEC_STATUS_PART_ERR); } @@ -876,6 +960,10 @@ E_CODEC_STATUS CodecHttp2::SendWaittingFrameData(CBuffer* pBuff) { vecCompletedStream.push_back(uiStreamId); } + if (CODEC_STATUS_OK != eStatus) + { + break; + } } if (pCurrent->pFirstChild != nullptr) { @@ -890,6 +978,10 @@ E_CODEC_STATUS CodecHttp2::SendWaittingFrameData(CBuffer* pBuff) { CloseStream(id); } + if (CODEC_STATUS_OK == eStatus) + { + SetWaittingFrame(false); + } return(eStatus); } return(CODEC_STATUS_OK); @@ -907,6 +999,17 @@ void CodecHttp2::TransferHoldingMsg(HttpMsg* pHoldingHttpMsg) m_pHoldingHttpMsg = pHoldingHttpMsg; } +void CodecHttp2::DebugDecodingTable() +{ + LOG4_TRACE("m_iNextDecodingDynamicTableHeaderIndex = %d, m_uiDecodingDynamicTableHeaderCount = %u", + m_iNextDecodingDynamicTableHeaderIndex, m_uiDecodingDynamicTableHeaderCount); + for (uint32 i = m_iNextDecodingDynamicTableHeaderIndex + 1; i < m_vecDecodingDynamicTable.size(); ++i) + { + LOG4_TRACE("m_vecDecodingDynamicTable[%u] %s %s", i, + m_vecDecodingDynamicTable[i].Name().c_str(), m_vecDecodingDynamicTable[i].Value().c_str()); + } +} + uint32 CodecHttp2::StreamIdGenerate() { if (GetBindChannel()->IsClient()) @@ -936,13 +1039,10 @@ uint32 CodecHttp2::StreamIdGenerate() Http2Stream* CodecHttp2::NewCodingStream(uint32 uiStreamId) { - uint32 uiNewWindowSize = m_uiRecvWindowSize + DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; try { m_pCodingStream = new Http2Stream(m_pLogger, GetCodecType(), GetBindChannel(), uiStreamId); - m_pCodingStream->WindowInit(m_uiSettingsMaxWindowSize); - m_uiRecvWindowSize = (uiNewWindowSize < SETTINGS_MAX_INITIAL_WINDOW_SIZE) - ? uiNewWindowSize : SETTINGS_MAX_INITIAL_WINDOW_SIZE; + m_pCodingStream->WindowInit(m_iSettingsMaxSendWindowSize, m_iSettingsMaxRecvWindowSize); m_mapStream.insert(std::make_pair(uiStreamId, m_pCodingStream)); return(m_pCodingStream); } @@ -955,7 +1055,7 @@ Http2Stream* CodecHttp2::NewCodingStream(uint32 uiStreamId) TreeNode* CodecHttp2::FindStreamWeight(uint32 uiStreamId, TreeNode* pTarget) { - if (pTarget == nullptr) + if (pTarget == nullptr || uiStreamId == 0) { return(nullptr); } @@ -1073,7 +1173,7 @@ E_CODEC_STATUS CodecHttp2::UnpackHeaderLiteralIndexing(CBuffer* pBuff, uint8 ucF strHeaderName = m_vecDecodingDynamicTable[uiVectorIndex].Name(); if (H2_HPACK_CONDITION_LITERAL_HEADER_WITH_INDEXING & ucFirstByte) { - UpdateDecodingDynamicTable(uiVectorIndex, strHeaderName, strHeaderValue); + UpdateDecodingDynamicTable(-1, strHeaderName, strHeaderValue); } return(CODEC_STATUS_PART_OK); } @@ -1456,7 +1556,7 @@ uint32 CodecHttp2::UpdateEncodingDynamicTable(int32 iRecoverSize) // move headers in dynamic table uint32 uiFrom = 0; uint32 uiTo = 0; - for (uint32 j = 0; j < uiEvictHeaderNum; ++j) + for (uint32 j = 0; j < m_uiEncodingDynamicTableHeaderCount; ++j) { uiTo = m_vecEncodingDynamicTable.size() - 1 - j; uiFrom = m_vecEncodingDynamicTable.size() - 1 - j - uiEvictHeaderNum; @@ -1488,8 +1588,8 @@ void CodecHttp2::UpdateDecodingDynamicTable(int32 iVectorIndex, const std::strin return; } // evict headers to required length. - int32 uiNeedRecoverSize = (m_uiDecodingDynamicTableSize + uiEntrySize) - m_uiSettingsHeaderTableSize; - UpdateDecodingDynamicTable(uiNeedRecoverSize); + int32 iNeedRecoverSize = (int32)(m_uiDecodingDynamicTableSize + uiEntrySize) - (int32)m_uiSettingsHeaderTableSize; + UpdateDecodingDynamicTable(iNeedRecoverSize); if (m_uiDecodingDynamicTableHeaderCount + 1 > m_vecDecodingDynamicTable.size()) // need to grow the dynamic table. { Http2Header oHeader("", ""); @@ -1519,7 +1619,7 @@ void CodecHttp2::UpdateDecodingDynamicTable(int32 iVectorIndex, const std::strin return; } // evict headers to the required length. - int32 iNeedRecoverSize = (m_uiDecodingDynamicTableSize + iAddSize) - m_uiSettingsHeaderTableSize; + int32 iNeedRecoverSize = (int32)(m_uiDecodingDynamicTableSize + iAddSize) - (int32)m_uiSettingsHeaderTableSize; uint32 uiEvictedNum = UpdateDecodingDynamicTable(iNeedRecoverSize); iVectorIndex += uiEvictedNum; m_vecDecodingDynamicTable[iVectorIndex] = std::move(oHeader); @@ -1550,7 +1650,7 @@ uint32 CodecHttp2::UpdateDecodingDynamicTable(int32 iRecoverSize) // move headers in dynamic table uint32 uiFrom = 0; uint32 uiTo = 0; - for (uint32 j = 0; j < uiEvictHeaderNum; ++j) + for (uint32 j = 0; j < m_uiDecodingDynamicTableHeaderCount; ++j) { uiTo = m_vecDecodingDynamicTable.size() - 1 - j; uiFrom = m_vecDecodingDynamicTable.size() - 1 - j - uiEvictHeaderNum; diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp index bffc6d51..212ae8c1 100644 --- a/src/codec/http2/CodecHttp2.hpp +++ b/src/codec/http2/CodecHttp2.hpp @@ -46,7 +46,10 @@ class CodecHttp2: public Codec return(CODEC_HTTP2); } + // request static int Write(uint32 uiFromLabor, uint32 uiToLabor, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg); + + // response static int Write(std::shared_ptr pChannel, uint32 uiFlags, uint32 uiStepSeq, const HttpMsg& oHttpMsg); virtual bool DecodeWithReactor() const @@ -66,15 +69,23 @@ class CodecHttp2: public Codec virtual void ConnectionSetting(CBuffer* pBuff); public: + inline bool IsEstablish() const + { + if (m_bClientPrefaceMagic && m_bServerPreface) + { + return(true); + } + return(false); + } void SetPriority(uint32 uiStreamId, const tagPriority& stPriority); void RstStream(uint32 uiStreamId); E_H2_ERR_CODE Setting(const std::vector& vecSetting, bool bRecvSetting = false); - void WindowUpdate(uint32 uiStreamId, uint32 uiIncrement); + bool WindowUpdate(uint32 uiStreamId, uint32 uiIncrement); void UpdateSendWindow(uint32 uiStreamId, uint32 uiSendLength); void UpdateRecvWindow(uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff); - uint32 GetSendWindowSize() + int32 GetSendWindowSize() { - return(m_uiSendWindowSize); + return(m_iSendWindowSize); } uint32 GetMaxEncodeFrameSize() { @@ -102,6 +113,8 @@ class CodecHttp2: public Codec m_bHasWaittingFrame = bHasWaittingFrame; } + void DebugDecodingTable(); + protected: uint32 StreamIdGenerate(); Http2Stream* NewCodingStream(uint32 uiStreamId); @@ -132,19 +145,21 @@ class CodecHttp2: public Codec void CloseStream(uint32 uiStreamId); private: - bool m_bWantMagic = true; + bool m_bClientPrefaceMagic = false; + bool m_bServerPreface = false; bool m_bHasWaittingFrame = false; uint32 m_uiStreamIdGenerate = 0; uint32 m_uiGoawayLastStreamId = 0; uint32 m_uiSettingsEnablePush = 1; uint32 m_uiSettingsHeaderTableSize = 4096; uint32 m_uiSettingsMaxConcurrentStreams = 100; - uint32 m_uiSettingsMaxWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + int32 m_iSettingsMaxSendWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + int32 m_iSettingsMaxRecvWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; uint32 m_uiSettingsMaxEncodeFrameSize = DEFAULT_SETTINGS_MAX_FRAME_SIZE; uint32 m_uiSettingsMaxDecodeFrameSize = DEFAULT_SETTINGS_MAX_FRAME_SIZE; uint32 m_uiSettingsMaxHeaderListSize = 8192; // TODO SettingsMaxHeaderListSize - uint32 m_uiSendWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; - uint32 m_uiRecvWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + int32 m_iSendWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; + int32 m_iRecvWindowSize = DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE; tagH2FrameHead m_stDecodeFrameHead; HttpMsg* m_pHoldingHttpMsg = nullptr; // upgrade未完成时暂存请求 Http2Frame* m_pFrame = nullptr; diff --git a/src/codec/http2/H2Comm.hpp b/src/codec/http2/H2Comm.hpp index 82ff94fe..14a746e4 100644 --- a/src/codec/http2/H2Comm.hpp +++ b/src/codec/http2/H2Comm.hpp @@ -16,8 +16,8 @@ namespace neb const uint32 H2_FRAME_HEAD_SIZE = 9; const uint32 SETTINGS_MAX_FRAME_SIZE = 16777215; // (2^24) - 1 const uint32 DEFAULT_SETTINGS_MAX_FRAME_SIZE = 16384; // (2^14) -const uint32 SETTINGS_MAX_INITIAL_WINDOW_SIZE = 2147483647; // (2^31) - 1 -const uint32 DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE = 65535; // (2^16) - 1 +const int32 SETTINGS_MAX_INITIAL_WINDOW_SIZE = 2147483647; // (2^31) - 1 +const int32 DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE = 65535; // (2^16) - 1 /* * @see https://httpwg.org/specs/rfc7540.html#FRAME_SIZE_ERROR @@ -56,6 +56,7 @@ enum E_H2_SETTING_REGISTRY H2_SETTINGS_INITIAL_WINDOW_SIZE = 0x4, H2_SETTINGS_MAX_FRAME_SIZE = 0x5, H2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x6, + H2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA = 65027, }; struct tagH2FrameHead diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp index 6812cdd5..a2fb274b 100644 --- a/src/codec/http2/Http2Frame.cpp +++ b/src/codec/http2/Http2Frame.cpp @@ -37,12 +37,6 @@ Http2Frame::Http2Frame(std::shared_ptr pLogger, E_CODEC_TYPE eCodecTy Http2Frame::~Http2Frame() { m_pStream = nullptr; - for (auto iter = m_listWaittingFrameData.begin(); - iter != m_listWaittingFrameData.end(); ++iter) - { - DELETE(*iter); - } - m_listWaittingFrameData.clear(); LOG4_TRACE("codec type %d", GetCodecType()); } @@ -134,10 +128,15 @@ E_CODEC_STATUS Http2Frame::Encode(CodecHttp2* pCodecH2, E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; if (oHttpMsg.headers_size() > 0 || oHttpMsg.pseudo_header_size() > 0) { + if (oHttpMsg.trailer_header_size() == 0 && oHttpMsg.body().empty()) + { + bEndStream = true; + } eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, H2_HEADER_PSEUDO | H2_HEADER_NORMAL, stPriority, strPadding, bEndStream, pBuff); if (CODEC_STATUS_PART_ERR == eCodecStatus - || CODEC_STATUS_ERR == eCodecStatus) + || CODEC_STATUS_ERR == eCodecStatus + || bEndStream) { return(eCodecStatus); } @@ -146,18 +145,21 @@ E_CODEC_STATUS Http2Frame::Encode(CodecHttp2* pCodecH2, { bEndStream = true; } - eCodecStatus = EncodeData(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, bEndStream, strPadding, pBuff); + eCodecStatus = EncodeData(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, bEndStream, strPadding, 0, pBuff); if (CODEC_STATUS_PART_ERR == eCodecStatus || CODEC_STATUS_ERR == eCodecStatus) { return(eCodecStatus); } - if (oHttpMsg.trailer_header_size() > 0) + if (CODEC_STATUS_OK == eCodecStatus) { - tagPriority stEmptyPriority; - bEndStream = true; - eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, - H2_HEADER_TRAILER, stEmptyPriority, "", bEndStream, pBuff); + if (oHttpMsg.trailer_header_size() > 0) + { + tagPriority stEmptyPriority; + bEndStream = true; + eCodecStatus = EncodeHeaders(pCodecH2, oHttpMsg.stream_id(), oHttpMsg, + H2_HEADER_TRAILER, stEmptyPriority, "", bEndStream, pBuff); + } } return(eCodecStatus); } @@ -279,7 +281,7 @@ E_CODEC_STATUS Http2Frame::DecodeHeaders(CodecHttp2* pCodecH2, stFrameHead.uiStreamIdentifier, (uint32)stPriority.E, (uint32)stPriority.ucWeight, stPriority.uiDependency); pCodecH2->SetPriority(stFrameHead.uiStreamIdentifier, stPriority); } - E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_PART_OK; if (H2_FRAME_FLAG_END_HEADERS & stFrameHead.ucFlag) { eCodecStatus = pCodecH2->UnpackHeader(uiHeaderBlockEndPos, pBuff, oHttpMsg); @@ -354,10 +356,10 @@ E_CODEC_STATUS Http2Frame::DecodeRstStream(CodecHttp2* pCodecH2, pBuff->Read(&iErrCode, 4); // the error code should be H2_ERR_CANCEL iErrCode = CodecUtil::N2H((uint32)iErrCode); pCodecH2->SetErrno(iErrCode); + LOG4_ERROR("stream %u error %d", stFrameHead.uiStreamIdentifier, iErrCode); if (m_pStream != nullptr) { m_pStream->SetState(H2_STREAM_CLOSE); - pCodecH2->RstStream(stFrameHead.uiStreamIdentifier); } return(CODEC_STATUS_PART_ERR); } @@ -564,26 +566,40 @@ E_CODEC_STATUS Http2Frame::DecodeWindowUpdate(CodecHttp2* pCodecH2, if (stFrameHead.uiStreamIdentifier == 0) { pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + LOG4_ERROR("goaway for H2_ERR_PROTOCOL_ERROR"); EncodeGoaway(pCodecH2, H2_ERR_PROTOCOL_ERROR, "TYPE_WINDOW_UPDATE length == 0 on connection", pReactBuff); return(CODEC_STATUS_ERR); } else { pCodecH2->SetErrno(H2_ERR_PROTOCOL_ERROR); + LOG4_ERROR("reset stream for H2_ERR_PROTOCOL_ERROR"); EncodeRstStream(pCodecH2, stFrameHead.uiStreamIdentifier, H2_ERR_PROTOCOL_ERROR, pReactBuff); return(CODEC_STATUS_PART_ERR); } } else { - pCodecH2->WindowUpdate(stFrameHead.uiStreamIdentifier, uiIncrement); + LOG4_TRACE("uiIncrement = %u", uiIncrement); + E_CODEC_STATUS eStatus; + if (!pCodecH2->WindowUpdate(stFrameHead.uiStreamIdentifier, uiIncrement)) + { + pCodecH2->SetErrno(H2_ERR_FLOW_CONTROL_ERROR); + LOG4_ERROR("reset stream for H2_ERR_FLOW_CONTROL_ERROR"); + EncodeRstStream(pCodecH2, stFrameHead.uiStreamIdentifier, H2_ERR_FLOW_CONTROL_ERROR, pReactBuff); + return(CODEC_STATUS_PART_ERR); + } if (m_pStream != nullptr && stFrameHead.uiStreamIdentifier == m_pStream->GetStreamId()) { - SendWaittingFrameData(pCodecH2, pReactBuff); + eStatus = SendWaittingFrameData(pCodecH2, pReactBuff); + if (CODEC_STATUS_OK == eStatus) + { + eStatus = pCodecH2->SendWaittingFrameData(pReactBuff); + } } else { - pCodecH2->SendWaittingFrameData(pReactBuff); + eStatus = pCodecH2->SendWaittingFrameData(pReactBuff); } } return(CODEC_STATUS_OK); @@ -625,7 +641,7 @@ E_CODEC_STATUS Http2Frame::DecodeContinuation(CodecHttp2* pCodecH2, E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, const HttpMsg& oHttpMsg, bool bEndStream, - const std::string& strPadding, CBuffer* pBuff) + const std::string& strPadding, uint32 uiDataOffset, CBuffer* pBuff) { if (uiStreamId == 0x0) { @@ -637,6 +653,15 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiDataLen = oHttpMsg.body().size(); uint32 uiHadEncodeDataLen = 0; uint32 uiEncodedDataLen = 0; + if (uiDataOffset < uiDataLen) + { + pBodyData += uiDataOffset; + uiHadEncodeDataLen += uiDataOffset; + } + else + { + return(CODEC_STATUS_PART_OK); + } do { eEncodeStatus = EncodeData(pCodecH2, uiStreamId, pBodyData, @@ -645,10 +670,28 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, { return(eEncodeStatus); } + LOG4_TRACE("uiDataLen = %u, uiEncodedDataLen = %u, uiHadEncodeDataLen = %u", + uiDataLen, uiEncodedDataLen, uiHadEncodeDataLen); + if (uiEncodedDataLen == 0) + { + if (uiHadEncodeDataLen == 0) + { + m_oSendingHttpMsg = oHttpMsg; + m_uiHadEncodeDataLen = uiHadEncodeDataLen; + m_strPadding = strPadding; + tagPriority stPriority; + stPriority.ucWeight = 16; + pCodecH2->SetPriority(uiStreamId, stPriority); + pCodecH2->SetWaittingFrame(true); + } + return(CODEC_STATUS_PART_OK); + } pBodyData += uiEncodedDataLen; uiHadEncodeDataLen += uiEncodedDataLen; + LOG4_TRACE("uiDataLen = %u, uiHadEncodeDataLen = %u", uiDataLen, uiHadEncodeDataLen); } while (uiHadEncodeDataLen < uiDataLen); + LOG4_TRACE("pBuff->ReadableBytes() = %u", pBuff->ReadableBytes()); return(eEncodeStatus); } @@ -690,6 +733,10 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, if (stFrameHead.uiLength > pCodecH2->GetMaxEncodeFrameSize()) { stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); @@ -708,6 +755,10 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; eCodecStatus = CODEC_STATUS_OK; } + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); @@ -736,6 +787,10 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, if (stFrameHead.uiLength > pCodecH2->GetMaxEncodeFrameSize()) { stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); EncodePriority(stPriority, pBuff); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); @@ -751,6 +806,10 @@ E_CODEC_STATUS Http2Frame::EncodeHeaders(CodecHttp2* pCodecH2, stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; eCodecStatus = CODEC_STATUS_OK; } + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); EncodePriority(stPriority, pBuff); pBuff->Write(oBuffer.GetRawReadBuffer(), stFrameHead.uiLength - uiAddtionLength); @@ -775,6 +834,10 @@ E_CODEC_STATUS Http2Frame::EncodePriority(CodecHttp2* pCodecH2, uiPriority <<= 31; uiPriority |= stPriority.uiDependency; uiPriority = CodecUtil::H2N(uiPriority); + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); pBuff->Write(&uiPriority, 4); pBuff->Write(&stPriority.ucWeight, 1); @@ -791,6 +854,10 @@ E_CODEC_STATUS Http2Frame::EncodeRstStream(CodecHttp2* pCodecH2, stFrameHead.ucFlag = 0; stFrameHead.uiStreamIdentifier = uiStreamId; stFrameHead.uiLength = 4; + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); uint32 uiErrCode = CodecUtil::H2N((uint32)iErrCode); pBuff->Write(&uiErrCode, 4); @@ -817,6 +884,10 @@ E_CODEC_STATUS Http2Frame::EncodeSetting(CodecHttp2* pCodecH2, { pCodecH2->Setting(vecSetting, false); } + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); for (uint32 i = 0; i < vecSetting.size(); ++i) { @@ -838,6 +909,10 @@ E_CODEC_STATUS Http2Frame::EncodeSetting(CodecHttp2* pCodecH2, CBuffer* pBuff) stFrameHead.uiStreamIdentifier = 0; stFrameHead.uiLength = 0; stFrameHead.ucFlag |= H2_FRAME_FLAG_ACK; + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); EncodeSetStreamState(stFrameHead); return(CODEC_STATUS_OK); @@ -877,6 +952,10 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, if (stFrameHead.uiLength > pCodecH2->GetMaxEncodeFrameSize()) { stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); @@ -895,6 +974,10 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, { stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; } + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); @@ -913,6 +996,10 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, if (stFrameHead.uiLength > pCodecH2->GetMaxEncodeFrameSize()) { stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); // ignore R pBuff->Write(&uiPromiseStreamId, 4); @@ -928,6 +1015,10 @@ E_CODEC_STATUS Http2Frame::EncodePushPromise(CodecHttp2* pCodecH2, { stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; } + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); // ignore R pBuff->Write(&uiPromiseStreamId, 4); @@ -951,6 +1042,10 @@ E_CODEC_STATUS Http2Frame::EncodePing(CodecHttp2* pCodecH2, { stFrameHead.ucFlag |= H2_FRAME_FLAG_ACK; } + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); pBuff->Write(&iPayload1, 4); pBuff->Write(&iPayload2, 4); @@ -967,6 +1062,10 @@ E_CODEC_STATUS Http2Frame::EncodeGoaway(CodecHttp2* pCodecH2, stFrameHead.ucFlag = 0; stFrameHead.uiStreamIdentifier = 0x0; stFrameHead.uiLength = 8 + strDebugData.size(); + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); uint32 uiNetStreamId = CodecUtil::H2N(pCodecH2->GetLastStreamId()); @@ -991,6 +1090,10 @@ E_CODEC_STATUS Http2Frame::EncodeWindowUpdate(CodecHttp2* pCodecH2, stFrameHead.ucFlag = 0; stFrameHead.uiStreamIdentifier = uiStreamId; stFrameHead.uiLength = 4; + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); uiIncrement = CodecUtil::H2N(uiIncrement); pBuff->Write(&uiIncrement, 4); @@ -1027,6 +1130,10 @@ E_CODEC_STATUS Http2Frame::EncodeContinuation(CodecHttp2* pCodecH2, } } EncodeFrameHeader(stFrameHead, pBuff); + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); pBuff->Write(pHpackBuff->GetRawBuffer(), stFrameHead.uiLength); pHpackBuff->AdvanceReadIndex(stFrameHead.uiLength); } @@ -1059,6 +1166,11 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, const char* pData, uint32 uiDataLen, bool bEndStream, const std::string& strPadding, uint32& uiEncodedDataLen, CBuffer* pBuff) { + // if (pCodecH2->IsEstablish()) + // { + // uiEncodedDataLen = 0; + // return(CODEC_STATUS_PART_OK); + // } E_CODEC_STATUS eCodecStatus = CODEC_STATUS_PART_OK; tagH2FrameHead stFrameHead; stFrameHead.cR = 0; @@ -1072,6 +1184,7 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, { stFrameHead.uiLength = pCodecH2->GetMaxEncodeFrameSize(); uiEncodedDataLen = stFrameHead.uiLength - 1 - strPadding.size(); + LOG4_TRACE("uiEncodedDataLen = %u", uiEncodedDataLen); } else { @@ -1081,11 +1194,16 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, stFrameHead.ucFlag |= H2_FRAME_FLAG_END_STREAM; } eCodecStatus = CODEC_STATUS_OK; + LOG4_TRACE("uiEncodedDataLen = %u", uiEncodedDataLen); } stFrameHead.ucFlag |= H2_FRAME_FLAG_PADDED; - if (uiEncodedDataLen < pCodecH2->GetSendWindowSize() + if ((int32)uiEncodedDataLen < pCodecH2->GetSendWindowSize() && (int32)uiEncodedDataLen < m_pStream->GetSendWindowSize()) { + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); uint16 unNetLength = CodecUtil::H2N(strPadding.size()); pBuff->Write(&unNetLength, 1); @@ -1093,27 +1211,11 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, pBuff->Write(strPadding.c_str(), strPadding.size()); pCodecH2->UpdateSendWindow(uiStreamId, uiEncodedDataLen); EncodeSetStreamState(stFrameHead); + LOG4_TRACE("uiEncodedDataLen = %u", uiEncodedDataLen); } else { - CBuffer* pWaittingBuff = nullptr; - try - { - pWaittingBuff = new CBuffer(); - } - catch(std::bad_alloc& e) - { - LOG4_ERROR("%s", e.what()); - return(CODEC_STATUS_PART_ERR); - } - m_listWaittingFrameData.push_back(pWaittingBuff); - pCodecH2->SetWaittingFrame(true); - EncodeFrameHeader(stFrameHead, pWaittingBuff); - uint16 unNetLength = CodecUtil::H2N(strPadding.size()); - pWaittingBuff->Write(&unNetLength, 1); - pWaittingBuff->Write(pData, uiEncodedDataLen); - pWaittingBuff->Write(strPadding.c_str(), strPadding.size()); - m_stLastDataFrameHead = stFrameHead; + uiEncodedDataLen = 0; eCodecStatus = CODEC_STATUS_PART_OK; } } @@ -1132,73 +1234,58 @@ E_CODEC_STATUS Http2Frame::EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, } eCodecStatus = CODEC_STATUS_OK; } - if (uiEncodedDataLen < pCodecH2->GetSendWindowSize() - && (int32)uiEncodedDataLen < m_pStream->GetSendWindowSize()) + LOG4_TRACE("uiDataLen = %u, stFrameHead.uiLength = %u, pCodecH2->GetMaxEncodeFrameSize() = %u", + uiDataLen, stFrameHead.uiLength, pCodecH2->GetMaxEncodeFrameSize()); + if ((int32)stFrameHead.uiLength < pCodecH2->GetSendWindowSize() + && (int32)stFrameHead.uiLength < m_pStream->GetSendWindowSize()) { uiEncodedDataLen = stFrameHead.uiLength; + LOG4_TRACE("stFrameHead.uiLength = %u, stFrameHead.ucType = %u," + "stFrameHead.ucFlag = %u, stFrameHead.uiStreamIdentifier = %u", + stFrameHead.uiLength, (uint32)stFrameHead.ucType, + (uint32)stFrameHead.ucFlag, stFrameHead.uiStreamIdentifier); EncodeFrameHeader(stFrameHead, pBuff); pBuff->Write(pData, uiEncodedDataLen); pCodecH2->UpdateSendWindow(uiStreamId, uiEncodedDataLen); EncodeSetStreamState(stFrameHead); + LOG4_ERROR("uiEncodedDataLen = %u", uiEncodedDataLen); } else { - CBuffer* pWaittingBuff = nullptr; - try - { - pWaittingBuff = new CBuffer(); - } - catch(std::bad_alloc& e) - { - LOG4_ERROR("%s", e.what()); - return(CODEC_STATUS_PART_ERR); - } - m_listWaittingFrameData.push_back(pWaittingBuff); - pCodecH2->SetWaittingFrame(true); - uiEncodedDataLen = stFrameHead.uiLength; - EncodeFrameHeader(stFrameHead, pWaittingBuff); - pWaittingBuff->Write(pData, uiEncodedDataLen); - m_stLastDataFrameHead = stFrameHead; + uiDataLen = 0; eCodecStatus = CODEC_STATUS_PART_OK; } } + LOG4_TRACE("pCodecH2->GetSendWindowSize() = %u, pStream->GetSendWindowSize() = %u, uiEncodedDataLen = %u", + pCodecH2->GetSendWindowSize(), m_pStream->GetSendWindowSize(), uiEncodedDataLen); return(eCodecStatus); } E_CODEC_STATUS Http2Frame::SendWaittingFrameData(CodecHttp2* pCodecH2, CBuffer* pBuff) { - LOG4_TRACE("m_listWaittingFrameData.size() = %u", m_listWaittingFrameData.size()); - if (m_listWaittingFrameData.empty()) + LOG4_TRACE("m_uiHadEncodeDataLen = %u", m_uiHadEncodeDataLen); + if (m_uiHadEncodeDataLen == m_oSendingHttpMsg.body().size()) { return(CODEC_STATUS_OK); } - uint32 uiDataLen = 0; - for (auto iter = m_listWaittingFrameData.begin(); - iter != m_listWaittingFrameData.end(); ++iter) - { - uiDataLen = (*iter)->ReadableBytes(); - if (uiDataLen < pCodecH2->GetSendWindowSize() - && (int32)uiDataLen < m_pStream->GetSendWindowSize()) - { - pBuff->Write(*iter, uiDataLen); - DELETE(*iter); - m_listWaittingFrameData.erase(iter); - iter = m_listWaittingFrameData.begin(); - } - else - { - break; - } - } - if (m_listWaittingFrameData.empty()) + bool bEndStream = false; + E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; + if (m_oSendingHttpMsg.trailer_header_size() == 0) { - EncodeSetStreamState(m_stLastDataFrameHead); - return(CODEC_STATUS_OK); + bEndStream = true; } - else + eCodecStatus = EncodeData(pCodecH2, m_oSendingHttpMsg.stream_id(), m_oSendingHttpMsg, bEndStream, m_strPadding, m_uiHadEncodeDataLen, pBuff); + if (CODEC_STATUS_OK == eCodecStatus) { - return(CODEC_STATUS_PART_OK); + if (m_oSendingHttpMsg.trailer_header_size() > 0) + { + tagPriority stEmptyPriority; + bEndStream = true; + eCodecStatus = EncodeHeaders(pCodecH2, m_oSendingHttpMsg.stream_id(), m_oSendingHttpMsg, + H2_HEADER_TRAILER, stEmptyPriority, "", bEndStream, pBuff); + } } + return(eCodecStatus); } } /* namespace neb */ diff --git a/src/codec/http2/Http2Frame.hpp b/src/codec/http2/Http2Frame.hpp index affe8514..48e41692 100644 --- a/src/codec/http2/Http2Frame.hpp +++ b/src/codec/http2/Http2Frame.hpp @@ -115,7 +115,7 @@ class Http2Frame: public Codec E_CODEC_STATUS EncodeData(CodecHttp2* pCodecH2, uint32 uiStreamId, const HttpMsg& oHttpMsg, bool bEndStream, - const std::string& strPadding, CBuffer* pBuff); + const std::string& strPadding, uint32 uiDataOffset, CBuffer* pBuff); E_CODEC_STATUS EncodeHeaders(CodecHttp2* pCodecH2, uint32 uiStreamId, const HttpMsg& oHttpMsg, int iHeaderType, const tagPriority& stPriority, const std::string& strPadding, @@ -152,9 +152,11 @@ class Http2Frame: public Codec friend class Http2Stream; static std::unordered_set s_setFrameType; - std::list m_listWaittingFrameData; + HttpMsg m_oSendingHttpMsg; + uint32 m_uiHadEncodeDataLen = 0; + std::string m_strPadding; tagH2FrameHead m_stLastDataFrameHead; - Http2Stream* m_pStream; + Http2Stream* m_pStream = nullptr; }; } /* namespace neb */ diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp index 8c87cbbc..048d2d89 100644 --- a/src/codec/http2/Http2Stream.cpp +++ b/src/codec/http2/Http2Stream.cpp @@ -39,6 +39,22 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, std::string strPadding; if (HTTP_REQUEST == oHttpMsg.type()) { + if (oHttpMsg.url().empty()) + { + std::string strUrl; + auto pChannel = GetBindChannel(); + if (pChannel->WithSsl()) + { + strUrl = "https://"; + } + else + { + strUrl = "http://"; + } + strUrl += pChannel->GetIdentify(); + strUrl += oHttpMsg.path(); + (const_cast(oHttpMsg)).set_url(strUrl); + } m_bChunkNotice = oHttpMsg.chunk_notice(); stPriority.uiDependency = 0; stPriority.ucWeight = 15; @@ -55,36 +71,45 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, pHeader->set_name(":scheme"); pHeader->set_value("http"); } - struct http_parser_url stUrl; - if(0 == http_parser_parse_url(oHttpMsg.url().c_str(), oHttpMsg.url().length(), 0, &stUrl)) + if (oHttpMsg.url().size() > 0) { - std::string strAuthority; - if(stUrl.field_set & (1 << UF_USERINFO) ) + struct http_parser_url stUrl; + if(0 == http_parser_parse_url(oHttpMsg.url().c_str(), oHttpMsg.url().length(), 0, &stUrl)) { - strAuthority += oHttpMsg.url().substr(stUrl.field_data[UF_USERINFO].off, stUrl.field_data[UF_USERINFO].len); - strAuthority += "@"; - } - if(stUrl.field_set & (1 << UF_HOST) ) - { - strAuthority += oHttpMsg.url().substr(stUrl.field_data[UF_HOST].off, stUrl.field_data[UF_HOST].len); - } - if(stUrl.field_set & (1 << UF_PORT)) - { - strAuthority += ":"; - strAuthority += oHttpMsg.url().substr(stUrl.field_data[UF_PORT].off, stUrl.field_data[UF_PORT].len); + std::string strAuthority; + if(stUrl.field_set & (1 << UF_USERINFO) ) + { + strAuthority += oHttpMsg.url().substr(stUrl.field_data[UF_USERINFO].off, stUrl.field_data[UF_USERINFO].len); + strAuthority += "@"; + } + if(stUrl.field_set & (1 << UF_HOST) ) + { + strAuthority += oHttpMsg.url().substr(stUrl.field_data[UF_HOST].off, stUrl.field_data[UF_HOST].len); + } + if(stUrl.field_set & (1 << UF_PORT)) + { + strAuthority += ":"; + strAuthority += oHttpMsg.url().substr(stUrl.field_data[UF_PORT].off, stUrl.field_data[UF_PORT].len); + } + auto pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); + pHeader->set_name(":authority"); + pHeader->set_value(strAuthority); + std::string strPath = "/"; + if(stUrl.field_set & (1 << UF_PATH)) + { + //strPath = oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off, stUrl.field_data[UF_PATH].len); + strPath = oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off); // including: ?param1=aaa¶m2=bbb + } + pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); + pHeader->set_name(":path"); + pHeader->set_value(strPath); } + } + else + { auto pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); - pHeader->set_name(":authority"); - pHeader->set_value(strAuthority); - std::string strPath = "/"; - if(stUrl.field_set & (1 << UF_PATH)) - { - //strPath = oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off, stUrl.field_data[UF_PATH].len); - strPath = oHttpMsg.url().substr(stUrl.field_data[UF_PATH].off); // including: ?param1=aaa¶m2=bbb - } - pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); pHeader->set_name(":path"); - pHeader->set_value(strPath); + pHeader->set_value(oHttpMsg.path()); } auto pHeader = (const_cast(oHttpMsg)).add_pseudo_header(); pHeader->set_name(":method"); @@ -104,7 +129,8 @@ E_CODEC_STATUS Http2Stream::Encode(CodecHttp2* pCodecH2, E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, const tagH2FrameHead& stFrameHead, CBuffer* pBuff, HttpMsg& oHttpMsg, CBuffer* pReactBuff) { - LOG4_TRACE("m_eStreamState = %d, stFrameHead.ucType = %u", (int)m_eStreamState, (uint32)stFrameHead.ucType); + LOG4_TRACE("m_uiStreamId = %u, m_eStreamState = %d, stFrameHead.ucType = %u, stFrameHead.ucFlag = %u, m_bEndHeaders = %d", + m_uiStreamId, (int)m_eStreamState, (uint32)stFrameHead.ucType, (uint32)stFrameHead.ucFlag, m_bEndHeaders); m_oHttpMsg.set_stream_id(m_uiStreamId); E_CODEC_STATUS eStatus = CODEC_STATUS_OK; eStatus = m_pFrame->Decode(pCodecH2, stFrameHead, pBuff, m_oHttpMsg, pReactBuff); @@ -279,6 +305,7 @@ E_CODEC_STATUS Http2Stream::Decode(CodecHttp2* pCodecH2, { if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) { + LOG4_TRACE("H2_FRAME_FLAG_END_STREAM, m_oHttpMsg.type() = %d", m_oHttpMsg.type()); oHttpMsg = std::move(m_oHttpMsg); eStatus = CODEC_STATUS_OK; } @@ -369,7 +396,7 @@ void Http2Stream::EncodeSetState(const tagH2FrameHead& stFrameHead) } if (H2_FRAME_FLAG_END_STREAM & stFrameHead.ucFlag) { - LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_HALF_CLOSE_REMOTE); + LOG4_TRACE("stream %u state from %d to %d", m_uiStreamId, m_eStreamState, H2_STREAM_HALF_CLOSE_LOCAL); m_eStreamState = H2_STREAM_HALF_CLOSE_LOCAL; } break; @@ -412,35 +439,48 @@ void Http2Stream::EncodeSetState(const tagH2FrameHead& stFrameHead) } } -void Http2Stream::WindowInit(uint32 uiWindowSize) +void Http2Stream::WindowInit(uint32 uiSendWindowSize, uint32 uiRecvWindowSize) { - m_iSendWindowSize = (int32)uiWindowSize; + m_iSendWindowSize = (int32)uiSendWindowSize; + m_uiRecvWindowSize = uiRecvWindowSize; } -void Http2Stream::WindowUpdate(int32 iIncrement) +bool Http2Stream::WindowUpdate(int32 iIncrement) { - m_iSendWindowSize += iIncrement; + int32 iSendWindowSize = m_iSendWindowSize + iIncrement; + LOG4_TRACE("m_iSendWindowSize = %d, iIncrement = %d", m_iSendWindowSize, iIncrement); + //if (iSendWindowSize > m_iSettingMaxSendWindowSize) + if (iSendWindowSize > SETTINGS_MAX_INITIAL_WINDOW_SIZE) + { + return(false); + } + m_iSendWindowSize = iSendWindowSize; + return(true); } -void Http2Stream::UpdateRecvWindow(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) +bool Http2Stream::UpdateRecvWindow(CodecHttp2* pCodecH2, uint32 uiMaxRecvWindowSize, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff) { m_uiRecvWindowSize -= uiRecvLength; + LOG4_TRACE("uiMaxRecvWindowSize = %u, m_uiRecvWindowSize = %u, m_eStreamState = %d", uiMaxRecvWindowSize, m_uiRecvWindowSize, m_eStreamState); if (m_eStreamState == H2_STREAM_OPEN || m_eStreamState == H2_STREAM_IDLE || m_eStreamState == H2_STREAM_RESERVED_LOCAL || m_eStreamState == H2_STREAM_RESERVED_REMOTE) { - if (m_uiRecvWindowSize < DEFAULT_SETTINGS_MAX_INITIAL_WINDOW_SIZE / 4) + if (m_uiRecvWindowSize < uiMaxRecvWindowSize / 4) { m_pFrame->EncodeWindowUpdate(pCodecH2, uiStreamId, - SETTINGS_MAX_INITIAL_WINDOW_SIZE - uiRecvLength, pBuff); - m_uiRecvWindowSize = SETTINGS_MAX_INITIAL_WINDOW_SIZE; + uiMaxRecvWindowSize - m_uiRecvWindowSize, pBuff); + m_uiRecvWindowSize = uiMaxRecvWindowSize; + return(true); } } + return(false); } E_CODEC_STATUS Http2Stream::SendWaittingFrameData(CodecHttp2* pCodecH2, CBuffer* pBuff) { + LOG4_TRACE("stream id %u", m_uiStreamId); return(m_pFrame->SendWaittingFrameData(pCodecH2, pBuff)); } diff --git a/src/codec/http2/Http2Stream.hpp b/src/codec/http2/Http2Stream.hpp index 237525ce..ad8f3a66 100644 --- a/src/codec/http2/Http2Stream.hpp +++ b/src/codec/http2/Http2Stream.hpp @@ -84,9 +84,9 @@ class Http2Stream: public Codec void EncodeSetState(const tagH2FrameHead& stFrameHead); - void WindowInit(uint32 uiWindowSize); - void WindowUpdate(int32 iIncrement); - void UpdateRecvWindow(CodecHttp2* pCodecH2, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff); + void WindowInit(uint32 uiWindowSize, uint32 uiRecvWindowSize); + bool WindowUpdate(int32 iIncrement); + bool UpdateRecvWindow(CodecHttp2* pCodecH2, uint32 uiMaxRecvWindowSize, uint32 uiStreamId, uint32 uiRecvLength, CBuffer* pBuff); E_CODEC_STATUS SendWaittingFrameData(CodecHttp2* pCodecH2, CBuffer* pBuff); private: diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp index e0710e22..a5d26fe3 100644 --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -110,7 +110,7 @@ void Dispatcher::AsyncCallback(struct ev_loop* loop, struct ev_async* watcher, i { auto pWatcher = static_cast(watcher->data); auto pChannel = pWatcher->GetSocketChannel(); - CodecFactory::OnEvent(pWatcher, pChannel); + CodecFactory::OnEvent(pWatcher->GetCodecType(), pChannel, nullptr); } } @@ -231,18 +231,7 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) { if (CODEC_NEBULA == pChannel->GetCodecType()) // 系统内部Server间通信 { - if (pChannel->GetRemoteWorkerIndex() < 0) // connect to Manager - { - std::shared_ptr pStepTellWorker - = m_pLabor->GetActorBuilder()->MakeSharedStep(nullptr, "neb::StepTellWorker", pChannel); - if (nullptr == pStepTellWorker) - { - return(false); - } - pStepTellWorker->Emit(ERR_OK); - pChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); - } - else + if (pChannel->IsClient()) { if (CHANNEL_STATUS_TRY_CONNECT == pChannel->GetChannelStatus()) // connect之后的第一个写事件 { @@ -261,6 +250,20 @@ bool Dispatcher::OnIoWrite(std::shared_ptr pChannel) return(true); } } + else + { + if (pChannel->GetRemoteWorkerIndex() < 0) // connect to Manager + { + std::shared_ptr pStepTellWorker + = m_pLabor->GetActorBuilder()->MakeSharedStep(nullptr, "neb::StepTellWorker", pChannel); + if (nullptr == pStepTellWorker) + { + return(false); + } + pStepTellWorker->Emit(ERR_OK); + pChannel->SetChannelStatus(CHANNEL_STATUS_ESTABLISHED); + } + } } else { @@ -627,6 +630,11 @@ void Dispatcher::DelNodeIdentify(const std::string& strNodeType, const std::stri } } +void Dispatcher::ReplaceNodes(const std::string& strNodeType, const std::set& setNodeIdentify) +{ + m_pSessionNode->ReplaceNodes(strNodeType, setNodeIdentify); +} + void Dispatcher::CircuitBreak(const std::string& strIdentify) { m_pSessionNode->NodeFailed(strIdentify); diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp index f03747f6..6f7b3268 100644 --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -140,6 +140,7 @@ class Dispatcher void DelNamedSocketChannel(const std::string& strIdentify); void AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); void DelNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); + void ReplaceNodes(const std::string& strNodeType, const std::set& setNodeIdentify); void CircuitBreak(const std::string& strIdentify); void SetChannelPingStep(int iCodec, const std::string& strStepName); void SetClientData(std::shared_ptr pChannel, const std::string& strClientData); diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp index 7682ae8c..71cf76d9 100644 --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -1642,6 +1642,7 @@ E_CODEC_STATUS IO::Recv(Dispatcher* pDispatcher, std::shared_ptrm_pLastActivityChannel = pChannel; } + pDispatcher->OnIoWrite(pChannel); return(eStatus); } @@ -1675,6 +1676,7 @@ E_CODEC_STATUS IO::Fetch(Dispatcher* pDispatcher, std::shared_ptrm_pLastActivityChannel = pChannel; } + pDispatcher->OnIoWrite(pChannel); return(eStatus); } diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp index 4d54fcad..4dd396a3 100644 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -385,6 +385,26 @@ void Nodes::AddNodeKetama(const std::string& strNodeType, const std::string& str } } +void Nodes::ReplaceNodes(const std::string& strNodeType, const std::set& setNodeIdentify) +{ + auto node_type_iter = m_mapNode.find(strNodeType); + if (node_type_iter == m_mapNode.end()) + { + for (auto it = setNodeIdentify.begin(); it != setNodeIdentify.end(); ++it) + { + AddNode(strNodeType, *it); + } + } + else + { + m_mapNode.erase(node_type_iter); + for (auto it = setNodeIdentify.begin(); it != setNodeIdentify.end(); ++it) + { + AddNode(strNodeType, *it); + } + } +} + bool Nodes::SplitAddAndGetNode(const std::string& strNodeType, std::string& strNodeIdentify) { std::vector vecAddress; diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp index b77d3eea..b429bbe3 100644 --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -104,6 +104,8 @@ class Nodes /** @deprecate */ void AddNodeKetama(const std::string& strNodeType, const std::string& strNodeIdentify); + void ReplaceNodes(const std::string& strNodeType, const std::set& setNodeIdentify); + /** * @brief 添加并获取节点 * @note 将形如192.168.1.47:6379,192.168.1.53:6379,192.168.1.38:6379的 diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp index 4e01ab28..b6c960b2 100644 --- a/src/labor/Worker.hpp +++ b/src/labor/Worker.hpp @@ -116,11 +116,11 @@ class Worker: public Labor m_stWorkerInfo.uiRecvByte += uiBytes; if (IO_STAT_UPSTREAM_RECV_BYTE & uiIoType) { - ++m_stWorkerInfo.uiUpStreamRecvByte; + m_stWorkerInfo.uiUpStreamRecvByte += uiBytes; } if (IO_STAT_DOWNSTREAM_RECV_BYTE & uiIoType) { - ++m_stWorkerInfo.uiDownStreamRecvByte; + m_stWorkerInfo.uiDownStreamRecvByte += uiBytes; } } virtual void IoStatAddSendNum(int iFd, uint32 uiIoType) @@ -140,11 +140,11 @@ class Worker: public Labor m_stWorkerInfo.uiSendByte += uiBytes; if (IO_STAT_UPSTREAM_SEND_BYTE & uiIoType) { - ++m_stWorkerInfo.uiUpStreamSendByte; + m_stWorkerInfo.uiUpStreamSendByte += uiBytes; } if (IO_STAT_DOWNSTREAM_SEND_BYTE & uiIoType) { - ++m_stWorkerInfo.uiDownStreamSendByte; + m_stWorkerInfo.uiDownStreamSendByte += uiBytes; } } diff --git a/src/pb/http.pb.cc b/src/pb/http.pb.cc old mode 100644 new mode 100755 diff --git a/src/pb/mydis.pb.cc b/src/pb/mydis.pb.cc old mode 100644 new mode 100755 diff --git a/src/pb/mydis.pb.h b/src/pb/mydis.pb.h old mode 100644 new mode 100755 diff --git a/src/pb/neb_sys.pb.cc b/src/pb/neb_sys.pb.cc old mode 100644 new mode 100755 diff --git a/src/util/StringCoder.cpp b/src/util/StringCoder.cpp index b4399c2c..edaf6cfc 100644 --- a/src/util/StringCoder.cpp +++ b/src/util/StringCoder.cpp @@ -184,8 +184,10 @@ void DecodeParameter(const std::string& strParameter, std::map& vecDest) { vecDest.clear(); + std::string strForSplit; + strForSplit.assign(strSrc.data(), strSrc.size()); char* p; - char* buff = const_cast(strSrc.data()); + char* buff = const_cast(strForSplit.data()); p = strsep(&buff, strPattern.data()); while (p != NULL) { @@ -201,8 +203,8 @@ std::string& Trim(std::string &s) return s; } - s.erase(0,s.find_first_not_of(" ")); - s.erase(s.find_last_not_of(" ") + 1); + s.erase(0,s.find_first_not_of(" \t\f\v\r\n")); + s.erase(s.find_last_not_of(" \t\f\v\r\n") + 1); return s; } diff --git a/src/util/json/CJsonObject.cpp b/src/util/json/CJsonObject.cpp old mode 100644 new mode 100755 index 51ac352a..9ae6c118 --- a/src/util/json/CJsonObject.cpp +++ b/src/util/json/CJsonObject.cpp @@ -659,6 +659,30 @@ bool CJsonObject::KeyExist(const std::string& strKey) const return(true); } +int CJsonObject::ValueType(const std::string& strKey) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(cJSON_False); + } + return(pJsonStruct->type); +} + bool CJsonObject::Get(const std::string& strKey, CJsonObject& oJsonObject) const { cJSON* pJsonStruct = NULL; @@ -2232,6 +2256,30 @@ int CJsonObject::GetArraySize() const return(0); } +int CJsonObject::ValueType(int iWhich) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(cJSON_False); + } + return(pJsonStruct->type); +} + bool CJsonObject::Get(int iWhich, CJsonObject& oJsonObject) const { cJSON* pJsonStruct = NULL; diff --git a/src/util/json/CJsonObject.hpp b/src/util/json/CJsonObject.hpp index 4f1575eb..14f59976 100644 --- a/src/util/json/CJsonObject.hpp +++ b/src/util/json/CJsonObject.hpp @@ -72,6 +72,7 @@ class CJsonObject CJsonObject& operator[](const std::string& strKey); std::string operator()(const std::string& strKey) const; bool KeyExist(const std::string& strKey) const; + int ValueType(const std::string& strKey) const; bool Get(const std::string& strKey, CJsonObject& oJsonObject) const; bool Get(const std::string& strKey, std::string& strValue) const; bool Get(const std::string& strKey, int32& iValue) const; @@ -141,6 +142,7 @@ class CJsonObject int GetArraySize() const; CJsonObject& operator[](unsigned int uiWhich); std::string operator()(unsigned int uiWhich) const; + int ValueType(int iWhich) const; bool Get(int iWhich, CJsonObject& oJsonObject) const; bool Get(int iWhich, std::string& strValue) const; bool Get(int iWhich, int32& iValue) const; From a5744bc4e53a97d55774e7e89c39880e439e7c53 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 17 Jun 2023 14:31:18 +0800 Subject: [PATCH 175/176] http2 stream weight bug fixed --- src/codec/http2/CodecHttp2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp index cc650670..2707bc85 100644 --- a/src/codec/http2/CodecHttp2.cpp +++ b/src/codec/http2/CodecHttp2.cpp @@ -558,6 +558,7 @@ void CodecHttp2::SetPriority(uint32 uiStreamId, const tagPriority& stPriority) if (pDependencyStreamWeight == nullptr) { pCurrentStreamWeight->pRightBrother = m_pStreamWeightRoot->pFirstChild; + pCurrentStreamWeight->pParent = m_pStreamWeightRoot; m_pStreamWeightRoot->pFirstChild = pCurrentStreamWeight; } else From 6651c2159840e0bb123b78b86082acc189d3b658 Mon Sep 17 00:00:00 2001 From: Bwar Date: Sat, 2 Sep 2023 21:37:31 +0800 Subject: [PATCH 176/176] 1. Add the client connection pool management; 2. Increase the transceiver buffer size limit; 3. Optimize the new connection processing --- README.md | 3 + README_cn.md | 3 + conf/nebula.json | 3 + src/Definition.hpp | 3 + src/actor/Actor.cpp | 18 +- src/actor/Actor.hpp | 7 +- src/actor/ActorBuilder.cpp | 14 +- src/actor/ActorBuilder.hpp | 2 +- src/actor/ActorFactory.hpp | 0 src/actor/ActorSender.cpp | 4 +- src/actor/ActorSender.hpp | 2 +- src/actor/ActorSys.hpp | 0 src/actor/DynamicCreator.hpp | 0 src/actor/chain/Chain.cpp | 0 src/actor/chain/Chain.hpp | 0 src/actor/cmd/CW.hpp | 0 src/actor/cmd/Cmd.hpp | 0 src/actor/cmd/DeliverCmd.hpp | 0 src/actor/cmd/GrpcModule.cpp | 0 src/actor/cmd/GrpcModule.hpp | 0 src/actor/cmd/Module.hpp | 0 src/actor/cmd/RawCmd.hpp | 0 src/actor/cmd/RedisCmd.hpp | 0 src/actor/cmd/sys_cmd/CmdBeat.cpp | 0 src/actor/cmd/sys_cmd/CmdBeat.hpp | 0 src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp | 0 src/actor/cmd/sys_cmd/CmdChannelMigrate.hpp | 0 src/actor/cmd/sys_cmd/CmdDataReport.cpp | 0 src/actor/cmd/sys_cmd/CmdDataReport.hpp | 0 src/actor/cmd/sys_cmd/CmdFdTransfer.cpp | 4 + src/actor/cmd/sys_cmd/CmdFdTransfer.hpp | 0 src/actor/cmd/sys_cmd/CmdNodeNotice.cpp | 0 src/actor/cmd/sys_cmd/CmdNodeNotice.hpp | 0 src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp | 0 src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp | 0 src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp | 0 src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp | 0 .../cmd/sys_cmd/CmdSetNodeCustomConf.cpp | 0 .../cmd/sys_cmd/CmdSetNodeCustomConf.hpp | 0 .../cmd/sys_cmd/CmdSpecChannelCreated.cpp | 15 +- .../cmd/sys_cmd/CmdSpecChannelCreated.hpp | 0 src/actor/cmd/sys_cmd/CmdToldWorker.cpp | 0 src/actor/cmd/sys_cmd/CmdToldWorker.hpp | 0 src/actor/cmd/sys_cmd/CmdUpdateNodeId.cpp | 0 src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp | 0 src/actor/cmd/sys_cmd/ModuleHealth.cpp | 0 src/actor/cmd/sys_cmd/ModuleHealth.hpp | 0 src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp | 0 src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp | 0 src/actor/cmd/sys_cmd/ModuleMetrics.cpp | 0 src/actor/cmd/sys_cmd/ModuleMetrics.hpp | 0 .../sys_cmd/manager/CmdOnGetCustomConf.cpp | 0 .../sys_cmd/manager/CmdOnGetCustomConf.hpp | 0 .../cmd/sys_cmd/manager/CmdOnGetNodeConf.cpp | 0 .../cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp | 0 .../manager/CmdOnGetNodeCustomConf.cpp | 0 .../manager/CmdOnGetNodeCustomConf.hpp | 0 .../cmd/sys_cmd/manager/CmdOnNodeNotice.cpp | 0 .../cmd/sys_cmd/manager/CmdOnNodeNotice.hpp | 0 .../manager/CmdOnOrientationFdTransfer.cpp | 0 .../manager/CmdOnOrientationFdTransfer.hpp | 0 .../sys_cmd/manager/CmdOnSetCustomConf.cpp | 0 .../sys_cmd/manager/CmdOnSetCustomConf.hpp | 0 .../cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp | 0 .../cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp | 0 .../manager/CmdOnSetNodeCustomConf.cpp | 0 .../manager/CmdOnSetNodeCustomConf.hpp | 0 .../cmd/sys_cmd/manager/CmdOnStartService.cpp | 0 .../cmd/sys_cmd/manager/CmdOnStartService.hpp | 0 .../cmd/sys_cmd/manager/CmdOnTellWorker.cpp | 0 .../cmd/sys_cmd/manager/CmdOnTellWorker.hpp | 0 .../cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp | 0 .../cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp | 0 src/actor/context/Context.cpp | 0 src/actor/context/Context.hpp | 0 src/actor/context/HttpContext.cpp | 0 src/actor/context/HttpContext.hpp | 0 src/actor/context/PbContext.cpp | 0 src/actor/context/PbContext.hpp | 0 src/actor/operator/Operator.hpp | 0 src/actor/session/Session.cpp | 0 src/actor/session/Session.hpp | 0 src/actor/session/Timer.cpp | 0 src/actor/session/Timer.hpp | 0 .../session/sys_session/SessionDataReport.cpp | 0 .../session/sys_session/SessionDataReport.hpp | 0 .../session/sys_session/SessionLogger.cpp | 0 .../session/sys_session/SessionLogger.hpp | 0 .../session/sys_session/TimerAddrinfo.cpp | 0 .../session/sys_session/TimerAddrinfo.hpp | 0 .../sys_session/manager/SessionManager.cpp | 6 +- .../sys_session/manager/SessionManager.hpp | 0 src/actor/step/CassStep.cpp | 0 src/actor/step/CassStep.hpp | 0 src/actor/step/DeliverStep.cpp | 0 src/actor/step/DeliverStep.hpp | 0 src/actor/step/GrpcStep.cpp | 0 src/actor/step/GrpcStep.hpp | 0 src/actor/step/HttpStep.cpp | 0 src/actor/step/HttpStep.hpp | 0 src/actor/step/PbStep.cpp | 0 src/actor/step/PbStep.hpp | 0 src/actor/step/RawStep.cpp | 0 src/actor/step/RawStep.hpp | 0 src/actor/step/RedisStep.cpp | 0 src/actor/step/RedisStep.hpp | 0 src/actor/step/Step.cpp | 0 src/actor/step/Step.hpp | 0 src/actor/step/sys_step/StepConnectWorker.cpp | 0 src/actor/step/sys_step/StepConnectWorker.hpp | 0 .../step/sys_step/StepNebualChannelPing.cpp | 0 .../step/sys_step/StepNebualChannelPing.hpp | 0 .../step/sys_step/StepRedisChannelPing.cpp | 0 .../step/sys_step/StepRedisChannelPing.hpp | 0 src/actor/step/sys_step/StepRedisCluster.cpp | 95 ++++-- src/actor/step/sys_step/StepRedisCluster.hpp | 16 +- src/actor/step/sys_step/StepTellWorker.cpp | 0 src/actor/step/sys_step/StepTellWorker.hpp | 0 .../sys_step/manager/StepReportToBeacon.cpp | 0 .../sys_step/manager/StepReportToBeacon.hpp | 0 src/channel/Channel.hpp | 11 +- src/channel/SelfChannel.cpp | 0 src/channel/SelfChannel.hpp | 0 src/channel/SocketChannel.cpp | 0 src/channel/SocketChannel.hpp | 0 src/channel/SocketChannelImpl.hpp | 32 ++ src/channel/SocketChannelSslImpl.hpp | 0 src/channel/SocketChannelSslImpl.inl | 0 src/channel/SpecChannel.hpp | 291 +++++++++++++++++- src/channel/SslContext.cpp | 0 src/channel/SslContext.hpp | 0 src/channel/migrate/SocketChannelMigrate.cpp | 0 src/channel/migrate/SocketChannelMigrate.hpp | 0 src/channel/migrate/SocketChannelPack.cpp | 0 src/channel/migrate/SocketChannelPack.hpp | 0 src/codec/Codec.cpp | 0 src/codec/Codec.hpp | 0 src/codec/CodecDefine.hpp | 0 src/codec/CodecFactory.cpp | 186 ++++++++--- src/codec/CodecFactory.hpp | 0 src/codec/CodecHttp.cpp | 0 src/codec/CodecHttp.hpp | 0 src/codec/CodecPrivate.cpp | 0 src/codec/CodecPrivate.hpp | 0 src/codec/CodecProto.cpp | 0 src/codec/CodecProto.hpp | 0 src/codec/CodecRaw.cpp | 13 + src/codec/CodecRaw.hpp | 1 + src/codec/CodecResp.cpp | 0 src/codec/CodecResp.hpp | 0 src/codec/CodecUtil.cpp | 0 src/codec/CodecUtil.hpp | 0 src/codec/CodecWsExtentJson.cpp | 0 src/codec/CodecWsExtentJson.hpp | 0 src/codec/CodecWsExtentPb.cpp | 0 src/codec/CodecWsExtentPb.hpp | 0 src/codec/cass/CassComm.hpp | 0 src/codec/cass/CassMessage.cpp | 0 src/codec/cass/CassMessage.hpp | 0 src/codec/cass/CassRequest.cpp | 0 src/codec/cass/CassRequest.hpp | 0 src/codec/cass/CassResponse.cpp | 0 src/codec/cass/CassResponse.hpp | 0 src/codec/cass/CodecCass.cpp | 0 src/codec/cass/CodecCass.hpp | 0 src/codec/cass/result/CassResult.cpp | 0 src/codec/cass/result/CassResult.hpp | 0 src/codec/cass/result/CassRow.cpp | 0 src/codec/cass/result/CassRow.hpp | 0 src/codec/grpc/Grpc.hpp | 0 src/codec/http2/CodecHttp2.cpp | 0 src/codec/http2/CodecHttp2.hpp | 0 src/codec/http2/H2Comm.hpp | 0 src/codec/http2/Http2Frame.cpp | 0 src/codec/http2/Http2Frame.hpp | 0 src/codec/http2/Http2Header.cpp | 0 src/codec/http2/Http2Header.hpp | 0 src/codec/http2/Http2Stream.cpp | 0 src/codec/http2/Http2Stream.hpp | 0 src/codec/http2/Huffman.cpp | 0 src/codec/http2/Huffman.hpp | 0 src/codec/http2/Tree.hpp | 0 src/codec/virtual/CodecDeliver.cpp | 0 src/codec/virtual/CodecDeliver.hpp | 0 src/ios/ActorWatcher.cpp | 0 src/ios/ActorWatcher.hpp | 0 src/ios/ChannelWatcher.cpp | 0 src/ios/ChannelWatcher.hpp | 0 src/ios/Dispatcher.cpp | 222 ++++++++++++- src/ios/Dispatcher.hpp | 25 +- src/ios/IO.hpp | 244 +++++++-------- src/ios/Nodes.cpp | 8 +- src/ios/Nodes.hpp | 3 +- src/ios/PollingWatcher.cpp | 116 +++++++ src/ios/PollingWatcher.hpp | 65 ++++ src/ios/SpecChannelWatcher.cpp | 0 src/ios/SpecChannelWatcher.hpp | 0 src/labor/Labor.hpp | 0 src/labor/LaborShared.cpp | 72 ++++- src/labor/LaborShared.hpp | 9 + src/labor/Loader.cpp | 0 src/labor/Loader.hpp | 0 src/labor/Manager.cpp | 35 +-- src/labor/Manager.hpp | 0 src/labor/NodeInfo.hpp | 4 +- src/labor/Worker.cpp | 51 +-- src/labor/Worker.hpp | 0 src/logger/AsyncLogger.cpp | 0 src/logger/AsyncLogger.hpp | 0 src/logger/FileLogger.cpp | 8 +- src/logger/FileLogger.hpp | 38 +++ src/logger/Logger.hpp | 0 src/logger/NetLogger.cpp | 0 src/logger/NetLogger.hpp | 0 src/mydis/DbOperator.cpp | 0 src/mydis/DbOperator.hpp | 0 src/mydis/MydisOperator.cpp | 0 src/mydis/MydisOperator.hpp | 0 src/mydis/Operator.cpp | 0 src/mydis/Operator.hpp | 0 src/mydis/RedisOperator.cpp | 0 src/mydis/RedisOperator.hpp | 0 src/pb/http.pb.h | 0 src/pb/msg.pb.cc | 0 src/pb/msg.pb.h | 0 src/pb/neb_sys.pb.h | 0 src/pb/redis.pb.cc | 0 src/pb/redis.pb.h | 0 src/pb/report.pb.cc | 0 src/pb/report.pb.h | 0 src/type/Notations.cpp | 0 src/type/Notations.hpp | 0 src/type/Package.cpp | 0 src/type/Package.hpp | 0 src/util/CBuffer.cpp | 0 src/util/CBuffer.hpp | 33 +- src/util/CTlv.cpp | 0 src/util/CTlv.hpp | 0 src/util/StreamCodec.hpp | 0 src/util/StringCoder.cpp | 17 +- src/util/StringCoder.hpp | 0 src/util/StringConverter.cpp | 0 src/util/StringConverter.hpp | 0 src/util/encrypt/base64.c | 0 src/util/encrypt/base64.h | 0 src/util/encrypt/city.cc | 0 src/util/encrypt/city.h | 0 src/util/encrypt/citycrc.h | 0 src/util/encrypt/config.h | 0 src/util/encrypt/crc16.c | 0 src/util/encrypt/crc16.h | 0 src/util/encrypt/hconv.c | 0 src/util/encrypt/hconv.h | 0 src/util/encrypt/rc5.c | 0 src/util/encrypt/rc5.h | 0 src/util/http/http_parser.c | 0 src/util/http/http_parser.h | 0 src/util/json/CJsonObject.hpp | 8 +- src/util/json/cJSON.c | 0 src/util/json/cJSON.h | 0 src/util/process_helper.c | 0 src/util/process_helper.h | 0 src/util/proctitle_helper.c | 0 src/util/proctitle_helper.h | 0 264 files changed, 1346 insertions(+), 341 deletions(-) mode change 100644 => 100755 conf/nebula.json mode change 100644 => 100755 src/actor/Actor.cpp mode change 100644 => 100755 src/actor/Actor.hpp mode change 100644 => 100755 src/actor/ActorBuilder.cpp mode change 100644 => 100755 src/actor/ActorBuilder.hpp mode change 100644 => 100755 src/actor/ActorFactory.hpp mode change 100644 => 100755 src/actor/ActorSender.cpp mode change 100644 => 100755 src/actor/ActorSender.hpp mode change 100644 => 100755 src/actor/ActorSys.hpp mode change 100644 => 100755 src/actor/DynamicCreator.hpp mode change 100644 => 100755 src/actor/chain/Chain.cpp mode change 100644 => 100755 src/actor/chain/Chain.hpp mode change 100644 => 100755 src/actor/cmd/CW.hpp mode change 100644 => 100755 src/actor/cmd/Cmd.hpp mode change 100644 => 100755 src/actor/cmd/DeliverCmd.hpp mode change 100644 => 100755 src/actor/cmd/GrpcModule.cpp mode change 100644 => 100755 src/actor/cmd/GrpcModule.hpp mode change 100644 => 100755 src/actor/cmd/Module.hpp mode change 100644 => 100755 src/actor/cmd/RawCmd.hpp mode change 100644 => 100755 src/actor/cmd/RedisCmd.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdBeat.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdBeat.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdChannelMigrate.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdDataReport.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdDataReport.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdFdTransfer.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdFdTransfer.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdNodeNotice.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdNodeNotice.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdSpecChannelCreated.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdSpecChannelCreated.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdToldWorker.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdToldWorker.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdUpdateNodeId.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/ModuleHealth.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/ModuleHealth.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/ModuleMetrics.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/ModuleMetrics.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnStartService.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnStartService.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp mode change 100644 => 100755 src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp mode change 100644 => 100755 src/actor/context/Context.cpp mode change 100644 => 100755 src/actor/context/Context.hpp mode change 100644 => 100755 src/actor/context/HttpContext.cpp mode change 100644 => 100755 src/actor/context/HttpContext.hpp mode change 100644 => 100755 src/actor/context/PbContext.cpp mode change 100644 => 100755 src/actor/context/PbContext.hpp mode change 100644 => 100755 src/actor/operator/Operator.hpp mode change 100644 => 100755 src/actor/session/Session.cpp mode change 100644 => 100755 src/actor/session/Session.hpp mode change 100644 => 100755 src/actor/session/Timer.cpp mode change 100644 => 100755 src/actor/session/Timer.hpp mode change 100644 => 100755 src/actor/session/sys_session/SessionDataReport.cpp mode change 100644 => 100755 src/actor/session/sys_session/SessionDataReport.hpp mode change 100644 => 100755 src/actor/session/sys_session/SessionLogger.cpp mode change 100644 => 100755 src/actor/session/sys_session/SessionLogger.hpp mode change 100644 => 100755 src/actor/session/sys_session/TimerAddrinfo.cpp mode change 100644 => 100755 src/actor/session/sys_session/TimerAddrinfo.hpp mode change 100644 => 100755 src/actor/session/sys_session/manager/SessionManager.cpp mode change 100644 => 100755 src/actor/session/sys_session/manager/SessionManager.hpp mode change 100644 => 100755 src/actor/step/CassStep.cpp mode change 100644 => 100755 src/actor/step/CassStep.hpp mode change 100644 => 100755 src/actor/step/DeliverStep.cpp mode change 100644 => 100755 src/actor/step/DeliverStep.hpp mode change 100644 => 100755 src/actor/step/GrpcStep.cpp mode change 100644 => 100755 src/actor/step/GrpcStep.hpp mode change 100644 => 100755 src/actor/step/HttpStep.cpp mode change 100644 => 100755 src/actor/step/HttpStep.hpp mode change 100644 => 100755 src/actor/step/PbStep.cpp mode change 100644 => 100755 src/actor/step/PbStep.hpp mode change 100644 => 100755 src/actor/step/RawStep.cpp mode change 100644 => 100755 src/actor/step/RawStep.hpp mode change 100644 => 100755 src/actor/step/RedisStep.cpp mode change 100644 => 100755 src/actor/step/RedisStep.hpp mode change 100644 => 100755 src/actor/step/Step.cpp mode change 100644 => 100755 src/actor/step/Step.hpp mode change 100644 => 100755 src/actor/step/sys_step/StepConnectWorker.cpp mode change 100644 => 100755 src/actor/step/sys_step/StepConnectWorker.hpp mode change 100644 => 100755 src/actor/step/sys_step/StepNebualChannelPing.cpp mode change 100644 => 100755 src/actor/step/sys_step/StepNebualChannelPing.hpp mode change 100644 => 100755 src/actor/step/sys_step/StepRedisChannelPing.cpp mode change 100644 => 100755 src/actor/step/sys_step/StepRedisChannelPing.hpp mode change 100644 => 100755 src/actor/step/sys_step/StepRedisCluster.cpp mode change 100644 => 100755 src/actor/step/sys_step/StepRedisCluster.hpp mode change 100644 => 100755 src/actor/step/sys_step/StepTellWorker.cpp mode change 100644 => 100755 src/actor/step/sys_step/StepTellWorker.hpp mode change 100644 => 100755 src/actor/step/sys_step/manager/StepReportToBeacon.cpp mode change 100644 => 100755 src/actor/step/sys_step/manager/StepReportToBeacon.hpp mode change 100644 => 100755 src/channel/Channel.hpp mode change 100644 => 100755 src/channel/SelfChannel.cpp mode change 100644 => 100755 src/channel/SelfChannel.hpp mode change 100644 => 100755 src/channel/SocketChannel.cpp mode change 100644 => 100755 src/channel/SocketChannel.hpp mode change 100644 => 100755 src/channel/SocketChannelImpl.hpp mode change 100644 => 100755 src/channel/SocketChannelSslImpl.hpp mode change 100644 => 100755 src/channel/SocketChannelSslImpl.inl mode change 100644 => 100755 src/channel/SpecChannel.hpp mode change 100644 => 100755 src/channel/SslContext.cpp mode change 100644 => 100755 src/channel/SslContext.hpp mode change 100644 => 100755 src/channel/migrate/SocketChannelMigrate.cpp mode change 100644 => 100755 src/channel/migrate/SocketChannelMigrate.hpp mode change 100644 => 100755 src/channel/migrate/SocketChannelPack.cpp mode change 100644 => 100755 src/channel/migrate/SocketChannelPack.hpp mode change 100644 => 100755 src/codec/Codec.cpp mode change 100644 => 100755 src/codec/Codec.hpp mode change 100644 => 100755 src/codec/CodecDefine.hpp mode change 100644 => 100755 src/codec/CodecFactory.cpp mode change 100644 => 100755 src/codec/CodecFactory.hpp mode change 100644 => 100755 src/codec/CodecHttp.cpp mode change 100644 => 100755 src/codec/CodecHttp.hpp mode change 100644 => 100755 src/codec/CodecPrivate.cpp mode change 100644 => 100755 src/codec/CodecPrivate.hpp mode change 100644 => 100755 src/codec/CodecProto.cpp mode change 100644 => 100755 src/codec/CodecProto.hpp mode change 100644 => 100755 src/codec/CodecRaw.cpp mode change 100644 => 100755 src/codec/CodecRaw.hpp mode change 100644 => 100755 src/codec/CodecResp.cpp mode change 100644 => 100755 src/codec/CodecResp.hpp mode change 100644 => 100755 src/codec/CodecUtil.cpp mode change 100644 => 100755 src/codec/CodecUtil.hpp mode change 100644 => 100755 src/codec/CodecWsExtentJson.cpp mode change 100644 => 100755 src/codec/CodecWsExtentJson.hpp mode change 100644 => 100755 src/codec/CodecWsExtentPb.cpp mode change 100644 => 100755 src/codec/CodecWsExtentPb.hpp mode change 100644 => 100755 src/codec/cass/CassComm.hpp mode change 100644 => 100755 src/codec/cass/CassMessage.cpp mode change 100644 => 100755 src/codec/cass/CassMessage.hpp mode change 100644 => 100755 src/codec/cass/CassRequest.cpp mode change 100644 => 100755 src/codec/cass/CassRequest.hpp mode change 100644 => 100755 src/codec/cass/CassResponse.cpp mode change 100644 => 100755 src/codec/cass/CassResponse.hpp mode change 100644 => 100755 src/codec/cass/CodecCass.cpp mode change 100644 => 100755 src/codec/cass/CodecCass.hpp mode change 100644 => 100755 src/codec/cass/result/CassResult.cpp mode change 100644 => 100755 src/codec/cass/result/CassResult.hpp mode change 100644 => 100755 src/codec/cass/result/CassRow.cpp mode change 100644 => 100755 src/codec/cass/result/CassRow.hpp mode change 100644 => 100755 src/codec/grpc/Grpc.hpp mode change 100644 => 100755 src/codec/http2/CodecHttp2.cpp mode change 100644 => 100755 src/codec/http2/CodecHttp2.hpp mode change 100644 => 100755 src/codec/http2/H2Comm.hpp mode change 100644 => 100755 src/codec/http2/Http2Frame.cpp mode change 100644 => 100755 src/codec/http2/Http2Frame.hpp mode change 100644 => 100755 src/codec/http2/Http2Header.cpp mode change 100644 => 100755 src/codec/http2/Http2Header.hpp mode change 100644 => 100755 src/codec/http2/Http2Stream.cpp mode change 100644 => 100755 src/codec/http2/Http2Stream.hpp mode change 100644 => 100755 src/codec/http2/Huffman.cpp mode change 100644 => 100755 src/codec/http2/Huffman.hpp mode change 100644 => 100755 src/codec/http2/Tree.hpp mode change 100644 => 100755 src/codec/virtual/CodecDeliver.cpp mode change 100644 => 100755 src/codec/virtual/CodecDeliver.hpp mode change 100644 => 100755 src/ios/ActorWatcher.cpp mode change 100644 => 100755 src/ios/ActorWatcher.hpp mode change 100644 => 100755 src/ios/ChannelWatcher.cpp mode change 100644 => 100755 src/ios/ChannelWatcher.hpp mode change 100644 => 100755 src/ios/Dispatcher.cpp mode change 100644 => 100755 src/ios/Dispatcher.hpp mode change 100644 => 100755 src/ios/IO.hpp mode change 100644 => 100755 src/ios/Nodes.cpp mode change 100644 => 100755 src/ios/Nodes.hpp create mode 100755 src/ios/PollingWatcher.cpp create mode 100755 src/ios/PollingWatcher.hpp mode change 100644 => 100755 src/ios/SpecChannelWatcher.cpp mode change 100644 => 100755 src/ios/SpecChannelWatcher.hpp mode change 100644 => 100755 src/labor/Labor.hpp mode change 100644 => 100755 src/labor/LaborShared.cpp mode change 100644 => 100755 src/labor/LaborShared.hpp mode change 100644 => 100755 src/labor/Loader.cpp mode change 100644 => 100755 src/labor/Loader.hpp mode change 100644 => 100755 src/labor/Manager.cpp mode change 100644 => 100755 src/labor/Manager.hpp mode change 100644 => 100755 src/labor/NodeInfo.hpp mode change 100644 => 100755 src/labor/Worker.cpp mode change 100644 => 100755 src/labor/Worker.hpp mode change 100644 => 100755 src/logger/AsyncLogger.cpp mode change 100644 => 100755 src/logger/AsyncLogger.hpp mode change 100644 => 100755 src/logger/FileLogger.cpp mode change 100644 => 100755 src/logger/FileLogger.hpp mode change 100644 => 100755 src/logger/Logger.hpp mode change 100644 => 100755 src/logger/NetLogger.cpp mode change 100644 => 100755 src/logger/NetLogger.hpp mode change 100644 => 100755 src/mydis/DbOperator.cpp mode change 100644 => 100755 src/mydis/DbOperator.hpp mode change 100644 => 100755 src/mydis/MydisOperator.cpp mode change 100644 => 100755 src/mydis/MydisOperator.hpp mode change 100644 => 100755 src/mydis/Operator.cpp mode change 100644 => 100755 src/mydis/Operator.hpp mode change 100644 => 100755 src/mydis/RedisOperator.cpp mode change 100644 => 100755 src/mydis/RedisOperator.hpp mode change 100644 => 100755 src/pb/http.pb.h mode change 100644 => 100755 src/pb/msg.pb.cc mode change 100644 => 100755 src/pb/msg.pb.h mode change 100644 => 100755 src/pb/neb_sys.pb.h mode change 100644 => 100755 src/pb/redis.pb.cc mode change 100644 => 100755 src/pb/redis.pb.h mode change 100644 => 100755 src/pb/report.pb.cc mode change 100644 => 100755 src/pb/report.pb.h mode change 100644 => 100755 src/type/Notations.cpp mode change 100644 => 100755 src/type/Notations.hpp mode change 100644 => 100755 src/type/Package.cpp mode change 100644 => 100755 src/type/Package.hpp mode change 100644 => 100755 src/util/CBuffer.cpp mode change 100644 => 100755 src/util/CBuffer.hpp mode change 100644 => 100755 src/util/CTlv.cpp mode change 100644 => 100755 src/util/CTlv.hpp mode change 100644 => 100755 src/util/StreamCodec.hpp mode change 100644 => 100755 src/util/StringCoder.cpp mode change 100644 => 100755 src/util/StringCoder.hpp mode change 100644 => 100755 src/util/StringConverter.cpp mode change 100644 => 100755 src/util/StringConverter.hpp mode change 100644 => 100755 src/util/encrypt/base64.c mode change 100644 => 100755 src/util/encrypt/base64.h mode change 100644 => 100755 src/util/encrypt/city.cc mode change 100644 => 100755 src/util/encrypt/city.h mode change 100644 => 100755 src/util/encrypt/citycrc.h mode change 100644 => 100755 src/util/encrypt/config.h mode change 100644 => 100755 src/util/encrypt/crc16.c mode change 100644 => 100755 src/util/encrypt/crc16.h mode change 100644 => 100755 src/util/encrypt/hconv.c mode change 100644 => 100755 src/util/encrypt/hconv.h mode change 100644 => 100755 src/util/encrypt/rc5.c mode change 100644 => 100755 src/util/encrypt/rc5.h mode change 100644 => 100755 src/util/http/http_parser.c mode change 100644 => 100755 src/util/http/http_parser.h mode change 100644 => 100755 src/util/json/CJsonObject.hpp mode change 100644 => 100755 src/util/json/cJSON.c mode change 100644 => 100755 src/util/json/cJSON.h mode change 100644 => 100755 src/util/process_helper.c mode change 100644 => 100755 src/util/process_helper.h mode change 100644 => 100755 src/util/proctitle_helper.c mode change 100644 => 100755 src/util/proctitle_helper.h diff --git a/README.md b/README.md index 4ce74c7b..0615a322 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,9 @@ A simple testing can be start with a NebulaInterface only, and also can be start - add spec channel - use SpecChannel instead of unix socket to transfer file descriptors - add SocketChannel migration between threads + - add the client connection pool management + - increase the transceiver buffer size limit + - optimize the new connection processing - remove process mode #### v1.7.3 - error callback caused by fuse node detection bug fixed diff --git a/README_cn.md b/README_cn.md index db062efb..8f813c0f 100644 --- a/README_cn.md +++ b/README_cn.md @@ -138,6 +138,9 @@ Nebula 完成的文档在 [Nebula参考手册](https://bwar.gitee.io/nebula)。 - 增加线程间无锁通信队列通道SpecChannel - 使用SpecChannel替代Unix Socket传送文件描述符 - 增加SocketChannel在线程间迁移功能 + - 增加客户端连接池管理 + - 增加收发缓冲区大小限制 + - 优化新建连接处理 - 移除进程模式,只支持线程模式 #### v1.7.3 - 增加连接队列配置 diff --git a/conf/nebula.json b/conf/nebula.json old mode 100644 new mode 100755 index 51f7bf1a..48e4fd46 --- a/conf/nebula.json +++ b/conf/nebula.json @@ -62,6 +62,9 @@ "addr_permit": { "stat_interval": 60.0, "permit_num": 1000000000 }, "uin_permit": { "stat_interval": 60.0, "permit_num": 600000000 } }, + "upstream_connection_pool_size":20, + "max_channel_send_buffer_size":104857600, + "max_channel_recv_buffer_size":104857600, "//connection_protection": "连接保护时间(单位:秒),当值>0时新建连接设置为这个时间,接收到第一个数据包后改设为io_timeout", "connection_protection": 0.0, "//io_timeout": "网络IO(连接)超时设置(单位:秒)小数点后面至少保留一位", diff --git a/src/Definition.hpp b/src/Definition.hpp index be148e30..afd993ea 100755 --- a/src/Definition.hpp +++ b/src/Definition.hpp @@ -97,9 +97,12 @@ #define LOG4_FATAL(args...) Logger(neb::Logger::FATAL, __FILE__, __LINE__, __FUNCTION__, ##args) #define LOG4_ERROR(args...) Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, ##args) +#define LOG4_ERROR_DISPATCH(args...) pDispatcher->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, ##args) +#define LOG4_ERROR_ACTOR(args...) pActor->Logger(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, ##args) #define LOG4_WARNING(args...) Logger(neb::Logger::WARNING, __FILE__, __LINE__, __FUNCTION__, ##args) #define LOG4_NOTICE(args...) Logger(neb::Logger::NOTICE, __FILE__, __LINE__, __FUNCTION__, ##args) #define LOG4_INFO(args...) Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, ##args) +#define LOG4_INFO_DISPATCH(args...) pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, ##args) #define LOG4_CRITICAL(args...) Logger(neb::Logger::CRITICAL, __FILE__, __LINE__, __FUNCTION__, ##args) #ifdef _TRACE #define LOG4_DEBUG(args...) Logger(neb::Logger::DEBUG, __FILE__, __LINE__, __FUNCTION__, ##args) diff --git a/src/actor/Actor.cpp b/src/actor/Actor.cpp old mode 100644 new mode 100755 index e092c961..d6c32a70 --- a/src/actor/Actor.cpp +++ b/src/actor/Actor.cpp @@ -205,6 +205,8 @@ bool Actor::SendTo(std::shared_ptr pChannel, const char* pRawData bool Actor::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody, E_CODEC_TYPE eCodecType) { ChannelOption stOption; + stOption.uiMaxSendBuffSize = GetNodeInfo().uiMaxChannelSendBuffSize; + stOption.uiMaxRecvBuffSize = GetNodeInfo().uiMaxChannelRecvBuffSize; stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(GetTraceId()); return(IO::SendTo(this, strIdentify, stOption, iCmd, uiSeq, oMsgBody)); @@ -213,6 +215,8 @@ bool Actor::SendTo(const std::string& strIdentify, int32 iCmd, uint32 uiSeq, con bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMsg, uint32 uiStepSeq) { ChannelOption stOption; + stOption.uiMaxSendBuffSize = GetNodeInfo().uiMaxChannelSendBuffSize; + stOption.uiMaxRecvBuffSize = GetNodeInfo().uiMaxChannelRecvBuffSize; if (oHttpMsg.headers().find("x-trace-id") == oHttpMsg.headers().end()) { (const_cast(oHttpMsg)).mutable_headers()->insert({"x-trace-id", GetTraceId()}); @@ -240,19 +244,23 @@ bool Actor::SendTo(const std::string& strHost, int iPort, const HttpMsg& oHttpMs bool Actor::SendTo(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiStepSeq) { ChannelOption stOption; + stOption.uiMaxSendBuffSize = GetNodeInfo().uiMaxChannelSendBuffSize; + stOption.uiMaxRecvBuffSize = GetNodeInfo().uiMaxChannelRecvBuffSize; stOption.bPipeline = bPipeline; stOption.bWithSsl = bWithSsl; return(IO::SendTo(this, strIdentify, stOption, oRedisMsg)); } -bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) +bool Actor::SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiReadMode) { - return(m_pLabor->GetActorBuilder()->SendToCluster(strIdentify, bWithSsl, bPipeline, oRedisMsg, GetSequence(), bEnableReadOnly)); + return(m_pLabor->GetActorBuilder()->SendToCluster(strIdentify, bWithSsl, bPipeline, oRedisMsg, GetSequence(), uiReadMode)); } bool Actor::SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline) { ChannelOption stOption; + stOption.uiMaxSendBuffSize = GetNodeInfo().uiMaxChannelSendBuffSize; + stOption.uiMaxRecvBuffSize = GetNodeInfo().uiMaxChannelRecvBuffSize; stOption.bPipeline = bPipeline; stOption.bWithSsl = bWithSsl; return(IO::SendRoundRobin(this, strIdentify, stOption, oRedisMsg)); @@ -261,6 +269,8 @@ bool Actor::SendRoundRobin(const std::string& strIdentify, const RedisMsg& oRedi bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { ChannelOption stOption; + stOption.uiMaxSendBuffSize = GetNodeInfo().uiMaxChannelSendBuffSize; + stOption.uiMaxRecvBuffSize = GetNodeInfo().uiMaxChannelRecvBuffSize; stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(GetTraceId()); return(IO::SendRoundRobin(this, strNodeType, stOption, iCmd, uiSeq, oMsgBody)); @@ -269,6 +279,8 @@ bool Actor::SendRoundRobin(const std::string& strNodeType, int32 iCmd, uint32 ui bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { ChannelOption stOption; + stOption.uiMaxSendBuffSize = GetNodeInfo().uiMaxChannelSendBuffSize; + stOption.uiMaxRecvBuffSize = GetNodeInfo().uiMaxChannelRecvBuffSize; stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(GetTraceId()); return(IO::SendOriented(this, strNodeType, uiFactor, stOption, iCmd, uiSeq, oMsgBody)); @@ -277,6 +289,8 @@ bool Actor::SendOriented(const std::string& strNodeType, uint32 uiFactor, int32 bool Actor::SendOriented(const std::string& strNodeType, int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody) { ChannelOption stOption; + stOption.uiMaxSendBuffSize = GetNodeInfo().uiMaxChannelSendBuffSize; + stOption.uiMaxRecvBuffSize = GetNodeInfo().uiMaxChannelRecvBuffSize; stOption.bPipeline = true; (const_cast(oMsgBody)).set_trace_id(GetTraceId()); if (oMsgBody.has_req_target()) diff --git a/src/actor/Actor.hpp b/src/actor/Actor.hpp old mode 100644 new mode 100755 index 2cea8266..37d4cf3d --- a/src/actor/Actor.hpp +++ b/src/actor/Actor.hpp @@ -249,12 +249,12 @@ class Actor: public std::enable_shared_from_this * @param oRedisMsg redis消息 * @param bWithSsl 是否需要SSL * @param bPipeline 是否支持pipeline - * @param bEnableReadOnly redis cluster集群从节点,官方默认设置的是不分担读请求 + * @param uiReadMode redis cluster 集群读模式(见E_REDIS_READ_MODE) * 只作备份和故障转移用,当有请求读向从节点时,会被重定向对应的主节点来处理。 * 这个readonly告诉redis cluster从节点客户端愿意读取可能过时的数据并对写请求不感兴趣 * @return 是否发送成功 */ - virtual bool SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, bool bEnableReadOnly = false); + virtual bool SendToCluster(const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiReadMode = 0); /** * @brief 发送redis请求到类似于codis proxy的服务 */ @@ -295,6 +295,9 @@ class Actor: public std::enable_shared_from_this /** * @brief 发送请求到当前worker + * @note 注意SendToSelf()如果未实际发生IO调用,则会在本函数调用中立即调用当前Step的Callback(), + * 如果在Callback()里又调用了当前Step的Emit(),则会在Emit()和Callback()之间发生递归调用,甚至 + * 可能是无限递归导致栈溢出。故使用SendToSelf()时应避免Emit()和Callback()之间递归调用。 * @return SelfChannel的seq */ uint32 SendToSelf(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBody); diff --git a/src/actor/ActorBuilder.cpp b/src/actor/ActorBuilder.cpp old mode 100644 new mode 100755 index ad4492ad..c9657546 --- a/src/actor/ActorBuilder.cpp +++ b/src/actor/ActorBuilder.cpp @@ -564,6 +564,9 @@ std::shared_ptr ActorBuilder::InitializeSharedActor(Actor* pCreator, std: return(pSharedActor); } break; + case Actor::ACT_UNDEFINE: + return(pSharedActor); + break; default: LOG4_ERROR("\"%s\" must be a Step, a Session, an Operator, a Cmd or a Module.", strActorName.c_str()); @@ -811,13 +814,18 @@ bool ActorBuilder::RegisterActor(const std::string& strActorName, Actor* pNewAct return(true); } -bool ActorBuilder::SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq, bool bEnableReadOnly) +bool ActorBuilder::SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq, uint32 uiReadMode) { bool bSendResult = false; auto iter = m_mapClusterChannelStep.find(strIdentify); if (iter == m_mapClusterChannelStep.end()) { - auto pSharedStep = MakeSharedStep(nullptr, "neb::StepRedisCluster", strIdentify, (bool)bWithSsl, (bool)bPipeline, (bool)bEnableReadOnly); + ChannelOption stOption; + stOption.bPipeline = bPipeline; + stOption.bWithSsl = bWithSsl; + stOption.uiMaxSendBuffSize = m_pLabor->GetNodeInfo().uiMaxChannelSendBuffSize; + stOption.uiMaxRecvBuffSize = m_pLabor->GetNodeInfo().uiMaxChannelRecvBuffSize; + auto pSharedStep = MakeSharedStep(nullptr, "neb::StepRedisCluster", strIdentify, stOption, uiReadMode); if (pSharedStep != nullptr) { m_mapClusterChannelStep.insert(std::make_pair(strIdentify, pSharedStep)); @@ -966,7 +974,7 @@ void ActorBuilder::DynamicLoad(CJsonObject& oDynamicLoadingConf) { std::string strSoFile = m_pLabor->GetNodeInfo().strWorkPath + std::string("/") + oDynamicLoadingConf[i]("so_path") + std::string(".") + oDynamicLoadingConf[i]("version"); - if (m_pLabor->GetNodeInfo().bThreadMode && Labor::LABOR_MANAGER != m_pLabor->GetLaborType()) + if (Labor::LABOR_MANAGER != m_pLabor->GetLaborType()) { LoadDynamicSymbol(oDynamicLoadingConf[i]); } diff --git a/src/actor/ActorBuilder.hpp b/src/actor/ActorBuilder.hpp old mode 100644 new mode 100755 index c5cb7668..5de9e104 --- a/src/actor/ActorBuilder.hpp +++ b/src/actor/ActorBuilder.hpp @@ -139,7 +139,7 @@ class ActorBuilder bool RegisterActor(const std::string& strActorName, Actor* pNewActor, Actor* pCreator = nullptr); public: - bool SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq, bool bEnableReadOnly); + bool SendToCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, const RedisMsg& oRedisMsg, uint32 uiStepSeq, uint32 uiReadMode = 0); virtual std::shared_ptr GetSession(uint32 uiSessionId); virtual std::shared_ptr GetSession(const std::string& strSessionId); virtual bool ExecStep(uint32 uiStepSeq, int iErrno = ERR_OK, const std::string& strErrMsg = "", void* data = NULL); diff --git a/src/actor/ActorFactory.hpp b/src/actor/ActorFactory.hpp old mode 100644 new mode 100755 diff --git a/src/actor/ActorSender.cpp b/src/actor/ActorSender.cpp old mode 100644 new mode 100755 index 38d9a7c1..c616eea2 --- a/src/actor/ActorSender.cpp +++ b/src/actor/ActorSender.cpp @@ -98,9 +98,9 @@ bool ActorSender::SendTo(Actor* pActor, const std::string& strIdentify, const Re return(IO::SendTo(pActor, strIdentify, SOCKET_STREAM, stOption, oRedisMsg)); } -bool ActorSender::SendToCluster(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) +bool ActorSender::SendToCluster(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline, uint32 uiReadMode) { - return(pActor->m_pLabor->GetActorBuilder()->SendToCluster(strIdentify, bWithSsl, bPipeline, oRedisMsg, pActor->GetSequence(), bEnableReadOnly)); + return(pActor->m_pLabor->GetActorBuilder()->SendToCluster(strIdentify, bWithSsl, bPipeline, oRedisMsg, pActor->GetSequence(), uiReadMode)); } bool ActorSender::SendRoundRobin(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl, bool bPipeline) diff --git a/src/actor/ActorSender.hpp b/src/actor/ActorSender.hpp old mode 100644 new mode 100755 index f7967ce0..45b926b0 --- a/src/actor/ActorSender.hpp +++ b/src/actor/ActorSender.hpp @@ -57,7 +57,7 @@ class ActorSender static bool SendTo(Actor* pActor, std::shared_ptr pChannel, const RedisReply& oRedisReply); static bool SendTo(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiStepSeq = 0); static bool SendRoundRobin(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = false); - static bool SendToCluster(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, bool bEnableReadOnly = false); + static bool SendToCluster(Actor* pActor, const std::string& strIdentify, const RedisMsg& oRedisMsg, bool bWithSsl = false, bool bPipeline = true, uint32 uiReadMode = 0); // send raw message static bool SendTo(Actor* pActor, std::shared_ptr pChannel, const char* pRawData, uint32 uiRawDataSize); diff --git a/src/actor/ActorSys.hpp b/src/actor/ActorSys.hpp old mode 100644 new mode 100755 diff --git a/src/actor/DynamicCreator.hpp b/src/actor/DynamicCreator.hpp old mode 100644 new mode 100755 diff --git a/src/actor/chain/Chain.cpp b/src/actor/chain/Chain.cpp old mode 100644 new mode 100755 diff --git a/src/actor/chain/Chain.hpp b/src/actor/chain/Chain.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/CW.hpp b/src/actor/cmd/CW.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/Cmd.hpp b/src/actor/cmd/Cmd.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/DeliverCmd.hpp b/src/actor/cmd/DeliverCmd.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/GrpcModule.cpp b/src/actor/cmd/GrpcModule.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/GrpcModule.hpp b/src/actor/cmd/GrpcModule.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/Module.hpp b/src/actor/cmd/Module.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/RawCmd.hpp b/src/actor/cmd/RawCmd.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/RedisCmd.hpp b/src/actor/cmd/RedisCmd.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdBeat.cpp b/src/actor/cmd/sys_cmd/CmdBeat.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdBeat.hpp b/src/actor/cmd/sys_cmd/CmdBeat.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp b/src/actor/cmd/sys_cmd/CmdChannelMigrate.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdChannelMigrate.hpp b/src/actor/cmd/sys_cmd/CmdChannelMigrate.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdDataReport.cpp b/src/actor/cmd/sys_cmd/CmdDataReport.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdDataReport.hpp b/src/actor/cmd/sys_cmd/CmdDataReport.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp b/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp old mode 100644 new mode 100755 index 57590a41..a3cec8d8 --- a/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp +++ b/src/actor/cmd/sys_cmd/CmdFdTransfer.cpp @@ -12,8 +12,11 @@ #include "channel/SocketChannel.hpp" #include "ios/Dispatcher.hpp" #include "labor/NodeInfo.hpp" +#include "labor/Labor.hpp" +#include "codec/CodecFactory.hpp" #include "actor/step/PbStep.hpp" + namespace neb { @@ -75,6 +78,7 @@ bool CmdFdTransfer::AnyMessage( ? GetNodeInfo().dConnectionProtection : GetNodeInfo().dIoTimeout; GetLabor(this)->GetDispatcher()->AddIoTimeout(pNewChannel, dIoTimeout); pNewChannel->SetKeepAlive(dIoTimeout); + CodecFactory::OnEvent(GetLabor(this)->GetDispatcher(), pNewChannel); } GetLabor(this)->IoStatAddConnection(IO_STAT_DOWNSTREAM_NEW_CONNECTION); return(true); diff --git a/src/actor/cmd/sys_cmd/CmdFdTransfer.hpp b/src/actor/cmd/sys_cmd/CmdFdTransfer.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp b/src/actor/cmd/sys_cmd/CmdNodeNotice.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp b/src/actor/cmd/sys_cmd/CmdNodeNotice.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp b/src/actor/cmd/sys_cmd/CmdReloadCustomConf.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp b/src/actor/cmd/sys_cmd/CmdSetNodeConf.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp b/src/actor/cmd/sys_cmd/CmdSetNodeConf.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.cpp b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/CmdSetNodeCustomConf.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.cpp b/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.cpp old mode 100644 new mode 100755 index 860d027d..35ffa4d8 --- a/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.cpp +++ b/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.cpp @@ -43,31 +43,34 @@ bool CmdSpecChannelCreated::AnyMessage( } else // forward notifications through manager { - auto pChannel = LaborShared::Instance()->GetSpecChannel(CodecNebulaInNode::Type(), GetLaborId(), oSpecInfo.to_labor()); - if (pChannel == nullptr) + LOG4_INFO("this labor %u, spec channel from %u to %u created, with codec type %u", + GetLaborId(), oSpecInfo.from_labor(), oSpecInfo.to_labor(), oSpecInfo.codec_type()); + auto pTransitChannel = LaborShared::Instance()->GetSpecChannel(CodecNebulaInNode::Type(), GetLaborId(), oSpecInfo.to_labor()); + if (pTransitChannel == nullptr) { LOG4_ERROR("no codec_type %u channel from %u to %u", CodecNebulaInNode::Type(), GetLaborId(), oSpecInfo.to_labor()); return(false); } else { - auto pNoticeSpecChannel = std::static_pointer_cast>(pChannel); + auto pNoticeSpecChannel = std::static_pointer_cast>(pTransitChannel); if (pNoticeSpecChannel == nullptr) { LOG4_ERROR("spec channel cast failed."); return(false); } - int iResult = pNoticeSpecChannel->Write(gc_uiCmdReq, 0, + int iCmd = oInMsgHead.cmd(); + int iErrno = pNoticeSpecChannel->Write(iCmd, 0, std::move(const_cast(oInMsgHead)), std::move(const_cast(oInMsgBody))); - if (iResult == ERR_OK) + if (iErrno == ERR_OK) { LaborShared::Instance()->GetDispatcher(oSpecInfo.to_labor())->AsyncSend( pNoticeSpecChannel->MutableWatcher()->MutableAsyncWatcher()); } else { - LOG4_ERROR("no codec_type %u channel write error %d", CodecNebulaInNode::Type(), iResult); + LOG4_ERROR("no codec_type %u channel write error %d", CodecNebulaInNode::Type(), iErrno); return(false); } } diff --git a/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.hpp b/src/actor/cmd/sys_cmd/CmdSpecChannelCreated.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdToldWorker.cpp b/src/actor/cmd/sys_cmd/CmdToldWorker.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdToldWorker.hpp b/src/actor/cmd/sys_cmd/CmdToldWorker.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdUpdateNodeId.cpp b/src/actor/cmd/sys_cmd/CmdUpdateNodeId.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp b/src/actor/cmd/sys_cmd/CmdUpdateNodeId.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/ModuleHealth.cpp b/src/actor/cmd/sys_cmd/ModuleHealth.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/ModuleHealth.hpp b/src/actor/cmd/sys_cmd/ModuleHealth.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp b/src/actor/cmd/sys_cmd/ModuleHttpUpgrade.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/ModuleMetrics.cpp b/src/actor/cmd/sys_cmd/ModuleMetrics.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/ModuleMetrics.hpp b/src/actor/cmd/sys_cmd/ModuleMetrics.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetCustomConf.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeConf.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnGetNodeCustomConf.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnNodeNotice.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnOrientationFdTransfer.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetCustomConf.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeConf.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnSetNodeCustomConf.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnStartService.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnStartService.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnStartService.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnStartService.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnTellWorker.hpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.cpp old mode 100644 new mode 100755 diff --git a/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp b/src/actor/cmd/sys_cmd/manager/CmdOnWorkerLoad.hpp old mode 100644 new mode 100755 diff --git a/src/actor/context/Context.cpp b/src/actor/context/Context.cpp old mode 100644 new mode 100755 diff --git a/src/actor/context/Context.hpp b/src/actor/context/Context.hpp old mode 100644 new mode 100755 diff --git a/src/actor/context/HttpContext.cpp b/src/actor/context/HttpContext.cpp old mode 100644 new mode 100755 diff --git a/src/actor/context/HttpContext.hpp b/src/actor/context/HttpContext.hpp old mode 100644 new mode 100755 diff --git a/src/actor/context/PbContext.cpp b/src/actor/context/PbContext.cpp old mode 100644 new mode 100755 diff --git a/src/actor/context/PbContext.hpp b/src/actor/context/PbContext.hpp old mode 100644 new mode 100755 diff --git a/src/actor/operator/Operator.hpp b/src/actor/operator/Operator.hpp old mode 100644 new mode 100755 diff --git a/src/actor/session/Session.cpp b/src/actor/session/Session.cpp old mode 100644 new mode 100755 diff --git a/src/actor/session/Session.hpp b/src/actor/session/Session.hpp old mode 100644 new mode 100755 diff --git a/src/actor/session/Timer.cpp b/src/actor/session/Timer.cpp old mode 100644 new mode 100755 diff --git a/src/actor/session/Timer.hpp b/src/actor/session/Timer.hpp old mode 100644 new mode 100755 diff --git a/src/actor/session/sys_session/SessionDataReport.cpp b/src/actor/session/sys_session/SessionDataReport.cpp old mode 100644 new mode 100755 diff --git a/src/actor/session/sys_session/SessionDataReport.hpp b/src/actor/session/sys_session/SessionDataReport.hpp old mode 100644 new mode 100755 diff --git a/src/actor/session/sys_session/SessionLogger.cpp b/src/actor/session/sys_session/SessionLogger.cpp old mode 100644 new mode 100755 diff --git a/src/actor/session/sys_session/SessionLogger.hpp b/src/actor/session/sys_session/SessionLogger.hpp old mode 100644 new mode 100755 diff --git a/src/actor/session/sys_session/TimerAddrinfo.cpp b/src/actor/session/sys_session/TimerAddrinfo.cpp old mode 100644 new mode 100755 diff --git a/src/actor/session/sys_session/TimerAddrinfo.hpp b/src/actor/session/sys_session/TimerAddrinfo.hpp old mode 100644 new mode 100755 diff --git a/src/actor/session/sys_session/manager/SessionManager.cpp b/src/actor/session/sys_session/manager/SessionManager.cpp old mode 100644 new mode 100755 index eafd558e..1b070af5 --- a/src/actor/session/sys_session/manager/SessionManager.cpp +++ b/src/actor/session/sys_session/manager/SessionManager.cpp @@ -196,7 +196,7 @@ bool SessionManager::SendToChild(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgBo continue; } MsgHead oMsgHead; // It will be cleared after each successful sending - MsgBody oOutMsgBody(oMsgBody); + MsgBody oOutMsgBody(oMsgBody); // It will be cleared after each successful sending oMsgHead.set_cmd(iCmd); oMsgHead.set_seq(uiSeq); IO::TransmitTo(this, m_vecWorkerInfo[i]->iWorkerIndex, uiSeq, oMsgHead, oOutMsgBody); @@ -213,10 +213,10 @@ bool SessionManager::SendToWorker(int32 iCmd, uint32 uiSeq, const MsgBody& oMsgB continue; } MsgHead oMsgHead; // It will be cleared after each successful sending - MsgBody oOutMsgBody(oMsgBody); + MsgBody oOutMsgBody(oMsgBody); // It will be cleared after each successful sending oMsgHead.set_cmd(iCmd); oMsgHead.set_seq(uiSeq); - IO::TransmitTo(this, m_vecWorkerInfo[i]->iWorkerIndex, uiSeq, oMsgHead, oMsgBody); + IO::TransmitTo(this, m_vecWorkerInfo[i]->iWorkerIndex, uiSeq, oMsgHead, oOutMsgBody); } return(true); } diff --git a/src/actor/session/sys_session/manager/SessionManager.hpp b/src/actor/session/sys_session/manager/SessionManager.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/CassStep.cpp b/src/actor/step/CassStep.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/CassStep.hpp b/src/actor/step/CassStep.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/DeliverStep.cpp b/src/actor/step/DeliverStep.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/DeliverStep.hpp b/src/actor/step/DeliverStep.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/GrpcStep.cpp b/src/actor/step/GrpcStep.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/GrpcStep.hpp b/src/actor/step/GrpcStep.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/HttpStep.cpp b/src/actor/step/HttpStep.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/HttpStep.hpp b/src/actor/step/HttpStep.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/PbStep.cpp b/src/actor/step/PbStep.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/PbStep.hpp b/src/actor/step/PbStep.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/RawStep.cpp b/src/actor/step/RawStep.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/RawStep.hpp b/src/actor/step/RawStep.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/RedisStep.cpp b/src/actor/step/RedisStep.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/RedisStep.hpp b/src/actor/step/RedisStep.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/Step.cpp b/src/actor/step/Step.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/Step.hpp b/src/actor/step/Step.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/sys_step/StepConnectWorker.cpp b/src/actor/step/sys_step/StepConnectWorker.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/sys_step/StepConnectWorker.hpp b/src/actor/step/sys_step/StepConnectWorker.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/sys_step/StepNebualChannelPing.cpp b/src/actor/step/sys_step/StepNebualChannelPing.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/sys_step/StepNebualChannelPing.hpp b/src/actor/step/sys_step/StepNebualChannelPing.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/sys_step/StepRedisChannelPing.cpp b/src/actor/step/sys_step/StepRedisChannelPing.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/sys_step/StepRedisChannelPing.hpp b/src/actor/step/sys_step/StepRedisChannelPing.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/sys_step/StepRedisCluster.cpp b/src/actor/step/sys_step/StepRedisCluster.cpp old mode 100644 new mode 100755 index f46a3aa6..93b9e8c9 --- a/src/actor/step/sys_step/StepRedisCluster.cpp +++ b/src/actor/step/sys_step/StepRedisCluster.cpp @@ -102,10 +102,11 @@ const std::unordered_set StepRedisCluster::s_setMultipleKeyValueCmd }; StepRedisCluster::StepRedisCluster( - const std::string& strIdentify, bool bWithSsl, bool bPipeline, bool bEnableReadOnly) + const std::string& strIdentify, const ChannelOption& stOption, uint32 uiReadMode) : RedisStep(7.0), - m_bWithSsl(bWithSsl), m_bPipeline(bPipeline), m_bEnableReadOnly(bEnableReadOnly), + m_uiReadMode(uiReadMode), m_uiAddressIndex(0), m_lLastCheckTime(0), + m_stOption(stOption), m_strIdentify(strIdentify) { Split(strIdentify, ",", m_vecAddress); @@ -126,7 +127,7 @@ E_CMD_STATUS StepRedisCluster::Callback( { std::shared_ptr pRedisRequest = nullptr; uint32 uiRealStepSeq = 0; - if (m_bPipeline) + if (m_stOption.bPipeline) { auto step_iter = m_mapPipelineRequest.find(pChannel->GetIdentify()); if (step_iter == m_mapPipelineRequest.end()) @@ -342,7 +343,8 @@ E_CMD_STATUS StepRedisCluster::Callback( E_CMD_STATUS StepRedisCluster::Timeout() { - SendCmdClusterSlots(); + //SendCmdClusterSlots(); + HealthCheck(); return(CMD_STATUS_RUNNING); } @@ -359,7 +361,7 @@ E_CMD_STATUS StepRedisCluster::ErrBack( void StepRedisCluster::CmdErrBack( std::shared_ptr pChannel, int iErrno, const std::string& strErrMsg) { - if (m_bPipeline) + if (m_stOption.bPipeline) { auto step_iter = m_mapPipelineRequest.find(pChannel->GetIdentify()); if (step_iter == m_mapPipelineRequest.end()) @@ -484,11 +486,11 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, const RedisMsg& oR bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptr pRedisMsg) { - bool bResult = IO::SendWithoutOption(this, strIdentify, (*pRedisMsg.get())); + bool bResult = IO::SendTo(this, strIdentify, m_stOption, (*pRedisMsg.get())); if (bResult) { auto pChannel = GetLastActivityChannel(); - auto dPenultimateActiveTime = pChannel->GetPenultimateActiveTime(); + /*auto dPenultimateActiveTime = pChannel->GetPenultimateActiveTime(); auto dLastRecvTime = pChannel->GetLastRecvTime(); auto dCheckTime = GetNowTime() - GetTimeout(); if (dPenultimateActiveTime > dCheckTime @@ -501,8 +503,8 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptrGetDispatcher()->Disconnect(pChannel); return(false); - } - if (m_bPipeline) + }*/ + if (m_stOption.bPipeline) { auto iter = m_mapPipelineRequest.find(strIdentify); if (iter == m_mapPipelineRequest.end()) @@ -530,6 +532,10 @@ bool StepRedisCluster::SendTo(const std::string& strIdentify, std::shared_ptrsecond->setFllower.size() == 0) - || (!m_bEnableReadOnly)) + if (REDIS_CMD_WRITE == iReadOrWrite) { strNodeIdentify = slot_iter->second->strMaster; bIsMaster = true; + return(true); } else { - for (uint32 i = 0; i < slot_iter->second->setFllower.size(); ++i) + switch (m_uiReadMode) + { + case REDIS_READ_MODE_MASTER: + strNodeIdentify = slot_iter->second->strMaster; + bIsMaster = true; + return(true); + case REDIS_READ_MODE_FLLOWER_MASTER: + if (slot_iter->second->iterFllower == slot_iter->second->setFllower.end()) + { + strNodeIdentify = slot_iter->second->strMaster; + bIsMaster = true; + slot_iter->second->iterFllower = slot_iter->second->setFllower.begin(); + return(true); + } + else + { + strNodeIdentify = *(slot_iter->second->iterFllower); + bIsMaster = false; + slot_iter->second->iterFllower++; + auto node_iter = m_setFailedNode.find(strNodeIdentify); + if (node_iter != m_setFailedNode.end()) + { + strNodeIdentify = slot_iter->second->strMaster; + bIsMaster = true; + } + return(true); + } + case REDIS_READ_MODE_FLLOWER: + for (uint32 i = 0; i < slot_iter->second->setFllower.size(); ++i) + { + slot_iter->second->iterFllower++; + if (slot_iter->second->iterFllower == slot_iter->second->setFllower.end()) + { + slot_iter->second->iterFllower = slot_iter->second->setFllower.begin(); + } + strNodeIdentify = *(slot_iter->second->iterFllower); + bIsMaster = false; + slot_iter->second->iterFllower++; + auto node_iter = m_setFailedNode.find(strNodeIdentify); + if (node_iter == m_setFailedNode.end()) + { + return(true); + } + } + LOG4_ERROR("no redis node found for slot %d", iSlotId); + return(false); + default: + strNodeIdentify = slot_iter->second->strMaster; + bIsMaster = true; + return(true); + } { - slot_iter->second->iterFllower++; - if (slot_iter->second->iterFllower == slot_iter->second->setFllower.end()) - { - slot_iter->second->iterFllower = slot_iter->second->setFllower.begin(); - } strNodeIdentify = *(slot_iter->second->iterFllower); bIsMaster = false; - auto node_iter = m_setFailedNode.find(strNodeIdentify); - if (node_iter == m_setFailedNode.end()) - { - return(true); - } } strNodeIdentify = slot_iter->second->strMaster; bIsMaster = true; diff --git a/src/actor/step/sys_step/StepRedisCluster.hpp b/src/actor/step/sys_step/StepRedisCluster.hpp old mode 100644 new mode 100755 index 90829a9c..e2f8e044 --- a/src/actor/step/sys_step/StepRedisCluster.hpp +++ b/src/actor/step/sys_step/StepRedisCluster.hpp @@ -32,6 +32,13 @@ enum E_REDIS_CMD_TYPE REDIS_CMD_WRITE = 1, }; +enum E_REDIS_READ_MODE +{ + REDIS_READ_MODE_MASTER = 0, ///< only read master (default) + REDIS_READ_MODE_FLLOWER = 1, ///< only read fllower + REDIS_READ_MODE_FLLOWER_MASTER = 2, ///< round robin fllower and master +}; + struct RedisNode { std::string strMaster; @@ -56,11 +63,11 @@ struct RedisNode }; class StepRedisCluster: public RedisStep, - public DynamicCreator, + public DynamicCreator, public ActorSys { public: - StepRedisCluster(const std::string& strIdentify, bool bWithSsl, bool bPipeline, bool bEnableReadOnly); + StepRedisCluster(const std::string& strIdentify, const ChannelOption& stOption, uint32 uiReadMode); virtual ~StepRedisCluster(); virtual E_CMD_STATUS Emit(int iErrno = neb::ERR_OK, const std::string& strErrMsg = "", void* data = NULL); @@ -102,11 +109,10 @@ class StepRedisCluster: public RedisStep, void HealthCheck(); private: - bool m_bWithSsl; ///< 是否支持SSL - bool m_bPipeline; ///< 是否支持pipeline - bool m_bEnableReadOnly; ///< 是否对从节点启用只读 + uint32 m_uiReadMode; uint32 m_uiAddressIndex; time_t m_lLastCheckTime; ///< last helth check time + ChannelOption m_stOption; std::string m_strIdentify; ///< 地址标识,由m_vecAddress合并而成,形如 192.168.47.101:6379,198.168.47.102:6379,192.168.47.103:6379 std::vector m_vecAddress; ///< 集群地址 diff --git a/src/actor/step/sys_step/StepTellWorker.cpp b/src/actor/step/sys_step/StepTellWorker.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/sys_step/StepTellWorker.hpp b/src/actor/step/sys_step/StepTellWorker.hpp old mode 100644 new mode 100755 diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.cpp b/src/actor/step/sys_step/manager/StepReportToBeacon.cpp old mode 100644 new mode 100755 diff --git a/src/actor/step/sys_step/manager/StepReportToBeacon.hpp b/src/actor/step/sys_step/manager/StepReportToBeacon.hpp old mode 100644 new mode 100755 diff --git a/src/channel/Channel.hpp b/src/channel/Channel.hpp old mode 100644 new mode 100755 index d9ddc8c9..778a6771 --- a/src/channel/Channel.hpp +++ b/src/channel/Channel.hpp @@ -20,7 +20,8 @@ struct ChannelOption bool bPipeline = false; bool bWithSsl = false; int iSocketType = SOCKET_STREAM; - uint32 uiMaxSendBuffSize = gc_iMaxBuffLen; + uint32 uiMaxSendBuffSize = gc_iMaxBuffLen * 1024; + uint32 uiMaxRecvBuffSize = gc_iMaxBuffLen * 1024; ev_tstamp dKeepAlive = 7.0; std::string strAuth; std::string strPassword; @@ -31,6 +32,8 @@ struct ChannelOption bPipeline = stOption.bPipeline; bWithSsl = stOption.bWithSsl; iSocketType = stOption.iSocketType; + uiMaxSendBuffSize = stOption.uiMaxSendBuffSize; + uiMaxRecvBuffSize = stOption.uiMaxRecvBuffSize; dKeepAlive = stOption.dKeepAlive; strAuth = stOption.strAuth; strPassword = stOption.strPassword; @@ -40,6 +43,8 @@ struct ChannelOption bPipeline = stOption.bPipeline; bWithSsl = stOption.bWithSsl; iSocketType = stOption.iSocketType; + uiMaxSendBuffSize = stOption.uiMaxSendBuffSize; + uiMaxRecvBuffSize = stOption.uiMaxRecvBuffSize; dKeepAlive = stOption.dKeepAlive; strAuth = std::move(stOption.strAuth); strPassword = std::move(stOption.strPassword); @@ -49,6 +54,8 @@ struct ChannelOption bPipeline = stOption.bPipeline; bWithSsl = stOption.bWithSsl; iSocketType = stOption.iSocketType; + uiMaxSendBuffSize = stOption.uiMaxSendBuffSize; + uiMaxRecvBuffSize = stOption.uiMaxRecvBuffSize; dKeepAlive = stOption.dKeepAlive; strAuth = stOption.strAuth; strPassword = stOption.strPassword; @@ -59,6 +66,8 @@ struct ChannelOption bPipeline = stOption.bPipeline; bWithSsl = stOption.bWithSsl; iSocketType = stOption.iSocketType; + uiMaxSendBuffSize = stOption.uiMaxSendBuffSize; + uiMaxRecvBuffSize = stOption.uiMaxRecvBuffSize; dKeepAlive = stOption.dKeepAlive; strAuth = std::move(stOption.strAuth); strPassword = std::move(stOption.strPassword); diff --git a/src/channel/SelfChannel.cpp b/src/channel/SelfChannel.cpp old mode 100644 new mode 100755 diff --git a/src/channel/SelfChannel.hpp b/src/channel/SelfChannel.hpp old mode 100644 new mode 100755 diff --git a/src/channel/SocketChannel.cpp b/src/channel/SocketChannel.cpp old mode 100644 new mode 100755 diff --git a/src/channel/SocketChannel.hpp b/src/channel/SocketChannel.hpp old mode 100644 new mode 100755 diff --git a/src/channel/SocketChannelImpl.hpp b/src/channel/SocketChannelImpl.hpp old mode 100644 new mode 100755 index 5ce3d049..9a8ab614 --- a/src/channel/SocketChannelImpl.hpp +++ b/src/channel/SocketChannelImpl.hpp @@ -220,6 +220,8 @@ class SocketChannelImpl: public SocketChannel m_strRemoteAddr = strRemoteAddr; } + void SetMaxBuffSize(uint32 uiMaxSendBuffSize, uint32 uiMaxRecvBuffSize); + void SetSecretKey(const std::string& strKey); void SetRemoteWorkerIndex(int16 iRemoteWorkerIndex); @@ -242,6 +244,8 @@ class SocketChannelImpl: public SocketChannel int32 m_iFd; ///< 文件描述符 uint32 m_uiSeq; ///< 文件描述符创建时对应的序列号 uint32 m_bPipeline; ///< 是否支持pipeline + uint32 m_uiMaxSendBuffSize; + uint32 m_uiMaxRecvBuffSize; uint32 m_uiUnitTimeMsgNum; ///< 统计单位时间内接收消息数量 uint32 m_uiMsgNum; ///< 接收消息数量 ev_tstamp m_dActiveTime; ///< 最后一次访问时间 @@ -274,6 +278,7 @@ SocketChannelImpl::SocketChannelImpl(Labor* pLabor, std::shared_ptr::SendRequest(uint32 uiStepSeq, Targs&&... ar LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); return(CODEC_STATUS_ERR); } + if (m_pSendBuff->ReadableBytes() > m_uiMaxSendBuffSize + || m_pWaitForSendBuff->ReadableBytes() > m_uiMaxSendBuffSize) + { + LOG4_ERROR("max send buff size %u, and now send buff size %u, send wait buff size %u, overflow.", + m_uiMaxSendBuffSize, m_pSendBuff->ReadableBytes(), m_pWaitForSendBuff->ReadableBytes()); + return(CODEC_STATUS_PART_ERR); + } E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; switch (m_ucChannelStatus) { @@ -649,6 +661,13 @@ E_CODEC_STATUS SocketChannelImpl::SendResponse(Targs&&... args) LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); return(CODEC_STATUS_ERR); } + if (m_pSendBuff->ReadableBytes() > m_uiMaxSendBuffSize + || m_pWaitForSendBuff->ReadableBytes() > m_uiMaxSendBuffSize) + { + LOG4_ERROR("max send buff size %u, and now send buff size %u, send wait buff size %u, overflow.", + m_uiMaxSendBuffSize, m_pSendBuff->ReadableBytes(), m_pWaitForSendBuff->ReadableBytes()); + return(CODEC_STATUS_PART_ERR); + } E_CODEC_STATUS eCodecStatus = CODEC_STATUS_OK; switch (m_ucChannelStatus) { @@ -757,6 +776,12 @@ E_CODEC_STATUS SocketChannelImpl::Recv(Targs&&... args) m_strIdentify.c_str(), m_iFd, m_uiSeq, (int)m_ucChannelStatus, m_strRemoteAddr.c_str()); return(CODEC_STATUS_ERR); } + if (m_pRecvBuff->ReadableBytes() > m_uiMaxRecvBuffSize) + { + LOG4_ERROR("max recv buff size %u, and now recv buff size %u, overflow.", + m_uiMaxRecvBuffSize, m_pRecvBuff->ReadableBytes()); + return(CODEC_STATUS_PART_ERR); + } if (m_pCodec == nullptr) { LOG4_ERROR("no codec found, please check whether the CODEC_TYPE is valid."); @@ -914,6 +939,13 @@ E_CODEC_STATUS SocketChannelImpl::Fetch(Targs&&... args) return(eCodecStatus); } +template +void SocketChannelImpl::SetMaxBuffSize(uint32 uiMaxSendBuffSize, uint32 uiMaxRecvBuffSize) +{ + m_uiMaxSendBuffSize = uiMaxSendBuffSize; + m_uiMaxRecvBuffSize = uiMaxRecvBuffSize; +} + template void SocketChannelImpl::SetSecretKey(const std::string& strKey) { diff --git a/src/channel/SocketChannelSslImpl.hpp b/src/channel/SocketChannelSslImpl.hpp old mode 100644 new mode 100755 diff --git a/src/channel/SocketChannelSslImpl.inl b/src/channel/SocketChannelSslImpl.inl old mode 100644 new mode 100755 diff --git a/src/channel/SpecChannel.hpp b/src/channel/SpecChannel.hpp old mode 100644 new mode 100755 index 793a2cc7..308818cd --- a/src/channel/SpecChannel.hpp +++ b/src/channel/SpecChannel.hpp @@ -26,13 +26,19 @@ class SpecChannel: public SocketChannel uint32 uiQueueSize, bool bWithHeader = false); virtual ~SpecChannel(); + int Write(uint32 uiFlags, uint32 uiStepSeq, const Tdata& oData); + int Write(uint32 uiFlags, uint32 uiStepSeq, const Thead& oHead, const Tdata& oData); int Write(uint32 uiFlags, uint32 uiStepSeq, Tdata&& oData); int Write(uint32 uiFlags, uint32 uiStepSeq, Thead&& oHead, Tdata&& oData); + int Write(uint32 uiFlags, uint32 uiStepSeq, Tdata* pData); + int Write(uint32 uiFlags, uint32 uiStepSeq, Thead* pHead, Tdata* pData); + int Write(uint32 uiFlags, uint32 uiStepSeq, Tdata** pData); + int Write(uint32 uiFlags, uint32 uiStepSeq, Thead** pHead, Tdata** pData); - bool Read(uint32& uiFlags, uint32& uiStepSeq, Tdata& oData); + bool Read(uint32& uiFlags, uint32& uiStepSeq, Tdata** oData); - bool Read(uint32& uiFlags, uint32& uiStepSeq, Thead& oHead, Tdata& oData); + bool Read(uint32& uiFlags, uint32& uiStepSeq, Thead** oHead, Tdata** oData); bool IsEmpty() const; @@ -98,8 +104,6 @@ class SpecChannel: public SocketChannel protected: virtual void SetIdentify(const std::string& strIdentify); virtual void SetRemoteAddr(const std::string& strRemoteAddr); - void WriteData(Tdata&& oData); - void WriteHeadAndData(Thead&& oHead, Tdata&& oData); private: bool m_bWithHeader; @@ -113,8 +117,8 @@ class SpecChannel: public SocketChannel std::string m_strIdentify; std::string m_strRemoteAddr; std::vector m_vecFlags; - std::vector> m_vecData; - std::vector m_vecHead; + std::vector> m_vecData; + std::vector m_vecHead; SpecChannelWatcher* m_pWatcher; }; @@ -132,6 +136,22 @@ SpecChannel::SpecChannel( template SpecChannel::~SpecChannel() { + for (uint32 i = 0; i < m_vecData.size(); ++i) + { + if (m_vecData[i].second != nullptr) + { + delete m_vecData[i].second; + m_vecData[i].second = nullptr; + } + } + for (uint32 i = 0; i < m_vecHead.size(); ++i) + { + if (m_vecHead[i] != nullptr) + { + delete m_vecHead[i]; + m_vecHead[i] = nullptr; + } + } if (nullptr != m_pWatcher) { delete m_pWatcher; @@ -139,6 +159,82 @@ SpecChannel::~SpecChannel() } } +template +int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, const Tdata& oData) +{ + auto uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); + auto uiNextRecord = uiCurrentWrite + 1; + uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; + if (m_uiEffectiveSize < m_uiSize) + { + m_vecFlags.push_back(uiFlags); + m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, new Tdata(oData)))); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + ++m_uiEffectiveSize; + return(ERR_OK); + } + else + { + if (uiNextRecord == m_uiReadIndex.load(std::memory_order_acquire)) // queue is full + { + return(ERR_SPEC_CHANNEL_FULL); + } + else + { + m_vecFlags[uiCurrentWrite] = uiFlags; + m_vecData[uiCurrentWrite].first = uiStepSeq; + if (m_vecData[uiCurrentWrite].second != nullptr) + { + delete m_vecData[uiCurrentWrite].second; + } + m_vecData[uiCurrentWrite].second = new Tdata(oData); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + return(ERR_OK); + } + } +} + +template +int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, const Thead& oHead, const Tdata& oData) +{ + auto uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); + auto uiNextRecord = uiCurrentWrite + 1; + uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; + if (m_uiEffectiveSize < m_uiSize) + { + m_vecFlags.push_back(uiFlags); + m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, new Tdata(oData)))); + m_vecHead.emplace_back(new Thead(oHead)); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + ++m_uiEffectiveSize; + return(ERR_OK); + } + else + { + if (uiNextRecord == m_uiReadIndex.load(std::memory_order_acquire)) // queue is full + { + return(ERR_SPEC_CHANNEL_FULL); + } + else + { + m_vecFlags[uiCurrentWrite] = uiFlags; + m_vecData[uiCurrentWrite].first = uiStepSeq; + if (m_vecData[uiCurrentWrite].second != nullptr) + { + delete m_vecData[uiCurrentWrite].second; + } + if (m_vecHead[uiCurrentWrite] != nullptr) + { + delete m_vecHead[uiCurrentWrite]; + } + m_vecData[uiCurrentWrite].second = new Tdata(oData); + m_vecHead[uiCurrentWrite] = new Thead(oHead); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + return(ERR_OK); + } + } +} + template int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Tdata&& oData) { @@ -148,7 +244,7 @@ int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Tdata&& o if (m_uiEffectiveSize < m_uiSize) { m_vecFlags.push_back(uiFlags); - m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, std::forward(oData)))); + m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, new Tdata(std::move(oData))))); m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); ++m_uiEffectiveSize; return(ERR_OK); @@ -163,7 +259,7 @@ int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Tdata&& o { m_vecFlags[uiCurrentWrite] = uiFlags; m_vecData[uiCurrentWrite].first = uiStepSeq; - m_vecData[uiCurrentWrite].second = std::forward(oData); + m_vecData[uiCurrentWrite].second = new Tdata(std::move(oData)); m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); return(ERR_OK); } @@ -179,10 +275,158 @@ int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Thead&& o if (m_uiEffectiveSize < m_uiSize) { m_vecFlags.push_back(uiFlags); - m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, std::forward(oData)))); - m_vecHead.emplace_back(std::forward(oHead)); + m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, new Tdata(std::move(oData))))); + m_vecHead.emplace_back(new Thead(std::move(oHead))); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + ++m_uiEffectiveSize; + return(ERR_OK); + } + else + { + if (uiNextRecord == m_uiReadIndex.load(std::memory_order_acquire)) // queue is full + { + return(ERR_SPEC_CHANNEL_FULL); + } + else + { + m_vecFlags[uiCurrentWrite] = uiFlags; + m_vecData[uiCurrentWrite].first = uiStepSeq; + m_vecData[uiCurrentWrite].second = new Tdata(std::move(oData)); + m_vecHead[uiCurrentWrite] = new Thead(std::move(oHead)); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + return(ERR_OK); + } + } +} + +template +int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Tdata* pData) +{ + auto uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); + auto uiNextRecord = uiCurrentWrite + 1; + uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; + if (m_uiEffectiveSize < m_uiSize) + { + m_vecFlags.push_back(uiFlags); + m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, pData))); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + ++m_uiEffectiveSize; + return(ERR_OK); + } + else + { + if (uiNextRecord == m_uiReadIndex.load(std::memory_order_acquire)) // queue is full + { + return(ERR_SPEC_CHANNEL_FULL); + } + else + { + m_vecFlags[uiCurrentWrite] = uiFlags; + m_vecData[uiCurrentWrite].first = uiStepSeq; + if (m_vecData[uiCurrentWrite].second != nullptr) + { + delete m_vecData[uiCurrentWrite].second; + } + m_vecData[uiCurrentWrite].second = pData; + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + return(ERR_OK); + } + } +} + +template +int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Thead* pHead, Tdata* pData) +{ + auto uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); + auto uiNextRecord = uiCurrentWrite + 1; + uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; + if (m_uiEffectiveSize < m_uiSize) + { + m_vecFlags.push_back(uiFlags); + m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, pData))); + m_vecHead.emplace_back(pHead); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + ++m_uiEffectiveSize; + return(ERR_OK); + } + else + { + if (uiNextRecord == m_uiReadIndex.load(std::memory_order_acquire)) // queue is full + { + return(ERR_SPEC_CHANNEL_FULL); + } + else + { + m_vecFlags[uiCurrentWrite] = uiFlags; + m_vecData[uiCurrentWrite].first = uiStepSeq; + if (m_vecData[uiCurrentWrite].second != nullptr) + { + delete m_vecData[uiCurrentWrite].second; + } + if (m_vecHead[uiCurrentWrite] != nullptr) + { + delete m_vecHead[uiCurrentWrite]; + } + m_vecData[uiCurrentWrite].second = pData; + m_vecHead[uiCurrentWrite] = pHead; + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + return(ERR_OK); + } + } +} + +template +int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Tdata** pData) +{ + auto uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); + auto uiNextRecord = uiCurrentWrite + 1; + uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; + if (m_uiEffectiveSize < m_uiSize) + { + m_vecFlags.push_back(uiFlags); + m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, *pData))); + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + ++m_uiEffectiveSize; + pData = nullptr; + return(ERR_OK); + } + else + { + if (uiNextRecord == m_uiReadIndex.load(std::memory_order_acquire)) // queue is full + { + return(ERR_SPEC_CHANNEL_FULL); + } + else + { + m_vecFlags[uiCurrentWrite] = uiFlags; + m_vecData[uiCurrentWrite].first = uiStepSeq; + if (m_vecData[uiCurrentWrite].second != nullptr) + { + delete m_vecData[uiCurrentWrite].second; + } + m_vecData[uiCurrentWrite].second = *pData; + m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + pData = nullptr; + return(ERR_OK); + } + } +} + +template +int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Thead** pHead, Tdata** pData) +{ + auto uiCurrentWrite = m_uiWriteIndex.load(std::memory_order_relaxed); + auto uiNextRecord = uiCurrentWrite + 1; + uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; + if (m_uiEffectiveSize < m_uiSize) + { + m_vecFlags.push_back(uiFlags); + m_vecData.emplace_back(std::move(std::make_pair(uiStepSeq, *pData))); + m_vecHead.emplace_back(*pHead); m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); ++m_uiEffectiveSize; + pData = nullptr; + pHead = nullptr; return(ERR_OK); } else @@ -195,16 +439,26 @@ int SpecChannel::Write(uint32 uiFlags, uint32 uiStepSeq, Thead&& o { m_vecFlags[uiCurrentWrite] = uiFlags; m_vecData[uiCurrentWrite].first = uiStepSeq; - m_vecData[uiCurrentWrite].second = std::forward(oData); - m_vecHead[uiCurrentWrite] = std::forward(oHead); + if (m_vecData[uiCurrentWrite].second != nullptr) + { + delete m_vecData[uiCurrentWrite].second; + } + if (m_vecHead[uiCurrentWrite] != nullptr) + { + delete m_vecHead[uiCurrentWrite]; + } + m_vecData[uiCurrentWrite].second = *pData; + m_vecHead[uiCurrentWrite] = *pHead; m_uiWriteIndex.store(uiNextRecord, std::memory_order_release); + pData = nullptr; + pHead = nullptr; return(ERR_OK); } } } template -bool SpecChannel::Read(uint32& uiFlags, uint32& uiStepSeq, Tdata& oData) +bool SpecChannel::Read(uint32& uiFlags, uint32& uiStepSeq, Tdata** pData) { auto uiCurrentRead = m_uiReadIndex.load(std::memory_order_relaxed); if (uiCurrentRead == m_uiWriteIndex.load(std::memory_order_acquire)) // queue is empty @@ -216,14 +470,15 @@ bool SpecChannel::Read(uint32& uiFlags, uint32& uiStepSeq, Tdata& uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; uiFlags = m_vecFlags[uiCurrentRead]; uiStepSeq = m_vecData[uiCurrentRead].first; - oData = std::move(m_vecData[uiCurrentRead].second); + *pData = m_vecData[uiCurrentRead].second; + m_vecData[uiCurrentRead].second = nullptr; m_uiPeerStepSeq = uiStepSeq; m_uiReadIndex.store(uiNextRecord, std::memory_order_release); return(true); } template -bool SpecChannel::Read(uint32& uiFlags, uint32& uiStepSeq, Thead& oHead, Tdata& oData) +bool SpecChannel::Read(uint32& uiFlags, uint32& uiStepSeq, Thead** pHead, Tdata** pData) { auto uiCurrentRead = m_uiReadIndex.load(std::memory_order_relaxed); if (uiCurrentRead == m_uiWriteIndex.load(std::memory_order_acquire)) // queue is empty @@ -235,8 +490,10 @@ bool SpecChannel::Read(uint32& uiFlags, uint32& uiStepSeq, Thead& uiNextRecord = (uiNextRecord == m_uiSize) ? 0 : uiNextRecord; uiFlags = m_vecFlags[uiCurrentRead]; uiStepSeq = m_vecData[uiCurrentRead].first; - oData = std::move(m_vecData[uiCurrentRead].second); - oHead = std::move(m_vecHead[uiCurrentRead]); + *pData = m_vecData[uiCurrentRead].second; + *pHead = m_vecHead[uiCurrentRead]; + m_vecData[uiCurrentRead].second = nullptr; + m_vecHead[uiCurrentRead] = nullptr; m_uiPeerStepSeq = uiStepSeq; m_uiReadIndex.store(uiNextRecord, std::memory_order_release); return(true); diff --git a/src/channel/SslContext.cpp b/src/channel/SslContext.cpp old mode 100644 new mode 100755 diff --git a/src/channel/SslContext.hpp b/src/channel/SslContext.hpp old mode 100644 new mode 100755 diff --git a/src/channel/migrate/SocketChannelMigrate.cpp b/src/channel/migrate/SocketChannelMigrate.cpp old mode 100644 new mode 100755 diff --git a/src/channel/migrate/SocketChannelMigrate.hpp b/src/channel/migrate/SocketChannelMigrate.hpp old mode 100644 new mode 100755 diff --git a/src/channel/migrate/SocketChannelPack.cpp b/src/channel/migrate/SocketChannelPack.cpp old mode 100644 new mode 100755 diff --git a/src/channel/migrate/SocketChannelPack.hpp b/src/channel/migrate/SocketChannelPack.hpp old mode 100644 new mode 100755 diff --git a/src/codec/Codec.cpp b/src/codec/Codec.cpp old mode 100644 new mode 100755 diff --git a/src/codec/Codec.hpp b/src/codec/Codec.hpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecDefine.hpp b/src/codec/CodecDefine.hpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecFactory.cpp b/src/codec/CodecFactory.cpp old mode 100644 new mode 100755 index 9e032c87..cfd199f2 --- a/src/codec/CodecFactory.cpp +++ b/src/codec/CodecFactory.cpp @@ -58,11 +58,15 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s auto pImpl = std::make_shared>( pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); + pImpl->SetMaxBuffSize(pLabor->GetNodeInfo().uiMaxChannelSendBuffSize, + pLabor->GetNodeInfo().uiMaxChannelRecvBuffSize); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); #else auto pImpl = std::make_shared>( pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); + pImpl->SetMaxBuffSize(pLabor->GetNodeInfo().uiMaxChannelSendBuffSize, + pLabor->GetNodeInfo().uiMaxChannelRecvBuffSize); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); #endif } @@ -71,6 +75,8 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s auto pImpl = std::make_shared>( pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); + pImpl->SetMaxBuffSize(pLabor->GetNodeInfo().uiMaxChannelSendBuffSize, + pLabor->GetNodeInfo().uiMaxChannelRecvBuffSize); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } break; @@ -82,11 +88,15 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s auto pImpl = std::make_shared>( pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); + pImpl->SetMaxBuffSize(pLabor->GetNodeInfo().uiMaxChannelSendBuffSize, + pLabor->GetNodeInfo().uiMaxChannelRecvBuffSize); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); #else auto pImpl = std::make_shared>( pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); + pImpl->SetMaxBuffSize(pLabor->GetNodeInfo().uiMaxChannelSendBuffSize, + pLabor->GetNodeInfo().uiMaxChannelRecvBuffSize); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); #endif } @@ -95,6 +105,8 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s auto pImpl = std::make_shared>( pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); + pImpl->SetMaxBuffSize(pLabor->GetNodeInfo().uiMaxChannelSendBuffSize, + pLabor->GetNodeInfo().uiMaxChannelRecvBuffSize); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } break; @@ -104,6 +116,8 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s auto pImpl = std::make_shared>( pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); + pImpl->SetMaxBuffSize(pLabor->GetNodeInfo().uiMaxChannelSendBuffSize, + pLabor->GetNodeInfo().uiMaxChannelRecvBuffSize); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } break; @@ -113,6 +127,8 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s auto pImpl = std::make_shared>( pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); + pImpl->SetMaxBuffSize(pLabor->GetNodeInfo().uiMaxChannelSendBuffSize, + pLabor->GetNodeInfo().uiMaxChannelRecvBuffSize); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } break; @@ -122,6 +138,8 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s auto pImpl = std::make_shared>( pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); + pImpl->SetMaxBuffSize(pLabor->GetNodeInfo().uiMaxChannelSendBuffSize, + pLabor->GetNodeInfo().uiMaxChannelRecvBuffSize); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } break; @@ -131,6 +149,8 @@ std::shared_ptr CodecFactory::CreateChannel(Labor* pLabor, std::s auto pImpl = std::make_shared>( pLabor, pLogger, bIsClient, bWithSsl, iFd, pLabor->GetSequence(), dKeepAlive); pImpl->SetCodec(pCodec); + pImpl->SetMaxBuffSize(pLabor->GetNodeInfo().uiMaxChannelSendBuffSize, + pLabor->GetNodeInfo().uiMaxChannelRecvBuffSize); pChannel->m_pImpl = std::dynamic_pointer_cast(pImpl); } break; @@ -186,23 +206,33 @@ E_CODEC_STATUS CodecFactory::OnEvent(uint32 uiSpecChannelCodecType, std::shared_ case CODEC_NEBULA: case CODEC_NEBULA_IN_NODE: { - MsgHead oMsgHead; - MsgBody oMsgBody; + MsgHead* pMsgHead = nullptr; + MsgBody* pMsgBody = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); if (pDispatcher == nullptr) { pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); } pDispatcher->m_pLastActivityChannel = pChannel; - while (pSpecChannel->Read(uiFlags, uiStepSeq, oMsgHead, oMsgBody)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pMsgHead, &pMsgBody)) { - if (gc_uiCmdReq & oMsgHead.cmd()) + if (gc_uiCmdReq & pMsgHead->cmd()) { - IO::OnMessage(pDispatcher, pChannel, oMsgHead, oMsgBody); + IO::OnMessage(pDispatcher, pChannel, *pMsgHead, *pMsgBody); } else { - IO::OnMessage(pDispatcher, pChannel, oMsgHead, oMsgBody); + IO::OnMessage(pDispatcher, pChannel, *pMsgHead, *pMsgBody); + } + if (pMsgHead != nullptr) + { + delete pMsgHead; + pMsgHead = nullptr; + } + if (pMsgBody != nullptr) + { + delete pMsgBody; + pMsgBody = nullptr; } } } @@ -210,60 +240,75 @@ E_CODEC_STATUS CodecFactory::OnEvent(uint32 uiSpecChannelCodecType, std::shared_ case CODEC_HTTP: case CODEC_HTTP2: { - HttpMsg oHttpMsg; + HttpMsg* pHttpMsg = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); if (pDispatcher == nullptr) { pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); } pDispatcher->m_pLastActivityChannel = pChannel; - while (pSpecChannel->Read(uiFlags, uiStepSeq, oHttpMsg)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pHttpMsg)) { if (gc_uiCmdReq & uiFlags) { - IO::OnRequest(pDispatcher, pChannel, oHttpMsg.path(), oHttpMsg); + IO::OnRequest(pDispatcher, pChannel, pHttpMsg->path(), *pHttpMsg); } else { - IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oHttpMsg); + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, *pHttpMsg); + } + if (pHttpMsg != nullptr) + { + delete pHttpMsg; + pHttpMsg = nullptr; } } } break; case CODEC_RESP: { - RedisMsg oRedisMsg; + RedisMsg* pRedisMsg = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); if (pDispatcher == nullptr) { pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); } pDispatcher->m_pLastActivityChannel = pChannel; - while (pSpecChannel->Read(uiFlags, uiStepSeq, oRedisMsg)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pRedisMsg)) { if (gc_uiCmdReq & uiFlags) { - IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_REDIS_PROXY, oRedisMsg); + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_REDIS_PROXY, *pRedisMsg); } else { - IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oRedisMsg); + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, *pRedisMsg); + } + if (pRedisMsg != nullptr) + { + delete pRedisMsg; + pRedisMsg = nullptr; } } } break; case CODEC_CASS: { - CassResponse oCassResponse; + CassResponse* pCassResponse = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); if (pDispatcher == nullptr) { pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); } pDispatcher->m_pLastActivityChannel = pChannel; - while (pSpecChannel->Read(uiFlags, uiStepSeq, oCassResponse)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pCassResponse)) { - IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oCassResponse); + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, *pCassResponse); + if (pCassResponse != nullptr) + { + delete pCassResponse; + pCassResponse = nullptr; + } } } break; @@ -271,44 +316,54 @@ E_CODEC_STATUS CodecFactory::OnEvent(uint32 uiSpecChannelCodecType, std::shared_ break; case CODEC_CHANNEL_MIGRATE: { - SocketChannelPack oPack; + SocketChannelPack* pPack = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); if (pDispatcher == nullptr) { pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); } pDispatcher->m_pLastActivityChannel = pChannel; - while (pSpecChannel->Read(uiFlags, uiStepSeq, oPack)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pPack)) { if (gc_uiCmdReq & uiFlags) { - IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_CHANNEL_MIGRATE, oPack); + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_CHANNEL_MIGRATE, *pPack); } else { ; // no response } + if (pPack != nullptr) + { + delete pPack; + pPack = nullptr; + } } } break; case CODEC_DELIVER: { - Package oPackage; + Package* pPackage = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); if (pDispatcher == nullptr) { pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); } pDispatcher->m_pLastActivityChannel = pChannel; - while (pSpecChannel->Read(uiFlags, uiStepSeq, oPackage)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pPackage)) { if (gc_uiCmdReq & uiFlags) { - IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_DELIVER, std::move(oPackage)); + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_DELIVER, std::move(*pPackage)); } else { - IO::OnResponse(pDispatcher, pChannel, uiStepSeq, std::move(oPackage)); + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, std::move(*pPackage)); + } + if (pPackage != nullptr) + { + delete pPackage; + pPackage = nullptr; } } } @@ -384,20 +439,30 @@ bool CodecFactory::OnSpecChannelCreated(uint32 uiCodecType, uint32 uiFromLabor, case CODEC_NEBULA: case CODEC_NEBULA_IN_NODE: { - MsgHead oMsgHead; - MsgBody oMsgBody; + MsgHead* pMsgHead = nullptr; + MsgBody* pMsgBody = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); pDispatcher->AddEvent(pSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); - while (pSpecChannel->Read(uiFlags, uiStepSeq, oMsgHead, oMsgBody)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pMsgHead, &pMsgBody)) { - if (gc_uiCmdReq & oMsgHead.cmd()) + if (gc_uiCmdReq & pMsgHead->cmd()) { - IO::OnMessage(pDispatcher, pChannel, oMsgHead, oMsgBody); + IO::OnMessage(pDispatcher, pChannel, *pMsgHead, *pMsgBody); } else { - IO::OnMessage(pDispatcher, pChannel, oMsgHead, oMsgBody); + IO::OnMessage(pDispatcher, pChannel, *pMsgHead, *pMsgBody); + } + if (pMsgHead != nullptr) + { + delete pMsgHead; + pMsgHead = nullptr; + } + if (pMsgBody != nullptr) + { + delete pMsgBody; + pMsgBody = nullptr; } } } @@ -405,70 +470,90 @@ bool CodecFactory::OnSpecChannelCreated(uint32 uiCodecType, uint32 uiFromLabor, case CODEC_HTTP: case CODEC_HTTP2: { - HttpMsg oHttpMsg; + HttpMsg* pHttpMsg = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); pDispatcher->AddEvent(pSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); - while (pSpecChannel->Read(uiFlags, uiStepSeq, oHttpMsg)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pHttpMsg)) { if (gc_uiCmdReq & uiFlags) { - IO::OnRequest(pDispatcher, pChannel, oHttpMsg.path(), oHttpMsg); + IO::OnRequest(pDispatcher, pChannel, pHttpMsg->path(), *pHttpMsg); } else { - IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oHttpMsg); + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, *pHttpMsg); + } + if (pHttpMsg != nullptr) + { + delete pHttpMsg; + pHttpMsg = nullptr; } } } break; case CODEC_RESP: { - RedisMsg oRedisMsg; + RedisMsg* pRedisMsg = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); pDispatcher->AddEvent(pSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); - while (pSpecChannel->Read(uiFlags, uiStepSeq, oRedisMsg)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pRedisMsg)) { if (gc_uiCmdReq & uiFlags) { - IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_REDIS_PROXY, oRedisMsg); + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_REDIS_PROXY, *pRedisMsg); } else { - IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oRedisMsg); + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, *pRedisMsg); + } + if (pRedisMsg != nullptr) + { + delete pRedisMsg; + pRedisMsg = nullptr; } } } break; case CODEC_CASS: { - CassResponse oCassResponse; + CassResponse* pCassResponse = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); pDispatcher->AddEvent(pSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); - while (pSpecChannel->Read(uiFlags, uiStepSeq, oCassResponse)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pCassResponse)) + { + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, *pCassResponse); + } + if (pCassResponse != nullptr) { - IO::OnResponse(pDispatcher, pChannel, uiStepSeq, oCassResponse); + delete pCassResponse; + pCassResponse = nullptr; } } break; case CODEC_DELIVER: { - Package oPackage; + Package* pPackage = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); pDispatcher->AddEvent(pSpecChannel->MutableWatcher()->MutableAsyncWatcher(), Dispatcher::AsyncCallback); pDispatcher->m_pLastActivityChannel = pChannel; - while (pSpecChannel->Read(uiFlags, uiStepSeq, oPackage)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pPackage)) { if (gc_uiCmdReq & uiFlags) { - IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_DELIVER, std::move(oPackage)); + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_DELIVER, std::move(*pPackage)); } else { - IO::OnResponse(pDispatcher, pChannel, uiStepSeq, std::move(oPackage)); + IO::OnResponse(pDispatcher, pChannel, uiStepSeq, std::move(*pPackage)); + } + if (pPackage != nullptr) + { + delete pPackage; + pPackage = nullptr; } } } @@ -477,20 +562,25 @@ bool CodecFactory::OnSpecChannelCreated(uint32 uiCodecType, uint32 uiFromLabor, break; case CODEC_CHANNEL_MIGRATE: { - SocketChannelPack oPack; + SocketChannelPack* pPack = nullptr; auto pSpecChannel = std::static_pointer_cast>(pChannel); auto pDispatcher = LaborShared::Instance()->GetDispatcher(pSpecChannel->GetOwnerId()); pDispatcher->m_pLastActivityChannel = pChannel; - while (pSpecChannel->Read(uiFlags, uiStepSeq, oPack)) + while (pSpecChannel->Read(uiFlags, uiStepSeq, &pPack)) { if (gc_uiCmdReq & uiFlags) { - IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_CHANNEL_MIGRATE, oPack); + IO::OnRequest(pDispatcher, pChannel, (int32)CMD_REQ_CHANNEL_MIGRATE, *pPack); } else { ; // no response } + if (pPack != nullptr) + { + delete pPack; + pPack = nullptr; + } } } break; diff --git a/src/codec/CodecFactory.hpp b/src/codec/CodecFactory.hpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecHttp.cpp b/src/codec/CodecHttp.cpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecHttp.hpp b/src/codec/CodecHttp.hpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecPrivate.cpp b/src/codec/CodecPrivate.cpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecPrivate.hpp b/src/codec/CodecPrivate.hpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecProto.cpp b/src/codec/CodecProto.cpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecProto.hpp b/src/codec/CodecProto.hpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecRaw.cpp b/src/codec/CodecRaw.cpp old mode 100644 new mode 100755 index 83c63582..f432d756 --- a/src/codec/CodecRaw.cpp +++ b/src/codec/CodecRaw.cpp @@ -102,6 +102,19 @@ E_CODEC_STATUS CodecRaw::Encode(const char* pRaw, uint32 uiRawSize, CBuffer* pBu return(Encode(pRaw, uiRawSize, pBuff)); } +E_CODEC_STATUS CodecRaw::Encode(CBuffer&& oBuff, CBuffer* pBuff, CBuffer* pSecondlyBuff) +{ + if (pBuff->ReadableBytes() == 0) + { + *pBuff = std::move(oBuff); + } + else + { + oBuff.Copyout(pBuff, oBuff.ReadableBytes()); + } + return(CODEC_STATUS_OK); +} + E_CODEC_STATUS CodecRaw::Decode(CBuffer* pBuff, CBuffer& oBuff) { oBuff.Write(pBuff->GetRawReadBuffer(), pBuff->ReadableBytes()); diff --git a/src/codec/CodecRaw.hpp b/src/codec/CodecRaw.hpp old mode 100644 new mode 100755 index b96ed3c2..f879977e --- a/src/codec/CodecRaw.hpp +++ b/src/codec/CodecRaw.hpp @@ -39,6 +39,7 @@ class CodecRaw: public Codec E_CODEC_STATUS Encode(CBuffer* pBuff, CBuffer* pSecondlyBuff = nullptr); E_CODEC_STATUS Encode(const char* pRaw, uint32 uiRawSize, CBuffer* pBuff); E_CODEC_STATUS Encode(const char* pRaw, uint32 uiRawSize, CBuffer* pBuff, CBuffer* pSecondlyBuff); + E_CODEC_STATUS Encode(CBuffer&& oBuff, CBuffer* pBuff, CBuffer* pSecondlyBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, CBuffer& oBuff); E_CODEC_STATUS Decode(CBuffer* pBuff, CBuffer& oBuff, CBuffer* pReactBuff); }; diff --git a/src/codec/CodecResp.cpp b/src/codec/CodecResp.cpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecResp.hpp b/src/codec/CodecResp.hpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecUtil.cpp b/src/codec/CodecUtil.cpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecUtil.hpp b/src/codec/CodecUtil.hpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecWsExtentJson.cpp b/src/codec/CodecWsExtentJson.cpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecWsExtentJson.hpp b/src/codec/CodecWsExtentJson.hpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecWsExtentPb.cpp b/src/codec/CodecWsExtentPb.cpp old mode 100644 new mode 100755 diff --git a/src/codec/CodecWsExtentPb.hpp b/src/codec/CodecWsExtentPb.hpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/CassComm.hpp b/src/codec/cass/CassComm.hpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/CassMessage.cpp b/src/codec/cass/CassMessage.cpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/CassMessage.hpp b/src/codec/cass/CassMessage.hpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/CassRequest.cpp b/src/codec/cass/CassRequest.cpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/CassRequest.hpp b/src/codec/cass/CassRequest.hpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/CassResponse.cpp b/src/codec/cass/CassResponse.cpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/CassResponse.hpp b/src/codec/cass/CassResponse.hpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/CodecCass.cpp b/src/codec/cass/CodecCass.cpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/CodecCass.hpp b/src/codec/cass/CodecCass.hpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/result/CassResult.cpp b/src/codec/cass/result/CassResult.cpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/result/CassResult.hpp b/src/codec/cass/result/CassResult.hpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/result/CassRow.cpp b/src/codec/cass/result/CassRow.cpp old mode 100644 new mode 100755 diff --git a/src/codec/cass/result/CassRow.hpp b/src/codec/cass/result/CassRow.hpp old mode 100644 new mode 100755 diff --git a/src/codec/grpc/Grpc.hpp b/src/codec/grpc/Grpc.hpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/CodecHttp2.cpp b/src/codec/http2/CodecHttp2.cpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/CodecHttp2.hpp b/src/codec/http2/CodecHttp2.hpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/H2Comm.hpp b/src/codec/http2/H2Comm.hpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/Http2Frame.cpp b/src/codec/http2/Http2Frame.cpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/Http2Frame.hpp b/src/codec/http2/Http2Frame.hpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/Http2Header.cpp b/src/codec/http2/Http2Header.cpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/Http2Header.hpp b/src/codec/http2/Http2Header.hpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/Http2Stream.cpp b/src/codec/http2/Http2Stream.cpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/Http2Stream.hpp b/src/codec/http2/Http2Stream.hpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/Huffman.cpp b/src/codec/http2/Huffman.cpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/Huffman.hpp b/src/codec/http2/Huffman.hpp old mode 100644 new mode 100755 diff --git a/src/codec/http2/Tree.hpp b/src/codec/http2/Tree.hpp old mode 100644 new mode 100755 diff --git a/src/codec/virtual/CodecDeliver.cpp b/src/codec/virtual/CodecDeliver.cpp old mode 100644 new mode 100755 diff --git a/src/codec/virtual/CodecDeliver.hpp b/src/codec/virtual/CodecDeliver.hpp old mode 100644 new mode 100755 diff --git a/src/ios/ActorWatcher.cpp b/src/ios/ActorWatcher.cpp old mode 100644 new mode 100755 diff --git a/src/ios/ActorWatcher.hpp b/src/ios/ActorWatcher.hpp old mode 100644 new mode 100755 diff --git a/src/ios/ChannelWatcher.cpp b/src/ios/ChannelWatcher.cpp old mode 100644 new mode 100755 diff --git a/src/ios/ChannelWatcher.hpp b/src/ios/ChannelWatcher.hpp old mode 100644 new mode 100755 diff --git a/src/ios/Dispatcher.cpp b/src/ios/Dispatcher.cpp old mode 100644 new mode 100755 index a5d26fe3..0dc407af --- a/src/ios/Dispatcher.cpp +++ b/src/ios/Dispatcher.cpp @@ -10,11 +10,13 @@ #include "Dispatcher.hpp" #include +#include #include "Definition.hpp" #include "labor/Manager.hpp" #include "labor/Worker.hpp" #include "IO.hpp" #include "ChannelWatcher.hpp" +#include "PollingWatcher.hpp" #include "actor/Actor.hpp" #include "actor/step/Step.hpp" #include "actor/step/RedisStep.hpp" @@ -28,7 +30,8 @@ namespace neb { Dispatcher::Dispatcher(Labor* pLabor, std::shared_ptr pLogger) - : m_pErrBuff(NULL), m_pLabor(pLabor), m_loop(NULL), m_lLastCheckNodeTime(0), + : m_pErrBuff(NULL), m_uiUpstreamConnectionPoolSize(100), m_pLabor(pLabor), m_loop(NULL), m_lLastCheckNodeTime(0), + m_pPollingWatcher(nullptr), m_pLogger(pLogger), m_pSessionNode(nullptr) { m_pErrBuff = (char*)malloc(gc_iErrBuffLen); @@ -114,6 +117,84 @@ void Dispatcher::AsyncCallback(struct ev_loop* loop, struct ev_async* watcher, i } } +void Dispatcher::IdleCallback(struct ev_loop* loop, struct ev_idle* watcher, int revents) +{ +} + +void Dispatcher::PollingCallback(struct ev_loop* loop, ev_timer* watcher, int revents) +{ + if (watcher->data != NULL) + { + auto pWatcher = static_cast(watcher->data); + auto pDispatcher = pWatcher->GetDispatcher(); + uint32 uiCodecType = 0; + uint32 uiFromLabor = 0; + uint32 uiOwnerLabor = pWatcher->GetLaborId(); + struct timeval timeval; + long lBeginTime = 0; + long lEndTime = 0; + ev_tstamp dTimeout = 0.0; + gettimeofday(&timeval, NULL); + lBeginTime = timeval.tv_sec * 1000000 + timeval.tv_usec; + auto pChannel = LaborShared::Instance()->PollSpecChannel(pDispatcher, uiOwnerLabor, uiFromLabor, uiCodecType); + LOG4_TRACE_DISPATCH("uiCodecType = %u, uiFromLabor = %u, uiOwnerLabor = %u", uiCodecType, uiFromLabor, uiOwnerLabor); + while (pChannel) + { + CodecFactory::OnEvent(uiCodecType, pChannel, pDispatcher); + ++uiFromLabor; + pChannel = LaborShared::Instance()->PollSpecChannel(pDispatcher, uiOwnerLabor, uiFromLabor, uiCodecType); + LOG4_TRACE_DISPATCH("uiCodecType = %u, uiFromLabor = %u, uiOwnerLabor = %u", uiCodecType, uiFromLabor, uiOwnerLabor); + } + gettimeofday(&timeval, NULL); + lEndTime = timeval.tv_sec * 1000000 + timeval.tv_usec; + dTimeout = 0.001 - (double(lEndTime - lBeginTime) / 1000000); + dTimeout = (dTimeout <= 0) ? 0.001 : dTimeout; + pDispatcher->RefreshEvent(watcher, dTimeout); + } +} + +void Dispatcher::PrepareCallback(struct ev_loop* loop, struct ev_prepare* watcher, int revents) +{ + if (watcher->data != NULL) + { + auto pWatcher = static_cast(watcher->data); + auto pDispatcher = pWatcher->GetDispatcher(); + uint32 uiCodecType = 0; + uint32 uiFromLabor = 0; + uint32 uiOwnerLabor = pWatcher->GetLaborId(); + auto pChannel = LaborShared::Instance()->PollSpecChannel(pDispatcher, uiOwnerLabor, uiFromLabor, uiCodecType); + LOG4_TRACE_DISPATCH("uiCodecType = %u, uiFromLabor = %u, uiOwnerLabor = %u", uiCodecType, uiFromLabor, uiOwnerLabor); + while (pChannel) + { + CodecFactory::OnEvent(uiCodecType, pChannel, pDispatcher); + ++uiFromLabor; + pChannel = LaborShared::Instance()->PollSpecChannel(pDispatcher, uiOwnerLabor, uiFromLabor, uiCodecType); + LOG4_TRACE_DISPATCH("uiCodecType = %u, uiFromLabor = %u, uiOwnerLabor = %u", uiCodecType, uiFromLabor, uiOwnerLabor); + } + } +} + +void Dispatcher::CheckCallback(struct ev_loop* loop, struct ev_check* watcher, int revents) +{ + if (watcher->data != NULL) + { + auto pWatcher = static_cast(watcher->data); + auto pDispatcher = pWatcher->GetDispatcher(); + uint32 uiCodecType = 0; + uint32 uiFromLabor = 0; + uint32 uiOwnerLabor = pWatcher->GetLaborId(); + auto pChannel = LaborShared::Instance()->PollSpecChannel(pDispatcher, uiOwnerLabor, uiFromLabor, uiCodecType); + LOG4_TRACE_DISPATCH("uiCodecType = %u, uiFromLabor = %u, uiOwnerLabor = %u", uiCodecType, uiFromLabor, uiOwnerLabor); + while (pChannel) + { + CodecFactory::OnEvent(uiCodecType, pChannel, pDispatcher); + ++uiFromLabor; + pChannel = LaborShared::Instance()->PollSpecChannel(pDispatcher, uiOwnerLabor, uiFromLabor, uiCodecType); + LOG4_TRACE_DISPATCH("uiCodecType = %u, uiFromLabor = %u, uiOwnerLabor = %u", uiCodecType, uiFromLabor, uiOwnerLabor); + } + } +} + void Dispatcher::ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents) { if (watcher->data != NULL) @@ -204,6 +285,17 @@ bool Dispatcher::DataRecvAndHandle(std::shared_ptr pChannel) } } +bool Dispatcher::ReactSend(std::shared_ptr pChannel) +{ + LOG4_TRACE(""); + auto eStatus = pChannel->Send(); + if (CODEC_STATUS_PAUSE == eStatus || CODEC_STATUS_WANT_WRITE == eStatus) + { + AddIoWriteEvent(pChannel); + } + return(true); +} + bool Dispatcher::MigrateChannelRecvAndHandle(std::shared_ptr pChannel) { LOG4_TRACE("codec type %d", pChannel->GetCodecType()); @@ -360,6 +452,9 @@ bool Dispatcher::OnClientConnFrequencyTimeout(tagClientConnWatcherData* pData, e void Dispatcher::EventRun() { + // AddEvent(MutablePollingWatcher()->MutablePrepareWatcher(), Dispatcher::PrepareCallback); + // AddEvent(MutablePollingWatcher()->MutableCheckWatcher(), Dispatcher::CheckCallback); + // AddEvent(MutablePollingWatcher()->MutableTimerWatcher(), Dispatcher::PollingCallback, 0.001); ev_run (m_loop, 0); } @@ -528,7 +623,7 @@ bool Dispatcher::Disconnect(const std::string& strIdentify, bool bChannelNotice) auto named_iter = m_mapNamedSocketChannel.find(strIdentify); if (named_iter != m_mapNamedSocketChannel.end()) { - std::unordered_set>::iterator channel_iter; + std::list>::iterator channel_iter; while (named_iter->second.size() > 1) { channel_iter = named_iter->second.begin(); @@ -571,18 +666,76 @@ bool Dispatcher::AddNamedSocketChannel(const std::string& strIdentify, std::shar auto named_iter = m_mapNamedSocketChannel.find(strIdentify); if (named_iter == m_mapNamedSocketChannel.end()) { - std::unordered_set> setChannel; - setChannel.insert(pChannel); - m_mapNamedSocketChannel.insert(std::make_pair(strIdentify, std::move(setChannel))); + std::list> listChannel; + listChannel.push_back(pChannel); + m_mapNamedSocketChannel.insert(std::make_pair(strIdentify, std::move(listChannel))); } else { - named_iter->second.insert(pChannel); + named_iter->second.push_back(pChannel); } pChannel->SetIdentify(strIdentify); return(true); } +bool Dispatcher::AdjustNamedSocketChannel(std::shared_ptr pChannel) +{ + auto named_iter = m_mapNamedSocketChannel.find(pChannel->GetIdentify()); + if (named_iter == m_mapNamedSocketChannel.end()) + { + std::list> listChannel; + listChannel.push_front(pChannel); + m_mapNamedSocketChannel.insert(std::make_pair(pChannel->GetIdentify(), std::move(listChannel))); + } + else + { + if (!pChannel->IsPipeline() && pChannel->PipelineIsEmpty()) + { + for (auto it = named_iter->second.begin(); it != named_iter->second.end(); ++it) + { + if ((*it)->GetSequence() == pChannel->GetSequence()) + { + named_iter->second.erase(it); + break; + } + } + named_iter->second.push_front(pChannel); + } + } + return(true); +} + +std::shared_ptr Dispatcher::ApplyNamedSocketChannel(const std::string& strIdentify, uint32& uiPoolSize) +{ + auto named_iter = m_mapNamedSocketChannel.find(strIdentify); + if (named_iter == m_mapNamedSocketChannel.end()) + { + uiPoolSize = 0; + return(nullptr); + } + else + { + for (auto it = named_iter->second.begin(); it != named_iter->second.end(); ++it) + { + if ((*it)->IsPipeline()) + { + uiPoolSize = named_iter->second.size(); + return(*it); + } + else if ((*it)->PipelineIsEmpty()) + { + auto pChannel = *it; + named_iter->second.erase(it); + named_iter->second.push_back(pChannel); + uiPoolSize = named_iter->second.size(); + return(pChannel); + } + } + uiPoolSize = named_iter->second.size(); + return(nullptr); + } +} + void Dispatcher::DelNamedSocketChannel(const std::string& strIdentify) { auto named_iter = m_mapNamedSocketChannel.find(strIdentify); @@ -718,6 +871,28 @@ bool Dispatcher::AddEvent(ev_async* async_watcher, async_callback pFunc) return(true); } +bool Dispatcher::AddEvent(ev_prepare* prepare_watcher, prepare_callback pFunc) +{ + if (NULL == prepare_watcher) + { + return(false); + } + ev_prepare_init(prepare_watcher, pFunc); + ev_prepare_start(m_loop, prepare_watcher); + return(true); +} + +bool Dispatcher::AddEvent(ev_check* check_watcher, check_callback pFunc) +{ + if (NULL == check_watcher) + { + return(false); + } + ev_check_init(check_watcher, pFunc); + ev_check_start(m_loop, check_watcher); + return(true); +} + bool Dispatcher::RefreshEvent(ev_timer* timer_watcher, ev_tstamp dTimeout) { if (NULL == timer_watcher) @@ -856,7 +1031,7 @@ int32 Dispatcher::GetConnectionNum() const return((int32)m_mapSocketChannel.size()); } -bool Dispatcher::Init() +bool Dispatcher::Init(uint32 uiUpstreamConnectionPoolSize, uint32 uiMaxSendBuffSize, uint32 uiMaxRecvBuffSize) { #if __cplusplus >= 201401L m_pSessionNode = std::make_unique(); @@ -866,6 +1041,11 @@ bool Dispatcher::Init() SetChannelPingStep(CODEC_PROTO, "neb::StepNebulaChannelPing"); SetChannelPingStep(CODEC_NEBULA, "neb::StepNebulaChannelPing"); SetChannelPingStep(CODEC_RESP, "neb::StepRedisChannelPing"); + m_uiUpstreamConnectionPoolSize = uiUpstreamConnectionPoolSize; + ChannelOption stOption; + stOption.uiMaxSendBuffSize = uiMaxSendBuffSize; + stOption.uiMaxRecvBuffSize = uiMaxRecvBuffSize; + m_pSessionNode->SetDefaultChannelOption(stOption); return(true); } @@ -878,6 +1058,11 @@ void Dispatcher::Destroy() { m_mapSocketChannel.clear(); m_mapNamedSocketChannel.clear(); + if (m_pPollingWatcher != nullptr) + { + delete m_pPollingWatcher; + m_pPollingWatcher = nullptr; + } if (m_loop != NULL) { ev_loop_destroy(m_loop); @@ -1299,6 +1484,7 @@ bool Dispatcher::AcceptFdAndTransfer(int iFd, int iFamily, int iBonding) else { LOG4_ERROR("transfer fd %d to worker %d error %d", iAcceptFd, iWorkerId, iResult); + close(iAcceptFd); return(false); } } @@ -1401,5 +1587,27 @@ void Dispatcher::EvBreak() ev_break (m_loop, EVBREAK_ALL); } +PollingWatcher* Dispatcher::MutablePollingWatcher() +{ + if (m_pPollingWatcher == nullptr) + { + uint32 uiLaborId = 0; + if (Labor::LABOR_MANAGER == m_pLabor->GetLaborType()) + { + uiLaborId = LaborShared::Instance()->GetManagerLaborId(); + } + else if (Labor::LABOR_LOADER == m_pLabor->GetLaborType()) + { + uiLaborId = 0; + } + else + { + uiLaborId = ((Worker*)m_pLabor)->GetWorkerInfo().iWorkerIndex; + } + m_pPollingWatcher = new PollingWatcher(this, uiLaborId); + } + return(m_pPollingWatcher); +} + } diff --git a/src/ios/Dispatcher.hpp b/src/ios/Dispatcher.hpp old mode 100644 new mode 100755 index 6f7b3268..d0a20cfc --- a/src/ios/Dispatcher.hpp +++ b/src/ios/Dispatcher.hpp @@ -42,7 +42,7 @@ extern "C" { #include #include -#include +#include #include #include @@ -65,6 +65,7 @@ class LoadStress; // not in Nebula project class Actor; class ActorBuilder; class CmdFdTransfer; +class PollingWatcher; struct tagClientConnWatcherData; template class IO; @@ -72,6 +73,8 @@ typedef void (*signal_callback)(struct ev_loop*,ev_signal*,int); typedef void (*timer_callback)(struct ev_loop*,ev_timer*,int); typedef void (*idle_callback)(struct ev_loop*,ev_idle*,int); typedef void (*async_callback)(struct ev_loop*,ev_async*,int); +typedef void (*prepare_callback)(struct ev_loop*, ev_prepare*, int); +typedef void (*check_callback)(struct ev_loop*, ev_check*, int); enum E_DISPATCHER { @@ -101,7 +104,7 @@ class Dispatcher Dispatcher(Labor* pLabor, std::shared_ptr pLogger); virtual ~Dispatcher(); - bool Init(); + bool Init(uint32 uiUpstreamConnectionPoolSize, uint32 uiMaxSendBuffSize, uint32 uiMaxRecvBuffSize); public: static void IoCallback(struct ev_loop* loop, struct ev_io* watcher, int revents); @@ -109,6 +112,10 @@ class Dispatcher static void PeriodicTaskCallback(struct ev_loop* loop, ev_timer* watcher, int revents); static void SignalCallback(struct ev_loop* loop, struct ev_signal* watcher, int revents); static void AsyncCallback(struct ev_loop* loop, struct ev_async* watcher, int revents); + static void IdleCallback(struct ev_loop* loop, struct ev_idle* watcher, int revents); + static void PollingCallback(struct ev_loop* loop, struct ev_timer* watcher, int revents); + static void PrepareCallback(struct ev_loop* loop, struct ev_prepare* watcher, int revents); + static void CheckCallback(struct ev_loop* loop, struct ev_check* watcher, int revents); static void ClientConnFrequencyTimeoutCallback(struct ev_loop* loop, ev_timer* watcher, int revents); bool OnIoRead(std::shared_ptr pChannel); @@ -117,6 +124,7 @@ class Dispatcher bool OnIoTimeout(std::shared_ptr pChannel); bool OnClientConnFrequencyTimeout(tagClientConnWatcherData* pData, ev_timer* watcher); bool DataRecvAndHandle(std::shared_ptr pChannel); + bool ReactSend(std::shared_ptr pChannel); bool MigrateChannelRecvAndHandle(std::shared_ptr pChannel); template @@ -137,6 +145,8 @@ class Dispatcher public: void SetChannelIdentify(std::shared_ptr pChannel, const std::string& strIdentify); bool AddNamedSocketChannel(const std::string& strIdentify, std::shared_ptr pChannel); + bool AdjustNamedSocketChannel(std::shared_ptr pChannel); + std::shared_ptr ApplyNamedSocketChannel(const std::string& strIdentify, uint32& uiPoolSize); void DelNamedSocketChannel(const std::string& strIdentify); void AddNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); void DelNodeIdentify(const std::string& strNodeType, const std::string& strIdentify); @@ -149,6 +159,10 @@ class Dispatcher std::shared_ptr GetChannelOption(const std::string& strIdentify); void SetChannelOption(const std::string& strIdentify, const ChannelOption& stOption); + uint32 GetUpstreamConnectionPoolSize() const + { + return(m_uiUpstreamConnectionPoolSize); + } time_t GetNowTime() const { return((time_t)ev_now(m_loop)); @@ -185,6 +199,8 @@ class Dispatcher bool AddEvent(ev_timer* timer_watcher, timer_callback pFunc, ev_tstamp dTimeout); bool AddEvent(ev_idle* idle_watcher, idle_callback pFunc); bool AddEvent(ev_async* async_watcher, async_callback pFunc); + bool AddEvent(ev_prepare* prepare_watcher, prepare_callback pFunc); + bool AddEvent(ev_check* check_watcher, check_callback pFunc); bool RefreshEvent(ev_timer* timer_watcher, ev_tstamp dTimeout); bool DelEvent(ev_io* io_watcher); bool DelEvent(ev_timer* timer_watcher); @@ -196,12 +212,15 @@ class Dispatcher bool PingChannel(std::shared_ptr pChannel); void CheckFailedNode(); void EvBreak(); + PollingWatcher* MutablePollingWatcher(); private: char* m_pErrBuff; + uint32 m_uiUpstreamConnectionPoolSize; Labor* m_pLabor; struct ev_loop* m_loop; time_t m_lLastCheckNodeTime; + PollingWatcher* m_pPollingWatcher; std::shared_ptr m_pLogger; std::unique_ptr m_pSessionNode; std::shared_ptr m_pLastActivityChannel; // 最近一个发送或接收过数据的channel @@ -210,7 +229,7 @@ class Dispatcher std::unordered_map > m_mapSocketChannel; /* named Channel */ - std::unordered_map > > m_mapNamedSocketChannel; ///< key为Identify,连接存在时,if(http连接)set.size()>=1;else set.size()==1; + std::unordered_map > > m_mapNamedSocketChannel; ///< key为Identify,连接存在时,if(http连接)set.size()>=1;else set.size()==1; std::unordered_map m_mapChannelPingStepName; // CODEC_TYPE as key std::unordered_map m_mapClientConnFrequency; ///< 客户端连接频率 diff --git a/src/ios/IO.hpp b/src/ios/IO.hpp old mode 100644 new mode 100755 index 71cf76d9..aa2e52dc --- a/src/ios/IO.hpp +++ b/src/ios/IO.hpp @@ -216,19 +216,41 @@ bool IO::Send(std::shared_ptr pChannel) { return(false); } + auto pDispatcher = pChannel->GetLabor()->GetDispatcher(); + E_CODEC_STATUS eStatus = CODEC_STATUS_OK; if (pChannel->WithSsl()) { #ifdef WITH_OPENSSL - std::static_pointer_cast>(pChannel->m_pImpl)->Send(); + eStatus = std::static_pointer_cast>(pChannel->m_pImpl)->Send(); #else - std::static_pointer_cast>(pChannel->m_pImpl)->Send(); + eStatus = std::static_pointer_cast>(pChannel->m_pImpl)->Send(); #endif } else { - std::static_pointer_cast>(pChannel->m_pImpl)->Send(); + eStatus = std::static_pointer_cast>(pChannel->m_pImpl)->Send(); + } + switch (eStatus) + { + case CODEC_STATUS_OK: + return(true); + case CODEC_STATUS_PAUSE: + case CODEC_STATUS_WANT_WRITE: + case CODEC_STATUS_PART_OK: + pDispatcher->AddIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_WANT_READ: + pDispatcher->RemoveIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_EOF: + LOG4_INFO_DISPATCH("eStatus = %d, %s", eStatus, pChannel->GetIdentify().c_str()); + pDispatcher->DiscardSocketChannel(pChannel); + return(true); + default: + LOG4_INFO_DISPATCH("eStatus = %d, %s", eStatus, pChannel->GetIdentify().c_str()); + pDispatcher->DiscardSocketChannel(pChannel); + return(false); } - return(true); } template @@ -478,27 +500,20 @@ std::shared_ptr IO::ApplySocketChannel(Actor* pActor, const Ch return(nullptr); } auto pDispatcher = pActor->m_pLabor->GetDispatcher(); - auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + uint32 uiConnectionPoolSize = 0; + auto pChannel = pDispatcher->ApplyNamedSocketChannel(strIdentify, uiConnectionPoolSize); + if (pChannel != nullptr) + { + return(pChannel); + } + if (uiConnectionPoolSize < pDispatcher->GetUpstreamConnectionPoolSize()) { return(NewSocketChannel(pDispatcher, stOption, strIdentify, strHost, iPort, iRemoteWorkerIndex)); } else { - if (named_iter->second.empty()) - { - return(NewSocketChannel(pDispatcher, stOption, strIdentify, strHost, iPort, iRemoteWorkerIndex)); - } - else - { - auto channel_iter = named_iter->second.begin(); - auto pChannel = *channel_iter; - if (!pChannel->IsPipeline()) - { - named_iter->second.erase(channel_iter); - } - return(pChannel); - } + LOG4_ERROR_ACTOR("connection pool size of \"%s\" overload.", strIdentify.c_str()); + return(nullptr); } } } @@ -515,27 +530,20 @@ std::shared_ptr IO::ApplySocketChannel(Actor* pActor, const Ch std::ostringstream ossIdentify; ossIdentify << strHost << ":" << iPort; auto pDispatcher = pActor->m_pLabor->GetDispatcher(); - auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(ossIdentify.str()); - if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + uint32 uiConnectionPoolSize = 0; + auto pChannel = pDispatcher->ApplyNamedSocketChannel(ossIdentify.str(), uiConnectionPoolSize); + if (pChannel != nullptr) + { + return(pChannel); + } + if (uiConnectionPoolSize < pDispatcher->GetUpstreamConnectionPoolSize()) { return(NewSocketChannel(pDispatcher, stOption, ossIdentify.str(), strHost, iPort, -1)); } else { - if (named_iter->second.empty()) - { - return(NewSocketChannel(pDispatcher, stOption, ossIdentify.str(), strHost, iPort, -1)); - } - else - { - auto channel_iter = named_iter->second.begin(); - auto pChannel = *channel_iter; - if (!pChannel->IsPipeline()) - { - named_iter->second.erase(channel_iter); - } - return(pChannel); - } + LOG4_ERROR_ACTOR("connection pool size of \"%s\" overload.", ossIdentify.str().c_str()); + return(nullptr); } } } @@ -632,30 +640,21 @@ template bool IO::SendWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, Targs&&... args) { LOG4_TRACE_DISPATCH("identify: %s", strIdentify.c_str()); - - auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + uint32 uiConnectionPoolSize = 0; + auto pChannel = pDispatcher->ApplyNamedSocketChannel(strIdentify, uiConnectionPoolSize); + if (pChannel != nullptr) + { + bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); + return(bResult); + } + if (uiConnectionPoolSize < pDispatcher->GetUpstreamConnectionPoolSize()) { - LOG4_TRACE_DISPATCH("no channel match %s.", strIdentify.c_str()); return(AutoSendWithoutOption(pDispatcher, uiStepSeq, strIdentify, std::forward(args)...)); } else { - if (named_iter->second.empty()) - { - return(AutoSendWithoutOption(pDispatcher, uiStepSeq, strIdentify, std::forward(args)...)); - } - else - { - auto channel_iter = named_iter->second.begin(); - auto pChannel = *channel_iter; - bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); - if (!pChannel->IsPipeline() && bResult) - { - named_iter->second.erase(channel_iter); - } - return(bResult); - } + LOG4_ERROR_DISPATCH("connection pool size of \"%s\" overload.", strIdentify.c_str()); + return(false); } } @@ -664,30 +663,21 @@ template bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& strIdentify, const ChannelOption& stOption, Targs&&... args) { LOG4_TRACE_DISPATCH("identify: %s", strIdentify.c_str()); - - auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(strIdentify); - if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + uint32 uiConnectionPoolSize = 0; + auto pChannel = pDispatcher->ApplyNamedSocketChannel(strIdentify, uiConnectionPoolSize); + if (pChannel != nullptr) { - LOG4_TRACE_DISPATCH("no channel match %s.", strIdentify.c_str()); - return(AutoSend(pDispatcher, uiStepSeq, strIdentify, stOption, std::forward(args)...)); + bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); + return(bResult); + } + if (uiConnectionPoolSize < pDispatcher->GetUpstreamConnectionPoolSize()) + { + return(AutoSendWithoutOption(pDispatcher, uiStepSeq, strIdentify, std::forward(args)...)); } else { - if (named_iter->second.empty()) - { - return(AutoSend(pDispatcher, uiStepSeq, strIdentify, stOption, std::forward(args)...)); - } - else - { - auto channel_iter = named_iter->second.begin(); - auto pChannel = *channel_iter; - bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); - if (!pChannel->IsPipeline() && bResult) - { - named_iter->second.erase(channel_iter); - } - return(bResult); - } + LOG4_ERROR_DISPATCH("connection pool size of \"%s\" overload.", strIdentify.c_str()); + return(false); } } @@ -744,26 +734,21 @@ bool IO::SendWithoutOption(Dispatcher* pDispatcher, uint32 uiStepSeq, const s pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "host %s port %d", strHost.c_str(), iPort); std::ostringstream ossIdentify; ossIdentify << strHost << ":" << iPort; - auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(ossIdentify.str()); - if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + uint32 uiConnectionPoolSize = 0; + auto pChannel = pDispatcher->ApplyNamedSocketChannel(ossIdentify.str(), uiConnectionPoolSize); + if (pChannel != nullptr) + { + bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); + return(bResult); + } + if (uiConnectionPoolSize < pDispatcher->GetUpstreamConnectionPoolSize()) { - LOG4_TRACE_DISPATCH("no channel match %s.", ossIdentify.str().c_str()); return(AutoSendWithoutOption(pDispatcher, uiStepSeq, strHost, iPort, std::forward(args)...)); } else { - auto channel_iter = named_iter->second.begin(); - if (channel_iter == named_iter->second.end()) - { - return(AutoSendWithoutOption(pDispatcher, uiStepSeq, strHost, iPort, std::forward(args)...)); - } - auto pChannel = *channel_iter; - bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); - if (!pChannel->IsPipeline() && bResult) - { - named_iter->second.erase(channel_iter); - } - return(bResult); + LOG4_ERROR_DISPATCH("connection pool size of \"%s\" overload.", ossIdentify.str().c_str()); + return(false); } } @@ -774,26 +759,21 @@ bool IO::SendTo(Dispatcher* pDispatcher, uint32 uiStepSeq, const std::string& pDispatcher->Logger(neb::Logger::TRACE, __FILE__, __LINE__, __FUNCTION__, "host %s port %d", strHost.c_str(), iPort); std::ostringstream ossIdentify; ossIdentify << strHost << ":" << iPort; - auto named_iter = pDispatcher->m_mapNamedSocketChannel.find(ossIdentify.str()); - if (named_iter == pDispatcher->m_mapNamedSocketChannel.end()) + uint32 uiConnectionPoolSize = 0; + auto pChannel = pDispatcher->ApplyNamedSocketChannel(ossIdentify.str(), uiConnectionPoolSize); + if (pChannel != nullptr) + { + bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); + return(bResult); + } + if (uiConnectionPoolSize < pDispatcher->GetUpstreamConnectionPoolSize()) { - LOG4_TRACE_DISPATCH("no channel match %s.", ossIdentify.str().c_str()); return(AutoSend(pDispatcher, uiStepSeq, strHost, iPort, stOption, std::forward(args)...)); } else { - auto channel_iter = named_iter->second.begin(); - if (channel_iter == named_iter->second.end()) - { - return(AutoSend(pDispatcher, uiStepSeq, strHost, iPort, stOption, std::forward(args)...)); - } - auto pChannel = *channel_iter; - bool bResult = SendRequest(pDispatcher, uiStepSeq, pChannel, std::forward(args)...); - if (!pChannel->IsPipeline() && bResult) - { - named_iter->second.erase(channel_iter); - } - return(bResult); + LOG4_ERROR_DISPATCH("connection pool size of \"%s\" overload.", ossIdentify.str().c_str()); + return(false); } } @@ -1427,14 +1407,29 @@ bool IO::AutoSend(Dispatcher* pDispatcher, uint32 uiStepSeq, pDispatcher->m_pLabor->IoStatAddSendNum(pChannel->GetFd(), IO_STAT_DOWNSTREAM_SEND_NUM); } pDispatcher->m_pLastActivityChannel = pChannel; - if (CODEC_STATUS_OK != eCodecStatus - && CODEC_STATUS_PAUSE != eCodecStatus - && CODEC_STATUS_WANT_WRITE != eCodecStatus - && CODEC_STATUS_WANT_READ != eCodecStatus) + switch (eCodecStatus) { - pDispatcher->DiscardSocketChannel(pChannel); + case CODEC_STATUS_OK: + return(true); + case CODEC_STATUS_PAUSE: + case CODEC_STATUS_WANT_WRITE: + case CODEC_STATUS_PART_OK: + pDispatcher->AddIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_WANT_READ: + pDispatcher->RemoveIoWriteEvent(pChannel); + return(true); + case CODEC_STATUS_EOF: // a case: http1.0 respone and close + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "eStatus = %d, %s", eCodecStatus, pChannel->GetIdentify().c_str()); + pDispatcher->DiscardSocketChannel(pChannel); + return(true); + default: + pDispatcher->Logger(neb::Logger::INFO, __FILE__, __LINE__, __FUNCTION__, + "eStatus = %d, %s", eCodecStatus, pChannel->GetIdentify().c_str()); + pDispatcher->DiscardSocketChannel(pChannel); + return(false); } - return(true); } } @@ -1496,6 +1491,12 @@ std::shared_ptr IO::NewSocketChannel(Dispatcher* pDispatcher, break; } + if (iFd == -1) + { + char szErrBuff[256] = {0}; + LOG4_ERROR_DISPATCH("error %d: %s", errno, strerror_r(errno, szErrBuff, 256)); + return(nullptr); + } /* No address succeeded */ // if (pAddrCurrent == NULL) @@ -1537,14 +1538,12 @@ std::shared_ptr IO::NewSocketChannel(Dispatcher* pDispatcher, std::static_pointer_cast>(pChannel->m_pImpl)->SetIdentify(strIdentify); std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteAddr(strHost); std::static_pointer_cast>(pChannel->m_pImpl)->SetPipeline(stOption.bPipeline); + std::static_pointer_cast>(pChannel->m_pImpl)->SetMaxBuffSize(stOption.uiMaxSendBuffSize, stOption.uiMaxRecvBuffSize); pDispatcher->m_pLastActivityChannel = pChannel; std::static_pointer_cast>(pChannel->m_pImpl)->SetChannelStatus(CHANNEL_STATUS_TRY_CONNECT); std::static_pointer_cast>(pChannel->m_pImpl)->SetRemoteWorkerIndex(iRemoteWorkerIndex); - if (stOption.bPipeline) - { - pDispatcher->AddNamedSocketChannel(strIdentify, pChannel); - } + pDispatcher->AddNamedSocketChannel(strIdentify, pChannel); return(pChannel); } else // 没有足够资源分配给新连接,直接close掉 @@ -1642,7 +1641,10 @@ E_CODEC_STATUS IO::Recv(Dispatcher* pDispatcher, std::shared_ptrm_pLastActivityChannel = pChannel; } - pDispatcher->OnIoWrite(pChannel); + if (pChannel->GetFd() > 0) + { + pDispatcher->ReactSend(pChannel); + } return(eStatus); } @@ -1676,7 +1678,10 @@ E_CODEC_STATUS IO::Fetch(Dispatcher* pDispatcher, std::shared_ptrm_pLastActivityChannel = pChannel; } - pDispatcher->OnIoWrite(pChannel); + if (pChannel->GetFd() > 0) + { + pDispatcher->ReactSend(pChannel); + } return(eStatus); } @@ -1772,10 +1777,7 @@ bool IO::OnResponse(ActorBuilder* pBuilder, std::shared_ptr pC auto uiStepSeq = pChannel->PopStepSeq(uiStreamId, eCodecStatus); LOG4_TRACE_BUILDER("stream id = %u, step seq = %u", uiStreamId, uiStepSeq); auto step_iter = pBuilder->m_mapCallbackStep.find(uiStepSeq); - if (!pChannel->IsPipeline() && pChannel->PipelineIsEmpty()) - { - pBuilder->m_pLabor->GetDispatcher()->AddNamedSocketChannel(pChannel->GetIdentify(), pChannel); // push back to named socket channel pool. - } + pBuilder->m_pLabor->GetDispatcher()->AdjustNamedSocketChannel(pChannel); if (step_iter == pBuilder->m_mapCallbackStep.end()) { LOG4_TRACE_BUILDER("no callback for reply from %s, stream id %u, step seq %u!", diff --git a/src/ios/Nodes.cpp b/src/ios/Nodes.cpp old mode 100644 new mode 100755 index 4dd396a3..3793f7b0 --- a/src/ios/Nodes.cpp +++ b/src/ios/Nodes.cpp @@ -23,6 +23,7 @@ namespace neb Nodes::Nodes(int iHashAlgorithm, int iVirtualNodeNum) : m_iHashAlgorithm(iHashAlgorithm), m_iVirtualNodeNum(iVirtualNodeNum) { + m_pDefaultChannelOption = std::make_shared(); } Nodes::~Nodes() @@ -444,7 +445,7 @@ std::shared_ptr Nodes::GetChannelOption(const std::string& strIde auto iter = m_mapChannelOption.find(strIdentify); if (iter == m_mapChannelOption.end()) { - return(nullptr); + return(m_pDefaultChannelOption); } else { @@ -466,6 +467,11 @@ void Nodes::SetChannelOption(const std::string& strIdentify, const ChannelOption } } +void Nodes::SetDefaultChannelOption(const ChannelOption& stOption) +{ + m_pDefaultChannelOption = std::make_shared(stOption); +} + void Nodes::DelNode(const std::string& strNodeType, const std::string& strNodeIdentify) { auto node_type_iter = m_mapNode.find(strNodeType); diff --git a/src/ios/Nodes.hpp b/src/ios/Nodes.hpp old mode 100644 new mode 100755 index b429bbe3..2e2adeac --- a/src/ios/Nodes.hpp +++ b/src/ios/Nodes.hpp @@ -120,6 +120,7 @@ class Nodes std::shared_ptr GetChannelOption(const std::string& strIdentify); void SetChannelOption(const std::string& strIdentify, const ChannelOption& stOption); + void SetDefaultChannelOption(const ChannelOption& stOption); /** * @brief 删除节点 @@ -154,7 +155,7 @@ class Nodes private: const int m_iHashAlgorithm; const int m_iVirtualNodeNum; - + std::shared_ptr m_pDefaultChannelOption; std::unordered_map > m_mapNode; std::unordered_map> m_mapNodeType; // key为节点标识 std::unordered_map> m_mapChannelOption; // key为节点标识 diff --git a/src/ios/PollingWatcher.cpp b/src/ios/PollingWatcher.cpp new file mode 100755 index 00000000..a40720ae --- /dev/null +++ b/src/ios/PollingWatcher.cpp @@ -0,0 +1,116 @@ +/******************************************************************************* + * Project: Nebula + * @file PollingWatcher.cpp + * @brief + * @author Bwar + * @date: 2023-08-05 + * @note + * Modify history: + ******************************************************************************/ +#include "PollingWatcher.hpp" + +namespace neb +{ + +PollingWatcher::PollingWatcher() + : m_uiLaborId(0), m_pDispatcher(nullptr), + m_pPrepareWatcher(nullptr), m_pCheckWatcher(nullptr), m_pTimerWatcher(nullptr) +{ +} + +PollingWatcher::PollingWatcher(Dispatcher* pDispatcher, uint32 uiLaborId) + : m_uiLaborId(uiLaborId), m_pDispatcher(pDispatcher), + m_pPrepareWatcher(nullptr), m_pCheckWatcher(nullptr), m_pTimerWatcher(nullptr) +{ +} + +PollingWatcher::~PollingWatcher() +{ + Reset(); +} + +ev_prepare* PollingWatcher::MutablePrepareWatcher() +{ + if (nullptr == m_pDispatcher) + { + return(nullptr); + } + if (nullptr == m_pPrepareWatcher) + { + m_pPrepareWatcher = new ev_prepare(); + if (nullptr != m_pPrepareWatcher) + { + memset(m_pPrepareWatcher, 0, sizeof(ev_prepare)); + m_pPrepareWatcher->data = this; + } + } + return(m_pPrepareWatcher); +} + +ev_check* PollingWatcher::MutableCheckWatcher() +{ + if (nullptr == m_pDispatcher) + { + return(nullptr); + } + if (nullptr == m_pCheckWatcher) + { + m_pCheckWatcher = new ev_check(); + if (nullptr != m_pCheckWatcher) + { + memset(m_pCheckWatcher, 0, sizeof(ev_check)); + m_pCheckWatcher->data = this; + } + } + return(m_pCheckWatcher); +} + +ev_timer* PollingWatcher::MutableTimerWatcher() +{ + if (nullptr == m_pDispatcher) + { + return(nullptr); + } + if (nullptr == m_pTimerWatcher) + { + m_pTimerWatcher = new ev_timer(); + if (nullptr != m_pTimerWatcher) + { + memset(m_pTimerWatcher, 0, sizeof(ev_timer)); + m_pTimerWatcher->data = this; + } + } + return(m_pTimerWatcher); +} + +void PollingWatcher::Set(Dispatcher* pDispatcher, uint32 uiLaborId) +{ + if (m_pDispatcher == nullptr) + { + m_pDispatcher = pDispatcher; + } + m_uiLaborId = uiLaborId; +} + +void PollingWatcher::Reset() +{ + m_pDispatcher = nullptr; + if (nullptr != m_pTimerWatcher) + { + delete m_pTimerWatcher; + m_pTimerWatcher = nullptr; + } + if (nullptr != m_pPrepareWatcher) + { + delete m_pPrepareWatcher; + m_pPrepareWatcher = nullptr; + } + if (nullptr != m_pCheckWatcher) + { + delete m_pCheckWatcher; + m_pCheckWatcher = nullptr; + } +} + +} /* namespace neb */ + diff --git a/src/ios/PollingWatcher.hpp b/src/ios/PollingWatcher.hpp new file mode 100755 index 00000000..eca6047d --- /dev/null +++ b/src/ios/PollingWatcher.hpp @@ -0,0 +1,65 @@ +/******************************************************************************* + * Project: Nebula + * @file PollingWatcher.hpp + * @brief + * @author Bwar + * @date: 2023-08-05 + * @note + * Modify history: + ******************************************************************************/ +#ifndef SRC_IOS_POLLINGWATCHER_HPP_ +#define SRC_IOS_POLLINGWATCHER_HPP_ + +#include +#include "Definition.hpp" + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif +#include "ev.h" +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +namespace neb +{ + +class Dispatcher; + +class PollingWatcher +{ +public: + PollingWatcher(); + PollingWatcher(Dispatcher* pDispatcher, uint32 uiLaborId); + virtual ~PollingWatcher(); + + ev_prepare* MutablePrepareWatcher(); + ev_check* MutableCheckWatcher(); + ev_timer* MutableTimerWatcher(); + + inline Dispatcher* GetDispatcher() const + { + return(m_pDispatcher); + } + + uint32 GetLaborId() const + { + return(m_uiLaborId); + } + + void Set(Dispatcher* pDispatcher, uint32 uiLaborId); + void Reset(); + +private: + uint32 m_uiLaborId; + Dispatcher* m_pDispatcher; + ev_prepare* m_pPrepareWatcher; + ev_check* m_pCheckWatcher; + ev_timer* m_pTimerWatcher; +}; + +} /* namespace neb */ + +#endif /* SRC_IOS_POLLINGWATCHER_HPP_ */ + diff --git a/src/ios/SpecChannelWatcher.cpp b/src/ios/SpecChannelWatcher.cpp old mode 100644 new mode 100755 diff --git a/src/ios/SpecChannelWatcher.hpp b/src/ios/SpecChannelWatcher.hpp old mode 100644 new mode 100755 diff --git a/src/labor/Labor.hpp b/src/labor/Labor.hpp old mode 100644 new mode 100755 diff --git a/src/labor/LaborShared.cpp b/src/labor/LaborShared.cpp old mode 100644 new mode 100755 index a35b54bb..8a8145bf --- a/src/labor/LaborShared.cpp +++ b/src/labor/LaborShared.cpp @@ -20,10 +20,12 @@ LaborShared* LaborShared::s_pInstance = nullptr; std::mutex LaborShared::s_mutex; LaborShared::LaborShared(uint32 uiLaborNum) - : m_uiLaborNum(uiLaborNum), m_uiSpecChannelQueueSize(128), m_uiCodecSize(0) + : m_uiLaborNum(uiLaborNum), m_uiSpecChannelQueueSize(512), m_uiCodecSize(0) { m_vecDispatcher.reserve(uiLaborNum); m_vecSpecChannel.reserve(CODEC_MAX + 1); + m_vecSpecChannelExist.reserve(CODEC_MAX + 1); + m_vecSpecChannelExist.resize(CODEC_MAX + 1, false); } LaborShared::~LaborShared() @@ -88,25 +90,35 @@ int LaborShared::AddSpecChannel(uint32 uiCodecType, uint32 uiFrom, uint32 uiTo, if (uiCodecType < m_vecSpecChannel.size()) { m_vecSpecChannel[uiCodecType][uiFrom][uiTo] = pChannel; + m_vecSpecChannelExist[uiCodecType] = true; } else { std::lock_guard guard(s_mutex); - for (uint32 i = m_vecSpecChannel.size(); i <= uiCodecType; ++i) + if (uiCodecType < m_vecSpecChannel.size()) { - T_VEC_CHANNEL_FROM vecFrom; - vecFrom.reserve(m_uiLaborNum); - for (uint32 j = 0; j < m_uiLaborNum; ++j) + m_vecSpecChannel[uiCodecType][uiFrom][uiTo] = pChannel; + m_vecSpecChannelExist[uiCodecType] = true; + } + else + { + for (uint32 i = m_vecSpecChannel.size(); i <= uiCodecType; ++i) { - auto pTo = std::make_shared(); - T_VEC_CHANNEL_TO vecTo; - vecTo.resize(m_uiLaborNum, nullptr); - vecFrom.emplace_back(std::move(vecTo)); + T_VEC_CHANNEL_FROM vecFrom; + vecFrom.reserve(m_uiLaborNum); + for (uint32 j = 0; j < m_uiLaborNum; ++j) + { + auto pTo = std::make_shared(); + T_VEC_CHANNEL_TO vecTo; + vecTo.resize(m_uiLaborNum, nullptr); + vecFrom.emplace_back(std::move(vecTo)); + } + m_vecSpecChannel.emplace_back(std::move(vecFrom)); } - m_vecSpecChannel.emplace_back(std::move(vecFrom)); + m_vecSpecChannel[uiCodecType][uiFrom][uiTo] = pChannel; + m_vecSpecChannelExist[uiCodecType] = true; + m_uiCodecSize.store(m_vecSpecChannel.size(), std::memory_order_release); } - m_vecSpecChannel[uiCodecType][uiFrom][uiTo] = pChannel; - m_uiCodecSize.store(m_vecSpecChannel.size(), std::memory_order_release); } // notice spec channel created @@ -179,6 +191,7 @@ std::shared_ptr> LaborShared::CreateInternalSpecCh if (uiCodecType < m_vecSpecChannel.size()) { m_vecSpecChannel[uiCodecType][uiFrom][uiTo] = pChannel; + m_vecSpecChannelExist[uiCodecType] = true; } else { @@ -196,11 +209,46 @@ std::shared_ptr> LaborShared::CreateInternalSpecCh m_vecSpecChannel.emplace_back(std::move(vecFrom)); } m_vecSpecChannel[uiCodecType][uiFrom][uiTo] = pChannel; + m_vecSpecChannelExist[uiCodecType] = true; m_uiCodecSize.store(m_vecSpecChannel.size(), std::memory_order_release); } return(pSpecChannel); } +std::shared_ptr LaborShared::PollSpecChannel(Dispatcher* pDispatcher, uint32 uiOwnerId, uint32& uiFromLaborId, uint32& uiCodecType) +{ + uint32 uiCodecLoop = 0; + for (uint32 i = uiCodecType; i < m_vecSpecChannel.size(); ++i) + { + if (m_vecSpecChannelExist[i] == false) + { + continue; + } + auto& vecFrom = m_vecSpecChannel[i]; + uint32 j = uiFromLaborId; + if (uiCodecLoop > 0) + { + j = 0; + } + for (; j < vecFrom.size(); ++j) + { + auto& vecTo = vecFrom[j]; + if (uiOwnerId < vecTo.size()) + { + if (vecTo[uiOwnerId] != nullptr) + { + uiCodecType = i; + uiFromLaborId = j; + return(vecTo[uiOwnerId]); + } + } + } + ++uiCodecLoop; + } + uiCodecType = CODEC_UNKNOW; + return(nullptr); +} + void LaborShared::AddWorkerThreadId(uint64 ullThreadId) { std::lock_guard guard(s_mutex); diff --git a/src/labor/LaborShared.hpp b/src/labor/LaborShared.hpp old mode 100644 new mode 100755 index 06d152dc..1dc35aac --- a/src/labor/LaborShared.hpp +++ b/src/labor/LaborShared.hpp @@ -51,6 +51,14 @@ class LaborShared int AddSpecChannel(uint32 uiCodecType, uint32 uiFrom, uint32 uiTo, std::shared_ptr pChannel); std::shared_ptr> CreateInternalSpecChannel(uint32 uiFrom, uint32 uiTo); + /** + * @brief 轮询SpecChannel + * @param[in] uiOwnerId 接收LaborId + * @param[in,out] uiFromLaborId 发送LaborId + * @param[in,out] uiCodecType 编解码类型 + */ + std::shared_ptr PollSpecChannel(Dispatcher* pDispatcher, uint32 uiOwnerId, uint32& uiFromLaborId, uint32& uiCodecType); + uint32 GetLaborNum() const { return(m_uiLaborNum); @@ -88,6 +96,7 @@ class LaborShared std::atomic m_uiCodecSize; std::vector m_vecDispatcher; T_VECCHANNEL_CODEC_TYPE m_vecSpecChannel; + std::vector m_vecSpecChannelExist; std::vector m_vecWorkerThreadId; }; diff --git a/src/labor/Loader.cpp b/src/labor/Loader.cpp old mode 100644 new mode 100755 diff --git a/src/labor/Loader.hpp b/src/labor/Loader.hpp old mode 100644 new mode 100755 diff --git a/src/labor/Manager.cpp b/src/labor/Manager.cpp old mode 100644 new mode 100755 index eae3179d..1dac355c --- a/src/labor/Manager.cpp +++ b/src/labor/Manager.cpp @@ -169,7 +169,8 @@ bool Manager::InitDispatcher() return(false); } LaborShared::Instance(m_stNodeInfo.uiWorkerNum + 2)->AddDispatcher(m_stNodeInfo.uiWorkerNum + 1, m_pDispatcher); - return(m_pDispatcher->Init()); + return(m_pDispatcher->Init(m_stNodeInfo.uiUpstreamConnectionPoolSize, + m_stNodeInfo.uiMaxChannelSendBuffSize, m_stNodeInfo.uiMaxChannelRecvBuffSize)); } bool Manager::InitActorBuilder() @@ -191,11 +192,8 @@ bool Manager::InitActorBuilder() LOG4_ERROR("ActorBuilder->Init() failed!"); return(false); } - if (m_stNodeInfo.bThreadMode) - { - m_pActorBuilder->LoadCmd(m_oCurrentConf["load_config"]["loader"]["dynamic_loading"]); - m_pActorBuilder->LoadCmd(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"]); - } + m_pActorBuilder->LoadCmd(m_oCurrentConf["load_config"]["loader"]["dynamic_loading"]); + m_pActorBuilder->LoadCmd(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"]); return(true); } @@ -383,6 +381,9 @@ bool Manager::GetConf() m_oCurrentConf.Get("connection_protection", m_stNodeInfo.dConnectionProtection); m_oCurrentConf.Get("io_timeout", m_stNodeInfo.dIoTimeout); m_oCurrentConf.Get("data_report", m_stNodeInfo.dDataReportInterval); + m_oCurrentConf.Get("upstream_connection_pool_size", m_stNodeInfo.uiUpstreamConnectionPoolSize); + m_oCurrentConf.Get("max_channel_send_buffer_size", m_stNodeInfo.uiMaxChannelSendBuffSize); + m_oCurrentConf.Get("max_channel_recv_buffer_size", m_stNodeInfo.uiMaxChannelRecvBuffSize); if (m_oLastConf.ToString().length() == 0) { std::string strSocketType = "TCP"; @@ -420,14 +421,10 @@ bool Manager::GetConf() bool Manager::Init() { - m_oCurrentConf.Get("thread_mode", m_stNodeInfo.bThreadMode); - if (m_stNodeInfo.bThreadMode) - { - uint32 uiSpecChannelQueueSize = 128; - m_oCurrentConf.Get("spec_channel_queue_size", uiSpecChannelQueueSize); - LaborShared::Instance(m_stNodeInfo.uiWorkerNum + 2)->SetSpecChannelQueueSize(uiSpecChannelQueueSize); - m_oCurrentConf.Get("async_logger", m_stNodeInfo.bAsyncLogger); - } + uint32 uiSpecChannelQueueSize = 128; + m_oCurrentConf.Get("spec_channel_queue_size", uiSpecChannelQueueSize); + LaborShared::Instance(m_stNodeInfo.uiWorkerNum + 2)->SetSpecChannelQueueSize(uiSpecChannelQueueSize); + m_oCurrentConf.Get("async_logger", m_stNodeInfo.bAsyncLogger); if (!InitLogger(m_oCurrentConf) || !InitDispatcher() || !InitActorBuilder()) { return(false); @@ -580,10 +577,7 @@ void Manager::RefreshServer() { MsgBody oMsgBody; oMsgBody.set_data(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"].ToString()); - if (m_stNodeInfo.bThreadMode) - { - m_pActorBuilder->DynamicLoad(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"]); - } + m_pActorBuilder->DynamicLoad(m_oCurrentConf["load_config"]["worker"]["dynamic_loading"]); m_pSessionManager->SendToWorker(CMD_REQ_RELOAD_SO, GetSequence(), oMsgBody); } if (m_oLastConf["load_config"]["loader"]["dynamic_loading"].ToString() @@ -591,10 +585,7 @@ void Manager::RefreshServer() { MsgBody oMsgBody; oMsgBody.set_data(m_oCurrentConf["load_config"]["loader"]["dynamic_loading"].ToString()); - if (m_stNodeInfo.bThreadMode) - { - m_pActorBuilder->DynamicLoad(m_oCurrentConf["load_config"]["loader"]["dynamic_loading"]); - } + m_pActorBuilder->DynamicLoad(m_oCurrentConf["load_config"]["loader"]["dynamic_loading"]); m_pSessionManager->SendToLoader(CMD_REQ_RELOAD_SO, GetSequence(), oMsgBody); } } diff --git a/src/labor/Manager.hpp b/src/labor/Manager.hpp old mode 100644 new mode 100755 diff --git a/src/labor/NodeInfo.hpp b/src/labor/NodeInfo.hpp old mode 100644 new mode 100755 index 4ad4c860..5fe9fda7 --- a/src/labor/NodeInfo.hpp +++ b/src/labor/NodeInfo.hpp @@ -34,7 +34,9 @@ struct NodeInfo int32 iGatewayPort = 0; ///< 对Client服务的真实端口 int32 iBacklog = 100; ///< 监听队列长度 int32 iConnectionDispatch = 0; ///< 新建连接分发方式 - bool bThreadMode = false; ///< 是否线程模型 + uint32 uiUpstreamConnectionPoolSize = 20; ///< 客户端连接池大小 + uint32 uiMaxChannelSendBuffSize = 10485760; ///< Nebula最大发送缓冲区 + uint32 uiMaxChannelRecvBuffSize = 10485760; ///< Nebula最大接收缓冲区 bool bAsyncLogger = false; ///< 是否启用异步文件日志 bool bIsAccess = false; ///< 是否接入Server bool bChannelVerify = false; ///< 是否需要连接验证 diff --git a/src/labor/Worker.cpp b/src/labor/Worker.cpp old mode 100644 new mode 100755 index 52801137..b55e9fa0 --- a/src/labor/Worker.cpp +++ b/src/labor/Worker.cpp @@ -48,13 +48,10 @@ Worker::~Worker() void Worker::Run() { LOG4_TRACE("%s:%d", __FILE__, __LINE__); - if (m_stNodeInfo.bThreadMode) - { - char szThreadName[64] = {0}; - snprintf(szThreadName, sizeof(szThreadName), "%s_W%d", m_oNodeConf("server_name").c_str(), m_stWorkerInfo.iWorkerIndex); - pthread_setname_np(pthread_self(), szThreadName); - LaborShared::Instance()->AddWorkerThreadId(gettid()); - } + char szThreadName[64] = {0}; + snprintf(szThreadName, sizeof(szThreadName), "%s_W%d", m_oNodeConf("server_name").c_str(), m_stWorkerInfo.iWorkerIndex); + pthread_setname_np(pthread_self(), szThreadName); + LaborShared::Instance()->AddWorkerThreadId(gettid()); if (!CreateEvents()) { @@ -65,7 +62,7 @@ void Worker::Run() #ifndef __CYGWIN__ bool bCpuAffinity = false; m_oNodeConf.Get("cpu_affinity", bCpuAffinity); - if (bCpuAffinity && m_stNodeInfo.bThreadMode) + if (bCpuAffinity) { int iCpuNum = sysconf(_SC_NPROCESSORS_CONF); cpu_set_t stCpuMask; @@ -206,15 +203,7 @@ bool Worker::Init(CJsonObject& oJsonConf) { char szProcessName[64] = {0}; snprintf(szProcessName, sizeof(szProcessName), "%s_W%d", oJsonConf("server_name").c_str(), m_stWorkerInfo.iWorkerIndex); - oJsonConf.Get("thread_mode", m_stNodeInfo.bThreadMode); - if (m_stNodeInfo.bThreadMode) - { - oJsonConf.Get("async_logger", m_stNodeInfo.bAsyncLogger); - } - else - { - ngx_setproctitle(szProcessName); - } + oJsonConf.Get("async_logger", m_stNodeInfo.bAsyncLogger); oJsonConf.Get("io_timeout", m_stNodeInfo.dIoTimeout); if (!oJsonConf.Get("step_timeout", m_stNodeInfo.dStepTimeout)) { @@ -230,6 +219,9 @@ bool Worker::Init(CJsonObject& oJsonConf) oJsonConf.Get("need_channel_verify", m_stNodeInfo.bChannelVerify); oJsonConf.Get("gateway", m_stNodeInfo.strGateway); oJsonConf.Get("gateway_port", m_stNodeInfo.iGatewayPort); + oJsonConf.Get("upstream_connection_pool_size", m_stNodeInfo.uiUpstreamConnectionPoolSize); + oJsonConf.Get("max_channel_send_buffer_size", m_stNodeInfo.uiMaxChannelSendBuffSize); + oJsonConf.Get("max_channel_recv_buffer_size", m_stNodeInfo.uiMaxChannelRecvBuffSize); m_oNodeConf = oJsonConf; m_oCustomConf = oJsonConf["custom"]; std::ostringstream oss; @@ -249,20 +241,6 @@ bool Worker::Init(CJsonObject& oJsonConf) bool bCpuAffinity = false; oJsonConf.Get("cpu_affinity", bCpuAffinity); - if (bCpuAffinity && !m_stNodeInfo.bThreadMode) - { -#ifndef __CYGWIN__ - /* get logical cpu number */ - int iCpuNum = sysconf(_SC_NPROCESSORS_CONF);; ///< cpu数量 - cpu_set_t stCpuMask; ///< cpu set - CPU_ZERO(&stCpuMask); - CPU_SET(m_stWorkerInfo.iWorkerIndex % iCpuNum, &stCpuMask); - if (sched_setaffinity(0, sizeof(cpu_set_t), &stCpuMask) == -1) - { - LOG4_WARNING("sched_setaffinity failed."); - } -#endif - } if (oJsonConf["with_ssl"]("config_path").length() > 0) { @@ -387,7 +365,8 @@ bool Worker::InitDispatcher() { if (NewDispatcher()) { - return(m_pDispatcher->Init()); + return(m_pDispatcher->Init(m_stNodeInfo.uiUpstreamConnectionPoolSize, + m_stNodeInfo.uiMaxChannelSendBuffSize, m_stNodeInfo.uiMaxChannelRecvBuffSize)); } return(false); } @@ -432,14 +411,6 @@ bool Worker::NewActorBuilder() bool Worker::CreateEvents() { - if (!m_stNodeInfo.bThreadMode) - { - signal(SIGPIPE, SIG_IGN); - // 注册信号事件 - ev_signal* signal_watcher = (ev_signal*)malloc(sizeof(ev_signal)); - signal_watcher->data = (void*)this; - m_pDispatcher->AddEvent(signal_watcher, Dispatcher::SignalCallback, SIGINT); - } AddPeriodicTaskEvent(); return(true); } diff --git a/src/labor/Worker.hpp b/src/labor/Worker.hpp old mode 100644 new mode 100755 diff --git a/src/logger/AsyncLogger.cpp b/src/logger/AsyncLogger.cpp old mode 100644 new mode 100755 diff --git a/src/logger/AsyncLogger.hpp b/src/logger/AsyncLogger.hpp old mode 100644 new mode 100755 diff --git a/src/logger/FileLogger.cpp b/src/logger/FileLogger.cpp old mode 100644 new mode 100755 index 008cd8ad..fde92f6c --- a/src/logger/FileLogger.cpp +++ b/src/logger/FileLogger.cpp @@ -193,13 +193,13 @@ void FileLogger::AppendLogPattern(int iLev, const char* szFileName, unsigned int } m_strLogLine.clear(); m_strLogLine.append(m_strLogFormatTime); - m_strLogLine.append(std::to_string(timeval.tv_usec / 1000)); + m_strLogLine.append(ToString((uint64)timeval.tv_usec / 1000)); m_strLogLine.append("]"); m_strLogLine.append(LogLevMsg[iLev]); m_strLogLine.append(" "); m_strLogLine.append(szFileName); m_strLogLine.append(":"); - m_strLogLine.append(std::to_string(uiFileLine)); + m_strLogLine.append(ToString(uiFileLine)); m_strLogLine.append(" "); m_strLogLine.append(szFunction); m_strLogLine.append(" "); @@ -237,13 +237,13 @@ void FileLogger::AppendLogPattern(const std::string& strTraceId, int iLev, const } m_strLogLine.clear(); m_strLogLine.append(m_strLogFormatTime); - m_strLogLine.append(std::to_string(timeval.tv_usec / 1000)); + m_strLogLine.append(ToString((uint64)timeval.tv_usec / 1000)); m_strLogLine.append("]"); m_strLogLine.append(LogLevMsg[iLev]); m_strLogLine.append(" "); m_strLogLine.append(szFileName); m_strLogLine.append(":"); - m_strLogLine.append(std::to_string(uiFileLine)); + m_strLogLine.append(ToString(uiFileLine)); m_strLogLine.append(" "); m_strLogLine.append(szFunction); m_strLogLine.append(" "); diff --git a/src/logger/FileLogger.hpp b/src/logger/FileLogger.hpp old mode 100644 new mode 100755 index 4adc2cee..874cd719 --- a/src/logger/FileLogger.hpp +++ b/src/logger/FileLogger.hpp @@ -18,6 +18,7 @@ #include #include #include "Logger.hpp" +#include "util/StringConverter.hpp" namespace neb { @@ -79,6 +80,42 @@ class FileLogger: public Logger template void Append(const char* szFormat, T&& arg, Targs&&... args); template const char* PrintfAppend(const char* szFormat, T&& arg); + inline const std::string& ToString(uint64 ullValue) + { + uint32 uiLength = neb::StringConverter::Digits10(ullValue); + m_strConvertMedium.resize(uiLength); + neb::StringConverter::uint64ToAscii(ullValue, uiLength, (char*)m_strConvertMedium.data()); + return(m_strConvertMedium); + } + inline const std::string& ToString(int64 llValue) + { + if (llValue < 0) + { + uint64 ullValue = ~(llValue - 1); + uint32 uiLength = neb::StringConverter::Digits10(ullValue); + m_strConvertMedium.resize(uiLength + 1); + m_strConvertMedium[0] = '-'; + neb::StringConverter::uint64ToAscii(ullValue, uiLength, (char*)m_strConvertMedium.data() + 1); + return(m_strConvertMedium); + } + else + { + return(ToString((uint64)llValue)); + } + } + inline const std::string& ToString(uint32 uiValue) + { + return(ToString((uint64)uiValue)); + } + inline const std::string& ToString(int32 iValue) + { + return(ToString((int64)iValue)); + } + inline const std::string& ToString(float fValue) + { + return(ToString((int64)fValue)); + } + private: static FileLogger* s_pInstance; #if __GNUC__ < 5 @@ -96,6 +133,7 @@ class FileLogger: public Logger std::string m_strLogFormatTime; std::string m_strLogLine; std::string m_strOutStr; + std::string m_strConvertMedium; }; template diff --git a/src/logger/Logger.hpp b/src/logger/Logger.hpp old mode 100644 new mode 100755 diff --git a/src/logger/NetLogger.cpp b/src/logger/NetLogger.cpp old mode 100644 new mode 100755 diff --git a/src/logger/NetLogger.hpp b/src/logger/NetLogger.hpp old mode 100644 new mode 100755 diff --git a/src/mydis/DbOperator.cpp b/src/mydis/DbOperator.cpp old mode 100644 new mode 100755 diff --git a/src/mydis/DbOperator.hpp b/src/mydis/DbOperator.hpp old mode 100644 new mode 100755 diff --git a/src/mydis/MydisOperator.cpp b/src/mydis/MydisOperator.cpp old mode 100644 new mode 100755 diff --git a/src/mydis/MydisOperator.hpp b/src/mydis/MydisOperator.hpp old mode 100644 new mode 100755 diff --git a/src/mydis/Operator.cpp b/src/mydis/Operator.cpp old mode 100644 new mode 100755 diff --git a/src/mydis/Operator.hpp b/src/mydis/Operator.hpp old mode 100644 new mode 100755 diff --git a/src/mydis/RedisOperator.cpp b/src/mydis/RedisOperator.cpp old mode 100644 new mode 100755 diff --git a/src/mydis/RedisOperator.hpp b/src/mydis/RedisOperator.hpp old mode 100644 new mode 100755 diff --git a/src/pb/http.pb.h b/src/pb/http.pb.h old mode 100644 new mode 100755 diff --git a/src/pb/msg.pb.cc b/src/pb/msg.pb.cc old mode 100644 new mode 100755 diff --git a/src/pb/msg.pb.h b/src/pb/msg.pb.h old mode 100644 new mode 100755 diff --git a/src/pb/neb_sys.pb.h b/src/pb/neb_sys.pb.h old mode 100644 new mode 100755 diff --git a/src/pb/redis.pb.cc b/src/pb/redis.pb.cc old mode 100644 new mode 100755 diff --git a/src/pb/redis.pb.h b/src/pb/redis.pb.h old mode 100644 new mode 100755 diff --git a/src/pb/report.pb.cc b/src/pb/report.pb.cc old mode 100644 new mode 100755 diff --git a/src/pb/report.pb.h b/src/pb/report.pb.h old mode 100644 new mode 100755 diff --git a/src/type/Notations.cpp b/src/type/Notations.cpp old mode 100644 new mode 100755 diff --git a/src/type/Notations.hpp b/src/type/Notations.hpp old mode 100644 new mode 100755 diff --git a/src/type/Package.cpp b/src/type/Package.cpp old mode 100644 new mode 100755 diff --git a/src/type/Package.hpp b/src/type/Package.hpp old mode 100644 new mode 100755 diff --git a/src/util/CBuffer.cpp b/src/util/CBuffer.cpp old mode 100644 new mode 100755 diff --git a/src/util/CBuffer.hpp b/src/util/CBuffer.hpp old mode 100644 new mode 100755 index 6f1ca964..6ef907d7 --- a/src/util/CBuffer.hpp +++ b/src/util/CBuffer.hpp @@ -58,8 +58,37 @@ class CBuffer inline CBuffer(const char* data, size_t len) : m_buffer(const_cast(data)), m_external_readonly_buffer(const_cast(data)), - m_buffer_len(0), m_write_idx(0), m_read_idx(0) + m_buffer_len(len), m_write_idx(len), m_read_idx(0) + { + } + inline CBuffer(CBuffer&& oBuff) : + m_buffer(oBuff.m_buffer), + m_external_readonly_buffer(oBuff.m_external_readonly_buffer), + m_buffer_len(oBuff.m_buffer_len), m_write_idx(oBuff.m_write_idx), m_read_idx(oBuff.m_read_idx) + { + oBuff.m_buffer = NULL; + oBuff.m_external_readonly_buffer = NULL; + oBuff.m_buffer_len = 0; + oBuff.m_write_idx = 0; + oBuff.m_read_idx = 0; + } + CBuffer& operator=(CBuffer&& oBuff) { + if (m_buffer != NULL) + { + free(m_buffer); + } + m_buffer = oBuff.m_buffer; + m_external_readonly_buffer = oBuff.m_external_readonly_buffer; + m_buffer_len = oBuff.m_buffer_len; + m_write_idx = oBuff.m_write_idx; + m_read_idx = oBuff.m_read_idx; + oBuff.m_buffer = NULL; + oBuff.m_external_readonly_buffer = NULL; + oBuff.m_buffer_len = 0; + oBuff.m_write_idx = 0; + oBuff.m_read_idx = 0; + return(*this); } inline size_t GetReadIndex() { @@ -302,7 +331,7 @@ class CBuffer { return -1; } - int ret = Copyout(unit->m_buffer + +unit->m_write_idx, + int ret = Copyout(unit->m_buffer + unit->m_write_idx, datlen); if (ret > 0) { diff --git a/src/util/CTlv.cpp b/src/util/CTlv.cpp old mode 100644 new mode 100755 diff --git a/src/util/CTlv.hpp b/src/util/CTlv.hpp old mode 100644 new mode 100755 diff --git a/src/util/StreamCodec.hpp b/src/util/StreamCodec.hpp old mode 100644 new mode 100755 diff --git a/src/util/StringCoder.cpp b/src/util/StringCoder.cpp old mode 100644 new mode 100755 index edaf6cfc..5697c49c --- a/src/util/StringCoder.cpp +++ b/src/util/StringCoder.cpp @@ -184,15 +184,18 @@ void DecodeParameter(const std::string& strParameter, std::map& vecDest) { vecDest.clear(); - std::string strForSplit; - strForSplit.assign(strSrc.data(), strSrc.size()); - char* p; - char* buff = const_cast(strForSplit.data()); - p = strsep(&buff, strPattern.data()); - while (p != NULL) + if (strSrc.size() > 0) { - vecDest.push_back(p); + std::string strForSplit; + strForSplit.assign(strSrc.data(), strSrc.size()); + char* p; + char* buff = const_cast(strForSplit.data()); p = strsep(&buff, strPattern.data()); + while (p != NULL) + { + vecDest.push_back(p); + p = strsep(&buff, strPattern.data()); + } } } diff --git a/src/util/StringCoder.hpp b/src/util/StringCoder.hpp old mode 100644 new mode 100755 diff --git a/src/util/StringConverter.cpp b/src/util/StringConverter.cpp old mode 100644 new mode 100755 diff --git a/src/util/StringConverter.hpp b/src/util/StringConverter.hpp old mode 100644 new mode 100755 diff --git a/src/util/encrypt/base64.c b/src/util/encrypt/base64.c old mode 100644 new mode 100755 diff --git a/src/util/encrypt/base64.h b/src/util/encrypt/base64.h old mode 100644 new mode 100755 diff --git a/src/util/encrypt/city.cc b/src/util/encrypt/city.cc old mode 100644 new mode 100755 diff --git a/src/util/encrypt/city.h b/src/util/encrypt/city.h old mode 100644 new mode 100755 diff --git a/src/util/encrypt/citycrc.h b/src/util/encrypt/citycrc.h old mode 100644 new mode 100755 diff --git a/src/util/encrypt/config.h b/src/util/encrypt/config.h old mode 100644 new mode 100755 diff --git a/src/util/encrypt/crc16.c b/src/util/encrypt/crc16.c old mode 100644 new mode 100755 diff --git a/src/util/encrypt/crc16.h b/src/util/encrypt/crc16.h old mode 100644 new mode 100755 diff --git a/src/util/encrypt/hconv.c b/src/util/encrypt/hconv.c old mode 100644 new mode 100755 diff --git a/src/util/encrypt/hconv.h b/src/util/encrypt/hconv.h old mode 100644 new mode 100755 diff --git a/src/util/encrypt/rc5.c b/src/util/encrypt/rc5.c old mode 100644 new mode 100755 diff --git a/src/util/encrypt/rc5.h b/src/util/encrypt/rc5.h old mode 100644 new mode 100755 diff --git a/src/util/http/http_parser.c b/src/util/http/http_parser.c old mode 100644 new mode 100755 diff --git a/src/util/http/http_parser.h b/src/util/http/http_parser.h old mode 100644 new mode 100755 diff --git a/src/util/json/CJsonObject.hpp b/src/util/json/CJsonObject.hpp old mode 100644 new mode 100755 index 14f59976..042652da --- a/src/util/json/CJsonObject.hpp +++ b/src/util/json/CJsonObject.hpp @@ -215,10 +215,10 @@ class CJsonObject std::map m_mapJsonObjectRef; std::map::iterator m_object_iter; #else - std::unordered_map m_mapJsonArrayRef; - std::unordered_map::iterator m_object_iter; - std::unordered_map m_mapJsonObjectRef; - std::unordered_map::iterator m_array_iter; + mutable std::unordered_map m_mapJsonArrayRef; + mutable std::unordered_map::iterator m_object_iter; + mutable std::unordered_map m_mapJsonObjectRef; + mutable std::unordered_map::iterator m_array_iter; #endif }; diff --git a/src/util/json/cJSON.c b/src/util/json/cJSON.c old mode 100644 new mode 100755 diff --git a/src/util/json/cJSON.h b/src/util/json/cJSON.h old mode 100644 new mode 100755 diff --git a/src/util/process_helper.c b/src/util/process_helper.c old mode 100644 new mode 100755 diff --git a/src/util/process_helper.h b/src/util/process_helper.h old mode 100644 new mode 100755 diff --git a/src/util/proctitle_helper.c b/src/util/proctitle_helper.c old mode 100644 new mode 100755 diff --git a/src/util/proctitle_helper.h b/src/util/proctitle_helper.h old mode 100644 new mode 100755

afs}-&ToL;_>eY>#lcLaj)bMy$wIQ^ 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