Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When to use Ajax Requests #369

Closed
skortchmark9 opened this issue Nov 28, 2016 · 4 comments
Closed

When to use Ajax Requests #369

skortchmark9 opened this issue Nov 28, 2016 · 4 comments

Comments

@skortchmark9
Copy link

Hey Miguel, thanks again for your help with my previous issues: #297 and #344 . Today my question is perhaps one you get a lot - when should one use a REST endpoint and when should you use a socket one?

Till now, my whole app's interface has been socketio. Recently I've been thinking about rewriting a lot of it to use REST. Here are my reasons:

1.) Compatibility: as more people & computers start using my app, http endpoints are simpler for them to work with. Unless they want to access a stream of data, running commands is easier w/a POST.

2.) Retries: part of the reason I am using socketio is because it allows me to take down my server and update it in the middle of running a job without inconveniencing connected clients. However, I've noticed that all events the client sends while the server is down are retried. This is really useful in some situations but it can cause the server to act on stale information.

3.) Hanging: here's a scenario that bugs me. When I load a page, a couple (currently socketio) events are fired: the client requests the state from the application, and it requests some data about the past. It takes an abnormally long time for the state to be displayed, and the history gets rendered at the same time as the state.

It seems like events on the socket are consumed sequentially. If I request a really big data payload followed by a small real-time update payload, the update payload doesn't get processed until after the data payload has been processed - so they render at effectively the same time. I'm not sure if it's a client or server issue but my hypothesis is that it's on the server side - basically, a new request greenlet is created for the http handler while the two socket events are handled by the same greenlet.

I guess what I'm asking is if you think these reasons for choosing AJAX are valid, and if not, what are some ways to use socketio to avoid these problems.

Thanks again,
Sam

@miguelgrinberg
Copy link
Owner

miguelgrinberg commented Nov 28, 2016

1.) Compatibility

At this point all major browsers on desktop and mobile support WebSocket, so this isn't really a strong argument anymore. I could accept it as valid if you had to write your own WebSocket client and server code, but all that work has been done for you with Socket.IO on both ends.

2.) Retries

Socket.IO on the client side does this, and it is really nice. Clients have an exponential backoff algorithm to reconnect whenever a connection is dropped. With Ajax, you would need to do this yourself, or find a retry library that does it for you.

3.) Hanging

Okay, this one is harder.

It seems like events on the socket are consumed sequentially

Socket.IO establishes a point to point connection between the server and the client. Packets with information are written on this single connection. If you write a large packet and then a small packet, the small packet goes after the big one, they can't both go together because there is one connection. If you want both packets to travel at the same time, then you need the client to open two connections, exactly what the browser does when you send multiple Ajax requests concurrently.

Also, large data packets in Socket.IO aren't a great idea. Consider "streaming" the data in small chunks. I believe that will solve your big + small packet problem nicely.

If I request a really big data payload followed by a small real-time update payload, the update payload

I don't understand this. In a Socket.IO connection there are no requests and responses in the HTTP sense, just events. If the client asks for something, it sends an event. The server then can block inside that event handler until it generates and sends the information, or it can just acknowledge the event and then spawn another greenlet to generate and send the data. I think the blocking situation that you have might be caused by your application, not by Socket.IO.

basically, a new request greenlet is created for the http handler while the two socket events are handled by the same greenlet.

There is an option to request that all event handlers are spawn in their own greenlet. Pass async_handlers=True in the SocketIO constructor. But once again, the choice to do actual work in an event handlers is yours, you can continue using sync handlers and still do all your work in background greenlets, which is better for the system, regardless of the async_handlers setting.

I guess what I'm asking is if you think these reasons for choosing AJAX are valid

My rule of thumb is this: If you need small, frequent updates with low latency from client to server or from server to client, then Socket.IO is a good idea. Larger and/or infrequent messages, without a low latency requirement are probably better handled via HTTP requests. Hope this helps!

@skortchmark9
Copy link
Author

Thanks for the quick response Miguel - you're a legend!

Re: 1.) While I agree that socketio is stupid easy to use from a browser, connecting a standalone client without the browser is a bit harder. I've been using https://pypi.python.org/pypi/socketIO-client and it's a bit difficult. Socket.IO's docs are also really unhelpful as far as I can tell.

Re: 2.) The retry behavior is definitely nice but in some situations it can shoot me in the foot - AFAIK there's no way to turn it off, and there's no way to know if an event was sent by a client while the server was alive or while it was down. Obviously you can work around it on the client-side by listening for the disconnect event but that means guarding all of your emits which is a pain.

Re: 3.)

you can continue using sync handlers and still do all your work in background greenlets, which is better for the system

Is there a good example of this somewhere? I was looking at your flack application but I was hoping not to have to include celery.

Larger and/or infrequent messages, without a low latency requirement are probably better handled via HTTP requests.

Seems like this would include all basic user input to the server (I'm thinking button clicks / form submissions, not mouse position). So I guess I've overdone it a bit w/all of the events I'm sending from the client.

Thanks again!

@miguelgrinberg
Copy link
Owner

Is there a good example of this somewhere?

I can't think of any direct example of this because I try to avoid long and expensive endpoints or events. If it is something simple, it's no problem to perform the work in the event handler, and it is something that cannot be reduced to one or more simple tasks, then Celery is usually more appropriate.

But in any case, the idea of spawning a greenlet is really easy to implement. Say you have this event handler:

@socketio.on('get_a_lot_of_data')
def get_data():
    data = generate_a_whole_lot_of_data()
    emit('data_for_client", data)

If the generate_a_whole_lot_of_data() function takes a long time, that's not good. The first thing to do would be to send that to a background greenlet:

def get_data_background(sid):
    data = generate_a_whole_lot_of_data()
    socketio.emit('data_for_client", data, room=sid)

@socketio.on('get_a_lot_of_data')
def get_data():
    socketio.start_background_task(get_data_background, request.sid)

The second thing you need to do, is make sure this long running function does not take the CPU for long periods of time. That means adding socketio.sleep(0) calls at regular intervals, for example inside the main loop (if there is one).

@skortchmark9
Copy link
Author

Okay sweet, thanks so much Miguel!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants