Nobody else has suggested this particular approach yet, and while it would definitely make writing applications much more reliable, there's no particularly easy way to distribute an interface that I can think of (even the ASGI specification ends up being enforced on the receiver side, though ultimately it's the conformance test suites that I rely on for safety).
Without a simple solution to that I think it's more the sort of thing I would like to make sure people can hook into themselves to provide validation rather than trying to ship something in the core release. There's also the idea of adding an easy way to ignore messages you're not interested in if you're using groups as a firehose rather than targeted broadcast - that does mean silent failures, though, which is my least-favourite kind. At least with the current design pattern something will complain if it's not working right (even though that thing is on the receiving side not the sending side). Andrew On Sat, Mar 24, 2018 at 4:31 AM, Josh Smeaton <[email protected]> wrote: > I've finally had the chance to use channels for a project (hack day > multiplayer game - hope to release and blog about it some time soon), and I > wanted to document some of the rough edges I hit and ask some questions > about them. > > Specifically though, I find the mapping of payload.type to a method on the > consumer confusing and somewhat brittle. > > The design we went with was to have a PlayerConsumer(AsyncWebsocketConsumer) > and a GameConsumer(SyncConsumer) running as a worker. The GameConsumer > starts the engine in a thread, and lets the engine fetch the channel layer. > The player and engine then communicate like so: > > # PlayerConsumer: receive game state updates > await self.channel_layer.group_add( > self.group_name, > self.channel_name > ) > > # PlayerConsumer: publish player joining event to game > await self.channel_layer.send( > 'game_engine', > { > 'type': 'player.new', > 'player': self.user.email, > 'channel': self.channel_name, > } > ) > > > # GameConsumer: publish state update > async_to_sync(self.channel_layer.group_send)( > self.group_name, > { > 'type': 'game_update', > 'state': state_json, > } > ) > > This works, provided PlayerConsumer has a method: > > async def game_update(self, event): > > And the GameConsumer has a method: > > def player_new(self, event): > > But if these two consumers are in completely different code > bases/packages, there is no real way to know what the interface is between > these consumers. Worse, it's extremely easy for a bad actor to crash a > listening consumer. Either of the following events will crash the consumer > receiving the message: > > await self.channel_layer.send( > 'game_engine', > { > 'type': 'i_am_a_bad_consumer' > } > ) > > async_to_sync(self.channel_layer.group_send)( > self.group_name, > { > 'type': 'gme_update', # typo > 'msg': 'hi', > } > ) > > I'm not so concerned about arbitrary asgi applications gatecrashing my app > as I ultimately have control over what is going to run. But each consumer > participating in a group must support the superset of all message types > that may be sent to that group if it wants to avoid crashing. And it has to > support the superset of message types without being able to discover what > they might be. Oh, and they're strings that **might** match a method I have > if periods are converted to underscores. > > This is all very good for adhoc eventing, but I think at some point you're > going to want to publish an explicit Interface. A GroupInterface might > define a collection of message types that are valid for members > participating in that group, and then a consumer could subscribe to > **that** rather than just a name. The interface members might be optional > or required, I haven't thought that far ahead, but at least each member > would be known. Attempting to publish an unknown message to a group would > crash the **sender** rather than the **receiver**. > > Has anything like this come up before? > > -- > You received this message because you are subscribed to the Google Groups > "Django developers (Contributions to Django itself)" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To post to this group, send email to [email protected]. > Visit this group at https://groups.google.com/group/django-developers. > To view this discussion on the web visit https://groups.google.com/d/ > msgid/django-developers/939460e7-45da-4dd0-afb7- > f386274faea1%40googlegroups.com > <https://groups.google.com/d/msgid/django-developers/939460e7-45da-4dd0-afb7-f386274faea1%40googlegroups.com?utm_medium=email&utm_source=footer> > . > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at https://groups.google.com/group/django-developers. To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/CAFwN1uo5XWvgR%2BzBQ0MMcbvpWn4bC2Fa5oajH9dWVnbCLncD4Q%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.
