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的硬件资源.以 ...
随机推荐
- Difference between Linearizability and Serializability
原文:http://stackoverflow.com/questions/8200015/what-is-the-difference-between-serializability-and-lin ...
- pthreads 0.1.0 测试报告
1 可以说已经稳定了 2 发现一个算是技巧的东西吧:在线程之间传递的类的实例,要保证能正常工作,需要类本身extends Stackable,所有方法都弄成public--我原来了写了一个数据库操作类 ...
- SuperGridControl 使用小技巧
1.显示行号 superGridControl1.PrimaryGrid.ShowRowGridIndex = true; 2.允许调整行头的宽度 superGridControl1.PrimaryG ...
- UVA 10294 等价类计数
题目大意: 项链和手镯都是若干珠子穿成的环形首饰,手镯可以旋转和翻转,但项链只能旋转,给n个珠子,t种颜色,求最后能形成的手镯,项链的数量 这里根据等价类计数的polya定理求解 对于一个置换f,若一 ...
- awt可视化界面上传数据到mysql,jsp通过jdbc方式查询数据库,并将结果打印在网页上
今天尝试写一个小demo实现下之前看过的代码,目的了解不同文件的数据访问,掌握如何获取前台数据,如何将数据库的数据在前端页面展示. awt可视化界面可已实现提交数据到数据库,也可查询数据在控制台打印. ...
- [转]source inslght使用指导
作为一个开放源代码的操作系统,Linux附带的源代码库使得广大爱好者有了一个广泛学习.深入钻研的机会,特别是Linux内核的组织极为复杂,同时,又不能像windows平台的程序一样,可以使用集成开发环 ...
- swing Event-Listener-Adapter 对照表
Source Event Event Listener AbstractButton (JButton,JToggleButton, JCheckBox,JRadioButton ActionEven ...
- <select>标签使用方法
前台页面: <form id="form1" runat="server"> <select runat="server" ...
- 弹框工作区(dialog)
弹出窗口分为普通弹出窗口和模态弹出窗口,普通弹出窗口可以铜鼓taskBar组件进行最小化等操作.弹出的窗口的DOM结构会放入主页面的body中,结构如下: <div class="bj ...
- Android 自带图标库 android.R.drawable
在xml文件中调用. android:title="@string/secure_connect"android:orderInCategory="100"an ...