webapi,定时轮询多入口,多线程如何实现并发处理.

业务需求如下图. 一个业务既要通过webapi实时的接收处理任务, 又需要定时从数据库轮询, 还需要从socket接收数据后,
为了统一方便后面的编程. 和性能提高. 分析了一下, 程序结构设计如下.

在这里插入图片描述

分析后结果如下
在这里插入图片描述
数据处理线程是一样的任务, 线程再多也是一个类

业务入口则需要根据具体情况来实现 一个入口就是一个线程, 并且任务内容不同 . 需要是多个类.

本程序通过python实现的, webapi 用到了tornado框架,
定时循环则是简单的while循环

主程序启动代码 start.py

# 所有的数据都从 JobList 中获取的.
jobList =  JobList()


# 启动主干活的线程1
scoring =  DoScoringThread(jobList)
scoring.start()

# 启动主干活的线程2
scoring2 =  DoScoringThread(jobList)
scoring2.start()


# 启动主干活的线程3
scoring3 =  DoScoringThread(jobList)
scoring3.start()


# 从数据库循环取任务的线程
dbing =  FromDBLoopGetJobThread(jobList)
dbing.start()

# 开启webapi服务
WebAPIServer.StartWebAPIServe(jobList)

干活的线程…


import threading 
# from common.Job.JobList import GetOneJob
from common.Job.ScoringBLL import DoScoring
import time
import traceback

#干活的线程
class  DoScoringThread(threading.Thread):
    '''
        自动打分线程
    '''
    def __init__(self,jobList): 
        threading.Thread.__init__(self)
        self.jobList = jobList

        
    def run(self):
        while(True):     #这一层确保发生错误仍然执行循环
            try:
                self.DoLoop() #发生错误仍然执行循环
            except Exception as ex:
                traceback.print_exc()
                time.sleep(0.5) # 防止死循环消耗大量资源

    def DoLoop(self): 
        #循环去任务池中取任务
        while(True):
            jobkey = self.jobList.GetOneJob() 
            if(jobkey == None):
                print("线程", threading.currentThread().ident,"未取到任务,休眠...")
                self.jobList.getThreadEvent().wait()  # 没任务等待主线程的通知
            else:
                print("线程", threading.currentThread().ident,"取到任务,开始执行...")
                DoScoring(jobkey)# 

  

轮询数据库取任务的线程.

import threading  
import time
import traceback
# from common.Job.JobList import AppendJob


class  FromDBLoopGetJobThread(threading.Thread):
    '''
        自动从数据库取任务的线程
    '''
    def __init__(self,jobList):
        self.jobList = jobList
        threading.Thread.__init__(self)  

    def run(self):
        while(True):     #这一层确保发生错误仍然执行循环
            try:
                self.DoLoop() #发生错误仍然执行循环
            except Exception as ex:
                traceback.print_exc()
                time.sleep(0.5) # 防止死循环消耗大量资源  


    def DoLoop(self):
        
        #循环去任务池中取任务
        while(True):
            # 从数据库取数据
            jobkey = "任务1111"
            
            print("从DB新增任务",jobkey)

            self.jobList.AppendJob(jobkey)  #只要调用
            time.sleep(1)

  

其实入口的调用很简单. 只要调用下面的代码即可.其它的代码自由发挥.

   jobList.AppendJob(jobkey) # 添加的时候, 内部默认会通知工作的线程.

核心代码是 JobList.py 这个类

import threading


class  JobList( ):
    '''
        工作任务列表
    '''
    def __init__(self):
       self.threadevent =  threading.Event()
       self.lock = threading.RLock()
       self.JobList=[] # 任务列表
 

    def AppendJob(self,jobkey):
      '''
      追加任务
      '''   
      self.lock.acquire() #加锁

      self.JobList.append(jobkey)

      self.lock.release() # 解锁
      self.threadevent.set() # 通知所有工作的线程, 有活干了, 线程就会从wait后面开始执行调用 GetOneJob 来获取任务


    def getThreadEvent(self):
      '''
         获取事件,一般是工作线程用来调用wait用
      ''' 
      return self.threadevent
 

    def GetOneJob(self):
      '''
      获取一个任务
      '''    
      self.lock.acquire()#加锁

      jobkey = None
      if len(self.JobList) ==  0:
         self.threadevent.clear() # 通知所有工作的线程, 已经没有任务了不要再来取了. 其它线程运行到wait的时候就会挂起, 直到set被调用
      else:
         jobkey = self.JobList.pop(0)

      self.lock.release()# 释放线程锁
      return jobkey  

 

WebApi的入口代码也一样很简单, 我就不贴了.
在合适的地方调用即可

   jobList.AppendJob(jobkey) # 添加的时候, 内部默认会通知工作的线程.

主要是用的多线程中的 threading.Event() 和 threading.RLock(). 参考了文章
https://blog.csdn.net/qq_34139994/article/details/108416241
https://blog.csdn.net/u012067766/article/details/79734630

猜你喜欢

转载自blog.csdn.net/phker/article/details/112945900