侧边栏壁纸
  • 累计撰写 8 篇文章
  • 累计创建 35 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Drogon MySQL 兼容补丁:处理 libmariadb-dev 与 libmysqlclient-dev 的开发库冲突

猿有味
2026-04-07 / 0 评论 / 0 点赞 / 2 阅读 / 0 字

摘要

同一台开发机同时承载 ROS 2 开发环境和 Drogon 后端时,问题往往不在数据库服务本身,而在开发库。至少在 Ubuntu Jammy 的官方包里,安装部分 ROS 2 相关包后,系统里可能已经使用 libmysqlclient-dev;而 Drogon v1.9.9 这一版要启用 MySQL 支持,实际更依赖 libmariadb-devlibmariadb-dev-compat 这一组开发包。两边单独使用都没问题,放到同一套构建环境里后,就容易卡在开发库冲突和接口不匹配上。

这类问题通常出现在这样的场景里:系统已经装有 ROS 2 相关开发环境,后端准备接 Drogon + MySQL,但 Drogon v1.9.9 在 Linux 下查找客户端库、以及使用非阻塞数据库 API 的这套逻辑,更接近 MariaDB 文档里的 *_start/*_cont 接口。只要系统当前安装的是 libmysqlclient-dev 这一侧,而不是 Drogon 预期的 MariaDB Connector/C 开发库,编译就很容易失败。

本文不讨论“最优架构”,只记录一个临时兼容方案:给 Drogon v1.9.9 打一个补丁,让它先识别当前客户端库是否提供 mysql_real_connect_start 这类符号;如果没有,就退回阻塞实现,先恢复构建和基本可用性。

一、问题背景

这个问题表面上看像是“数据库冲突”,实际更接近开发库选择与 Drogon 代码假设之间的冲突

先限定一下讨论范围。这里说的是开发机上的数据库开发库兼容性,而不是“整个 ROS 2 默认依赖 MySQL”这样的结论。至于具体是哪几个 ROS 2 相关包会带来这套环境,不同发行版、安装方式和系统仓库组合都可能不同,因此不在文中固定列出。

这类场景通常有几个共同点:

  • 当前系统已经装好了 ROS 2 开发环境,并额外安装了部分 ROS 2 相关包
  • 后端项目准备用 Drogon
  • 部分 ROS 2 相关包使用 libmysqlclient-dev
  • Drogon v1.9.9 要启用 MySQL 支持时,更接近 libmariadb-dev 及其兼容包 libmariadb-dev-compat 这套开发库路径

这里讨论的也不是 MySQL Server 和 MariaDB Server 能否在理论上共存,而是源码构建时用到的开发库,是否与 Drogon v1.9.9 的 API 假设一致。

问题首先体现在包管理和开发库接口两层:

  • 至少在 Ubuntu Jammy 的官方包关系里,libmariadb-devlibmariadb-dev-compat 都会 Conflicts/Breaks/Replaces libmysqlclient-dev,因此两套开发包不能直接共装
  • Drogon v1.9.9MysqlConnection 默认走了 MYSQL_OPT_NONBLOCKmysql_real_connect_start 这一类接口路径,这条路径更接近 MariaDB Connector/C,而不是 Oracle MySQL 8 的 _nonblocking 接口

因此,问题往往不在 mysqldmariadbd 服务是否启动,而在于当前机器到底装的是哪一套开发库。 如果当前头文件和库里没有 Drogon 这条代码路径需要的符号,构建会直接失败。即便通过其他方式绕过编译阶段,后续连接和查询流程也可能不稳定。

二、冲突的根因

核心问题不在于库的命名,而在于当前安装的开发库到底提供了哪些能力。

Drogon v1.9.9 的顶层 CMakeLists.txtBUILD_MYSQL 分支里直接注明了 only mariadb client library is supported。从 Ubuntu Jammy 的包说明看,libmariadb-dev 用于面向 MariaDB Connector/C 的源码,libmariadb-dev-compat 则提供“让期望 MySQL client libraries 的源码构建到 MariaDB Connector/C 上”的兼容符号链接;而这两个包都与 libmysqlclient-dev 冲突。对应的 MysqlConnection.cc 里也确实直接调用了下面这些接口:

  • MYSQL_OPT_NONBLOCK
  • mysql_real_connect_start
  • mysql_real_connect_cont
  • mysql_real_query_start
  • mysql_store_result_start
  • mysql_set_character_set_start

MariaDB 官方文档把非阻塞客户端接口写成 *_start/*_cont 这一组;而 MySQL 8 官方 C API 异步接口文档写的是 mysql_real_connect_nonblocking()mysql_real_query_nonblocking()mysql_store_result_nonblocking() 这一组。 因此,关键不在库名本身,而在于当前头文件和客户端库到底导出了哪一组 API

如果当前环境里装的是 libmysqlclient-dev,而没有 Drogon 这条代码路径所需的 *_start/*_contMYSQL_OPT_NONBLOCK 符号,按原逻辑编译就会遇到符号或能力不匹配的问题。

这个补丁的处理思路如下:

  • 先把 MySQL 客户端库查找范围补全
  • 再显式检测当前头文件里有没有 mysql_real_connect_start
  • 如果有,就继续走 Drogon 原来的异步路径
  • 如果没有,就退回阻塞连接和阻塞查询路径

它不是完整重构,而是一个以兼容性为目标的临时修正。

三、处理方案

以下步骤直接基于 Drogon v1.9.9 打补丁。

先拉代码并初始化子模块:

git clone https://github.com/drogonframework/drogon -b v1.9.9
cd drogon
git submodule update --init

把下面的内容保存成 drogon-mysql-fix.patch,然后应用:

git apply drogon-mysql-fix.patch

补丁打完以后,按正常方式编译安装即可:

cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
sudo cmake --install build

如果之前在外层工程里显式关掉了 BUILD_MYSQL,记得补上 -DBUILD_MYSQL=ON。 Drogon v1.9.9 源码默认是开的,但前提是 BUILD_ORM 也处于开启状态。

如果安装路径、编译选项或者工具链有特殊要求,可按实际环境调整。 重点不在构建命令本身,而在于让 Drogon 先正确识别当前客户端库能力

四、补丁改了什么

这份补丁主要包含四类修改。

(一)补全 FindMySQL.cmake 的查找逻辑

原始版本在 Linux 下查找库文件时,更偏向以下名称:

  • mysqlclient_r
  • mariadbclient
  • mariadb

补丁把 mysqlclient 也纳入查找范围,同时补充了常见库目录,例如:

  • /usr/lib
  • /usr/lib64
  • /usr/lib/x86_64-linux-gnu
  • /usr/local/lib

这样做的目的很明确:避免 CMake 因为路径和库名范围过窄,漏掉系统里已经存在的 MySQL 客户端库。

(二)在 CMake 阶段检测是否存在非阻塞 API

补丁里新增了这样一段检查:

include(CheckSymbolExists)
check_symbol_exists(mysql_real_connect_start "mysql.h" HAS_MYSQL_NONBLOCK_API)
if (HAS_MYSQL_NONBLOCK_API)
    add_definitions(-DHAS_MYSQL_NONBLOCK_API)
endif ()

这一步很关键。 它不是依赖库名推断,而是直接检测头文件里是否存在 mysql_real_connect_start 这个符号。

有这个符号,就说明当前可以继续使用 Drogon 原本依赖的非阻塞实现。 没有这个符号,就不再强行编译异步路径,而是切到兼容的阻塞分支。

(三)给 MysqlConnection 增加阻塞 fallback

这是补丁中改动最大的一部分。

原始实现默认走非阻塞流程,核心逻辑依赖:

  • MYSQL_OPT_NONBLOCK
  • mysql_real_connect_start / cont
  • mysql_real_query_start / cont
  • mysql_set_character_set_start / cont
  • mysql_store_result_start / cont

补丁在 HAS_MYSQL_NONBLOCK_API 不存在时,补了一套阻塞路径:

  • 连接阶段直接调用 mysql_real_connect
  • 字符集设置直接调用 mysql_set_character_set
  • 查询阶段直接调用 mysql_real_query
  • 结果集读取直接调用 mysql_store_result

同时新增了:

  • startQueryBlocking()
  • getResultBlocking(MYSQL_RES *res)

这样改完以后,Drogon 至少可以在缺少 *_start/*_cont 这组符号的前提下完成连接、查询和结果回调,避免在编译阶段直接失败。

但需要明确一点:这个 fallback 只是兼容性兜底,不是语义完全等价的替换。 因为这些阻塞调用发生在该数据库连接所属的 EventLoop 线程里,所以它会改变原来这条连接的并发与时延特性。

(四)补充了一些防御性处理

补丁里还补充了两类防御性处理:

  • channelPtr_ 为空时的判空保护
  • exceptionCallback_ 调用时的异常兜底

这两处不是本次兼容性问题的主因,但在 fallback 分支存在时,防御性代码可以降低异常路径上的二次崩溃风险。

五、完整补丁

如果只想先复现这套方案,直接把下面内容保存成 drogon-mysql-fix.patch 即可:

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7cf091e9..980d5628 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -414,6 +414,11 @@ if (BUILD_MYSQL)

     if (DROGON_FOUND_MYSQL)
         message(STATUS "Ok! We find mariadb!")
+        include(CheckSymbolExists)
+        check_symbol_exists(mysql_real_connect_start "mysql.h" HAS_MYSQL_NONBLOCK_API)
+        if (HAS_MYSQL_NONBLOCK_API)
+            add_definitions(-DHAS_MYSQL_NONBLOCK_API)
+        endif ()
         include(CheckLibraryExists)
         check_library_exists(${MYSQL_LIB_NAME} mysql_optionsv "" HAS_MYSQL_OPTIONSV)
         if (HAS_MYSQL_OPTIONSV)
diff --git a/cmake_modules/FindMySQL.cmake b/cmake_modules/FindMySQL.cmake
index ab598152..881a1456 100644
--- a/cmake_modules/FindMySQL.cmake
+++ b/cmake_modules/FindMySQL.cmake
@@ -99,8 +99,16 @@ if(WIN32)
                      $ENV{SystemDrive}/MySQL/*/lib/${libsuffixDist})
 else(WIN32)
   find_library(MYSQL_LIBRARIES
-               NAMES mysqlclient_r mariadbclient mariadb
-               PATHS /usr/lib/mysql
+               NAMES mysqlclient mysqlclient_r mariadbclient mariadb
+               PATHS /usr/lib
+                     /usr/lib64
+                     /usr/lib/x86_64-linux-gnu
+                     /usr/lib/aarch64-linux-gnu
+                     /usr/lib/arm-linux-gnueabihf
+                     /usr/lib/arm-linux-gnueabi
+                     /usr/local/lib
+                     /usr/local/lib64
+                     /usr/lib/mysql
                      /usr/lib/mariadb
                      /usr/local/lib/mysql
                      /usr/local/lib/mariadb
diff --git a/orm_lib/src/mysql_impl/MysqlConnection.cc b/orm_lib/src/mysql_impl/MysqlConnection.cc
index fb352fec..a9d4e7dc 100644
--- a/orm_lib/src/mysql_impl/MysqlConnection.cc
+++ b/orm_lib/src/mysql_impl/MysqlConnection.cc
@@ -59,7 +59,9 @@ MysqlConnection::MysqlConnection(trantor::EventLoop *loop,
     static MysqlEnv env;
     static thread_local MysqlThreadEnv threadEnv;
     mysql_init(mysqlPtr_.get());
+##if defined(HAS_MYSQL_NONBLOCK_API)
     mysql_options(mysqlPtr_.get(), MYSQL_OPT_NONBLOCK, nullptr);
+##endif
 ##ifdef HAS_MYSQL_OPTIONSV
     mysql_optionsv(mysqlPtr_.get(), MYSQL_OPT_RECONNECT, &reconnect_);
 ##endif
@@ -108,6 +110,7 @@ void MysqlConnection::init()
     loop_->queueInLoop([this]() {
         MYSQL *ret;
         status_ = ConnectStatus::Connecting;
+##if defined(HAS_MYSQL_NONBLOCK_API)
         waitStatus_ =
             mysql_real_connect_start(&ret,
                                      mysqlPtr_.get(),
@@ -135,11 +138,57 @@ void MysqlConnection::init()
         channelPtr_ = std::make_unique<trantor::Channel>(loop_, fd);
         channelPtr_->setEventCallback([this]() { handleEvent(); });
         setChannel();
+##else
+        ret = mysql_real_connect(mysqlPtr_.get(),
+                                 host_.empty() ? nullptr : host_.c_str(),
+                                 user_.empty() ? nullptr : user_.c_str(),
+                                 passwd_.empty() ? nullptr : passwd_.c_str(),
+                                 dbname_.empty() ? nullptr : dbname_.c_str(),
+                                 port_.empty() ? 3306 : atol(port_.c_str()),
+                                 nullptr,
+                                 0);
+        if (!ret)
+        {
+            LOG_ERROR << "Error(" << mysql_errno(mysqlPtr_.get()) << ") \""
+                      << mysql_error(mysqlPtr_.get()) << "\"";
+            LOG_ERROR << "Failed to mysql_real_connect()";
+            handleClosed();
+            return;
+        }
+        if (characterSet_.empty())
+        {
+            status_ = ConnectStatus::Ok;
+            if (okCallback_)
+            {
+                auto thisPtr = shared_from_this();
+                okCallback_(thisPtr);
+            }
+        }
+        else
+        {
+            if (mysql_set_character_set(mysqlPtr_.get(), characterSet_.data()) !=
+                0)
+            {
+                LOG_ERROR << "Error(" << mysql_errno(mysqlPtr_.get()) << ") \""
+                          << mysql_error(mysqlPtr_.get()) << "\"";
+                LOG_ERROR << "Failed to mysql_set_character_set()";
+                handleClosed();
+                return;
+            }
+            status_ = ConnectStatus::Ok;
+            if (okCallback_)
+            {
+                auto thisPtr = shared_from_this();
+                okCallback_(thisPtr);
+            }
+        }
+##endif
     });
 }

 void MysqlConnection::setChannel()
 {
+##if defined(HAS_MYSQL_NONBLOCK_API)
     if ((waitStatus_ & MYSQL_WAIT_READ) || (waitStatus_ & MYSQL_WAIT_EXCEPT))
     {
         if (!channelPtr_->isReading())
@@ -161,6 +210,7 @@ void MysqlConnection::setChannel()
         auto thisPtr = shared_from_this();
         loop_->runAfter(timeout, [thisPtr]() { thisPtr->handleTimeout(); });
     }
+##endif
 }

 void MysqlConnection::handleClosed()
@@ -169,8 +219,11 @@ void MysqlConnection::handleClosed()
     if (status_ == ConnectStatus::Bad)
         return;
     status_ = ConnectStatus::Bad;
-    channelPtr_->disableAll();
-    channelPtr_->remove();
+    if (channelPtr_)
+    {
+        channelPtr_->disableAll();
+        channelPtr_->remove();
+    }
     assert(closeCallback_);
     auto thisPtr = shared_from_this();
     closeCallback_(thisPtr);
@@ -183,8 +236,11 @@ void MysqlConnection::disconnect()
     auto f = pro.get_future();
     loop_->runInLoop([thisPtr, &pro]() {
         thisPtr->status_ = ConnectStatus::Bad;
-        thisPtr->channelPtr_->disableAll();
-        thisPtr->channelPtr_->remove();
+        if (thisPtr->channelPtr_)
+        {
+            thisPtr->channelPtr_->disableAll();
+            thisPtr->channelPtr_->remove();
+        }
         thisPtr->mysqlPtr_.reset();
         pro.set_value(1);
     });
@@ -193,6 +249,7 @@ void MysqlConnection::disconnect()

 void MysqlConnection::handleTimeout()
 {
+##if defined(HAS_MYSQL_NONBLOCK_API)
     int status = 0;
     status |= MYSQL_WAIT_TIMEOUT;
     MYSQL *ret;
@@ -235,10 +292,12 @@ void MysqlConnection::handleTimeout()
     else if (status_ == ConnectStatus::Ok)
     {
     }
+##endif
 }

 void MysqlConnection::handleCmd(int status)
 {
+##if defined(HAS_MYSQL_NONBLOCK_API)
     switch (execStatus_)
     {
         case ExecStatus::RealQuery:
@@ -308,10 +367,14 @@ void MysqlConnection::handleCmd(int status)
         default:
             return;
     }
+##else
+    (void)status;
+##endif
 }

 void MysqlConnection::handleEvent()
 {
+##if defined(HAS_MYSQL_NONBLOCK_API)
     int status = 0;
     auto revents = channelPtr_->revents();
     if (revents & POLLIN)
@@ -361,10 +424,12 @@ void MysqlConnection::handleEvent()
     {
         continueSetCharacterSet(status);
     }
+##endif
 }

 void MysqlConnection::continueSetCharacterSet(int status)
 {
+##if defined(HAS_MYSQL_NONBLOCK_API)
     int err;
     waitStatus_ = mysql_set_character_set_cont(&err, mysqlPtr_.get(), status);
     if (waitStatus_ == 0)
@@ -385,10 +450,14 @@ void MysqlConnection::continueSetCharacterSet(int status)
         }
     }
     setChannel();
+##else
+    (void)status;
+##endif
 }

 void MysqlConnection::startSetCharacterSet()
 {
+##if defined(HAS_MYSQL_NONBLOCK_API)
     int err;
     waitStatus_ = mysql_set_character_set_start(&err,
                                                 mysqlPtr_.get(),
@@ -415,6 +484,22 @@ void MysqlConnection::startSetCharacterSet()
         status_ = ConnectStatus::SettingCharacterSet;
     }
     setChannel();
+##else
+    if (mysql_set_character_set(mysqlPtr_.get(), characterSet_.data()) != 0)
+    {
+        LOG_ERROR << "Error(" << mysql_errno(mysqlPtr_.get()) << ") \""
+                  << mysql_error(mysqlPtr_.get()) << "\"";
+        LOG_ERROR << "Failed to mysql_set_character_set()";
+        handleClosed();
+        return;
+    }
+    status_ = ConnectStatus::Ok;
+    if (okCallback_)
+    {
+        auto thisPtr = shared_from_this();
+        okCallback_(thisPtr);
+    }
+##endif
 }

 void MysqlConnection::execSqlInLoop(
@@ -523,7 +608,10 @@ void MysqlConnection::execSqlInLoop(

 void MysqlConnection::outputError()
 {
-    channelPtr_->disableAll();
+    if (channelPtr_)
+    {
+        channelPtr_->disableAll();
+    }
     auto errorNo = mysql_errno(mysqlPtr_.get());
     LOG_ERROR << "Error(" << errorNo << ") [" << mysql_sqlstate(mysqlPtr_.get())
               << "] \"" << mysql_error(mysqlPtr_.get()) << "\"";
@@ -531,9 +619,23 @@ void MysqlConnection::outputError()
     if (isWorking_)
     {
         // TODO: exception type
-        auto exceptPtr = std::make_exception_ptr(
-            SqlError(mysql_error(mysqlPtr_.get()), sql_));
-        exceptionCallback_(exceptPtr);
+        auto exceptPtr =
+            std::make_exception_ptr(SqlError(mysql_error(mysqlPtr_.get()), sql_));
+        if (exceptionCallback_)
+        {
+            try
+            {
+                exceptionCallback_(exceptPtr);
+            }
+            catch (const std::exception &e)
+            {
+                LOG_ERROR << "exception callback threw: " << e.what();
+            }
+            catch (...)
+            {
+                LOG_ERROR << "exception callback threw unknown exception";
+            }
+        }
         exceptionCallback_ = nullptr;

         callback_ = nullptr;
@@ -551,6 +653,7 @@ void MysqlConnection::outputError()

 void MysqlConnection::startQuery()
 {
+##if defined(HAS_MYSQL_NONBLOCK_API)
     int err;
     // int mysql_real_query_start(int *ret, MYSQL *mysql, const char *q,
     // unsigned long length)
@@ -571,10 +674,14 @@ void MysqlConnection::startQuery()
         }
         startStoreResult(true);
     }
+##else
+    startQueryBlocking();
+##endif
 }

 void MysqlConnection::startStoreResult(bool queueInLoop)
 {
+##if defined(HAS_MYSQL_NONBLOCK_API)
     MYSQL_RES *ret;
     execStatus_ = ExecStatus::StoreResult;
     waitStatus_ = mysql_store_result_start(&ret, mysqlPtr_.get());
@@ -606,10 +713,14 @@ void MysqlConnection::startStoreResult(bool queueInLoop)
             getResult(ret);
         }
     }
+##else
+    (void)queueInLoop;
+##endif
 }

 void MysqlConnection::getResult(MYSQL_RES *res)
 {
+##if defined(HAS_MYSQL_NONBLOCK_API)
     auto resultPtr = std::shared_ptr<MYSQL_RES>(res, [](MYSQL_RES *r) {
         mysql_free_result(r);
     });
@@ -644,4 +755,126 @@ void MysqlConnection::getResult(MYSQL_RES *res)
             }
         }
     }
+##else
+    (void)res;
+##endif
+}
+
+void MysqlConnection::startQueryBlocking()
+{
+    execStatus_ = ExecStatus::None;
+    int err = mysql_real_query(mysqlPtr_.get(), sql_.c_str(), sql_.length());
+    if (err)
+    {
+        LOG_ERROR << "error:" << err;
+        outputError();
+        return;
+    }
+    MYSQL_RES *ret = mysql_store_result(mysqlPtr_.get());
+    if (!ret && mysql_errno(mysqlPtr_.get()))
+    {
+        outputError();
+        return;
+    }
+    getResultBlocking(ret);
+}
+
+void MysqlConnection::getResultBlocking(MYSQL_RES *res)
+{
+    auto resultPtr = std::shared_ptr<MYSQL_RES>(res, [](MYSQL_RES *r) {
+        mysql_free_result(r);
+    });
+    auto Result = makeResult(std::move(resultPtr),
+                             mysql_affected_rows(mysqlPtr_.get()),
+                             mysql_insert_id(mysqlPtr_.get()));
+    if (!isWorking_)
+    {
+        return;
+    }
+    try
+    {
+        callback_(Result);
+    }
+    catch (...)
+    {
+        auto exceptPtr = std::current_exception();
+        if (exceptionCallback_)
+        {
+            try
+            {
+                exceptionCallback_(exceptPtr);
+            }
+            catch (const std::exception &e)
+            {
+                LOG_ERROR << "exception callback threw: " << e.what();
+            }
+            catch (...)
+            {
+                LOG_ERROR << "exception callback threw unknown exception";
+            }
+        }
+        callback_ = nullptr;
+        exceptionCallback_ = nullptr;
+        isWorking_ = false;
+        idleCb_();
+        return;
+    }
+    while (mysql_more_results(mysqlPtr_.get()))
+    {
+        int err = mysql_next_result(mysqlPtr_.get());
+        if (err)
+        {
+            LOG_ERROR << "error:" << err;
+            outputError();
+            return;
+        }
+        MYSQL_RES *next = mysql_store_result(mysqlPtr_.get());
+        if (!next && mysql_errno(mysqlPtr_.get()))
+        {
+            outputError();
+            return;
+        }
+        auto nextPtr = std::shared_ptr<MYSQL_RES>(next, [](MYSQL_RES *r) {
+            mysql_free_result(r);
+        });
+        auto nextResult = makeResult(std::move(nextPtr),
+                                     mysql_affected_rows(mysqlPtr_.get()),
+                                     mysql_insert_id(mysqlPtr_.get()));
+        if (!isWorking_)
+        {
+            return;
+        }
+        try
+        {
+            callback_(nextResult);
+        }
+        catch (...)
+        {
+            auto exceptPtr = std::current_exception();
+            if (exceptionCallback_)
+            {
+                try
+                {
+                    exceptionCallback_(exceptPtr);
+                }
+                catch (const std::exception &e)
+                {
+                    LOG_ERROR << "exception callback threw: " << e.what();
+                }
+                catch (...)
+                {
+                    LOG_ERROR << "exception callback threw unknown exception";
+                }
+            }
+            callback_ = nullptr;
+            exceptionCallback_ = nullptr;
+            isWorking_ = false;
+            idleCb_();
+            return;
+        }
+    }
+    callback_ = nullptr;
+    exceptionCallback_ = nullptr;
+    isWorking_ = false;
+    idleCb_();
 }
diff --git a/orm_lib/src/mysql_impl/MysqlConnection.h b/orm_lib/src/mysql_impl/MysqlConnection.h
index 4ec0c618..5a1833bf 100644
--- a/orm_lib/src/mysql_impl/MysqlConnection.h
+++ b/orm_lib/src/mysql_impl/MysqlConnection.h
@@ -133,6 +133,8 @@ class MysqlConnection : public DbConnection,
         std::function<void(const std::exception_ptr &)> &&exceptCallback);
     void startSetCharacterSet();
     void continueSetCharacterSet(int status);
+    void startQueryBlocking();
+    void getResultBlocking(MYSQL_RES *res);
     std::unique_ptr<trantor::Channel> channelPtr_;
     std::shared_ptr<MYSQL> mysqlPtr_;
     std::string characterSet_;

六、这个方案的边界

这个方案可以解决“当前环境中无法编译、无法使用”的问题,但代价也很明确。

最需要提前说清楚的一点是:fallback 到阻塞实现以后,数据库操作会失去原来那套基于 *_start/*_cont 的非阻塞驱动方式。

这意味着:

  • 如果服务并发不高,这个方案通常可以满足需求
  • 如果查询很重,阻塞路径会阻塞该数据库连接所在的 EventLoop 线程
  • 如果本来就依赖 Drogon 这条异步 MySQL 路径的时延特性,这只是临时过渡,不是长期方案

标题里使用“兼容补丁”而不是“最终修复方案”,原因就在这里。

长期看,更稳的方向通常还是这几类:

  • 把 ROS 2 和后端构建环境拆开
  • 用容器把依赖隔离开
  • 等上游修复或自己整理成更完整的兼容提交
  • 在数据库客户端选型上彻底统一

如果当前目标只是让 ROS 2 开发环境和 Drogon 后端在同一台机器上完成构建并恢复基本可用性,这个补丁可以作为临时方案。 如果后续要进入生产环境或高并发场景,仍然应该回到依赖治理和运行模型本身解决问题。

结论

ROS 2 开发环境和 Drogon 放到同一套系统里时,真正的冲突点通常不在“数据库名字”,而在于 Drogon 对客户端库能力的假设过于具体。

临时处理思路可以概括为:

  • 让 CMake 先认出 mysqlclient
  • 再检测当前是否具备 mysql_real_connect_start 这类 *_start/*_cont 符号
  • 有就走原始异步路径
  • 没有就退回阻塞实现

这不是最终形态的解决方案,但足够直接,也符合眼前最实际的目标:先恢复构建、连接和基本运行能力。

参考

0

评论区