使用SQLAlchemy连接ClickHouse遇UnexpectedPacketFromServerError求助
问题分析与解决方案
核心原因:协议不匹配
你遇到的asynch.errors.UnexpectedPacketFromServerError错误,本质是客户端使用的协议与目标端口的服务协议不兼容。
你用clickhouse+asynch:// URI时,底层是基于clickhouse-driver的原生TCP协议通信,但DataGrid能正常连接31123端口,说明这个端口对应的是ClickHouse的HTTP协议端口(默认是8123),而非原生TCP端口(默认是9000)。两种协议的数据包格式完全不同,因此会出现"未知数据包"的错误。
解决方案
方案1:切换到正确的TCP端口
确认你的ClickHouse服务的原生TCP端口(通常是9000,若修改过则对应实际端口),将URI中的端口替换为该TCP端口:
self.ch_uri: str = "clickhouse+asynch://admin:Password123@host:[TCP_PORT]/db"
方案2:改用HTTP协议连接(同步/异步适配)
如果必须使用31123端口(HTTP端口),则需要调整连接协议:
- 同步场景:使用
clickhouse+http://URI:from sqlalchemy import create_engine engine = create_engine("clickhouse+http://admin:Password123@host:31123/db") - 异步场景:由于
asynch库仅支持原生TCP协议,若要通过HTTP异步连接,可改用aiohttp配合ClickHouse的HTTP API,或者使用支持异步HTTP的SQLAlchemy适配器(如sqlalchemy-clickhouse-http,需自行安装)。
额外代码修复:Session管理错误
你的代码中存在Session复用的问题:初始化AsyncDatabase时创建了一个全局ch_session,但get_ch_session每次返回这个已创建的Session,关闭后该Session就会失效,后续无法再使用。正确的做法是在上下文管理器中每次创建新的Session:
@contextlib.asynccontextmanager async def get_ch_session() -> typing.AsyncGenerator[AsyncSession, None]: # 每次请求创建新的Session session = AsyncSession(bind=async_db.ch_engine) try: yield session # 正常执行时提交 await session.commit() except Exception as e: print(traceback.print_exc()) await session.rollback() finally: await session.close()
验证步骤
- 先确认ClickHouse服务的端口映射:查看ClickHouse配置文件
config.xml中的tcp_port(原生TCP)和http_port(HTTP)配置项。 - 根据端口对应的协议选择正确的URI格式。
- 修复Session管理逻辑,避免复用已关闭的Session。
内容的提问来源于stack exchange,提问作者Nguyễn Đức Huy
相关产品推荐
相关产品推荐

