请求处理简单框架

#ifndef _OTA_REQUEST_HANDLER_H_
#define _OTA_REQUEST_HANDLER_H_

#include "type_def.h"
#include "gsl/span"
#include "boost/asio.hpp"
#include "zros/app_log/log.h"

#include <thread>
#include <memory>
#include <functional>
#include <vector>

template<class REQ_CMD_TYPE, class REQ_TYPE, class RES_TYPE>
bool process_req(const REQ_CMD_TYPE& req_cmd, gsl::span<const uint8_t> req_datas, std::vector<uint8_t>& resp_datas) {
  REQ_TYPE req_data;

  size_t data_length = 0;
  auto parse_error = parse_req_data(req_cmd, req_datas, req_data, data_length);
  if (parse_error != parse_error_type::OK) {
    return false;
  }
  
  RES_TYPE handle_result;
  auto err_code = handle_req(req_data, handle_result);
  resp_datas = make_response_frame(req_cmd, err_code, to_byte_array(handle_result));

  return true;
}

template<class REQ_CMD_TYPE>
class request_handler
{
public:
  using req_cmd_handler_pair = request_cmd_handler_pair<REQ_CMD_TYPE>;
  using data_send_func = std::function<void(const std::vector<uint8_t>&)>;
  using error_handler_func = std::function<void()>;
  using req_handle_func = std::function<void(const REQ_CMD_TYPE&)>;

  request_handler(data_send_func data_sender, 
    error_handler_func error_handler, 
    req_handle_func before_handle_req_func = nullptr, 
    req_handle_func after_handle_req_func = nullptr)
  : m_is_stop(false),
    m_data_sender(data_sender),
    m_error_handler(error_handler),
    m_before_handle_req_func(before_handle_req_func),
    m_after_handle_req_func(after_handle_req_func),
    m_work(new boost::asio::io_service::work(m_io_service)) {
      get_req_cmd_handlers(m_request_cmd_handler);
      m_work_thread = std::thread([&](){
        m_io_service.run();
      });
    }
  
  ~request_handler() {
    //assert(m_is_stop && "not stop request handler before destruct it!");
    if (!m_is_stop) {
      zinfo("not stop request handler before destruct it!");
      stop();
    }
  }

  request_handler(request_handler &&) = delete;
  request_handler(const request_handler &) = delete;
  request_handler &operator=(request_handler &&) = delete;
  request_handler &operator=(const request_handler &) = delete;
  
  void handle_request(gsl::span<const uint8_t> datas) {
    std::vector<uint8_t> local_datas{datas.begin(), datas.end()};

    m_io_service.post([local_datas, this](){
      zdebug("receive %d datas", local_datas.size());
      m_buffer.insert(m_buffer.end(), local_datas.begin(), local_datas.end());
      handle_req_impl();
    });
  }

  void stop() {
    m_is_stop = true;
    m_work.reset();
    m_io_service.stop();
    m_work_thread.join();
  }

private:
  void handle_error() {
    if (m_error_handler) {
      m_error_handler();
    }
  }

  void before_handle(const REQ_CMD_TYPE& req_cmd) {
    if (m_before_handle_req_func) {
      m_before_handle_req_func(req_cmd);
    }
  }

  void after_handle(const REQ_CMD_TYPE& req_cmd) {
    if (m_after_handle_req_func) {
      m_after_handle_req_func(req_cmd);
    }
  }

  void handle_req_impl() {
    while (true) {
      if (m_is_stop || m_buffer.empty()) {
        zdebug("handle_req_impl return directly\n");
        return;
      }

      REQ_CMD_TYPE req_cmd;
      size_t req_cmd_length = 0;
      gsl::span<const uint8_t> one_frame;

      auto parse_res = parse_one_frame(m_buffer, one_frame, req_cmd);
      if (parse_res == parse_error_type::DATA_ERROR) {
        zinfo("parse_one_frame return data error");
        m_data_sender(make_invalid_cmd_resp_frame(req_cmd));
        m_buffer.clear();
        handle_error();
        return;
      }

      if (parse_res == parse_error_type::DATA_NOT_ENOUGH) {
        zinfo("parse_one_frame return data not enouth");
        return;
      }

      parse_res = parse_req_cmd(one_frame, req_cmd, req_cmd_length);
      if (parse_res == parse_error_type::DATA_ERROR) {
        zinfo("peek_req_cmd return data error");
        m_data_sender(make_invalid_cmd_resp_frame(req_cmd));
        m_buffer.erase(m_buffer.begin(), m_buffer.begin()+one_frame.size());
        continue;
      }

      zdebug("get cmd length: %d,cmd_handler num:%d,", req_cmd_length,m_request_cmd_handler.size());

      auto ite = std::find_if(m_request_cmd_handler.begin(),
                              m_request_cmd_handler.end(),
                              [&req_cmd](const req_cmd_handler_pair& cmd_handler_pair){
                                return (cmd_handler_pair.cmd == req_cmd);
                              });
      if (m_request_cmd_handler.end() == ite) {
        zdebug("cmd_handler num:%d",m_request_cmd_handler.size());
        m_data_sender(make_invalid_cmd_resp_frame(req_cmd));
        m_buffer.erase(m_buffer.begin(), m_buffer.begin()+one_frame.size());
        handle_error();
        return;
      }

      before_handle(req_cmd);
      
      std::vector<uint8_t> resp_datas;
      auto res = ite->handler(req_cmd, gsl::span<const uint8_t>(one_frame.data()+req_cmd_length, one_frame.size()-req_cmd_length), resp_datas);
      m_buffer.erase(m_buffer.begin(), m_buffer.begin()+one_frame.size());

      after_handle(req_cmd);

      if (!res) {
        m_data_sender(make_invalid_cmd_resp_frame(req_cmd));
        handle_error();
        return;
      }

      if (!resp_datas.empty()) {
        m_data_sender(resp_datas);
      }
    }
  }

private:
  bool m_is_stop;
  std::vector<uint8_t> m_buffer;

  data_send_func m_data_sender;
  error_handler_func m_error_handler;

  req_handle_func m_before_handle_req_func;
  req_handle_func m_after_handle_req_func; 

  std::thread m_work_thread;
  boost::asio::io_service m_io_service;
  std::unique_ptr<boost::asio::io_service::work> m_work;

  std::vector<req_cmd_handler_pair> m_request_cmd_handler;
};

#endif // !_OTA_REQUEST_HANDLER_H_

如上代码:

从整体结构开始看起,确定这是一个头文件,定义了模板类和函数,用于处理服务请求。

首先,代码的开头和结尾使用了头文件保护宏#ifndef _OTA_REQUEST_HANDLER_H_和#define等,防止重复包含。接下来包含了一些头文件,比如type_def.h、GSL的span、Boost.Asio库、日志库以及一些标准库头文件,比如线程、内存、函数式、向量等。这些依赖项表明代码可能涉及异步IO操作、内存管理、多线程以及数据处理。

接下来看第一个模板函数process_req,它接受请求命令类型、请求数据类型、响应数据类型,以及请求数据和响应数据的容器。函数内部首先解析请求数据,如果解析成功,则处理请求并生成响应数据。这个函数似乎是一个通用的请求处理流程,可能被不同的请求处理实例复用。

 

然后看到request_handler类,这是一个模板类,模板参数是REQ_CMD_TYPE。这个类的作用处理具体的请求,管理请求的处理流程,包括数据的接收、解析、处理以及响应发送。类中有几个重要的类型别名,比如req_cmd_handler_pair、data_send_func等,这些可能用于定义回调函数和处理函数对。

 

构造函数接收几个函数对象:data_sender用于发送响应数据,error_handler处理错误,before和after处理请求前后的回调。构造函数中初始化了成员变量,并启动了一个工作线程来运行Boost.Asio的io_service,这通常用于异步任务处理。析构函数则负责停止服务并等待线程结束,确保资源正确释放。

 

成员函数handle_request接收一个数据span,将其拷贝到本地vector后,通过io_service.post投递到工作线程中处理,这保证了数据处理的线程安全。数据被添加到m_buffer中,随后调用handle_req_impl处理请求。

 

handle_req_impl函数是核心处理逻辑。它在一个循环中解析缓冲区中的数据,解析出请求命令,查找对应的处理函数,处理请求,生成响应,并发送响应。过程中处理可能的错误情况,比如数据不足或无效命令,并调用相应的错误处理回调。

 

在解析请求帧时,调用了parse_one_frame函数,该函数可能负责从原始数据中提取出一个完整的请求帧。如果解析失败,会发送无效命令的响应,并清空缓冲区。如果找到对应的命令处理函数,则调用process_req来处理具体请求,并生成响应数据。

 

错误处理部分,如handle_error函数,会调用用户提供的错误处理回调。而before_handle和after_handle分别在处理请求前后执行用户定义的操作,比如日志记录或预处理。

 

整个类使用了Boost.Asio的io_service来实现异步处理,通过工作线程来执行实际的数据处理任务,避免阻塞主线程。m_work对象用于保持io_service的运行,直到显式停止。

 

需要注意的是,代码中的一些函数如parse_req_data、handle_req、make_response_frame等并未在此头文件中定义,可能在别处实现。此外,m_request_cmd_handler通过get_req_cmd_handlers函数初始化,这个函数可能负责填充不同命令对应的处理函数。

 

可能存在的一些问题或需要注意的点包括:

 

1. 线程安全性:尽管数据通过io_service.post投递到工作线程,但需要确保所有对m_buffer和其他成员变量的访问都在同一线程内,避免竞态条件。这里由于所有处理都在io_service所在的线程中进行,应该是线程安全的。

 

2. 数据解析的正确性:parse_one_frame和parse_req_cmd的实现是否正确,是否能够正确分割和解析请求帧,否则可能导致数据错误或处理不完整。

 

3. 资源管理:在析构函数中,如果未正确停止handler,可能导致资源泄漏或线程未正确结束。代码中在析构时检查m_is_stop,并在必要时调用stop(),确保资源释放。

 

4. 错误处理机制:当解析或处理失败时,是否能够正确清理缓冲区并通知上层,这里通过发送错误响应并调用错误处理回调来实现。

 

5. 性能问题:频繁的数据拷贝(如handle_request中将数据复制到local_datas)可能影响性能,需要评估是否必要。此处可能为了避免数据在异步处理中被修改,所以进行拷贝,但若数据量大,可能需优化。

 

6. Boost.Asio的使用:是否正确管理io_service的生命周期,work对象的创建和销毁时机是否正确。这里在构造函数中创建work,防止io_service在没有任务时停止,stop()时释放work,允许io_service正常结束。

 

总结来说,这个request_handler类是一个基于Boost.Asio的异步请求处理器,支持多种命令类型,通过注册处理函数来处理不同的请求,并能够发送响应和错误处理。适用于需要异步处理网络请求或类似IO密集型任务的场景。

这段代码实现了一个基于模板的异步请求处理框架,主要用于处理OTA(空中下载)请求。以下是对其结构和功能的解析:


核心组件

  1. 模板函数 process_req

    • 功能:通用请求处理流程。

    • 输入:请求命令、请求数据、响应数据容器。

    • 流程:

      1. 解析请求数据(parse_req_data)。

      2. 若解析成功,调用请求处理函数(handle_req)。

      3. 生成响应帧(make_response_frame)。

    • 返回值:布尔值表示处理成功与否。

  2. 模板类 request_handler

    • 功能:管理请求的异步处理流程。

    • 模板参数:REQ_CMD_TYPE(请求命令类型)。


类成员与关键设计

  1. 类型别名

    • req_cmd_handler_pair:命令与处理函数的绑定。

    • data_send_func:发送响应数据的回调。

    • error_handler_func:错误处理回调。

    • req_handle_func:请求处理前后的钩子函数。

  2. 构造函数

    • 接收数据发送、错误处理、预处理和后处理回调。

    • 初始化 Boost.Asio 的 io_service 和工作线程,确保异步任务执行。

  3. 析构函数

    • 确保安全停止处理线程,避免资源泄漏。

  4. 核心方法

    • handle_request:接收原始数据,投递到工作线程处理。

    • stop:停止服务并等待线程结束。

    • handle_req_impl:实际解析和处理请求的核心逻辑。


异步处理机制

  • Boost.Asio 集成

    • 使用 io_service 管理异步任务队列。

    • work 对象保持 io_service 持续运行,直到显式停止。

    • 工作线程通过 io_service.run() 执行任务。

  • 线程安全

    • 数据通过 io_service.post 提交到工作线程,确保对缓冲区的操作单线程执行,避免竞态条件。


请求处理流程

  1. 数据接收

    • handle_request 接收数据并拷贝到本地,通过 io_service 投递到工作线程。

  2. 数据解析

    • parse_one_frame:从原始缓冲区提取完整请求帧。

    • parse_req_cmd:解析请求命令和长度。

  3. 处理函数查找

    • 在注册的命令处理列表中查找匹配项(m_request_cmd_handler)。

  4. 请求执行

    • 调用预处理钩子(before_handle)。

    • 执行对应的处理函数,生成响应数据。

    • 调用后处理钩子(after_handle)。

  5. 响应发送

    • 通过 m_data_sender 回调发送响应数据。

  6. 错误处理

    • 解析或处理失败时,发送无效命令响应并触发错误回调。


关键依赖

  • GSL Span:用于安全的数据视图操作,避免不必要的拷贝。

  • Boost.Asio:提供异步IO和任务调度能力。

  • 自定义日志库:通过 zinfo 和 zdebug 记录日志。


潜在问题与优化

  1. 数据拷贝开销

    • handle_request 中将数据拷贝到 local_datas 可能影响性能,需评估是否必要。

  2. 错误恢复

    • 遇到数据错误时直接清空缓冲区,可能导致后续合法数据被丢弃,需考虑更精细的错误恢复策略。

  3. 处理函数注册

    • get_req_cmd_handlers 的实现未展示,需确保正确注册所有命令处理函数。

  4. 线程管理

    • 确保 stop() 被正确调用,避免析构时资源未释放。


应用场景

适用于需要异步处理网络请求、支持多种命令类型的场景,例如:

  • 固件升级服务。

  • 物联网设备的远程控制。

  • 高并发请求处理的中间件。


总结

该代码通过模板化和异步设计,提供了灵活的请求处理框架,支持自定义命令解析、处理逻辑和回调,结合 Boost.Asio 实现高效的任务调度,适合需要可靠异步处理的嵌入式或服务器端应用。

posted @ 2025-03-05 17:34  白伟碧一些小心得  阅读(30)  评论(0)    收藏  举报