• Save
Learn How Disqus Does 'It' When 'It' Isn't Django
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Learn How Disqus Does 'It' When 'It' Isn't Django

on

  • 2,558 views

Over the past few years, Disqus has become one of the biggest Django apps in existence, crossing over a billion unique visitors a month. But sometimes Django isn't the right tool for the job.

Over the past few years, Disqus has become one of the biggest Django apps in existence, crossing over a billion unique visitors a month. But sometimes Django isn't the right tool for the job.

Join Disqus engineer Adam Hitchcock to learn how nginx modules and Lua can replace Python services, about the infrastructure that launched realtime and ad services, as well as about some of the failures they've encountered along the way.

** For more resources and a video of this presentation, head to http://mrkn.co/0h7ey

Statistics

Views

Total Views
2,558
Views on SlideShare
2,127
Embed Views
431

Actions

Likes
7
Downloads
0
Comments
0

6 Embeds 431

http://marakana.com 291
https://thenewcircle.com 120
http://harajuku-tech.org 10
https://twitter.com 8
http://localhost.marakana.com 1
http://crcl.to 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Learn How Disqus Does 'It' When 'It' Isn't Django Presentation Transcript

  • 1. Adam Hitchcock @NorthIsUp How DISQUS does ‘it’ when ‘it’ isn’t Django Tuesday, June 25, 13
  • 2. Tuesday, June 25, 13
  • 3. we’re hiring disqus.com/jobs If this is interesting to you... Tuesday, June 25, 13
  • 4. what is DISQUS? Tuesday, June 25, 13
  • 5. Tuesday, June 25, 13
  • 6. DISQUS sees a lot of traffic Google Analytics: Feb 2013 - March 2013 Tuesday, June 25, 13
  • 7. what makes up Disqus? Tuesday, June 25, 13
  • 8. mostly Python Tuesday, June 25, 13
  • 9. (and javascript) Tuesday, June 25, 13
  • 10. but mostly Python Tuesday, June 25, 13
  • 11. and we love it! Tuesday, June 25, 13
  • 12. why python? ๏ because it is fast… ๏ to develop in ๏ good community ๏ lots of libraries ๏ so active we can’t keep up with it ๏ is anybody using 3.3 yet? ๏ Disqus has a really good dev loop for it Tuesday, June 25, 13
  • 13. how does our dev loop work? ๏ diff code (with phabricator) ๏ get that reviewed ๏ review with phabricator ๏ ci done by jenkins ๏ land it on master (git push) ๏ wait for the daily deploy (ops team) ๏ and we are moving to autodeploy (we’ll revisit this one) Tuesday, June 25, 13
  • 14. break it down. ๏ mostly Django ๏ two years ago 95% Django* ๏ today ~70% Django* ๏ what is in that growing gap? ๏ *totally made up numbers Tuesday, June 25, 13
  • 15. how does this actually work at Disqus? Tuesday, June 25, 13
  • 16. disqus-web (kinda monolithic) ๏ Django + celery on postgres + rabbit ๏ we roll up commits for a deploy ๏ risk of revert that isn’t your fault ๏ you bring down the whole thing if you forgot to remove that pdb.settrace() ๏ high scrutiny in code review (see above) ๏ high volume of code review (code review RTT can be a full day) ๏ lots of legacy code to work around (and not break) Tuesday, June 25, 13
  • 17. problems that don’t fit ๏ high concurrency ๏ isolation ๏ feature ๏ failure ๏ speed (cpu cycles) ๏ speed (dev iteration cycle) ๏ fun Tuesday, June 25, 13
  • 18. what did we play with? Tuesday, June 25, 13
  • 19. for fun and concurrency! ๏ nginx + nginx-modules ๏ https://speakerdeck.com/northisup/ scaling-realtime-at-disqus?slide=39 ๏ nginx + lua ๏ https://github.com/NorthIsUp/nginx- oauth-on-dotcloud/blob/master/nginx/ access.persona.lua.in ๏ go ๏ http://blog.disqus.com/post/ 51155103801/trying-out-this-go-thing Tuesday, June 25, 13
  • 20. embedly in nginx + lua local http = require "resty.http" local cjson = require "cjson" local url = "https://api.embed.ly/1/oembed?key=" .. ngx.var.api_key .. "&url=" .. ngx.var.url local hc = http:new() local ok, code, headers, status, body = hc:request { url = url, method = "GET" } if code ~= 200 then ngx.exit(code) end local thumbnail = cjson.decode(body).thumbnail_url ngx.var.thumbnail = thumbnail Tuesday, June 25, 13
  • 21. python still works too Tuesday, June 25, 13
  • 22. isolation and iteration speed ๏ failure should be isolated to a small service ๏ successes should be allowed to occur quickly ๏ python is still great for these use cases Tuesday, June 25, 13
  • 23. when we needed it right now! ๏ if __name__ == ‘__main__’: ๏ from wsgiref.simple_server import make_server ๏ from xmlrpclib import ServerProxy Tuesday, June 25, 13
  • 24. it works great! ๏ for one deploy ๏ then you have to update it… Tuesday, June 25, 13
  • 25. lessons ๏ consistency is good! ๏ protects you from somebody quitting or getting hit by a bus ๏ anybody can just pick it up and run ๏ simplicity is good! ๏ modularity is good ๏ the ability to borrow/combine features from other projects ๏ but copypasta is bad ๏ and not bugging ops for a deploy is the Tuesday, June 25, 13
  • 26. enter disqus-service Tuesday, June 25, 13
  • 27. disqus-service ๏ the goal is consistent/free access to… ๏ config ๏ switches ๏ logging ๏ stats ๏ other systems ๏ and should be easy to run Tuesday, June 25, 13
  • 28. @service ๏ equivalent to if __name__ == ‘__main__’: ๏ that is basically it, you can now run it Tuesday, June 25, 13
  • 29. hello.py from disqus.service.application import service @service def world(): print "hello world" $ toil run hello.world hello world Tuesday, June 25, 13
  • 30. but you want more power ๏ class Service: ๏ @handler ๏ @pre_config ๏ @post_config ๏ @pre_setup ๏ @post_setup ๏ @pre_update_config ๏ @post_update_config Tuesday, June 25, 13
  • 31. hello-v2.0.py from disqus.service.application import (Service, handler, post_config) def World(Service): @handler def world(self): print self.redis.get("hello world") @post_config def setup_redis(self) self.redis = Redis( host=self.config.REDIS_HOST, port=self.config.REDIS_PORT ) world = World() Tuesday, June 25, 13
  • 32. mixins! ๏ FlaskService ๏ the service instance is also a wsgi app ๏ GeventService ๏ concurrency helpers ๏ RedisService ๏ KafkaService ๏ DjangoORMService (currently in progress) Tuesday, June 25, 13
  • 33. real life Disqus code! from __future__ import absolute_import from IPython.frontend.terminal.embed import InteractiveShellEmbed from disqus.service.application import handler import tempest from tempest.services.mixins.data import RedisMixin class Shell(RedisMixin): @handler def shell(self, *args, **kwargs): """ runs an ipython shell loaded in the tempest module """ ipshell = InteractiveShellEmbed() ipshell(local_ns=locals(), global_ns=globals(), module=tempest) shell = Shell() Tuesday, June 25, 13
  • 34. I want to talk more about mixins, because I really like them. So the following slides are about mixins, but not about services. Please use your imagination that I did not just copypasta them from my pycon slides Tuesday, June 25, 13
  • 35. data pipelines service class Pipeline(object): def parse(self, data): raise NotImplemented('No ParserMixin used') def compute(self, data, parsed_data): raise NotImplemented('No ComputeMixin used') def publish(self, data, parsed_data, computed_data): raise NotImplemented('No PublisherMixin used') def handle(self, data): parsed_data = self.parse(data) computed_data = self.compute(data, parsed_data) return self.publish(data, parsed_data, computed_data) Tuesday, June 25, 13
  • 36. example mixins class JSONParserMixin(Pipeline): def parse(self, data): return json.loads(data) class AnnomizeDataMixin(Pipeline): def compute(self, data, parsed_data): return {} class SuperSecureEncryptDataMixin(Pipeline): def compute(self, data, parsed_data): return parsed_data.encode('rot13') class HTTPPublisher(Pipeline): def publish(self, data, parsed_data, computed_data): u = urllib2.urlopen(self.dat_url, computed_data) return u class FilePublisher(Pipeline): def publish(self, data, parsed_data, computed_data): with open(self.output, 'a') as f: f.write(computed_data) Tuesday, June 25, 13
  • 37. final pipeline service class JSONAnnonHTTPPipeline( JSONParserMixin, AnnomizeDataMixin, HTTPPublisherMixin): pass class JSONSecureHTTPPipeline( JSONParserMixin, SuperSecureEncyptionMixin, HTTPPublisherMixin): pass class JSONAnnonFilePipeline( JSONParserMixin, AnnomizeDataMixin, FilePublisherMixin): pass Tuesday, June 25, 13
  • 38. what about tests? class JSONAnnonHTTPPipelineTest( BasePipelineTest, JSONParserMixinTest, AnnomizeDataMixinTest, HTTPPublisherMixinTest): pass Tuesday, June 25, 13
  • 39. open source as output ๏ we have lots of open source stuff ๏ sentry for errors ๏ nydus (for redis connections) ๏ django-mailviews ๏ gargoyle (and v2.0 renamed to gutter) ๏ and more! https://github.com/disqus/ ๏ not yet for disqus-service ๏ but I want your feedback! ๏ would you use actually it? Tuesday, June 25, 13
  • 40. psst, we’re hiring disqus.com/jobs If this was interesting to you... Tuesday, June 25, 13
  • 41. psst, we’re hiring disqus.com/jobs meetup.com/ArchCamp meetup.com/Disqus Tuesday, June 25, 13
  • 42. What did I just talk about? ๏ Lots of python at Disqus! ๏ But not all of it ๏ because sometimes other tools are better ๏ Services are awesome ๏ Mixins make testing super easy ๏ And we don’t have all the answers... Tuesday, June 25, 13
  • 43. questions? ๏ How do you run services? ๏ RPC vs HTTP vs Queues (I like queues) ๏ what do you use for highly concurrent systems? ๏ do you lint for print or pdb.settrace() or import debug ๏ if yes, can I have it? ๏ dev != prod, or does it? ๏ if you run uwsgi + nginx in prod ๏ why dev with ./manage.py runserver? @NorthIsUp Tuesday, June 25, 13