How would I add an area that shows which users are online until they leave the page or log out?
I thought about making a table that keeps the users session until they leave the page, but how would I be able to tell when they left? I only need it to be updated maybe every 5 seconds. I want to stay away from cron jobs just in case of windows, (windows doesn't support cron does it?)
Thanks for any help
I did similar thing in one of our applications some time ago. Bind an Ajax call to some event like click or mousemove. In that call write the timestamp to the active_users table. Write another function to query this table and select users with timestamp older than say 5 min. Call this via Ajax as well in specific time intervals or on broeser event.
There are many possible solutions to it and you should probably post some code for your implementation if you want code back.
As an overall design question, you would probably need a means for storing how many users are online (more on detecting that below) either in memory (backend service, in memory DB like redis or memcached) or on disk (regular DB).
As for detecting when users are online, it can be a fairly difficult problem, but assuming that exact up to the second numbers don't matter that much, you could increment the number (in the DB or memory from above) when you serve the page (PHP) or when the user loads the page (Javascript).
As for detecting when a user has navigated away, you could use an onunload handler in Javascript such that it notifies the server when the user navigates away. One caveat to watch out for is that this message could get lost or not be sent (ie. JS off, browser crash, etc.) and your count could become wildly inaccurate.
Alternatively, the client can ping the server with a heartbeat every [predefined interval] and the server would know they are online (decrementing the counter when the user stops sending the heartbeat, assuming they have navigated away)
Based on these high level solutions, they could also be combined to form a hybrid solution, using heartbeats to detect that users are still online and an onunload handler to notify the server right away on close/navigation.
Finally, just as a note about cron jobs, Windows doesn't have cron per se but can accomplish a similar goal using Scheduled Tasks.
There are various options. Here is the one I prefer, but your mileage may vary.
Use JavaScript to ask your users to poll/bump your server once in a while and use this request to store which users are online. An example of the JavaScript part using jQuery:
var ajaxCall;
var livedataTimer;
function poll(timeout){
livedataTimer = setTimeout(function(){
if (ajaxCall != null){
ajaxCall.abort();
}
if (timeout == 5){
timeout = 10000;
}
ajaxCall = $.ajax({ url: '/ajax/bump.php', type: 'POST', success: function(data){
poll(timeout);
}, dataType: "json"});
}, timeout);
return livedataTimer;
}
An example of the /ajax/bump.php file:
<?php
session_start(); ## to know which user is online
## connect to your DB somewhere
mysqli_query($connection,"
INSERT INTO `online-status`
(`user`, `time`)
VALUES
(".(int)$_SESSION['user']['id'].", UNIX_TIMESTAMP())
");
return TRUE; ## return TRUE so that ajax knows the file has completed
?>
Now to get the list of online users, you can lookup your online-status table with a condition, for example, "where time is not older than 20 seconds."
It's a good practice to INSERT the data into a separate database, not UPDATE the timestamps in your userlist database. These tend to be quite slow.
I used the time(); function to insert a value into the users table every time a user visited a page, and pulled it out and and used this function,
echo '<h3>Online Users</h3>';
$query = "SELECT * FROM users";
$data = mysqli_query($dbc, $query);
while ($row = mysqli_fetch_array($data)){
if(time() - 10 < $row['active']){
echo ''.$row['username'].'';
}
}
Related
For some reasons (that I think it is not the point of my question, but if it help, ask me and I can describe why), I need to check MySQL tables continuously for new records. If any new records come, I want to do some related actions that are not important now.
Question is, how I should continuously check the database to make sure I am using the lowest resources and getting the results, close to the realtime.
For now, I have this:
$new_record_come = false;
while(! $new_record_come) {
$sql = "SELECT id FROM Notificatins WHERE insert_date > (NOW() - INTERVAL 5 SECONDS)";
$result = $conn->query($sql);
if ($result)
{
//doing some related actions...
$new_record_come = true;
}
else
{
sleep(5); //5 seconds delay
}
}
But I am worry that if I get thousands of users, it will make the server down, even if the server is a high price one!
Do you have any advice to make it better in performance or even change the way completely or even change the type of query or any other suggestion?
Polling a database is costly, so you're right to be wary of that solution.
If you need to scale this application up to handle thousands of concurrent users, you probably should consider additional technology that complements the RDBMS.
For this, I'd suggest using a message queue. After an app inserts a new notification to the database, the app will also post an item to a topic on the message queue. Typically the primary key (id) is the item you post.
Meanwhile, other apps are listening to the topic. They don't need to do polling. The way message queues work is that the client just waits until there's a new item in the queue. The wait will return the item.
A comment suggested using a trigger to invoke a PHP script. This won't work, because triggers execute while the transaction that spawned them is not yet committed. So if the trigger runs a PHP script, which probably needs to read the record from the database. But an uncommitted record is not visible to any other database session, so the PHP script can never read the data that it was notified about.
Another angle (much simpler than message queue I think):
I once implemented this on a website by letting the clients poll AND compare it to their latest id they received.
For example: You have a table with primary key, and want to watch if new items are added.
But you don't want to set up a database connection and query the table if there is nothing new in it.
Let's say the primary key is named 'postid'.
I had a file containing the latest postid.
I updated it with each new entry in tblposts, so it contains alsways the latest postid.
The polling scripts on the clientside simply retrieved that file (do not use PHP, just let Apache serve it, much faster: name it lastpostid.txt or something).
Client compares to its internal latest postid. If it is bigger, the client requests the ones after the last one. This step DOES include a query.
Advantage is that you only query the database when something new is in, and you can also tell the PHP script what your latest postid was, so PHP can only fetch the later ones.
(Not sure if this will work in your situation becuase it assumes an increasing number meaning 'newer'.)
This might not be possible with your current system design but how about instead of using triggers or a heartbeat to poll the database continuously that you go where the updates, etc happen and from there execute other code? This way, you can avoid polling the database continuously and code will fire ONLY IF somebody initiates a request?
I'm thinking about a webchat working with PHP, JQuery and MySQL but I'm in some trouble about how the queries should be send to the db.
Structure:
MySQL receives all data from chat and saves it like in a table "conversation" with columns from/to.
Jquery do the queries and verifies if there is any new message for the current user.
Problem:
How to do the queries with Jquery in a way to not overload the db server (in consideration to have a lot of users)? I think to query the db with a timer in Jquery, so within some secs. a new query will be done and it will be repeated for all users at same time.
The best way to do it (according to me) is to have a 1 second interval in jquery. Once a second you call a php file (fetch_new.php for example).
In your database, you have two tables:
users
messages
When a user client makes a call to fetch_new.php (through JavaScript), you update the users table and set a column (last_sync) to the current time (preferably in microtime, that way the user will never get two of the same messages sent to them). The new message(s) get appended to the conversation.
When you execute your query, it should look something like this:
SELECT * FROM `messages` WHERE `to` = $user_id AND `timestamp` > $last_sync
It is perfectly fine to send many questions per second to MySQL as long as it can handle many connections (enough RAM and CPU). Keep an eye on the "Status" tab in PHPMyAdmin to see how much CPU and RAM is being used.
In the chat applications I've built I've added some "intelligence" features. If a user hasn't received any messages in the last (let's say) 20 seconds, I increase the interval to make a request to fetch_new.php every 2 seconds instead of every second. If over a minute has passed, I increase it to 3 seconds and so on. This way you won't slow down your application by having a lot of idle users.
my approach would be something like:
$(document).ready(function()
{
setTimeout(function()
{
// do something here
}, 2000);
});
The above code will execute your function every 2 seconds. Don't know about efficiency though. So perhaps another approach would work better.
I think I understand the concept of polling fairly well. You basically just request data from the server, but only once the data has changed, does the server return it. Straight forward stuff. My problem comes with this example.
Let's say I have auctions with data that changes constantly. Among this data are things like
Closing time of the auction
Number of current bidders on the auction
When I start the long poll, I basically have something like this:
while($counter < $MESSAGE_TIMEOUT_SECONDS) {
$newData = getNewData();
$hasDataChanged = hasDataChanged($newData, $oldData);
if ( $hasDataChanged ) {
return $newData;
}
usleep($MESSAGE_POLL_MICROSECONDS);
}
Where do I get the old data from? I mean, when doing the request, I can either post the current state as it was last given to me, or I can store the data in Session. Am I allowed to store stuff in session when doing a long poll, or should I do a POST from the Javascript with the current state of that page?
Also, how would I stop someone opening 50 pages from killing the database? I mean, getNewData() effectively goes to the database. With a polling interval of about half a second, this could mean 50 requests every half a second, which could mean 50 x 2 x 30 = 3000 requests to the database in 30 seconds by just one user, if he decided to open 50 tabs?
Any ideas?
I would cache all ajax response data in memory along with last date that each auction had any change so you don't have to compare old and new data but just datetime. Invalidate cache on some change (closed, new bid, etc.) for auction.
Then from client side send time from last known data (last ajax call or when user opened page) and compare dates to see if something changed, if it didn't just return status:nochange (now client side knows there is nothing to update) and if it did return all necessary data from cache and update users page.
This model should protect database from overloading.
I want to show users who else is logged in as part of a comment system. What are best practices for keeping track of users? For example:
Do you keep track of all sessions and then mark them as closed. Or do you delete users upon logout, keeping track only of active users.
I'm thinking I should create a table with userid, time logged in, time logged out and/or status. Is that the way to go or is there some alternative approach of tracking session ids. If using a table, is there value in keeping sessionid. Should I delete row when session no longer active, negating need for whenloggedout field.
There is a login so easy to keep track of users logging in. However, it is harder to track users logging out since their session may be broken by browser crashing etc.
Is it best practice to consider users logged in as long as they have not destroyed session... for example, FB and Gmail will leave you logged in almost indefinitely--or should there be a time limit since last activity? The idea of saving to this table every time there is activity on site is not appealing.
Right now, I'm thinking of following:
create table loggedin (userid (int), whenloggedin (datetime), whenlogged out (datetime), loggedin(tinyint))
with the latter going to 0 either if whenloggedout not null or after some long time limit like 24 hours. I imagine FB while leaving you logged in for long periods of time, also keeps track of activity for purposes of chat etc. but not sure. I'm also thinking of letting the table expand, rather than deleting closed sessions but maybe that's a mistake.
Would this approach be considered adequate or is there a better way. Many thx for advice on this.
Depending on how you want it to work you basically have two options:
Define a timeout after which you consider a user logged out
Use ajax/websockets/whatever to poll user
1: Timeout
This is the simpler use case. Every time the user requests a page, you update a timestamp in your database.
To find out how many users are online, you would do a query against this database and do a COUNT of users who have been active in the last N minutes.
This way you will get a relatively accurate idea of how many people are actively using the site at the moment.
2: Constant polling
This is a bit more complex to implement due to having to update the server with Ajax. Otherwise it works in a similar fashion to #1.
Whenever a user is on a page, you can keep a websocket open or do ajax requests every N seconds to the server.
This way you can get a pretty good idea of how many people have pages open on your site currently, but if a user leaves the page open in their browser and doesn't do anything, it would still count them as being online.
A slight modification to the idea would be to use a script on the client to monitor mouse movement. If the user doesn't move the mouse on your page for say 10 minutes, you would stop the polling or disconnect the websocket. This would fix the problem of showing users who are idle as being online.
To circumvent the problem with knowing if a user has logged out or browser crash ect, is to use a heartbeat/polling of sorts here is a stripped down example of how you can do that with jQuery
function heartbeat(){
setTimeout(function(){
$.ajax({ url: "http://example.com/api/heartbeat", cache: false,
success: function(data){
//Next beat
heartbeat();
}, dataType: "json"});
}, 10000);//10secs
}
$(document).ready(function(){
heartbeat();
});
http://example.com/api/heartbeat would keep the session alive & update a timestamp in your db, then on each page load you would check the time stamp with current time ect and if its lower then say 15 seconds then you would log them out.
Constant Polling and using heartbeat are a good idea, but for some scenarios they may create useless load on server. I think you should think about the importance of keeping track of your users and use it very appropriately, especially considering the impacts your changes may have on load time.
I have log table for all users of website
I'm recording various data about user righ after successfull login.
If signout_dt field not filled and status is 1 for some user_id, website prevents login automatically.
For that who have cookies - there is no problem.
The problem is,lets say user signed in without cookies: only sessions variables. I have no idea, how can I update db table and signout user let's say after 30 minute inactivity. Note that I can't create cron job or something serverside, because using shared hosting.
Heard that, it's possible to create some script like heartbeat that continously sends some data about user activity. But I think this will heavily load the server especially if there are more than 1000 users.. Any suggestion, tutorial, article, something else?
Update
Deceze tried to explain but I really need better explanation (better idea), with code.
To "timeout" a user, simply note the time he was last seen. Then, when necessary, check if the last time you've seen the user was over x minutes/hours/days, and consider the last session timed out. You don't need to run a cron job or anything that cleans up after users in realtime, you only need to be able to determine if some information should be considered stale when you need that information.
You may want to occasionally run a cron job or something to clean out old, unnecessary data, but that doesn't need to happen in realtime. You could even run this as part of a regular page request:
if (mt_rand(1, 1000) == 1) {
mysql_query('DELETE FROM `table` WHERE `last_seen` < some point in time');
}
To note the last seen time, just run this query on each page load:
UPDATE `table` SET `last_seen` = NOW() WHERE `user_id` = ...
To avoid thrashing the database with these queries, you can also just do it every so often. Keep a "last_seen_last_updated" timestamp in the user's session, then on each page load check if you might want to update the database:
if ($_SESSION['last_seen_last_updated'] < strtotime('-5 minutes')) {
mysql_query(...);
$_SESSION['last_seen_last_updated'] = time();
}
That gives you 5 minutes of jitter, but that's usually perfectly acceptable.
Your management of sessions is broken and does not conform to accepted stateless behaviour - in as much as you apparently require the user to sign out, which rarely is the case in web applications -- most people just closes the browser, and the cookies will just float around and appear next time the user accesses the website. If the system wants the user to sign in again, then the web server will have to validate the session -- for example using a timestamp and/or cookie signing etc, and invalidate the cookie to force the user to re-login if needed.
Hence you should treat cookies and sessions variables the same -- that is; have your server side generate a unique signed value. Keep an expiration time (for example now()+20min) either in the cookie/session variable or keep the expiration time in the database.
At each access check that the cookie/session-variable is correctly signed, and check that it is not beyond the expiration time, and update the expiration time to allow another 20min.
If the access is past the expiration time -- i.e. the user has been idle for too long, then clear the cookie/session-variable and force the user to login again.
If you keep the expiration time in the database, you simply write a small program which once and day or once an hour run though all records and remove those which you deem too old.
As per my understanding of your question, you want to address following things:
a. If for a given period of time, a user is inactive then he should be logged out and your database table gets updated. Here being inactive means, user has not used keyboard/mouse for a given period of time.
b. If a user closes the browser without logging out, then he should be forcefully logged out and database table gets updated.
Both these things can be accomplished using Javascript Functions and Ajax. Following is the flow which we have in our application for addressing above issues:
Create a Javascript function, say logoutUser(), which will send an Ajax request for updating the database tables and destroying the session.
Use Javascript function - setTimeOut - to call logoutUser() function after time period you have set for inactivity.
Use Javascript events to catch mouse movement and keyboard activity and in every such event call use successively clearTimeOut (in order to remove the old time for execution of logoutUser()) and setTimeOut (for setting the new time of execution of logoutUser()). This way you would be able to catch the inactivity and logout the user after a period of time.
For taking care of the issue related to closing of browser window use 'onbeforeunload' event of javascript and in this event send the Ajax request for updating the database tables.
As our application uses ExtJS, thus, we have used ExtJs library functions to detect events. You can also prefer using some Javascript library for catching the events and implemeting the above solution.
Hope this helps.