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

至此问题解决。

总结:

​ 在排查问题的时候需要针对问题深入排查,而不是无根据的臆测,要针对提示去逐步排查,而不是一味无目的去尝试。

​ 虽然该问题已解决,但是推测应该是缺失相关配置导致了这个问题,但是我加上了优先级相关的配置并不起作用,所以暂时通过修改源码修复这个问题。

posted @ 2018-07-03 19:01  堕落门徒  阅读(1873)  评论(0编辑  收藏  举报