Airflow:TypeError an integer is required (got type NoneType) 一次诡异问题排查
当使用rabbitmq作为airflow的broker的时候,启动scheduler,即执行airflow scheduler命令的时候抛出以下异常:
Traceback (most recent call last):
File "/anaconda/anaconda3/bin/airflow", line 27, in <module>
args.func(args)
File "/anaconda/anaconda3/lib/python3.6/site-packages/airflow/bin/cli.py", line 818, in scheduler
......
......
File "/anaconda/anaconda3/lib/python3.6/site-packages/kombu/connection.py", line 494, in _ensured
return fun(*args, **kwargs)
File "/anaconda/anaconda3/lib/python3.6/site-packages/kombu/messaging.py", line 203, in _publish
mandatory=mandatory, immediate=immediate,
File "/anaconda/anaconda3/lib/python3.6/site-packages/librabbitmq/__init__.py", line 122, in basic_publish
mandatory or False, immediate or False,
TypeError: an integer is required (got type NoneType)
整体环境描述:
python3.6 + apache-airflow1.9.0 + rabbitmq 3.6
因为使用redis作为broker是可以正常运行的,但是换成rabbitmq之后就出现了这种情况。尝试过对rabbitmq降版本,对airflow降低版本,发现依然无解,说明并不是软件版本兼容问题。
于是进一步排查,单独使用celery4.x进行调试,发现celery可以正常运行,但是到了airflow下就出现问题,说明是配置问题。
但是airflow的官方文档中配置是相当简陋的,而源码中相关的配置又相当的多,实在无法定位。于是采用最粗暴的做法,调试,但是服务器无法远程,没有外网端口,不能远程调试,而且airflow并不支持windows平台。于是只能通过日志调试:
首先,根据异常提示,说明有配置属性为空导致了这个异常,于是在异常处加上打印日志:
$ vi /anaconda/anaconda3/lib/python3.6/site-packages/librabbitmq/__init__.py
......
......
if isinstance(body, tuple):
body, properties = body
elif isinstance(body, self.Message):
body, properties = body.body, body.properties
print("---------------------------------------")
print(self.channel_id)
print("---------------------------------------")
print(body)
print("---------------------------------------")
print(exchange)
print("---------------------------------------")
print(routing_key)
print("---------------------------------------")
print(properties)
print("---------------------------------------")
print(mandatory)
print("---------------------------------------")
print(immediate)
print("---------------------------------------")
#if properties["priority"] is None: 加上这一句后就不会抛出异常了
# properties["priority"] = 0
return self.connection._basic_publish(
self.channel_id, body, exchange, routing_key, properties,
mandatory or False, immediate or False,
)
......
......
打印结果如下:
---------------------------------------
1
---------------------------------------
b'\x80\x02}q\x00(X\x04\x00\x00\x00taskq\x01X1\x00\x00\x00airflow.executors.celery_executor.execute_commandq\x02X\x02\x00\x00\x00idq\x03X$\x00\x00\x0077410b3a-75c6-4ba0-a448-7048c029e80cq\x04X\x04\x00\x00\x00argsq\x05]q\x06X\xcc\x00\x00\x00airflow run example_passing_params_via_test_command run_this 2018-07-02T01:04:00 --local -sd /anaconda/anaconda3/lib/python3.6/site-packages/airflow/example_dags/example_passing_params_via_test_command.pyq\x07aX\x06\x00\x00\x00kwargsq\x08}q\tX\x07\x00\x00\x00retriesq\nK\x00X\x03\x00\x00\x00etaq\x0bNX\x07\x00\x00\x00expiresq\x0cNX\x03\x00\x00\x00utcq\r\x88X\t\x00\x00\x00callbacksq\x0eNX\x08\x00\x00\x00errbacksq\x0fNX\t\x00\x00\x00timelimitq\x10NN\x86q\x11X\x07\x00\x00\x00tasksetq\x12NX\x05\x00\x00\x00chordq\x13Nu.'
---------------------------------------
default
---------------------------------------
celery
---------------------------------------
{'reply_to': 'd0552f0c-b341-30b6-9edb-fa9599715d6c', 'correlation_id': '77410b3a-75c6-4ba0-a448-7048c029e80c', 'delivery_mode': 2, 'content_type': 'application/x-python-serialize', 'content_encoding': 'binary', 'headers': {}, 'priority': None}
---------------------------------------
None
---------------------------------------
None
---------------------------------------
因为mandatory和immediate是bool类型,且都尝试过给其设置值,但是依然报错,最后还为None的就剩下priority属性为None了。于是尝试在代码中对其设置一个int类型的值,结果再次运行并无异常,说明缺少优先级属性导致了这个问题, 于是可以在priority属性为None的时候给它一个默认值:
......
......
if isinstance(body, tuple):
body, properties = body
elif isinstance(body, self.Message):
body, properties = body.body, body.properties
if properties["priority"] is None: #加上这一句后就不会抛出异常了
properties["priority"] = 0
return self.connection._basic_publish(
self.channel_id, body, exchange, routing_key, properties,
mandatory or False, immediate or False,
)
......
......
后续发现这样修改源码确实不会出现之前的问题,但是会出现
TypeError can't pickle memoryview objects
之类的异常,后确认是librabbitmq 2.0.0 + celery 4.x的兼容性问题,于是选择使用pyamqp协议而不是使用默认的amqp协议,具体操作就是将broker_url改为如下格式:
broker_url = pyamqp://cord:123456@10.55.63.51:5672//
### transport://userid:password@hostname:port/virtual_host
而将celery_result_backend改为其他实现,比如mysql:
celery_result_backend = db+mysql://af:123456@10.55.63.51/airflow
至此问题解决。
总结:
在排查问题的时候需要针对问题深入排查,而不是无根据的臆测,要针对提示去逐步排查,而不是一味无目的去尝试。
虽然该问题已解决,但是推测应该是缺失相关配置导致了这个问题,但是我加上了优先级相关的配置并不起作用,所以暂时通过修改源码修复这个问题。