在同一系统上运行的多个boost::asio-ssl客户端

multiple boost::asio ssl clients running on same system

本文关键字:boost asio-ssl 客户端 系统 运行      更新时间:2023-10-16

我有一个简单的Boost ASIO SSL客户端,它调用web api。客户端是对Boost SSL文档示例的轻微修改。

//http.h
class Http {
public:
    static void WebApiCall(...);
}
//http.cpp
void Http::WebApiCall(...) {
    try {
        // .......
        boost::asio::io_service io_service;
        tcp::resolver resolver(io_service);
        tcp::resolver::query query(serverip, serverport);
        tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
        boost::asio::ssl::context ctx(io_service, boost::asio::ssl::context::tlsv1); // ERROR # 1
        // ....
        // Setting SSL Context Properties Here
        // ....
        boost::shared_ptr<boost::asio::ssl::stream<tcp::socket> >  ssocket(new boost::asio::ssl::stream<tcp::socket>(io_service, ctx));
        boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
        ssocket->lowest_layer().connect(endpoint);
        boost::system::error_code er;
        ssocket->handshake(boost::asio::ssl::stream_base::client,er);
        boost::asio::streambuf request;
        std::ostream request_stream(&request);
        // ....
        // Set Headers & Body of HTTP Request here
        // ....
        size_t written = 0;
        written = boost::asio::write(*ssocket, request); // ERROR # 2
        // .....
        // Read server response
        boost::asio::streambuf response;
        boost::system::error_code error;
        int read_bytes = 0;
        std::string TempBuf = "";
        std::ostringstream responseStringstream; 
        std::stringstream  response_stream;
        while ( boost::asio::read(*ssocket,response,boost::asio::transfer_at_least(1), error)) {
                read_bytes  = read_bytes + response.size();
                responseStringstream << &response;
            }
        }
        // Do some stuff with server response....
        // ....
    } catch ( const boost::system::system_error &error ) {
        // Print the exception ..
    }
}
// client.cpp
Http::WebApiCall(<api_to_call>)

您可以看到它是一个简单的HTTP客户端,带有一个静态函数,它使用ASIO实现了实际的启用SSL的HTTP客户端。

用例:

1000个进程正在一台机器上运行此客户端。所有进程都在大致相同的时间内周期性地(例如每一分钟)向一个资源发出POST请求。机器是Ubuntu,我似乎没有内存不足(我有大约6GB的空闲空间)

这个客户端运行得很好,但在一种情况下,我必须模拟服务器上的一些负载,我已经启动了这个客户端的1000个进程,所有进程都在一台计算机上,所有进程使用相同的公共证书向同一服务器调用相同的API,只是每个客户端都有自己的OAuth令牌。在这种情况下,我得到了两种类型的异常:

错误:

  • 错误#2:写入时某些客户端(不是全部)出现错误(写入:短读)。从不同的论坛和Boost来源来看,服务器似乎正在发送SSL_Shutdown,导致ASIO抛出此错误,根据我的发现,这是正常行为。我的问题是,为什么服务器此时发送SSL_Shutdown?这与多个进程从同一台机器调用同一资源有关吗?从ASIO文档来看,ASIO SSL不是线程安全的,但在这种情况下,我只运行一个线程,但运行不同的进程(我认为这是完全安全的),此外,上面的代码本身就是线程安全的。底层openssl的行为是否不稳定?

  • 错误#1:有时在创建Boost ASIO SSL上下文时会出现异常,只是简单地说"Context:SSL ERROR"。同样的想法,为什么它会这样?这与多个进程有关吗?openssl在这种情况下是不是把事情搞混了?

在过去的一年里,我的客户每台机器只运行一个进程,我以前从未见过这些错误。任何想法都值得赞赏。


cco(只是提到了OAuth,但我不认为这与它有任何关系)

  • Q:一些客户端(并非全部)在写入时出错(写入:短读)。从不同的论坛和Boost来源来看,服务器似乎正在发送SSL_Shutdown,导致ASIO抛出此错误,根据我的发现,这是正常行为。

    最有可能的原因是您正在将系统推到资源限制之外。例如,客户端或服务器可能会用完文件句柄。例如,在我的Linux盒子上,打开的文件数量默认限制为1024ulimit -a输出:

    core file size          (blocks, -c) 0
    data seg size           (kbytes, -d) unlimited
    scheduling priority             (-e) 0
    file size               (blocks, -f) unlimited
    pending signals                 (-i) 256878
    max locked memory       (kbytes, -l) unlimited
    max memory size         (kbytes, -m) unlimited
    open files                      (-n) 1024
    pipe size            (512 bytes, -p) 8
    POSIX message queues     (bytes, -q) 819200
    real-time priority              (-r) 95
    stack size              (kbytes, -s) 8192
    cpu time               (seconds, -t) unlimited
    max user processes              (-u) 256878
    virtual memory          (kbytes, -v) unlimited
    file locks                      (-x) unlimited
    
  • Q:我的问题是,为什么服务器此时发送SSL_Shutdown?

    很可能是因为以上原因。

  • Q:这与多个进程从同一台机器调用同一资源有关吗?

    没有。

  • Q:从ASIO文档来看,ASIO SSL不是线程安全的,但在这种情况下,我只运行一个线程,但运行不同的进程(我认为这是完全安全的),此外,上述代码本身也是线程安全的。底层openssl的行为是否不稳定?

    线程安全或底层SSL库不是这里的问题。

  • Q:有时在创建Boost ASIO SSL上下文时会出现异常,只是简单地说"Context:SSL error"。同样的想法,为什么它会这样?这与多个进程有关吗?openssl在这种情况下是不是把事情搞混了?

    不太可能,但ssl::context的每个实例都会产生开销,这是可能的。您可以尝试静态地/在循环之外分配它。

    也就是说,如果SSL上下文只是遇到(相同的)资源限制,初始化更有可能,因为它可能会打开一些系统配置文件和/或检查是否存在已知路径(例如CApath等)