Python通过HTTPS子域名连接Cassandra时遭遇ProtocolError求助
问题场景
我的Cassandra运行在虚拟机的9042端口,通过Nginx映射到HTTPS子域名mysub.original.com的443端口,使用以下Python代码尝试连接:
from cassandra.cluster import Cluster CASSANDRA_DB_HOST="mysub.original.com" CASSANDRA_DB_PORT = 443 CASSANDRA_DB_KEYSPACE = "test" table_name = "test_tab" def read_cassandra_table(host, port, keyspace, table): cluster = Cluster([host], port=port,protocol_version=4) session = cluster.connect(keyspace) session.shutdown() cluster.shutdown() read_cassandra_table(CASSANDRA_DB_HOST, CASSANDRA_DB_PORT, CASSANDRA_DB_KEYSPACE, table_name)
无论更换cassandra-driver版本,还是添加/移除protocol_version=4参数,都会触发以下错误:
python test.py Traceback (most recent call last): File "/home/poc/latest_abc_backend/abc-backend/test.py", line 101, in <module> read_cassandra_table(CASSANDRA_DB_HOST, CASSANDRA_DB_PORT, CASSANDRA_DB_KEYSPACE, table_name) File "/home/poc/latest_abc_backend/abc-backend/test.py", line 87, in read_cassandra_table session = cluster.connect(keyspace) ^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/poc/updated_venv/lib/python3.11/site-packages/cassandra/cluster.py", line 1690, in connect self.control_connection.connect() File "/home/poc/updated_venv/lib/python3.11/site-packages/cassandra/cluster.py", line 3488, in connect self._set_new_connection(self._reconnect_internal()) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/poc/updated_venv/lib/python3.11/site-packages/cassandra/cluster.py", line 3533, in _reconnect_internal raise NoHostAvailable("Unable to connect to any servers", errors) cassandra.cluster.NoHostAvailable: ('Unable to connect to any servers', {'155.69.11.95:443': ProtocolError('This version of the driver does not support protocol version 72')})
错误原因
核心问题是协议不兼容:Cassandra使用专属的原生二进制协议,而你用Nginx配置的是HTTPS(HTTP over TLS)服务。当Python driver连接443端口时,Nginx会将请求识别为HTTP请求并返回HTTP响应,driver误将响应中的某个字段当成了Cassandra的协议版本号,才会出现“不支持协议版本72”的异常——Cassandra的合法协议版本最高仅到v5,72完全不属于它的版本体系。
解决方案
1. 调整Nginx配置为TCP转发
需要使用Nginx的stream模块处理TCP流量,而非HTTP/HTTPS的server块,因为Cassandra是基于TCP的服务。如果需要加密传输,可选择以下两种方案:
方案1:Nginx stream模块做TLS终止
在Nginx配置中添加stream块,将443端口的TLS流量转发到虚拟机内的Cassandra 9042端口:
stream { server { listen 443 ssl; proxy_pass 127.0.0.1:9042; # 替换为虚拟机内Cassandra的实际地址和端口 # 配置SSL证书和密钥路径 ssl_certificate /path/to/your/cert.pem; ssl_certificate_key /path/to/your/key.pem; } }
注:大部分发行版的默认Nginx包已包含stream模块,若未包含需重新编译启用。
方案2:直接启用Cassandra自带SSL
若不想通过Nginx做TLS终止,可直接在Cassandra的cassandra.yaml配置文件中开启SSL,指定加密端口(默认9142),再让Nginx做纯TCP转发,或让driver直接连接Cassandra的SSL端口。
2. 修改Python连接代码
如果采用方案1的Nginx TLS终止配置,Python driver需要开启SSL连接,调整Cluster初始化参数:
import ssl from cassandra.cluster import Cluster def read_cassandra_table(host, port, keyspace, table): # 若使用自签名证书,需指定CA证书路径 ssl_context = ssl.create_default_context(cafile="/path/to/your/ca.pem") cluster = Cluster( [host], port=port, ssl=True, ssl_context=ssl_context ) session = cluster.connect(keyspace) session.shutdown() cluster.shutdown()
若证书是权威机构签发的,可省略ssl_context参数,直接设置ssl=True即可。
内容的提问来源于stack exchange,提问作者Dev acc

