twisted的defer模式和线程池
前言:
最近帮朋友review其模块服务代码, 使用的是python的twisted网络框架. 鉴于之前并没有使用过, 于是决定好好研究一番.
twisted的reactor模型很好的处理了网络IO事件, 以及定时任务触发. 但包处理后的业务逻辑操作, 需要根据具体的场景来决定.
本文将讲述twisted如何实现half-sync/half-async的模式, 其线程池和defer模式是如何设计和使用的.
场景构造:
twisted服务接受业务请求, 后端需要访问mysql. 由于mysql的接口是同步的, 如果安装twisted默认的方式处理话, 其业务操作(mysql)会阻塞reactor的IO事件循环. 这大大降低了twisted的服务能力.
为了解决该类问题, twisted支持线程池. 把业务逻辑和IO事件分离, IO操作依旧是异步的, 而业务逻辑则采用线程池来处理.
工作线程池:
在具体讲述defer模式之前, 先谈谈reactor自带的线程池, 这也符合使用half-sync/half-async模式的直观理解.
先来构造下一个基础样例代码:
#! /usr/bin/python
#-*- coding: UTF-8 -*- from twisted.internet import reactor
from twisted.internet import protocol
from twisted.protocols.basic import LineReceiver import time class DemoProtocol(LineReceiver): def lineReceived(self, line):
# 进行数据包的处理
reactor.callInThread(self.handle_request, line) def handle_request(self, line):
"""
hanlde_request:
进行具体的业务逻辑处理
"""
# 边使用sleep(1)来代替模拟
time.sleep(1)
# 借助callFromThread响应结果
reactor.callFromThread(self.write_response, line) def write_response(self, result):
self.transport.write("ack:" + str(result) + "\r\n") class DemoProtocolFactory(protocol.Factory):
def buildProtocol(self, addr):
return DemoProtocol() reactor.listenTCP(9090, DemoProtocolFactory())
reactor.run()
DemoProtocol在收到一行消息, 需要处理一个业务需耗时一秒, 于是其调用callInThread来借助reactor的线程池来执行.
其callInThread的函数定义如下:
def callInThread(self, _callable, *args, **kwargs):
self.getThreadPool().callInThread(_callable, *args, **kwargs)
从中, 我们可以印证之前的观点, 借助线程池来完成耗时阻塞的业务工作.
再来看一下callFromThread的函数定义:
def callFromThread(self, f, *args, **kw):
assert callable(f), "%s is not callable" % (f,)
self.threadCallQueue.append((f, args, kw))
self.wakeUp()
其作用是把回调放入主线程(也是reactor主事件循环)的待执行队列中, 并及时唤醒reactor.
我们把写入响应的操作放入主循环中, 是为了让IO集中在主循环中进行, 避免潜在的线程不安全的问题.
defer模式:
直接使用reactor的线程池, 非常容易实现half-sync/half-async的模式, 也让IO和业务逻辑隔离. 但reactor设计之初, 更倾向于隐藏其内部的线程池. 于是其引入了defer模式.
让我们实现与上等同的代码片段:
#! /usr/bin/python
#-*- coding: UTF-8 -*- from twisted.internet import reactor
from twisted.internet import protocol
from twisted.protocols.basic import LineReceiver
from twisted.internet.threads import deferToThread import time class DemoProtocol(LineReceiver): def lineReceived(self, line):
# 进行数据包的处理
deferToThread(self.handle_request, line).addCallback(self.write_response) def handle_request(self, line):
"""
hanlde_request:
进行具体的业务逻辑处理
"""
# 边使用sleep(1)来代替模拟
time.sleep(1)
return line def write_response(self, result):
self.transport.write("ack:" + str(result) + "\r\n") class DemoProtocolFactory(protocol.Factory):
def buildProtocol(self, addr):
return DemoProtocol() reactor.listenTCP(9090, DemoProtocolFactory())
reactor.run()
使用defer后, 代码更加的简洁. 其defer对象, 其实借用了线程池.
threads.deferToThread定义如下:
def deferToThread(f, *args, **kwargs):
from twisted.internet import reactor
return deferToThreadPool(reactor, reactor.getThreadPool(),
f, *args, **kwargs) def deferToThreadPool(reactor, threadpool, f, *args, **kwargs):
d = defer.Deferred() def onResult(success, result):
if success:
reactor.callFromThread(d.callback, result)
else:
reactor.callFromThread(d.errback, result) threadpool.callInThreadWithCallback(onResult, f, *args, **kwargs) return d
这边我们可以发现deferToThread, 就是间接调用了callInThread函数, 另一方面, 对其回调函数的执行结果, 进行了onCallback, 以及onErrback的调用. 这些回调函数在主线程中运行.
defer模式简化了程序编写, 也改变了人们开发的思维模式.
测试回顾:
使用telnet进行测试, 结果正常.
另一方面, twisted的线程池, 其默认是采用延迟初始化的方式.
服务开启时, 只有主线程一个, 随着请求的到来, 其按需产生更多的worker thread.
而其线程池默认为10. 我们可以借助suggestThreadPoolSize方法来修改.
写在最后:
如果你觉得这篇文章对你有帮助, 请小小打赏下. 其实我想试试, 看看写博客能否给自己带来一点小小的收益. 无论多少, 都是对楼主一种由衷的肯定.
twisted的defer模式和线程池的更多相关文章
- 适配器、工厂模式、线程池、线程组、互斥锁、Timer类、Runtime类、单例设计模式(二十四)
1.多线程方法 * Thread 里面的俩个方法* 1.yield让出CPU,又称为礼让线程* 2.setPriority()设置线程的优先级 * 优先级最大是10,Thread.MAX_PRIORI ...
- Java多线程设计模式(4)线程池模式
前序: Thread-Per-Message Pattern,是一种对于每个命令或请求,都分配一个线程,由这个线程执行工作.它将“委托消息的一端”和“执行消息的一端”用两个不同的线程来实现.该线程模式 ...
- 【C#多线程】2.线程池简述+两种传统的异步模式
线程池简述+两种传统的异步编程模式 1.线程池简述 首先我们要明确一点,编程中讲的线程与平时我们形容CPU几核几线程中的线程是不一样的,CPU线程是指逻辑处理器,比如4核8线程,讲的是这个cpu有8个 ...
- C# 多线程线程池( 一 )
我们将在这里进一步讨论一些.NET类,以及他们在多线程编程中扮演的角色和怎么编程.它们是: System.Threading.ThreadPool 类 System.Threading.Timer 类 ...
- MYSQL线程池总结(一)
线程池是Mysql5.6的一个核心功能,对于服务器应用而言,无论是web应用服务还是DB服务,高并发请求始终是一个绕不开的话题.当有大量请求并发访问时,一定伴随着资源的不断创建和释放,导致资源利用率低 ...
- android线程池ThreadPoolExecutor的理解
android线程池ThreadPoolExecutor的理解 线程池 我自己理解看来.线程池顾名思义就是一个容器的意思,容纳的就是ThreadorRunable, 注意:每一个线程都是需要CPU分配 ...
- MySQL具体解释(7)-----------MySQL线程池总结(一)
线程池是Mysql5.6的一个核心功能.对于server应用而言,不管是web应用服务还是DB服务,高并发请求始终是一个绕不开的话题.当有大量请求并发訪问时,一定伴随着资源的不断创建和释放.导致资源利 ...
- 【温故而知新-万花筒】C# 异步编程 逆变 协变 委托 事件 事件参数 迭代 线程、多线程、线程池、后台线程
额基本脱离了2.0 3.5的时代了.在.net 4.0+ 时代.一切都是辣么简单! 参考文档: http://www.cnblogs.com/linzheng/archive/2012/04/11/2 ...
- Linux高性能server规划——处理池和线程池
进程池和线程池 池的概念 由于server的硬件资源"充裕".那么提高server性能的一个非常直接的方法就是以空间换时间.即"浪费"server的硬件资源.以 ...
随机推荐
- sql语句查询重复的数据
查找所有重复标题的记录: SELECT *FROM t_info aWHERE ((SELECT COUNT(*)FROM t_infoWHERE Title = a.Title) > 1)OR ...
- $(":input").each()和$.each()的区别
<body> <form action="strutsAction" method="get"> <input ...
- C char** 的一点儿理解
理解是就是char** 相当于字符串数组,我以往纠结于该用 **arr还是*arr还是 (*arr),还是(**arr): 对于**arr而言:*arr代表数组的最开头,也就是第一个字串的内容.**a ...
- C++定义构造函数必须使用初始化列表的场合
明其理,而知其然也. 先给理论.1. 初始化 != 赋值. a.初始化代表为变量分配内存. 变量在其定义处被编译器初始化(编译时). 在函数中, 函数参数初始化发生在函数调用时(运行时). b.赋值代 ...
- 使用ASP.Net WebAPI构建REST服务(二)——路由
REST并没有像传统的RPC服务那样显式指定了服务器函数的访问路径,而是将URL根据一定的规则映射为服务函数入口,这个规则就称之为路由.Asp.Net WebAPI的路由方式和Asp.Net MVC是 ...
- C++-继承名称的掩盖
/////////////////////////////////////////////////////////////////////////////// // // FileName : eff ...
- 上次遗留下来的XMLUtil的问题
·在上周留下了一个关于XMLUtil的问题,问题大概是这样的,需要通过读取一个XML文件,然后在内存中生成一个对应的javaBean.之前写的那个很是糟糕,照着一个XML去写了一个"Util ...
- hdu 1033 (bit masking, utilization of switch, '\0' as end of c string) 分类: hdoj 2015-06-15 21:47 37人阅读 评论(0) 收藏
bit masking is very common on the lower level code. #include <cstdio> #include <algorithm&g ...
- Project和Module的介绍
Project 和 Module 介绍 这两个概念是 IntelliJ IDEA 的必懂知识点之一,请务必要学会. 如果你是 Eclipse 用户,并且已经看了上面给的链接,那 IntelliJ ID ...
- Python单元测试——unittest
unittest是python自带的一个模块 python344\Lib\unittest 官方参考文档: http://docs.python.org/2.7/library/unittest.ht ...