On 01/16/2014 10:10 PM, Stefan Adams wrote:
On Wed, Jan 15, 2014 at 4:48 PM, Ben van Staveren
<[email protected]>wrote:

Okay, so... blocking/non-blocking 101 (sortakinda):

Thanks so much, Ben, for this 101 primer!  It has really helped a lot!
  Seems to me it would be worth while to take as is and put on Mojo's wiki.
  At least there'd be a start to some non-blocking related information.


When an operation is "blocking" what it literally means is that the
operation blocks further execution of the program. For example opening a
socket with Socket is a blocking operation, since the actual connection
attempt blocks until a result is available. This also applies to files and
reading data from them.

Does this mean that the actual connection attempt which blocks is
equivalent to waiting on a user to type some data at an <STDIN> prompt?

Sorta-kinda, what happens when you do a connect with a socket is that you get name resolution first (unless you pass an IP) which in itself is a blocking operation, then the actual connection attempt and getting a result out of that is blocking as well. But yes, mostly it's the same as asking a user to type something on STDIN.


Socket is to <STDIN> as Mojo::IOLoop::Client is to Term::ReadKey.  Is that
right?

And Socket blocks quite simply because the Socket author didn't write it to
take advantage of an event loop?  Darn it -- why not??  :D  Basically, this
is out of the scope of the purpose of Socket and someone else is invited to
come along and add the non-blocking functionality.  Is that what
AnyEvent::Socket is?  Or is AnyEvent::Socket a brand new implementation of
socket connecting but it just chooses to include event loop support in the
scope of its purpose?

Also sorta-kinda, it's more a readkey type thing where you constantly loop and go:

press a key yet? press a key yet? ah, a key was pressed, read it! press a key yet? press a key yet? and so on and so forth. Of course the event loop often abstracts that away behind a very simple callback, e.g. like my $watcher = Eventloop->watch_a_file_descriptor(on_read => sub { my $key = shift; # a key was pressed and here it is! }).

Socket can be used in a non-blocking fashion as well, but it requires additional glue and logic that you as an app author have to pour in yourself, take a look at the IO::Select module for example, and at things like Net::Server::Multiplex - they also work in non-blocking fashion but only deal with sockets, whereas most "modern" event loops include support for timers, recurring timers, and basically watching anything that exposes a file descriptor.

AnyEvent::Socket is what you said, a regular Socket, with the extra glue and logic tacked on, and tailored for use with AnyEvent (as in, it abstracts away a lot of nitty gritty and basically just exposes a few simple callback handlers).


An event loop basically boils down to:
while(1) {
   do_some_timer_stuff;
   check_any_fds_for_readability;
   handle_readable_fds;
   check_any_fds_for_writabiliy;
   handle_writable_fds;
}

This is really great to see -- how an event loop basically works.  I had no
idea!  Which of these pseudo-functions in the loop would be the most likely
place for the blocking to occur?  For example, when Nacho tries to connect
to a Socket or when I am waiting for a response from a Database?

Anywhere really, because connecting means writing - and reading, and waiting for a response also means reading. That's also the reason why there's no "magic sauce" to just make non-blocking work; it's kind of like taking a go-kart and figuring as long as you throw a top fuel dragster body on it it'll behave like a dragster - it'll only actually run like a dragster if the entire thing was designed to be... a dragster :D

Stupid example but... :P




So, you do still need to actually inform the event loop that it needs to
check for something. Now Mojo::IOLoop does let you do this on plain
"Socket" connections, but you'd have to get the file descriptor, then use
Mojo::IOLoop's functionality to get it to be taken up into the checks.

That's why Mojo::IOLoop->client works out of the box, because it does that
part already, for you.

If I'm understanding correctly, Nacho's problem could be resolved by using
Mojo::IOLoop->client instead of Socket?

Correct, but you'd still have to add the read/write handling of data you want to read or write. You may have to add some additional glue on top, for example, an app I wrote that interfaces with a piece of hardware kit over a socket has to deal with the fact that the protocol the hardware desires to speak has no real headers or anything, it's just a raw stream of bytes and based on some of the data itself you know when you have a "packet" of data ready.

So in your "read" callback you would have to basically use read() or sysread() to grab, say, 256 bytes at a time, stuff it into a temporary buffer, and only when your own temporary buffer satisfies whatever requirements you have do you pass it on for processing - that part doesn't differ too much from regular blocking socket use.



But... you can in fact block inside an event loop.

Are there any tools to help pinpoint where blocking is occurring?  For
example, when I wrote my first app connecting to a database, I had no
reason to suspect that the connection to the database is what was causing
the event loop to back up.  Especially when I run a query on the mysql>
prompt and it tells me the query takes 0.00 seconds or 0.02 seconds at
most.  The problem of course was when dozens of users were connecting to
the app and therefore the database concurrently and I had only a few
preforked processes at most.

Not really that I know of, it's just one of those things that after a while becomes a problem on the level of a missing ; somewhere, you generally will have a good idea of where it's at. The key is that you need to design your entire app top to bottom to use non-blocking, and generally if you've done that there are no places where you block without knowing it, only places where you block but for a very explicit reason (and sometimes that reason can be something as simple as "there is no non-blocking module that does what I need and I can't write one yet")




The above pattern is what you'd use to talk to a backend API over HTTP.
You can also do this for MongoDB as long as you use the Mango driver
(because sri made it and it's also designed to support non-blocking out of
the box); for other things such as ZMQ or RabbitMQ you will have to find a
module that implements the appropriate protocol *and* does things in a
non-blocking fashion; for example AnyEvent::RabbitMQ can do it for RabbitMQ.

So if I really wanted to connect to a MySQL database non-blockingly -- much
like Mango is doing for MongoDB -- I could use Mojo::IOLoop->client to
connect to :3306 and get to work implementing the entire MySQL protocol!
  Is that right?  Likewise with connecting to an NTP server or anything else.

Yes, but the drawback is that you, yourself, have to implement the entire protocol front to back, which for things like NTP isn't too much of an issue but for MySQL I imagine it's a fair chunk of work. I do however think there are some non-blocking (or asynchronous, as it's sometimes called) MySQL modules available, not entirely sure how stable any of them are, though.

The advantage of course of having Mojolicious is that anything to do with HTTP is already there, so there's that - for databases, it's a bit of a tossup. MongoDB via Mango for example is non-blocking (in the same way that Mojo::UserAgent does it, pass a callback to make it non-blocking (and that is a bit simplified)), if you're using CouchDB if I'm not mistaken it has a HTTP REST interface (which means it can be non-blocking from your app via abovementioned Mojo::UserAgent), Redis has a non-blocking driver module IIRC, and so on. Mainly it'll boil down to scouring CPAN for the bits you need, and asking around for the bits that you can't find on CPAN :)


Anyway, writing out stuff like this at 5am in the morning isn't going to
make it super clear but it's the best I can do :P

Again, Ben, thanks so much for this response and especially for taking the
time to do so so early in the morning.  I would request putting it on a
Mojo wiki in an effort to reduce the number of non-blocking related
messages and to give all of us poor folk who desire to write good apps but
just haven't got the background to get off to a good start.

You're welcome, just hope it's useful - keep in mind that a lot of what I said has been maybe overly simplified, there's usually a lot more going on behind the scenes, but I figure if you develop an interest in that you'll go take a peek at some source code :D

--
You received this message because you are subscribed to the Google Groups 
"Mojolicious" 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 http://groups.google.com/group/mojolicious.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to