Обзор фреймворка Twisted

  • View
    54

  • Download
    0

Embed Size (px)

Transcript

  • 1. Twisted Python Minsk Meetup 03.2014

2. C10K problem * = :( 3. 1 2 3 a X Y Z 1 a X b c 1 , IO 3 , IO block Y 2 b Y block 3 c Z 4. 3 , IO + GIL 1 a X 1 , IO 2 b Y 3 c Z 5. Event loop started exit()False sched[0][0] twistd -y pinger_1234.tac 33. twistd plugins class IServiceMaker(Interface): tapname = Attribute("A short string naming this Twisted plugin") description = Attribute("A brief summary of the features") options = Attribute("A C{twisted.python.usage.Options} subclass") def makeService(options): """ Create and return an object providing L{twisted.application.service.IService}. """ ~> twistd [opts] [plugin-opts] ~> twistd -l bs.log procmon buggy-script.py 34. Spread perspective broker (RPC) (jelly / banana) (Referencable) (Copyable) (Cacheable) 35. from twisted.spread import pb from twisted.application import service, internet class Formatter(pb.Referenceable): def __init__(self, format_spec): self.format_spec = format_spec def remote_formatIt(self, value): return format(value, self.format_spec) class ServerObject(pb.Root): def remote_getFormatter(self, format_spec): return Formatter(format_spec) application = service.Application("pb-server") sobj = ServerObject() bs = internet.TCPServer(8800, pb.PBServerFactory(sobj)) bs.setServiceParent(application) 36. from twisted.internet import defer, task from twisted.spread import pb @defer.inlineCallbacks def main(reactor): cf = pb.PBClientFactory() reactor.connectTCP("localhost", 8800, cf) root = yield cf.getRootObject() fmt = yield root.callRemote('getFormatter', ".2f") res = yield fmt.callRemote('formatIt', 1.2345) print(res) if __name__ == '__main__': task.react(main) 37. websocket, memcache, redis, riak, couchdb, cassandra, postgresql, amqp, stomp, solr, xmpp, oscar, msn, snmp, smpp, ldap, webdav 38. Threads class IReactorThreads(Interface): def getThreadPool(): "Return the threadpool used by L{callInThread}." def callInThread(callable, *args, **kwargs): "Run the callable object in a separate thread." def callFromThread(callable, *args, **kw): "Cause a function to be executed by the reactor." def suggestThreadPoolSize(size): "Suggest the size of the internal threadpool." Helpers: blockingCallFromThread(reactor, f, *a, **kw) deferToThread(f, *args, **kwargs) callMultipleInThread(tupleList) 39. adbapi from twisted.enterprise import adbapi from twisted.internet import defer, task dbpool = adbapi.ConnectionPool('sqlite3', "file.db", check_same_thread=False) @defer.inlineCallbacks def useless_work(): yield dbpool.runOperation( "CREATE TABLE Users (name, nick)") yield dbpool.runOperation( "INSERT INTO Users (name, nick) VALUES (?, ?)", ["Andrei", "anjensan"]) nick = yield dbpool.runQuery( "SELECT nick FROM Users WHERE name = ?", ["Andrei"]) print(nick) 40. adbapi def txn_work_with_cursor(cursor, values): # cursor.executemany( "INSERT INTO Users (name, nick) VALUES (?, ?)", values, ) threads.blockingCallFromThread(reactor, lambda: task.deferLater(reactor, 3, int)) # cursor.execute("SELECT COUNT(*) FROM Users") return cursor.fetchone()[0] @defer.inlineCallbacks def useless_work2(): row = yield dbpool.runInteraction( txn_work_with_cursor, [("Ivan", "ivann"), ("Petr", "pettr")]) print(row) 41. Twisted Web HTTP server WSGI (threads!) XMLRPC (pb) HTML () HTTP 42. from twisted.internet import reactor from twisted.application import service, internet from twisted.web.resource import Resource from twisted.web.server import NOT_DONE_YET, Site import time class ClockResource(Resource): isLeaf = True def render_GET(self, request): reactor.callLater(2, self._delayed_render, request) return NOT_DONE_YET def _delayed_render(self, request): request.write("%s" % time.ctime()) request.finish() resource = ClockResource() site = Site(resource) application = service.Application("hello-world") internet.TCPServer(8080, site).setServiceParent(application) 43. class ClockResource(Resource): def _delayed_body(self, requset): return task.deferLater(reactor, 2, lambda: str(time.ctime())) def send_response(self, body, request): request.write(body) request.finish() def fail_response(self, fail, request): if fail.check(defer.CancelledError): return request.setResponseCode(501) request.write(str(fail)) request.finish() def response_failed(self, err, d): d.cancel() err.trap(error.ConnectionDone) def render_GET(self, request): d = self._delayed_body(request) request.notifyFinish().addErrback(self.response_failed, d) d.addCallback(self.send_response, request) d.addErrback(self.fail_response, request) return NOT_DONE_YET 44. Resource tree Site (extends HTTPFactory) /foo 'foo' /bar /bar/ham /bar/fam/.../..... 'bar' 'ham' 'fam' /foo/ getChild(path) /foo/abc 'abc' /bar/ '' isLeaf = 1 / '' 45. from twisted.internet import reactor from twisted.application import internet, service from twisted.web import static, server, twcgi, wsgi, proxy def wsgi_hello_world(environ, start_response): start_response('200 OK', [('Content-type','text/plain')]) return ["Hello World!"] root = static.File("/var/www/htdocs") root.putChild("about", static.Data("lorem ipsum", 'text/plain')) root.putChild("doc", static.File("/usr/share/doc")) root.putChild("cgi-bin", twcgi.CGIDirectory("/var/www/cgi-bin")) root.putChild("tut.by", proxy.ReverseProxyResource("tut.by", 80, "")) hw_resource = wsgi.WSGIResource( reactor, reactor.getThreadPool(), wsgi_hello_world) root.putChild("hello-world", hw_resource) site = server.Site(root) ss = internet.TCPServer(8080, site) application = service.Application('web') ss.setServiceParent(application) 46. ~> twistd web --wsgi my_module.wsgi_app ~> twistd web --path ~/my/directory/ WSGI + *.{rpy,epy} ~> twistd web --resource-script myfile.rpy Resource () *.rpy ~> twistd web --class my_module.ResourceClass Resource () 47. import sys from StringIO import StringIO from twisted.internet import reactor, task, defer from twisted.web import client @defer.inlineCallbacks def send_post(reactor, url, data): agent = client.Agent(reactor) request_body = client.FileBodyProducer(StringIO(data)) response = yield agent.request( 'POST', url, body_producer=request_body) response_body = yield client.readBody(response) print("code:", response.code) print("response_body:", response_body) if __name__ == '__main__': task.react(send_post, sys.argv[1:]) HTTP 48. import sys from twisted.internet import defer, task from twisted.web import template from twisted.python.filepath import FilePath class MyElement(template.Element): loader = template.XMLFile(FilePath("template.xml")) @template.renderer def title(self, request, tag): return tag("Title") @template.renderer def widgets(self, request, tag): for wn in ["first", "second", "third"]: yield task.deferLater( reactor, 1, lambda: tag.clone().fillSlots(widget_name=wn)) def main(reactor): return template.flatten(None, MyElement(), sys.stdout.write) task.react(main)

Twisted web templates 49. Twisted projects Core Asynchronous event loop and networking framework. Web An HTTP protocol implementation. Conch An SSH and SFTP protocol implementation. Mail An SMTP, IMAP and POP protocol implementation. Names A DNS protocol implementation with client and server. News An NNTP protocol implementation with client and server. Words Chat and Instant Messaging. Runner Process management, including an inetd server. Pair Low-level networking transports and utilities. Trial Unittest-compatible automated testing. 50. In a Nutshell, Twisted... has had 16,823 commits made by 86 contributors representing 196,503 lines of code took an estimated 51 years of effort (COCOMO model) starting with its first commit in July, 2001 http://ohloh.net/p/twisted