前言:
  最近帮朋友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模式和线程池的更多相关文章

  1. 适配器、工厂模式、线程池、线程组、互斥锁、Timer类、Runtime类、单例设计模式(二十四)

    1.多线程方法 * Thread 里面的俩个方法* 1.yield让出CPU,又称为礼让线程* 2.setPriority()设置线程的优先级 * 优先级最大是10,Thread.MAX_PRIORI ...

  2. Java多线程设计模式(4)线程池模式

    前序: Thread-Per-Message Pattern,是一种对于每个命令或请求,都分配一个线程,由这个线程执行工作.它将“委托消息的一端”和“执行消息的一端”用两个不同的线程来实现.该线程模式 ...

  3. 【C#多线程】2.线程池简述+两种传统的异步模式

    线程池简述+两种传统的异步编程模式 1.线程池简述 首先我们要明确一点,编程中讲的线程与平时我们形容CPU几核几线程中的线程是不一样的,CPU线程是指逻辑处理器,比如4核8线程,讲的是这个cpu有8个 ...

  4. C# 多线程线程池( 一 )

    我们将在这里进一步讨论一些.NET类,以及他们在多线程编程中扮演的角色和怎么编程.它们是: System.Threading.ThreadPool 类 System.Threading.Timer 类 ...

  5. MYSQL线程池总结(一)

    线程池是Mysql5.6的一个核心功能,对于服务器应用而言,无论是web应用服务还是DB服务,高并发请求始终是一个绕不开的话题.当有大量请求并发访问时,一定伴随着资源的不断创建和释放,导致资源利用率低 ...

  6. android线程池ThreadPoolExecutor的理解

    android线程池ThreadPoolExecutor的理解 线程池 我自己理解看来.线程池顾名思义就是一个容器的意思,容纳的就是ThreadorRunable, 注意:每一个线程都是需要CPU分配 ...

  7. MySQL具体解释(7)-----------MySQL线程池总结(一)

    线程池是Mysql5.6的一个核心功能.对于server应用而言,不管是web应用服务还是DB服务,高并发请求始终是一个绕不开的话题.当有大量请求并发訪问时,一定伴随着资源的不断创建和释放.导致资源利 ...

  8. 【温故而知新-万花筒】C# 异步编程 逆变 协变 委托 事件 事件参数 迭代 线程、多线程、线程池、后台线程

    额基本脱离了2.0 3.5的时代了.在.net 4.0+ 时代.一切都是辣么简单! 参考文档: http://www.cnblogs.com/linzheng/archive/2012/04/11/2 ...

  9. Linux高性能server规划——处理池和线程池

    进程池和线程池 池的概念 由于server的硬件资源"充裕".那么提高server性能的一个非常直接的方法就是以空间换时间.即"浪费"server的硬件资源.以 ...

随机推荐

  1. 下拉刷新列表添加SwipeDismissListViewTouchListener实现滑动删除某一列。

    <Android SwipeToDismiss:左右滑动删除ListView条目Item> Android的SwipeToDismiss是github上一个第三方开源框架(github上的 ...

  2. Apache启用性能优化——启用Gzip,JS压缩

    #Add deflate module for enable GZIP function LoadModule deflate_module     modules/mod_deflate.so #A ...

  3. python开发规则

    1.Python优点:简单.优雅.明确 python缺点 2.强大的模块三房库 1.代码不能加密 3.易移植 2.速度慢 4.面向对象 5.可扩展(c\java\c#....) cpython ipy ...

  4. Android Phonebook编写联系人UI加载及联系人保存流程(五)

    2014-01-07 10:46:30 将百度空间里的东西移过来. 在前面的文章中我们分析了UI的加载,其中提到了一个重要的对象:RawContactDeltaList mState,我前面说过这个对 ...

  5. Oracle连接的若干错误

    用PL/SQL连接Oracle时会抛若干错误,如下: 1.ora-12154:TNS:无法解析指定的连接标识符 答:plsql在%Oracle_Home%\Network\Admin或者c:\inst ...

  6. COleDateTime类型的应用

    使用COleDateTime类1) 获取当前时间.      CTime time;      time = CTime::GetCurrentTime();2) 获取时间元素.      int y ...

  7. MonoRail学习:可重复组件ViewComponents的使用

    在MonoRail中我们可以定义一些可重用的组件,在其他需要使用的页面引入这个组件就可以了.有点相当于.NET中的自定义控件,可以节约代码,方便开发,提高重用性. 在MonoRail中把这一功能叫做V ...

  8. java static

    一. static代表着什么 在Java中并不存在全局变量的概念,但是我们可以通过static来实现一个“伪全局”的概念,在Java中static表示“全局”或者“静态”的意思,用来修饰成员变量和成员 ...

  9. 每天学一点JAVA

    1.JAVA的反射机制 在运行时判断任意一个对象所属的类:在运行时构造任意一个类的对象:在运行时判断任意一个类所具有的成员变量和方法:在运行时调用任意一个对象的方法:生成动态代理. 2.关于ARRAY ...

  10. shell指令expr和test指令

    通过expr指令可以进行+.-.*.\.%等运算,但是有一点值得注意,使用乘法时,要在*前加上一个\符号. 通过test指令可以进行逻辑测试,进行测试的情况有四种: 1.整数测试 a.判断两个整数是否 ...