Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP infinitive loop or jQuery setInterval?

Js:

<script>
function cometConnect(){
    $.ajax({
          cache:false,
          type:"post",
          data:'ts='+1,
          url: 'Controller/chatting',
          async: true,
          success: function (arr1) {
              $(".page-header").append(arr1);
          },
          complete:function(){
            cometConnect(true);
            nerr=false;
          },
          dataType: "text"
        }); 
}
cometConnect();
</script>

Php:

public function chatting()
{
    while(true)
    {
       if(memcache_get(new_message))
          return new_message; 
       sleep(0.5);
    }
}

Is this a better solution than setting setInterval which connects to the PHP method which returns message if there is any every 1 second (1 sec increases +0.25 every 5 seconds let's say)?

If I used first solution, I could probably use sleep(0.5) it would give me messages instantly, because php loop is cheap, isn't?

So, what solution is better (more importantly, which takes less resources?). Because there are going to be hundreds of chats like this.

Plus, can first solution cause problems? Let's say I would reload a page or I would stop execution every 30 secs so I wouldn't get 502 Bad Gateway.

EDIT: I believe the second solution is better, so I am going to reimplement my site, but I am just curious if this can cause problems to the user or not? Can something not expected happen? First problem I noticed is that you can't go to other page until there is at least one new message.

like image 890
good_evening Avatar asked Dec 26 '11 16:12

good_evening


3 Answers

A chat is a one to many communication, while each one of the many can send messages and will receive messages from everybody else.

These two actions (sending, receiving) happen continuously. So this looks like an endless loop whereas the user can enter into (join the chat) and exit (leave the chat).

  1. enter
  2. send message
  3. receive message
  4. exit

So the loop looks like this (pseudo-code) on the client side:

while (userInChat)
{
    if (userEnteredMessages)
    {
        userSendMessages(userEnteredMessages)
    }
    if (chatNewMessages)
    {
        displayMessages(chatNewMessages)
    }
}

As you already note in your question, the problem is in implementing such a kind of chat for a website.

To implement such a "loop" for a website, you are first of all facing the situation that you don't want to have an actual loop here. As long as the user is in chat, it would run and run and run. So you want to distribute the execution of the loop over time.

To do this, you can convert it into a collection of event functions:

ChatClient
{
    function onEnter()
    {
    }
    function onUserInput(messages)
    {
        sendMessages = send(messages)

        display(sendMessages)
    }
    function onReceive(messages)
    {
        display(messages)
    }
    function onExit()
    {
    }
}

It's now possible to trigger events instead of having a loop. Only left is the implementation to trigger these events over time, but for the moment this is not really interesting because it would be dependent to how the chat data exchange is actually implemented.

There always is a remote point where a chat client is (somehow) connected to to send it's own messages and to receive new messages from.

This is some sort of a stream of chat messages. Again this looks like a loop, but infact it's a stream. Like in the chat clients loop, at some point in time it hooks onto the stream and will send input (write) and receive output (read) from that stream.

This is already visible in the ChatClient pseudo code above, there is an event when the user inputs one or multiple messages which then will be send (written). And read messages will be available in the onReceive event function.

As the stream is data in order, there needs to be order. As this is all event based and multiple clients are available, this needs some dedicated handling. As order is relative, it will only work in it's context. The context could be the time (one message came before another message), but if the chat client has another clock as the server or another client, we can't use the existing clock as time-source for the order of messages, as it normally differs between computers in a WAN.

Instead you create your own time to line-up all messages. With a shared time across all clients and servers an ordered stream can be implemented. This can be easily done by just numbering the messages in a central place. Luckily your chat has a central place, the server.

The message stream starts with the first message and ends with the last one. So what you simply do is to give the first message the number 1 and then each new message will get the next higher number. Let's call it the message ID.

So still regardless which server technology you'll be using, the chat knows to type of messages: Messages with an ID and messages without an ID. This also represents the status of a message: either not part or part of the stream.

Not stream associated messages are those that the user has already entered but which have not been send to the server already. While the server receives the "free" messages, it can put them into the stream by assigning the ID:

    function onUserInput(messages)
    {
        sendMessages = send(messages)

        display(sendMessages)
    }

As this pseudo code example shows, this is what is happening here. The onUserInput event get's messages that are not part of the stream yet. The sendMessages routine will return their streamed representation which are then displayed.

The display routine then is able to display messages in their stream order.

So still regardless how the client/server communication is implemented, with such a structure you can actually roughly handle a message based chat system and de-couple it from underlying technologies.

The only thing the server needs to do is to take the messages, gives each message an ID and return these IDs. The assignment of the ID is normally done when the server stores the messages into it's database. A good database takes care to number messages properly, so there is not much to do.

The other interaction is to read new messages from the server. To do this over network effectively, the client tells the server from which message on it likes to read from. The server will then pass the messages since that time (ID) to the client.

As this shows, from the "endless" loop in the beginning it's now turned into an event based system with remote calls. As remote calls are expensive, it is better to make them able to transfer much data with one connection. Part of that is already in the pseudo code as it's possible to send one or multiple messages to the server and to receive zero or more messages from the server at once.

The ideal implementation would be to have one connection to the server that allows to read and write messages to it in full-duplex. However no such technology exists yet in javascript. These things are under development with Websockets and Webstream APIs and the like but for the moment let's take things simple and look what we have: stateless HTTP requests, some PHP on the server and a MySQL database.

The message stream can be represented in a database table that has an auto-incrementing unique key for the ID and other fields to store the message.

The write transaction script will just connect to the database, insert the message(s) and return the IDs. That's a very common operation and it should be fast (mysql has a sort of memcache bridge which should make the store operation even more fast and convenient).

The read transaction script is equally simple, it will just read all messages with an ID higher than passed to it and return it to the client.

Keep these scripts as simple as possible and optimize the read/write time to the store, so they can execute fast and you're done even with chatting over plain HTTP.

Still your webserver and the overall internet connection might not be fast enough (although there is keep-alive).

However, HTTP should be good enough for the moment to test if you chat system is actually working without any loops, not client, nor server side.

It's also good to keep servers dead simple, because each client relies on them, so they should just do their work and that's it.

You can at any time change the server (or offer different type of servers) that can interact with your chat client by giving the chat client different implementations of the send and receive functions. E.g. I see in your question that you're using comet, this should work as well, it's probably easy to directly implement the server for comet.

If in the future websockets are more accessible (which might never be the case because of security considerations), you can offer another type of server for websockets as well. As long as the data-structure of the stream is intact, this will work with different type of servers next to each other. The database will take care of the congruency.

Hope this is helpful.


Just as an additional note: HTML5 offers something called Stream Updates with Server-Sent Events with an online demo and PHP/JS sources. The HTML 5 feature offers already an event object in javascript which could be used to create an exemplary chat client transport implementation.

like image 159
hakre Avatar answered Oct 04 '22 05:10

hakre


I wrote a blog post about how I had to handle a similar problem (using node.js, but the principles apply). http://j-query.blogspot.com/2011/11/strategies-for-scaling-real-time-web.html

My suggestion is, if it's going to be big either a) you need to cache like crazy on your web server layer, which probably means your AJAX call needs to have a timestamp on it or b) use something like socket.io, which is built for scaling real-time web apps and has built-in support for channels.

like image 32
Jamund Ferguson Avatar answered Oct 04 '22 06:10

Jamund Ferguson


Infinite loops in php can and will use 100% of your CPU. Sleep functions will fix that problem. However, you probably don't want to have a separate HTTP process running all the time for every client that is connected to your server because you'll run out of connections. You could just have one php process that looks at all inbound messages and routes them to the right person as they come in. This process could be launched from a cron job once a minute. I've written this type of thing many times and it works like a charm. Note: Make sure you don't run the process if it's already running or you will run into multiprocessing problems (like getting double messages). In other words, you need to make the process thread safe.

If you want to get real time chatting, then you might want to take a look at StreamHub which opens a full duplex connection to the client's browser.

like image 21
Brainware Avatar answered Oct 04 '22 06:10

Brainware