Heartbeat
From Ajax Patterns
There's an archived version of this pattern available, taken from the Ajax Pattern book draft, showing roughly how it appeared before the page became publicly editable.
对于红彤美容整形也许大家还不太相信它的神奇,看看韩国好多整过形的美女演员出演的电视剧,整形前是多么地
难看,整形后判若两人,大家看到的都是他们整形后的美丽容颜,那在中国的整形美容行业发展的怎么样呢?我们
现在来设想下,如果要是把网络红人“芙蓉姐姐”整形成香港明星李嘉欣,那么结果会怎么样呢?她们两个不同风格的人能整形出来什么效果
?芙蓉能整出李嘉欣的形象气质出来吗? 美容整形在国外一直是很热门的产业,许多面部有缺陷的女孩子经过整形以后能成为耀眼的明星。一些胸部整形过
的女星也能跟美国的艳星乔丹相媲美,演艺事业也蒸蒸日上,导演也亲睐她们,经常给她们抛来橄榄枝。[http://www.100woman.cn 处女膜修
复]手术,还你一个做纯洁女人的机会,双眼皮 手术,能给那些单眼皮女生一次跨越的机会,一下子跻身双眼皮行
列,男生们好象比较流行单眼皮,像Rain!割个双眼皮,轻松;做个双眼皮,方便;光子脱毛,快捷;
清除腋臭,简单;去整形美容医院,给自己一次完美的机会,去雅靓看看,能给你一个意外的惊喜! Evidence:' 0/3
Tags: ACK Announcement Flash Heartbeat Monitor Signal
Contents |
Ajax/Javascript Programming and Usability in "Ajax Design Patterns" Book |
In A Blink
List of current players in a MUD game (back-end). With "last heartbeat" field.
Goal Story
For auditing purposes, Frank must be running the factory monitoring system at least 50% of the time, so the server records the time he's running it. But the client is fat, with most activity only occurring inside the browser and not following through to the server. Thus, the browser explicitly uploads a heartbeat message every 10 minutes, to tell the server Frank's logged in and actively working with the system.
Problem
How do you know the user still has an application in the browser, and is actively working with it?
Forces
- It's often useful for the server to track if the user is active, with several applications highlighted in the Solution of Timeout.
- Due to the state-less nature of HTTP, the server doesn't know when the user has quit the browser, experienced a browser crash, or surfed away to a different URL. Note that you could use the Javascript onunload event to catch the last of these, but it won't work for the first two. In tracking the user's activity, the server may be assuming the user's still active when they've in fact abandoned the application.
- The user sometimes spends a long time working in the browser, but in a manner which yields no calls to the server. For example, filling out a long form or playing a game. The server will have no way to know the user's still around if no calls are made.
Solution
Have the browser periodically upload heartbeat messages to indicate the application is still loaded in the browser and the user is still active. The server keeps track of each user's "last heartbeat". If the heartbeat interval is 10 minutes, and the user's last heartbeat was longer than 10 minutes ago, the server knows the user is no longer active. The objective is to help the server track which users are active; note that
TODO graph against time
Heartbeat messages are uploaded to a special "heartbeat service" using XMLHttpRequest Calls. As they affect server state, they should be POSTed in. The heartbeat service will update the user's last heartbeat record, but how does it associate a message with a user? Heartbeat relies on some form of session management. One approach is to use cookies, either directly or via a cookie-based session framework. However, if you do that, the conversation will be stateful, thus violating a fundamental RESTFul Service principle. A cleaner approach is to explicitly include the session ID in the heartbeat body. Note that you shouldn't just upload the user ID, as others could easily fake the user ID.
Heartbeat is closely related Timeout, but they work in different ways. You can use either independently, but Heartbeat works best as a supplement to Timeout. I'll tell you why. The main purpose of Timeout is to stop the browser application upon timeout for security and bandwidth reduction. Notifying the server of timeouts yields extra benefits, but it's optional. Moreover, it won't work especially well anyway, because it relies on the application still sitting in the browser and working fine. That's why we use Heartbeat messages, which are the inverse of Timeouts, and therefore a good supplement. Where Timeout has the browser announce when a timeout has occurred, Heartbeat has it continuously announce that a timeout hasn't occurred. As soon as the server detects a missed heartbeat, it can assume the application is no longer running.
As a variant of Heartbeat, you can maintain a "last seen" or "last request" field, to track the last time the user issues a request. Thus, it's not just heartbeat messages, but any other messages that will refresh the user's record. The heartbeat is just a backup Doing so might paint a more meaningful picture if you're showing the timestamp to other users or feeding it into analysis.
This pattern is purely speculative in the context of Ajax applications. However, Heartbeats are commonplace in enterprise messaging systems, where they are used to monitor the status of components throughout a network.
Decisions
How will you maintain user records?
There are two main options for maintaining user records:
- In memory, which means the data is not persisted and will be lost once the user is timed out. In most environments, this is what will happen if you rely on standard session objects.
- Directly in the database, against persistent user records. There's some extra storage and data maintenance involved, but the benefit is that you always know when the user was last seen.
What, if anything, will cause the browser application to stop sending heartbeats?
One option is for the browser application to always send heartbeats. This lets the server track if the application is still sitting in the browser, which may be useful for analysis purposes. More likely, though, you probably want to send heartbeats only if the user is actively working with the browser. Thus, as mentioned in the solution, the heartbeat is effectively a message that says "the user has not yet timed out".
How much time between heartbeats? How much delay until a user is declared inactive?
You need to decide on the period between heartbeats. If it's too long, the information will not be very useful - imagine discovering that A.User is assumed active because his last heartbeat was 12 hours ago. If it's too short, you'll be placing strain on the network, as well as impacting on browser and server performance.
The right figure could vary from sub-second up to 30 minutes or more. It depends on the following factors:
- Application: Some applications make up-to-the-second information more critical. In a multi-user system, for example, users' work may be dictated by whoever else is present. If you wait 10 minutes to tell Alice that Bob has quit the chess game, she might be annoyed that she's wasted the last 10 minutes thinking about her next move.
- Available Resources: Ideally, the period should be as short as possible, but you can't always justify it. For an Intranet application, you're likely to use a shorter period in recognition of better resources per user.
Real-World Examples
As this pattern is speculative, there are no real-world examples at this time.
Refactoring Illustration
The Timeout pattern refactored the wiki to produce the Timeout Wiki Demo, and two further refactorings from there. This illustration creates a third refactoring of the basic Timeout demo, introducing a Heartbeat. Note that Heartbeat can work independently of Timeout, but works better in tandem, as this pattern demonstrates.
To illustrate Heartbeat, one of its main applications is shown: to let users see who else is "currently online", i.e. who else has a recent heartbeat registered. Thus, each user is assigned a random ID and a Heartbeat mechanism is used to maintain the "Currently Online" list.
But first, let's look at the heartbeat mechanism. Server-side, the Author class contains a lastRequest property. (lastAction is not used in this demo.)
class Author {
private $id;
private $lastAction;
private $lastRequest;
...
}
session.php is the heartbeat service - it accepts a message with an author ID and updates the user's lastRequest. Note that a production system should accept a session ID, rather than a user ID. The service extracts the author from the database and updates its lastRequest to the present. If the author does not yet exist, a new record is created with lastRequest set to the present. Finally, the record is persisted.
if (isset($_POST['renew']) && $_POST['renew']=="true") {
$authorId = $_POST['author'];
$now = time();
$author = $authorDAO->fetch($authorId);
logInfo("Got author record for $authorId");
if ($author) {
logInfo("Updating timestamp for user $authorId");
$author->setLastRequest($now);
} else {
logInfo("Adding author $authorId");
$author = new Author($authorId, $now, $now);
}
$authorDAO->persist($author);
}
To access the heartbeat service, a periodic loop is set up. Because the heartbeat call is a "fire-and-forget", an empty callback function is used.
window.onload = function() {
...
heartbeatTimer = setInterval(sendHeartbeat, HEARTBEAT_PERIOD);
...
}
function sendHeartbeat() {
vars = {
renew: true,
author: authorId
};
ajaxCaller.postForPlainText("session.php", vars, function() {});
}
And that's the basic heartbeat pattern. But there's a bit more here, because we're integrating it into Timeout. We want to stop sending heartbeats when a timeout has occurred in the browser. Above, we created a variable, heartbeatTimer, to track the periodic heartbeat calls. Since we have a handle on the periodic process, we are able to cancel it upon timeout.
function onTimeout() {
...
clearInterval(heartbeatTimer);
...
}
The other feature here is the list of online users, which illustrates one way to use the heartbeat data. session.php exposes an XML list of currently online users. The fetchRecentlyActive method accepts a parameter indicating just how recently active the records should be. In this case, we ask for users with a request in the past 10 seconds.
... if ($_GET="current") {
header("Content-type: text/xml");
echo "<authors>";
foreach ($authorDAO->fetchRecentlyActive(10) as $author) {
echo "<author>{$author->getId()}</author>";
}
echo "</authors>";
In the browser, there's a div to contain the list.
<h1>Who's Online?</h1>
<div id="currentlyOnline"></div>
A timer is established to perform a Periodic Refresh on the current data. Every few seconds, the XML is requested and the callback function transforms it into HTML for display.
currentlyOnlineTimer = setInterval(updateCurrentlyOnline, CURRENTLY_ONLINE_PERIOD);
function updateCurrentlyOnline() {
ajaxCaller.getXML("session.php?current", function(xml) {
$("currentlyOnline").innerHTML = "<ul>";
var authors = xml.getElementsByTagName("author");
for (var i=0; i<authors.length; i++) {
var authorName = authors[i].firstChild.nodeValue;
$("currentlyOnline").innerHTML += "<li> " + authorName + "</li>";
}
$("currentlyOnline").innerHTML += "</ul>";
});
}
As with the heartbeat timer, the "currently online" timer is cancelled on timeout, so no more Periodic Refreshes will occur.
function onTimeout() {
...
clearInterval(currentlyOnlineTimer);
...
}
Alternatives
Related Patterns
Timeout
Timeout is a companion pattern, as discussed in the Solution above.
Submission Throttling
Heartbeat resembles Submission Throttling and Periodic Refresh insofar as all have a continuous XMLHttpRequest Call cycle. However, Heartbeat has a more specific aim, namely informing the server of browser state.
Visual Metaphor
Heartbeat, of course, is based on a biological metaphor.
Want to Know More?
Brief summary of Enterprise Java "Heartbeat" pattern. See Mark Grand's "Java Enterprise Design Patterns" for the full pattern. 热转印机 有机玻璃 IBM服务器 Dell服务器 IBM服务器 HP服务器 CISCO交换机 IBM服务器 [1] 调查 单片机培训 工控机 北京搬家 北京搬家 会议服务 装饰装潢 展览制作 北京装潢公司 北京装饰公司 北京装修公司 北京月嫂 门窗厂 代开发票 北京物流 北京搬家 门窗厂 北京保洁 [2] 代开发票 北京汽车陪练 空调维修 顺义房产 空调回收 大金中央空调 电地暖 冷库 格力中央空调 顺义旅游 约克中央空调 风幕机 劳保用品 北京物流公司 机柜 机柜 电机修理 净化工程 五粮液酒 燃气灶维修
Time your website with
WebWait - from the creator of AjaxPatterns.org
