背景
后端接管到一个用户申请后,在申请其余第三方接口时,须要携带用户申请中的trace_id,以便后续梳理申请链路和定位问题。
原有的代码基于Python的Django框架开发,不同的用户申请之间是线程隔离的,不同用户申请的trace_id天然也不同。除此之外,为了便于读取trace_id所以将其存储为一个全局变量。基于这两点,原代码中采纳threading.local()
来存储trace_id。
为了放慢申请速度,须要通过多线程来并发申请第三方接口(PS:也能够应用协程,然而原有的接口调用封装并不反对异步,改变起来工作量太大)。然而因为threading.local()
本就是线程隔离的,所以子线程根本无法拿到父线程(申请线程)的trace_id,而后就会呈现报错:'_thread._local' object has no attribute 'trace_id'
。
在简略的搜寻当前,发现Java中自带InheritableThreadLocal
(与ThreadLocal
不同,应用InheritableThreadLocal
创立的变量的属性能够被子线程继承,以持续应用)。然而不分明为什么Python没有(PS:事实上stackoverflow下面也有发问,然而不晓得为什么没有答复),所以做了一个简略的实现。
代码实现
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import copy from _threading_local import local from threading import current_thread, Thread class InheritableThread(Thread): def __init__(self, *args, **kwargs): self.parent = current_thread() Thread.__init__(self, *args, **kwargs) def copy_parent_attribute(self): """ :raise AttributeError: parent may not exist """ parent = current_thread().parent parent_local = self._local__impl.dicts.get(id(parent)) if parent_local is not None: parent_dicts = parent_local[1] for key, value in parent_dicts.items(): self.__setattr__(key, copy.deepcopy(value)) class InheritableLocal(local): """ Please use InheritableThread to create threads, if you want your son threads can inherit parent threads' local """ def __new__(cls, *args, **kwargs): self = local.__new__(cls, *args, **kwargs) try: copy_parent_attribute(self) except AttributeError: # Some threads may not be created by InheritableThread pass return self def __getattribute__(self, item): try: return local.__getattribute__(self, item) except AttributeError as e: try: copy_parent_attribute(self) return local.__getattribute__(self, item) except AttributeError: raise e def __delattr__(self, item): try: return local.__delattr__(self, item) except AttributeError as e: try: copy_parent_attribute(self) return local.__delattr__(self, item) except AttributeError: raise e
- 为了可能在子线程中拜访父线程,所以须要在创立子线程时,将父线程记录下来:
self.parent = current_thread()
- 子线程在构建
InheritableLocal()
时,依据记录下来的父线程,将父线程中的InheritableLocal()
的属性复制过去,因为是InheritableLocal()
基于threading.local()
,所以这里复制时须要分明threading.local()
的构造
threading.local()
的构造:
应用办法
thread_ctx = InheritableLocal() t = InheritableThread(target=function, args=(arg1, arg2, arg3)) t.start() t.join()
- 应用
InheritableLocal()
代替threading.local()
来创立一个线程环境变量,没有其余副作用 - 应用
InheritableThread()
代替Thread()
来创立一个线程,同样没有副作用
而后创立进去的子线程就能够继承父线程的环境变量了,当然两个线程之间的环境变量还是互相隔离的,继承只会产生在子线程创立时。
本文由博客一文多发平台 OpenWrite 公布!