├── .gitignore ├── README.md ├── djangowebsocket ├── __init__.py ├── view.py └── wsgi.py ├── example ├── apps │ ├── __init__.py │ ├── web │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tests.py │ │ └── views.py │ └── websocket │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── migrations │ │ └── __init__.py │ │ ├── models.py │ │ ├── tests.py │ │ └── views.py ├── example │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ ├── wsgi.py │ └── wsgi_websocket.py ├── manage.py ├── nginx.conf ├── redis_sessions │ ├── __init__.py │ ├── session.py │ └── settings.py ├── templates │ ├── chat.html │ └── login.html └── uwsgi-config │ ├── uwsgi.ini │ └── vassals │ ├── web-uwsgi.ini │ └── websocket-uwsgi.ini └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | ### Python template 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | .idea 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DjangoWebsocket 2 | 3 | 在Django中使用Websocket 4 | 5 | 需要 6 | 7 | * uwsgi 8 | * gevent 9 | * redis 10 | 11 | 12 | #### 完善中... 13 | 14 | 15 | ## Usage 16 | 17 | 18 | #### wsgi 19 | 20 | 在项目目录中创建一个 `wsgi_websocket.py` 文件, 建议与 django自己的 `wsgi.py` 放在相同目录下 21 | 22 | 内容如下: 23 | 24 | ```python 25 | import os 26 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") 27 | 28 | from djangowebsocket.wsgi import get_wsgi_application 29 | application = get_wsgi_application() 30 | 31 | ``` 32 | 33 | #### view 34 | 35 | view要继承 `djangowebsocket.view.WebSocketView` 36 | 37 | 并且实现四个方法 38 | 39 | 例子: 40 | 41 | ```python 42 | 43 | from djangowebsocket.view import WebSocketView 44 | 45 | class MyView(WebSocketView): 46 | def on_connect(self): 47 | # 客户端链接上来会调用的函数 48 | # 在这里做一些初始化工作 49 | # 50 | # 属性: 51 | # self.request django WSGIRequest 52 | # self.redis redis client 53 | # self.channel redis pubsub endpiont 54 | # 55 | # 方法: 56 | # self.send(text) 向客户端发送text数据 57 | # self.send_binary(binary) 向客户端发送binary数据 58 | # self.publish_global(text) 向所有链接的客户端广播数据 59 | 60 | def on_websocket_data(self, data): 61 | # 从client接收到的数据 62 | 63 | def on_channel_data(self, data): 64 | # 从 redis pubsub 中获取的数据 65 | 66 | def on_connection_lost(self): 67 | # 客户端断开链接 68 | 69 | ``` 70 | 71 | #### urls 72 | 73 | 将 你的url 映射到 `myapp.views.MyView.as_view()` 74 | 75 | 76 | ## Example 77 | example 是一个聊天例子, 在这里可以测试: http://114.215.129.77:8111 78 | 79 | 80 | 81 | ## 注意 82 | 83 | uwsgi 跑在 gevent 的事件循环上,每个客户端链接跑在一个greenlet中, 84 | 所以当链接的客户端数量 大于 uwsgi 配置中的 processes * gevent 总数,新客户端就不能链接 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /djangowebsocket/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __author__ = 'Wang Chao' 4 | __date__ = '15-1-9' 5 | 6 | version_info = (0, 1, 0) 7 | __version__ = VERSION = '.'.join(map(str, version_info)) 8 | -------------------------------------------------------------------------------- /djangowebsocket/view.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __author__ = 'Wang Chao' 4 | __date__ = '15-1-9' 5 | 6 | import logging 7 | 8 | import uwsgi 9 | import redis 10 | from gevent import select 11 | 12 | from django.conf import settings 13 | from django.http import HttpResponse 14 | from django.utils.decorators import classonlymethod 15 | 16 | 17 | logger = logging.getLogger('django.websocket') 18 | 19 | def _get_setting(key, default_value): 20 | return getattr(settings, key, default_value) 21 | 22 | redis_client = redis.Redis( 23 | connection_pool=redis.ConnectionPool( 24 | host=_get_setting('WS_REDIS_HOST', '127.0.0.1'), 25 | port=_get_setting('WS_REDIS_PORT', 6379), 26 | db=_get_setting('WS_REDIS_DB', 0) 27 | ) 28 | ) 29 | 30 | REDIS_GLOBAL_CHANNEL = _get_setting('WS_REDIS_GLOBAL_CHANNEL', 'DJWS-CHANNEL') 31 | TIMEOUT = _get_setting('WS_WAIT_TIMEOUT', 10) 32 | 33 | 34 | class WebSocketView(object): 35 | class WebSocketError(Exception): 36 | pass 37 | 38 | 39 | def __init__(self, request, *args, **kwargs): 40 | self.request = request 41 | self.args = args 42 | self.kwargs = kwargs 43 | self.redis = redis_client 44 | self.channel = self.redis.pubsub() 45 | self.channel.subscribe(REDIS_GLOBAL_CHANNEL) 46 | self.on_connect() 47 | 48 | 49 | def publish_global(self, text): 50 | print "PUBLISH GLOBAL", text 51 | self.redis.publish(REDIS_GLOBAL_CHANNEL, text) 52 | 53 | def recv(self): 54 | try: 55 | data = uwsgi.websocket_recv_nb() 56 | if data: 57 | self.on_websocket_data(data) 58 | except: 59 | raise self.WebSocketError() 60 | 61 | def send(self, text): 62 | uwsgi.websocket_send(text) 63 | 64 | def send_binary(self, binary_data): 65 | uwsgi.websocket_send_binary(binary_data) 66 | 67 | def on_connect(self): 68 | raise NotImplementedError() 69 | 70 | def on_connection_lost(self): 71 | raise NotImplementedError() 72 | 73 | def on_websocket_data(self, data): 74 | raise NotImplementedError() 75 | 76 | def on_channel_data(self, data): 77 | raise NotImplementedError() 78 | 79 | 80 | def run(self): 81 | websocket_fd = uwsgi.connection_fd() 82 | channel_fd = self.channel.connection._sock.fileno() 83 | 84 | fds = [websocket_fd, channel_fd] 85 | 86 | try: 87 | while True: 88 | readable, _, _ = select.select(fds, [], [], TIMEOUT) 89 | if not readable: 90 | self.recv() 91 | continue 92 | 93 | self.recv() 94 | data = self.channel.get_message(ignore_subscribe_messages=True) 95 | if data: 96 | self.on_channel_data(data) 97 | 98 | except self.WebSocketError: 99 | return HttpResponse('ws ok') 100 | finally: 101 | self.channel.unsubscribe() 102 | self.on_connection_lost() 103 | 104 | @classonlymethod 105 | def as_view(cls): 106 | def wrapper(request, *args, **kwargs): 107 | self = cls(request, *args, **kwargs) 108 | return self.run() 109 | return wrapper 110 | 111 | -------------------------------------------------------------------------------- /djangowebsocket/wsgi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __author__ = 'Wang Chao' 4 | __date__ = '15-1-9' 5 | 6 | import gevent.monkey 7 | gevent.monkey.patch_all() 8 | 9 | import uwsgi 10 | 11 | import django 12 | from django.core.handlers.wsgi import WSGIHandler 13 | 14 | class WebSocketApplication(WSGIHandler): 15 | def _fake_start_response(self, *args, **kwargs): 16 | pass 17 | 18 | def __call__(self, environ, start_response): 19 | uwsgi.websocket_handshake() 20 | return super(WebSocketApplication, self).__call__(environ, self._fake_start_response) 21 | 22 | 23 | def get_wsgi_application(): 24 | django.setup() 25 | return WebSocketApplication() 26 | 27 | -------------------------------------------------------------------------------- /example/apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yueyoum/djangowebsocket/4f19a3cec9469818d01ada7d27612e01a8e710e8/example/apps/__init__.py -------------------------------------------------------------------------------- /example/apps/web/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yueyoum/djangowebsocket/4f19a3cec9469818d01ada7d27612e01a8e710e8/example/apps/web/__init__.py -------------------------------------------------------------------------------- /example/apps/web/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /example/apps/web/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yueyoum/djangowebsocket/4f19a3cec9469818d01ada7d27612e01a8e710e8/example/apps/web/migrations/__init__.py -------------------------------------------------------------------------------- /example/apps/web/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /example/apps/web/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /example/apps/web/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render_to_response 2 | from django.views.generic import View 3 | from django.http import HttpResponseRedirect 4 | from django import forms 5 | 6 | class LoginForm(forms.Form): 7 | name = forms.CharField(required=True, widget=forms.TextInput(attrs={'class': 'pure-input-1-1'})) 8 | 9 | 10 | class Index(View): 11 | def get(self, request): 12 | name = request.session.get('name', None) 13 | if not name: 14 | return render_to_response( 15 | 'login.html', 16 | { 17 | 'action': request.path, 18 | 'form': LoginForm().as_p() 19 | } 20 | ) 21 | 22 | return render_to_response( 23 | 'chat.html', 24 | { 25 | 'websocket_url': 'ws://' + request.get_host() 26 | } 27 | ) 28 | 29 | def post(self, request): 30 | form = LoginForm(request.POST) 31 | if not form.is_valid(): 32 | return HttpResponseRedirect(request.path) 33 | 34 | name = form.cleaned_data['name'] 35 | request.session['name'] = name 36 | return HttpResponseRedirect(request.path) 37 | -------------------------------------------------------------------------------- /example/apps/websocket/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yueyoum/djangowebsocket/4f19a3cec9469818d01ada7d27612e01a8e710e8/example/apps/websocket/__init__.py -------------------------------------------------------------------------------- /example/apps/websocket/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /example/apps/websocket/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yueyoum/djangowebsocket/4f19a3cec9469818d01ada7d27612e01a8e710e8/example/apps/websocket/migrations/__init__.py -------------------------------------------------------------------------------- /example/apps/websocket/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /example/apps/websocket/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /example/apps/websocket/views.py: -------------------------------------------------------------------------------- 1 | #! -*- coding: utf-8 -*- 2 | 3 | 4 | import datetime 5 | 6 | from djangowebsocket.view import WebSocketView 7 | 8 | now = lambda : datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") 9 | def log(text): 10 | print "{0}: {1}".format(now(), text) 11 | 12 | 13 | class Chat(WebSocketView): 14 | def on_connect(self): 15 | print "on connect" 16 | 17 | self.name = self.request.session['name'].encode('utf-8') 18 | client_amount = self.redis.incr('demo-client-amount') 19 | self.send("{0} {1} 人在线".format(now(), client_amount)) 20 | self.publish_global("{0} {1} 上线了".format(now(), self.name)) 21 | 22 | def on_websocket_data(self, data): 23 | print "websocket data:", data 24 | self.publish_global("{0} {1}: {2}".format(now(), self.name, data)) 25 | 26 | 27 | def on_channel_data(self, data): 28 | print "channel data", data 29 | data = str(data['data']) 30 | self.send(data) 31 | 32 | def on_connection_lost(self): 33 | print "connection lost" 34 | self.redis.incr('demo-client-amount', -1) 35 | self.publish_global("{0} {1} 下线了".format(now(), self.name)) 36 | -------------------------------------------------------------------------------- /example/example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yueyoum/djangowebsocket/4f19a3cec9469818d01ada7d27612e01a8e710e8/example/example/__init__.py -------------------------------------------------------------------------------- /example/example/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for example project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8a1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/dev/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/dev/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 16 | 17 | 18 | # Quick-start development settings - unsuitable for production 19 | # See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/ 20 | 21 | # SECURITY WARNING: keep the secret key used in production secret! 22 | SECRET_KEY = 'xp1g)+n^pf-_0nlr^%xatu2su$&ze6d**#!oaw1rw&*lm_mk&%' 23 | 24 | # SECURITY WARNING: don't run with debug turned on in production! 25 | DEBUG = True 26 | 27 | TEMPLATE_DEBUG = True 28 | 29 | ALLOWED_HOSTS = [] 30 | 31 | 32 | # Application definition 33 | 34 | INSTALLED_APPS = ( 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | 'apps.web', 42 | 'apps.websocket', 43 | ) 44 | 45 | MIDDLEWARE_CLASSES = ( 46 | 'django.contrib.sessions.middleware.SessionMiddleware', 47 | 'django.middleware.common.CommonMiddleware', 48 | # 'django.middleware.csrf.CsrfViewMiddleware', 49 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 50 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 51 | 'django.contrib.messages.middleware.MessageMiddleware', 52 | # 'django.middleware.clickjacking.XFrameOptionsMiddleware', 53 | # 'django.middleware.security.SecurityMiddleware', 54 | ) 55 | 56 | ROOT_URLCONF = 'example.urls' 57 | 58 | TEMPLATES = [ 59 | { 60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 61 | 'DIRS': [os.path.join(BASE_DIR, 'templates'),], 62 | 'APP_DIRS': True, 63 | 'OPTIONS': { 64 | 'context_processors': [ 65 | 'django.template.context_processors.debug', 66 | 'django.template.context_processors.request', 67 | 'django.contrib.auth.context_processors.auth', 68 | 'django.contrib.messages.context_processors.messages', 69 | ], 70 | }, 71 | }, 72 | ] 73 | 74 | WSGI_APPLICATION = 'example.wsgi.application' 75 | 76 | 77 | # Database 78 | # https://docs.djangoproject.com/en/dev/ref/settings/#databases 79 | 80 | DATABASES = { 81 | 'default': { 82 | 'ENGINE': 'django.db.backends.sqlite3', 83 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 84 | } 85 | } 86 | 87 | 88 | # Internationalization 89 | # https://docs.djangoproject.com/en/dev/topics/i18n/ 90 | 91 | LANGUAGE_CODE = 'en-us' 92 | 93 | TIME_ZONE = 'UTC' 94 | 95 | USE_I18N = True 96 | 97 | USE_L10N = True 98 | 99 | USE_TZ = True 100 | 101 | 102 | # Static files (CSS, JavaScript, Images) 103 | # https://docs.djangoproject.com/en/dev/howto/static-files/ 104 | 105 | STATIC_URL = '/static/' 106 | 107 | 108 | LOGGING = { 109 | 'version': 1, 110 | 'formatters': { 111 | 'verbose': { 112 | 'format': '%(levelname)s %(asctime)s %(message)s' 113 | }, 114 | }, 115 | 'handlers': { 116 | 'console': { 117 | 'class': 'logging.StreamHandler', 118 | 'level': 'DEBUG', 119 | 'formatter': 'verbose', 120 | } 121 | }, 122 | 'loggers': { 123 | 'django.request': { 124 | 'handlers': ['console'], 125 | 'level': 'ERROR', 126 | 'propagate': True, 127 | } 128 | } 129 | } 130 | 131 | 132 | 133 | 134 | 135 | 136 | # redis session 137 | SESSION_ENGINE = 'redis_sessions.session' 138 | SESSION_REDIS_HOST = '127.0.0.1' 139 | SESSION_REDIS_PORT = 6379 140 | SESSION_REDIS_DB = 0 141 | 142 | SESSION_COOKIE_AGE = 60 143 | 144 | # websocket 145 | WS_REDIS_HOST = '127.0.0.1' 146 | WS_REDIS_PORT = 6379 147 | WS_REDIS_DB = 0 148 | WS_REDIS_GLOBAL_CHANNEL = 'my_test_channel' 149 | WS_WAIT_TIMEOUT = 10 # seconds 150 | -------------------------------------------------------------------------------- /example/example/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url 2 | from django.contrib import admin 3 | 4 | import apps.web.views 5 | import apps.websocket.views 6 | 7 | 8 | urlpatterns = [ 9 | # Examples: 10 | # url(r'^$', 'example.views.home', name='home'), 11 | # url(r'^blog/', include('blog.urls')), 12 | 13 | # url(r'^admin/', include(admin.site.urls)), 14 | 15 | url(r'^$', apps.web.views.Index.as_view()), 16 | url(r'^ws/$', apps.websocket.views.Chat.as_view()), 17 | ] 18 | -------------------------------------------------------------------------------- /example/example/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for example project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /example/example/wsgi_websocket.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __author__ = 'Wang Chao' 4 | __date__ = '15-1-23' 5 | 6 | import os 7 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") 8 | 9 | from djangowebsocket.wsgi import get_wsgi_application 10 | application = get_wsgi_application() 11 | -------------------------------------------------------------------------------- /example/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /example/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | # add following settings in http scope! 3 | # map $http_upgrade $connection_upgrade { 4 | # default upgrade; 5 | # '' close; 6 | # } 7 | 8 | 9 | 10 | upstream web { 11 | server 127.0.0.1:7000; 12 | } 13 | 14 | upstream websocket { 15 | server 127.0.0.1:7001; 16 | } 17 | 18 | server { 19 | listen 8111; 20 | 21 | access_log off; 22 | error_log off; 23 | 24 | 25 | location ~ ^/ws/ { 26 | proxy_pass http://websocket; 27 | proxy_http_version 1.1; 28 | proxy_set_header Upgrade $http_upgrade; 29 | proxy_set_header Connection "upgrade"; 30 | } 31 | 32 | location / { 33 | uwsgi_pass web; 34 | include uwsgi_params; 35 | } 36 | } -------------------------------------------------------------------------------- /example/redis_sessions/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.4.0' 2 | -------------------------------------------------------------------------------- /example/redis_sessions/session.py: -------------------------------------------------------------------------------- 1 | import redis 2 | 3 | try: 4 | from django.utils.encoding import force_unicode 5 | except ImportError: # Python 3.* 6 | from django.utils.encoding import force_text as force_unicode 7 | from django.contrib.sessions.backends.base import SessionBase, CreateError 8 | from redis_sessions import settings 9 | 10 | 11 | # Avoid new redis connection on each request 12 | 13 | if settings.SESSION_REDIS_SENTINEL_LIST is not None: 14 | from redis.sentinel import Sentinel 15 | 16 | redis_server = Sentinel(settings.SESSION_REDIS_SENTINEL_LIST, socket_timeout=0.1) \ 17 | .master_for(settings.SESSION_REDIS_SENTINEL_MASTER_ALIAS, socket_timeout=0.1) 18 | 19 | elif settings.SESSION_REDIS_URL is not None: 20 | 21 | redis_server = redis.StrictRedis.from_url(settings.SESSION_REDIS_URL) 22 | elif settings.SESSION_REDIS_UNIX_DOMAIN_SOCKET_PATH is None: 23 | 24 | redis_server = redis.StrictRedis( 25 | host=settings.SESSION_REDIS_HOST, 26 | port=settings.SESSION_REDIS_PORT, 27 | db=settings.SESSION_REDIS_DB, 28 | password=settings.SESSION_REDIS_PASSWORD 29 | ) 30 | else: 31 | 32 | redis_server = redis.StrictRedis( 33 | unix_socket_path=settings.SESSION_REDIS_UNIX_DOMAIN_SOCKET_PATH, 34 | db=settings.SESSION_REDIS_DB, 35 | password=settings.SESSION_REDIS_PASSWORD, 36 | ) 37 | 38 | 39 | class SessionStore(SessionBase): 40 | """ 41 | Implements Redis database session store. 42 | """ 43 | def __init__(self, session_key=None): 44 | super(SessionStore, self).__init__(session_key) 45 | 46 | self.server = redis_server 47 | 48 | def load(self): 49 | try: 50 | session_data = self.server.get( 51 | self.get_real_stored_key(self._get_or_create_session_key()) 52 | ) 53 | return self.decode(force_unicode(session_data)) 54 | except: 55 | self.create() 56 | return {} 57 | 58 | def exists(self, session_key): 59 | return self.server.exists(self.get_real_stored_key(session_key)) 60 | 61 | def create(self): 62 | while True: 63 | self._session_key = self._get_new_session_key() 64 | 65 | try: 66 | self.save(must_create=True) 67 | except CreateError: 68 | continue 69 | self.modified = True 70 | return 71 | 72 | def save(self, must_create=False): 73 | if must_create and self.exists(self._get_or_create_session_key()): 74 | raise CreateError 75 | data = self.encode(self._get_session(no_load=must_create)) 76 | if redis.VERSION[0] >= 2: 77 | self.server.setex( 78 | self.get_real_stored_key(self._get_or_create_session_key()), 79 | self.get_expiry_age(), 80 | data 81 | ) 82 | else: 83 | self.server.set( 84 | self.get_real_stored_key(self._get_or_create_session_key()), 85 | data 86 | ) 87 | self.server.expire( 88 | self.get_real_stored_key(self._get_or_create_session_key()), 89 | self.get_expiry_age() 90 | ) 91 | 92 | def delete(self, session_key=None): 93 | if session_key is None: 94 | if self.session_key is None: 95 | return 96 | session_key = self.session_key 97 | try: 98 | self.server.delete(self.get_real_stored_key(session_key)) 99 | except: 100 | pass 101 | 102 | def get_real_stored_key(self, session_key): 103 | """Return the real key name in redis storage 104 | @return string 105 | """ 106 | prefix = settings.SESSION_REDIS_PREFIX 107 | if not prefix: 108 | return session_key 109 | return ':'.join([prefix, session_key]) 110 | -------------------------------------------------------------------------------- /example/redis_sessions/settings.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | SESSION_REDIS_HOST = getattr(settings, 'SESSION_REDIS_HOST', 'localhost') 5 | SESSION_REDIS_PORT = getattr(settings, 'SESSION_REDIS_PORT', 6379) 6 | SESSION_REDIS_DB = getattr(settings, 'SESSION_REDIS_DB', 0) 7 | SESSION_REDIS_PREFIX = getattr(settings, 'SESSION_REDIS_PREFIX', '') 8 | SESSION_REDIS_PASSWORD = getattr(settings, 'SESSION_REDIS_PASSWORD', None) 9 | SESSION_REDIS_UNIX_DOMAIN_SOCKET_PATH = getattr( 10 | settings, 'SESSION_REDIS_UNIX_DOMAIN_SOCKET_PATH', None 11 | ) 12 | SESSION_REDIS_URL = getattr(settings, 'SESSION_REDIS_URL', None) 13 | 14 | # should be on the format [(host, port), (host, port), (host, port)] 15 | SESSION_REDIS_SENTINEL_LIST = getattr( 16 | settings, 'SESSION_REDIS_SENTINEL_LIST', None 17 | ) 18 | SESSION_REDIS_SENTINEL_MASTER_ALIAS = getattr( 19 | settings, 'SESSION_REDIS_SENTINEL_MASTER_ALIAS', None 20 | ) -------------------------------------------------------------------------------- /example/templates/chat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DjangoWebsocet Chat Demo 7 | 8 | 9 | 10 | 11 | 61 | 62 | 79 | 80 | 81 | 82 |
83 |
84 |
85 | 86 |
87 |
88 | 89 | 90 | 91 |
92 |
93 | 94 | 95 | -------------------------------------------------------------------------------- /example/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DjangoWebsocket Demo. Login 6 | 7 | 8 | 13 | 14 | 15 |
16 |
17 |
18 | {{ form }} 19 | 20 |
21 |
22 |
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /example/uwsgi-config/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | chdir = %d 3 | master = true 4 | emperor = vassals 5 | vassal-set = pythonpath=../ 6 | 7 | -------------------------------------------------------------------------------- /example/uwsgi-config/vassals/web-uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | chdir = %d../../ 3 | env = DJANGO_SETTINGS_MODULE=example.settings 4 | module = example.wsgi:application 5 | socket = 127.0.0.1:7000 6 | master = true 7 | processes = 1 8 | enable-threads = true 9 | 10 | -------------------------------------------------------------------------------- /example/uwsgi-config/vassals/websocket-uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | chdir = %d../../ 3 | env = DJANGO_SETTINGS_MODULE=example.settings 4 | module = example.wsgi_websocket:application 5 | http-socket = 127.0.0.1:7001 6 | master = true 7 | processes = 1 8 | enable-threads = true 9 | 10 | gevent = 1000 11 | 12 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | name='djangowebsocket', 5 | version='0.0.1', 6 | packages=[''], 7 | url='https://github.com/yueyoum/djangowebsocket', 8 | license='', 9 | author='Yueyoum', 10 | author_email='yueyoum@gmail.com', 11 | description='' 12 | ) 13 | --------------------------------------------------------------------------------