What's new in Channels 2? ========================= Channels 1 and Channels 2 are substantially different codebases, and the upgrade **is a major one**. While we have attempted to keep things as familiar and backwards-compatible as possible, major architectural shifts mean you will need at least some code changes to upgrade. Requirements ------------ First of all, Channels 2 is *Python 3.5 and up only*. If you are using Python 2, or a previous version of Python 3, you cannot use Channels 2 as it relies on the ``asyncio`` library and native Python async support. This decision was a tough one, but ultimately Channels is a library built around async functionality and so to not use these features would be foolish in the long run. Apart from that, there are no major changed requirements, and in fact Channels 2 deploys do not need separate worker and server processes and so should be easier to manage. Conceptual Changes ------------------ The fundamental layout and concepts of how Channels work have been significantly changed; you'll need to understand how and why to help in upgrading. Channel Layers and Processes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Channels 1 terminated HTTP and WebSocket connections in a separate process to the one that ran Django code, and shuffled requests and events between them over a cross-process *channel layer*, based on Redis or similar. This not only meant that all request data had to be re-serialized over the network, but that you needed to deploy and scale two separate sets of servers. Channels 2 changes this by running the Django code in-process via a threadpool, meaning that the network termination and application logic are combined, like WSGI. Application Instances ~~~~~~~~~~~~~~~~~~~~~ Because of this, all processing for a socket happens in the same process, so ASGI applications are now instantiated once per socket and can use local variables on ``self`` to store information, rather than the ``channel_session`` storage provided before (that is now gone entirely). The channel layer is now only used to communicate between processes for things like broadcast messaging - in particular, you can talk to other application instances in direct events, rather than having to send directly to client sockets. This means, for example, to broadcast a chat message, you would now send a new-chat-message event to every application instance that needed it, and the application code can handle that event, serialize the message down to the socket format, and send it out (and apply things like multiplexing). New Consumers ~~~~~~~~~~~~~ Because of these changes, the way consumers work has also significantly changed. Channels 2 is now a turtles-all-the-way-down design; every aspect of the system is designed as a valid ASGI application, including consumers and the routing system. The consumer base classes have changed, though if you were using the generic consumers before, the way they work is broadly similar. However, the way that user authentication, sessions, multiplexing, and similar features work has changed. Full Async ~~~~~~~~~~ Channels 2 is also built on a fundamental async foundation, and all servers are actually running an asynchronous event loop and only jumping to synchronous code when you interact with the Django view system or ORM. That means that you, too, can write fully asynchronous code if you wish. It's not a requirement, but it's there if you need it. We also provide convenience methods that let you jump between synchronous and asynchronous worlds easily, with correct blocking semantics, so you can write most of a consumer in an async style and then have one method that calls the Django ORM run synchronously. Removed Components ------------------ The binding framework has been removed entirely - it was a simplistic implementation, and it being in the core package prevented people from exploring their own solutions. It's likely similar concepts and APIs will appear in a third-party (non-official-Django) package as an option for those who want them. How to Upgrade -------------- While this is not an exhaustive guide, here are some rough rules on how to proceed with an upgrade. Given the major changes to the architecture and layout of Channels 2, it is likely that upgrading will be a significant rewrite of your code, depending on what you are doing. It is **not** a drop-in replacement; we would have done this if we could, but changing to ``asyncio`` and Python 3 made it almost impossible to keep things backwards-compatible, and we wanted to correct some major design decisions. Function-based consumers and Routing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Channels 1 allowed you to route by event type (e.g. ``websocket.connect``) and pass individual functions with routing that looked like this:: channel_routing = [ route("websocket.connect", connect_blog, path=r'^/liveblog/(?P[^/]+)/stream/$'), ] And function-based consumers that looked like this:: def connect_blog(message, slug): ... You'll need to convert these to be class-based consumers, as routing is now done once, at connection time, and so all the event handlers have to be together in a single ASGI application. In addition, URL arguments are no longer passed down into the individual functions - instead, they will be provided in ``scope`` as the key ``url_route``, a dict with an ``args`` key containing a list of positional regex groups and a ``kwargs`` key with a dict of the named groups. Routing is also now the main entry point, so you will need to change routing to have a ProtocolTypeRouter with URLRouters nested inside it. See :doc:`/topics/routing` for more. channel_session and enforce_ordering ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Any use of the ``channel_session`` or ``enforce_ordering`` decorators can be removed; ordering is now always followed as protocols are handled in the same process, and ``channel_session`` is not needed as the same application instance now handles all traffic from a single client. Anywhere you stored information in the ``channel_session`` can be replaced by storing it on ``self`` inside a consumer. HTTP sessions and Django auth ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ All :doc:`authentication ` and :doc:`sessions ` are now done with middleware. You can remove any decorators that handled them, like ``http_session``, ``channel_session_user`` and so on (in fact, there are no decorators in Channels 2 - it's all middleware). To get auth now, wrap your URLRouter in an ``AuthMiddlewareStack``:: from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack application = ProtocolTypeRouter({ "websocket": AuthMiddlewareStack( URLRouter([ ... ]) ), }) You need to replace accesses to ``message.http_session`` with ``self.scope["session"]``, and ``message.user`` with ``self.scope["user"]``. There is no need to do a handoff like ``channel_session_user_from_http`` any more - just wrap the auth middleware around and the user will be in the scope for the lifetime of the connection. Channel Layers ~~~~~~~~~~~~~~ Channel layers are now an optional part of Channels, and the interface they need to provide has changed to be async. Only ``channels_redis``, formerly known as ``asgi_redis``, has been updated to match so far. Settings are still similar to before, but there is no longer a ``ROUTING`` key (the base routing is instead defined with ``ASGI_APPLICATION``):: CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [("redis-server-name", 6379)], }, }, } All consumers have a ``self.channel_layer`` and ``self.channel_name`` object that is populated if you've configured a channel layer. Any messages you send to the ``channel_name`` will now go to the consumer rather than directly to the client - see the :doc:`/topics/channel_layers` documentation for more. The method names are largely the same, but they're all now awaitables rather than synchronous functions, and ``send_group`` is now ``group_send``. Group objects ~~~~~~~~~~~~~ Group objects no longer exist; instead you should use the ``group_add``, ``group_discard``, and ``group_send`` methods on the ``self.channel_layer`` object inside of a consumer directly. As an example:: from asgiref.sync import async_to_sync class ChatConsumer(AsyncWebsocketConsumer): async def connect(self): await self.channel_layer.group_add("chat", self.channel_name) async def disconnect(self): await self.channel_layer.group_discard("chat", self.channel_name) Delay server ~~~~~~~~~~~~ If you used the delay server before to put things on hold for a few seconds, you can now instead use an ``AsyncConsumer`` and ``asyncio.sleep``:: class PingConsumer(AsyncConsumer): async def websocket_receive(self, message): await asyncio.sleep(1) await self.send({ "type": "websocket.send", "text": "pong", }) Testing ~~~~~~~ The :doc:`testing framework ` has been entirely rewritten to be async-based. While this does make writing tests a lot easier and cleaner, it means you must entirely rewrite any consumer tests completely - there is no backwards-compatible interface with the old testing client as it was synchronous. You can read more about the new testing framework in the :doc:`testing documentation `. Also of note is that the live test case class has been renamed from ``ChannelLiveServerTestCase`` to ``ChannelsLiveServerTestCase`` - note the extra ``s``. Exception Handling ~~~~~~~~~~~~~~~~~~ Because the code that's handling a socket is now in the same process as the socket itself, Channels 2 implements cleaner exception handling than before - if your application raises an unhandled error, it will close the connection (HTTP or WebSocket in the case of Daphne) and log the error to console. Additionally, sending malformed messages down to the client is now caught and raises exceptions where you're sending, rather than silently failing and logging to the server console.