±¾ÎÄÒªµã
1.ÔÚWebʱ´úµÄǰ¶þÊ®Ä꣬ÔÚÓû§ÊÓͼ¼°ÆäÏÖʵ»òÐéÄâÊÀ½ç¼äµÄMVC¿É¹Û²ìµÄʼþÇý¶¯Í¬²½ÒѾ²»ÔÙ·¢»Óʲô×÷ÓÃÁË¡£
2.½üÆÚµÄһЩнøÕ¹Ê¹ÕâÒ»»ù´¡ÀíÄîµÃÒÔÔÚWeb¿ª·¢ÉçÇø¸´ËÕ¡£
3.dWMVC ºÍ pWMVC¼Ü¹¹·¶Ê½¿ÉÒÔÓÃÓÚÍê³É¶Ëµ½¶Ë±ä»¯¹Û²ì¡°Ê¼þ»·¡±È¥´´½¨ÎÞ·ì¸ßЧµÄʵʱÏìÓ¦Ó¦ÓÃÐÐΪ¡£
4.´«Í³Öмä¼þ¼Ü¹¹ºÍÐÂÉú´úµÄ·þÎñÆ÷Ô˶¯ÆÚ»·¾³¶¼¿É±»ÓÃÓÚÈ¥Íê³ÉÕâЩʵʱÏìÓ¦ÐÐΪ¡£
5.·Ç´«Í³·þÎñÔËÐл·¾³ºÍÊý¾Ý¿âÏíÓÐʹÓ÷DZê×¼»¯¼¼ÊõÈ¥´´½¨´´Ð½â¾ö·½°¸µÄ¼Ü¹¹×ÔÓÉ¡£
֮ǰÔÚ¡¶ ¶àÐÎ̬MVCʽWeb¼Ü¹¹µÄ·ÖÀà ¡·ÖУ¬ÎÒÃDzûÊö²¢ÌÖÂÛÁËWMVC(»ùÓÚWebµÄ MVC ) ¼Ü¹¹·¶Ê½µÄÈý¸öÖÖÀà¡£ËüÃÇÊÇ·þÎñÆ÷¶ËWMVC
(sWMVC)¡¢Ë«ÖØWMVC (dWMVC)ÒÔ¼°µã¶ÔµãWMVC (pWMVC)¡£ sWMVCͨ³£±¾ÖÊÉÏÊǾ²Ì¬µÄ£¬¶øÆäËûÁ½¸ö¼Ü¹¹·¶Ê½¿ÉÓÃÓÚ¹¹½¨ÊµÊ±ÏìÓ¦µÄWebÓ¦ÓÃ×é¼þ¡£ÕâÊÇÆäºóÐøÎÄÕ£¬ÔÚÕâÆªÎÄÕÂÖУ¬ÎÒÃǽ«ÀûÓÃÕâÁ½¸ö¼Ü¹¹·¶Ê½È¥Éè¼ÆºÍÑÝʾÍêÈ«¶¯Ì¬ºÍÏìӦʽÏÖ´úWeb×é¼þ¡£
MVC¼Ü¹¹·½·¨µÄºËÐÄÊÇʵÏÖÓû§ÊÓͼÓëËüÃÇËù·´Ó³µÄÕæÊµ»òÐéÄâÊÀ½çÖ®¼äͬ²½µÄʼþÇý¶¯µÄ¹Û²ìÕßģʽ¡£¸ÃÊÓͼ£¨°üÀ¨»òδ°üÀ¨À´×ÔÓÚÓû§µÄ¶îÍâÖ¸ÁԤÆÚÊÇ·´Ó³ÊÀ½çµÄ±ä»¯¡£ÔÚÐí¶àMVCʵÏÖÖж¼ÌåÏÖÁËÕâһ˼Ï룬´Ó×î³õµÄ×ÀÃæ½çÃæµ½ÏÖ´úÔöÇ¿ºÍÐéÄâÏÖʵ£¨
AR ºÍ VR £©¡£ÔÚ¡¶ ¶àÐÎ̬MVCʽWeb¼Ü¹¹µÄ·ÖÀà ¡·µÄÌÖÂÛÖÐÌáµ½£¬Õâ¸ö»ù±¾Ë¼ÏëÔÚWebµÄǰ¶þÊ®ÄêÖ®ºóÒѾ²»ÔÙ·¢»Óʲô×÷ÓÃÁË¡£ÔÚÕâ¶Îʱ¼ä£¬WebÓ¦ÓÃÒÔ»ùÓÚsWMVCµÄ·½·¨ÎªÖ÷µ¼¡£ÔÚ×î½ü¼¸Ä꣬ËüÔÚWUI£¨WebÓû§½çÃæ£©Ó¦Óÿª·¢ÉçÇøÖÐÓÐËù¸´ÐË¡£Õâ¸öÐÂÔ˶¯ÊÇÓÉÐí¶à¼¼Êõ²úÆ·ºÍ±ê×¼ÐÒéÇý¶¯µÄ¡£
ÔÚ±¾ÎÄÖУ¬ÎÒÃǽ«ÔËÓÃһЩеĽø²½È¥ÊµÏÖÒì²½µÄ¡¢×ÔÈ»µÄ¡¢ÎÞ·ìµÄÒÔ¼°¸ßЧµÄ´ÓWUIµ½ºó¶ËSoRÔʼ¼Ç¼£¨source
of record£©±ä»¯¹Û²ìÏìӦʽ¡°Ê¼þ»·¡±¡£Õâ·½Ãæ¹Ø¼üʵÓü¼ÊõÊÇ£º
ÒÔÏÂÌÖÂÛÏà¹ØµÄÔ´Âë¿É µã»÷ ÔÚGitHubÉÏ»ñÈ¡¡£
Óû§¹ÊÊÂ
ÎÒÃǼٶ¨¿Í»§ÏëÒªÒ»¸ö»ùÓÚä¯ÀÀÆ÷µÄ²©¿ÍÆÀÂÛϵͳ¡£¸ÃWebÓ¦ÓÃÔÊÐíÓû§¹Û¿´Ò»¸ö²©¿ÍÖ÷Ìâ²¢·¢±íÆÀÂÛ¡£
ÏÂÃæÊÇÒ»ÕÅWebÒ³ÃæµÄ¸ÅÄîÉè¼Æ½ØÍ¼£¬ÓÉÈý¸ö×ÓÊÓͼ¹¹³É¡£×îÉϲãÄÇÒ»¿éÏÔʾµÄÊDz©¿ÍÖ÷Ì⣬ÆäºóÊÇÆÀÂÛÊäÈëºÍÌá½»Óò¡£×îºóÒ»¿éÇøÓò¸ºÔðÏÔʾËùÓÐÓû§ÊäÈëµÄÆÀÂÛ¡£

ͼ1 ²©¿ÍÆÀÂÛÉè¼Æ½ØÍ¼
¸ÃÈÕ־ϵͳӦ¸Ã°üÀ¨Á½¸öºÜÓÐÌØÉ«µÄÓ¦Óãº
µÚÒ»¸öÓ¦Óûá»ñÈ¡²©¿ÍÆÀÂÛµÄËùÓÐȨ²¢°ÑËüÃÇ´æ´¢½ø¼¯ÖÐʽÊý¾Ý¿âÖС£
µÚ¶þ¸öÓ¦ÓÃÔÚ¼¯ÖÐʽÊý¾Ý¿âÖв»±£´æÈκÎÓû§ÆÀÂÛ£¬ÒÔÈ·±£Óû§Òþ˽¼°¿Í»§ÔðÈΡ£
¸ÃϵͳµÄµÚÈý¸ö×é¼þÊÇ°ÑÆäËûÀ´Ô´µÄ²©¿ÍÆÀÂÛÕûºÏµ½Õâ¸ö¼¯ÖÐʽÊý¾Ý¿âÖУ¬Ëü½«ÔÚδÀ´¿ª·¢¡£
ËùÓÐÓ¦ÓÃÓû§Ó¦¸ÃÓÐÒ»¸öÊÓͼÓÀÔ¶ÄÜ¿´µ½×îеIJ©¿ÍÆÀÂÛ¡£
ÔÚÒ»¸öÓû§ÕýÔÚÔĶÁÆÀÂÛʱ£¬ÓÉÆäËûÓû§»òͨ¹ý×Ô¶¯»¯ÕûºÏÌí¼ÓÁËÐÂµÄÆÀÂÛ£¬ÄǾÍÓ¦¸ÃÁ¢¼´ÏÔʾÔÚËùÓÐÓû§WebÒ³ÃæÉÏ£¬¶ø²»±ØËû»òËýÊÖ¹¤Ë¢Ð¡£
ϵͳ¼Ü¹¹
¾ßÓм¯ÖÐʽÊý¾Ý¿âµÄ²©¿ÍWebÓ¦Óý«ÓÃdWMVC·¶Ê½ÓèÒÔÉè¼ÆºÍ¿ª·¢¡£×ܵÄÀ´Ëµ£¬Ó¦ÓÃ×é¼þ¼äµÄͨѶ½«ÓÃAngularJS¡¢SSE¡¢InSoRºÍ
CDCÀ´ÊµÏÖ¡£ÕâЩ¼¼Êõ½«Ê¹ÏµÍ³Äܹ»ÏìÓ¦ÈκζԼ¯ÖÐʽÊý¾Ý¿âÖмǼµÄÐ޸ģ¨Í¨¹ýÕâ¸öWebÓ¦ÓûòδÀ´µÄ¼¯³ÉÄ£¿é£©£¬²¢ÊµÊ±´«µÝÕâÖֱ仯¸ø×îÖÕÓû§£¬¸ÅÀÀͼÈçͼ2¡£
ͼ2 ¼¯ÖÐʽʵʱ²©¿ÍWebÓ¦ÓÃϵͳ¼Ü¹¹
¿Í»§¶ËÓë·þÎñÆ÷¶ËÖ®¼äµÄͨѶ»ùÓÚµÄÊÇHTTPºÍSSEÐÒ飬ÒòΪ InSoR ºÍ CDC ÍêÈ«ÊÇÔÚÓ¦Ó÷þÎñÆ÷ºÍÊý¾Ý¿âÖ®¼äÍù·µµÄ¡£
µÚ¶þ¸öWebÓ¦Óý«ÒÔpWMVCģʽʵÏÖ£¨Èçͼ3£©¡£Ëü½«µ£ÈÎÒ»¸öʹÄÜÕߵĽÇÉ«£¬ÔÚ²»±Ø¸Ä±äÄÚÈÝËùÓÐȨµÄÇé¿öϰÑÓû§¾Ûµ½Ò»Æð¡£

ͼ3 µã¶Ôµãʵʱ²©¿ÍWebÓ¦ÓõÄϵͳ¼Ü¹¹
ͨ¹ýdWMVCʵÏֵļ¯ÖÐʽWebÓ¦ÓÃ
ÏÂÃæµÄͼ4ÊÇ»ùÓÚdWMVCµÄ²©¿ÍWebÓ¦ÓÃÉè¼Æ¸ÅÀÀ¡£ÔÚä¯ÀÀÆ÷¶Ë£¬ÊÓͼºÍ¿ØÖÆÆ÷×é¼þÊÇ»ùÓÚAngularJSµÄ¡£Á½¸ö²»Í¬µÄ·þÎñÆ÷¶Ë¼¼ÊõÕ»×éºÏ±»ÓÃÓÚdWMVCÄ£ÐÍ×é¼þµÄʵÏÖ¡£×ó²àµÄÊÇ´«Í³JavaÕ»ºÍJ2EE¼Ü¹¹£¬ÒÔ¼°¹ØÏµÐÍÊý¾Ý¿â
PostgresSQL ¡£NodeJSºÍ RethinkDB ÄÇÒ»²àÓÃÓÚͼ½â»ùÓÚJavaScriptµÄ·þÎñÆ÷¶ËÔËÐл·¾³ºÍ
NoSQL Êý¾Ý¿âµÄ¼Ü¹¹·¶Ê½¡£ÕâЩ²»Í¬µÄ·þÎñÆ÷¶ËÉè¼ÆºÍʵÏÖ´ú±íÁËʵÏÖͬһ¹¦ÄܵÄÁ½ÖÖ²»Í¬·½·¨¡£³ýÁËNodeJSµÄÒì²½ÌØÐÔ£¬ÔÚInSoR
ºÍ CDCÖÐÒ²´æÔÚÌØ±ðÃ÷ÏԵIJîÒ죬ÔÚNoSQLÊý¾Ý¿âÌṩÕßÖпÉ×ÔÓÉ¿ØÖƼܹ¹£¬´Ó¶ø¿ÉÒÔʹÓ÷DZê×¼»¯µÄ¼¼ÊõÈ¥´´½¨´´ÐµĽâ¾ö·½°¸£¨±ÈÈç
lazy evaluation ºÍ lazy loading £©¡£ÕâÁ½ÖÖʵÏÖ»¹ÌṩÁ˺ܶ༼ÊõÐÔÑ¡Ôñ£¬ÒÔÂú×ãWeb¿ª·¢ÉçÇø£¨´Ó´«Í³Öмä¼þʵ¼ùÕßµ½NodeJS/NoSQL
¿ñÈÈÕߣ©¹ã·ºµÄÐËȤ¡£

ͼ4 ²©¿ÍÓ¦ÓÃdWMVCÉè¼ÆÄ£Ê½µÄ¼Ü¹¹Í¼¡£¿Í»§¶ËWMVCÊÓͼºÍ¿ØÖÆÆ÷ÊÇ»ùÓÚAngularJSµÄ¡£·þÎñÆ÷¶ËÄ£ÐÍ×é¼þµÄÁ½¸öÑ¡ÔñÊÇ£ºJava-RDBMS£¨×ó²à£©ºÍNodeJS-NoSQL£¨ÓҲࣩ¡£
dWMVCµÄÊÓͼºÍ¿ØÖÆÆ÷
¸Ã²©¿ÍÍøÒ³ÊÇÓÃAngularJS¾Ö²¿Ä£°åʵÏֵġ£ËüÊÇÒ»¸ö¸´ºÏÊÓͼ£¬ÓÃÓÚΪ²©¿ÍÈÕÖ¾µÄÌá½»ºÍÏÔʾÌṩ·þÎñ¡£
<div
class="blocker1">
<h3>Topic: WMVC Real Time Reactive Fulfillment</h3>
</div>
<div id="castingId" class="blocker2">
<div>
<h4>Post a Comment:</h4>
</div>
<form id="commentFormId">
<div>
<input type="text" style="width:
30%" name="newCommentId" placeholder="What
is in your mind?" ng-model="newComment"/>
<button role="submit" class="btn
btn-default" ng-click="addComment()"><span
class="glyphicon glyphicon-plus"></span>Send</button>
</div>
</form>
</div>
<div>
<h4>All Comments:</h4>
</div>
<div class="view" ng-switch on="comments.comments.length"
ng-cloak>
<ul ng-switch-when="0">
<li>
<em>No comments yet available. Be the first
to add a comment.</em>
</li>
</ul>
<ul ng-switch-default>
<li ng-repeat="comment in comments.comments">
<span>{{comment.comment}} </span>
</li>
</ul>
</div> |
¸ÃHTMLÒ³ÃæÒÀÀµÓÚdWMVC¿ØÖÆÆ÷£¨Èçͼ5£©Óë·þÎñÆ÷¶ËµÄͨÐÅÈ¥Ôö¼ÓÐÂµÄÆÀÂÛ£¬²¢ÎªÆäËûÓû§Ë¢ÐÂÒ³Ãæ¡£

ͼ5 ²©¿ÍÆÀÂÛÓ¦ÓõÄÊÓͼºÍ¿ØÖÆÆ÷×é¼þ¡£
ΪÁËΪÓû§ÏÔʾºÍˢв©¿ÍÆÀÂÛ£¬¸Ã¿ØÖÆÆ÷£º
ͨ¹ýHTTPÖ®ÉϵÄSSEÁ¬½Óºó¶Ë·þÎñÆ÷¡£
Èç¹ûÓеϰ£¬ÔòÒì²½½ÓÊÕºÍÏÔʾËùÓÐÒÑÓеÄÈÕÖ¾ÆÀÂÛ¡£
±£³ÖÁ¬½Ó²¢¼àÌýδÀ´µÄSSEʼþ£¬Ëü½«¸üÐÂµÄÆÀÂÛʼþ×÷Ϊʼþ¸ºÔؽøÐд«µÝ¡£
µ±SSEʼþ·¢Éúʱ£¬ÍÆËͺͰ󶨸üеÄÈÕÖ¾ÆÀÂÛµ½Óû§µÄÊÓÍ¼Ò³Ãæ¡£
ËùÓÐÕâЩ½»»¥ºÍ·´Ó¦ÊÇÓÃÒÔÏ´úÂë¶ÎʵÏֵģº
var dataHandler = function (event) { var data = JSON.parse(event.data); console.log('Real time feeding => ' + JSON.stringify(data)); $scope.$apply(function () { $scope.comments = data; }); }; var eventSource = new EventSource('/wmvcapp/svc/comments/all'); eventSource.addEventListener('message', dataHandler, false);
|
µ±Ò»ÃûÓû§Ôö¼ÓÒ»¸öÐÂµÄÆÀÂÛʱ£¬Ëü»áÖ±½Ó´«Ë͵½·þÎñÆ÷¶ËÓÃÓÚ´¦Àí£º
$scope.addComment = function () { var newInput = $scope.newComment.trim(); if (!newInput.length) { return; }
var url = '/wmvcapp/svc/comments/cast';
$http.post(url, newInput);
$scope.newComment = '';
}; |
½ÓÏÂÀ´£¬ÔÚÏÂÃæ½«ÌÖÂÛÓÉ·þÎñÆ÷Ä£ÐÍ×é¼þ²¶»ñºÍ´¦ÀíËüËù¹ØÁªµÄÊý¾Ý±ä¸ü¡£
dWMVCµÄJavaºÍPostgreSQLÄ£ÐÍ×é¼þ
Ö÷Òª×é¼þ¶¼°üº¬ÔÚ´«Í³¼¼ÊõÕ»ÄÚ£¬Ò»¸ö»ùÓÚJavaµÄÖмä¼þÓ¦ÓóÌÐò¿â×éºÏºÍÒ»¸ö¹ØÏµÐÍÊý¾Ý¿â£¬Èçͼ6Ëùʾ¡£

ͼ6 »ùÓÚJavaºÍPostgreSQLµÄdWMVCÄ£ÐÍ×é¼þ¡£
ÕâЩģÐÍÖеĽ»»¥ºÍÏìÓ¦Èçͼ7Ëùʾ¡£ËüչʾÁËÁ½¸ö·ÃÎʸò©¿ÍÓ¦ÓõÄÓû§¡£

ͼ7 Ϊ²é¿´²©¿ÍÆÀÂÛµÄÓû§Ìṩʵʱ¶èÐÔ¸üеÄһϵÁн»»¥£¬»ùÓÚµÄÊÇJavaºÍPostgreSQL¹ØÏµÐÍÊý¾Ý¿â¡£
µ±Ò»¸öÓû§´ò¿ª²©¿ÍÒ³ÃæÊ±£¬dWMVC¿ØÖÆÆ÷Á¢¼´ÊµÀý»¯Ò»¸öSSEʵÀý£¬ËüÆô¶¯Óë·þÎñÆ÷µÄͨÐÅÒÔ½ÓÊÕ²©¿ÍÆÀÂÛ¡£ÆäÏà¹ØµÄ·þÎñÆ÷×é¼þÈçÏÂËùʾ£¬×¢½âÁËSSEÇëÇóºÍʵÏÖ»ùÓÚSSEµÄÊä³ö¡£µ±·þÎñÆ÷¶Ë×é¼þ½ÓÊÕµ½À´×ÔÓÚdWMVC¿ØÖÆÆ÷»ùÓÚSSEµÄÇëÇóʱ£¬ËüÊ×ÏÈÕë¶ÔÒÑÓÐÆÀÂÛ²éѯһÏÂÊý¾Ý¿â£¬È»ºó¹ã²¥Ò»¸öÒì²½
EventOutput µ½¸Ã¿ØÖÆÆ÷£¬´Ó¶ø½«¸ÃÆÀÂÛÏÔʾ¸øÓû§ä¯ÀÀÆ÷¡£Óë´ËÆÚ¼ä£¬ÎªÁ˽ÓÊÕÔÚ¸ÃPostgreSQLÄڶԸò©¿ÍÖ÷ÌâºóÐø±ä¸üµÄ³ÖÐøÍ¨Öª£¬¸Ã·þÎñÆ÷¶Ë×é¼þÔö¼ÓÒ»¸ö¼àÌýÒÔ±£³Ö¶ÔPostgreSQLÊý¾ÝµÄ¡°Ö÷Ìâ¹Û²ìÕß¡±µÄ¼àÌý
@GET @Path("/all") @Produces(SseFeature.SERVER_SENT_EVENTS) public EventOutput getAllComments() throws Exception { final EventOutput eventOutput = new EventOutput(); Statement sqlStatement = null; //Query and return current data String comments = BlogByPostgreSQL.getInstance().findComments(ConfigStringConstants.TOPIC_ID); this.writeToEventOutput(comments, eventOutput); //Listen to future change notifications PGConnection conn = (PGConnection)BlogByPostgreSQL.getInstance().getConnection(); sqlStatement = conn.createStatement(); sqlStatement.execute("LISTEN topics_observer"); conn.addNotificationListener("topics_observer", new PGNotificationListener() { @Override public void notification(int processId, String channelName, String payload) { JSONObject plJson = new JSONObject(payload); String plComments = plJson.getJSONObject("topic_comments").toString(); writeToEventOutput(plComments, eventOutput); } }); return eventOutput; } private void writeToEventOutput(String comments, EventOutput eventOutput) { OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder(); eventBuilder.mediaType(MediaType.APPLICATION_JSON_TYPE); if(comments == null || comments.trim().equals("")) { comments = NO_COMMENTS; } eventBuilder.data(String.class, comments);
OutboundEvent event = eventBuilder.build();
eventOutput.write(event);
} |
PostgreSQLÊÇÒ»¸ö¿ªÔ´µÄ¹ØÏµÐÍÊý¾Ý¿â¡£Ëü½üÆÚÌí¼ÓµÄÆäÖÐÒ»¸öÌØÐÔÊÇ£¬²¶»ñ²¢·¢ËÍËùÓмǼµÄ±ä¸ü×÷ΪÁ¬½ÓÓ¦ÓÃ×é¼þµÄÈëÕ¾¸ºÔØ¡£Õâ¸öInSoRÄÜÁ¦ÊÇÒÔÒ»¶ÔÊý¾Ý¿â´¥·¢Æ÷ºÍº¯ÊýÅäÖÃʵÏֵġ£Õë¶ÔÎÒÃǵIJ©¿ÍÖ÷Ìâ±í½øÐÐÈçÏÂÅäÖãº
CREATE OR REPLACE FUNCTION proc_topics_notify_trigger() RETURNS trigger AS $$ DECLARE BEGIN PERFORM pg_notify('topics_observer', json_build_object('topic_id', NEW.topic_id, 'topic_comments', NEW.comments)::text); RETURN new; END; $$ LANGUAGE plpgsql DROP TRIGGER trigger_topics_notify ON topics; CREATE TRIGGER trigger_topics_notify AFTER INSERT OR UPDATE OR DELETE ON topics FOR EACH ROW EXECUTE PROCEDURE proc_topics_notify_trigger()
|
¼ÙÉ裬ÔÚÓÐЩÓû§ÕýÔÚÔĶÁ²©¿ÍÆÀÂÛµÄͬʱ£¬ÆäÖÐÓÐÈË´òËãÔö¼ÓÒ»ÌõÐÂµÄÆÀÂÛ¡£
@POST @Path("/cast") @Consumes(MediaType.APPLICATION_JSON) public void addComment(String newComment) throws Exception { if(newComment != null && !newComment.trim().equals("")) { ObjectMapper mapper = new ObjectMapper(); TopicComments topicComments; String comments = BlogByPostgreSQL.getInstance().findComments(ConfigStringConstants.TOPIC_ID);
if(comments == null || comments.trim().equals(""))
{
topicComments = new TopicComments();
topicComments.addComment(newComment);
String topicCommentsStr = mapper.writeValueAsString(topicComments);
BlogByPostgreSQL.getInstance().addTopic(topicCommentsStr);
}
else
{
if(!comments.contains(newComment))
{
topicComments = mapper.readValue(comments, TopicComments.class);
topicComments.addComment(newComment);
String topicCommentsStr = mapper.writeValueAsString(topicComments);
BlogByPostgreSQL.getInstance().updateTopic(topicCommentsStr);
}
}
}
} |
È»ºó£¬Êý¾Ý¿âÖиüǼ±»ÕâÌõÐÂÆÀÂÛÒ»¸Ä£¬¸ÃÊý¾Ý¿âµÄ¡°trigger_topics_notify¡±´¥·¢Æ÷¾Í½«µ÷ÓÃÆäÏà¹ØµÄ¡°proc_topics_notify_trigger¡±º¯ÊýÕë¶Ô¡°topic_observer¡±·¢ÆðÒ»¸ö±ä¸üʼþ֪ͨ¡£¸Ã¡°topic_observer¡±Í¨Öª½«Á¢¼´ÍÆË͸ø¡°topic_observer¡±µÄ¼àÌýÕߣ¬Á¬Í¬JSON¸ñʽµÄ¸üÐÂÆÀÂÛÒ»Æð×÷ΪÊý¾Ý¸ºÔØ¡£¸ÃÓ¦ÓÃ×é¼þÓëÕâЩ¼àÌýÕß±£³ÖÁªÏµ£¬ÒÀ´Î´¦ÀíºÍ±àдÁíÍâµÄSSE
EventOutputµ½¸Ã¿ØÖÆÆ÷ȥˢÐÂÕâЩ¸üÐÂµÄÆÀÂÛµ½ËùÓÐÓû§ÊÓͼ¡£ÕâÑùËùÓÐʾͶ¼ÒѾʵÏÖÁË£¬²»ÐèҪΪÓû§·¢ÆðеÄÇëÇó£¨Èçͼ7£©¡£
dWMVCµÄ½ÚµãºÍRethinkDBÄ£ÐÍ×é¼þ
¹ýÈ¥µÄ¼¸ÄêÀNodeJSÒѾ³ÉΪÓÃÓÚ¹¹½¨WebÓ¦Ó÷þÎñÆ÷¶ËÔËÐÐÆÚ»·¾³ÐÂÈñÑ¡Ôñ¡£ËüµÄºËÐļܹ¹ÊÇʼþÇý¶¯¡¢Òì²½´¦Àí¡£RethinkDBÊÇÒ»¸ö¿ªÔ´NoSQLÊý¾Ý¿â£¬Ëü½«ÊµÊ±WebÓ¦ÓõĿª·¢·Åµ½ËüµÄ¼Ü¹¹ºÍÉè¼ÆÖнøÐÐÁËÉî˼ÊìÂÇ¡£ÆäÖÐÒ»¸öÄÚÖõÄÌØÐÔÊÇ£¬ÌṩÁ˱ä¸üʼþµÄ֪ͨȥµ÷ÓÃÓ¦ÓÃ×é¼þ¡£
¶Ô±Èͼ6£¬ÏÂÃæµÄͼ8×î´óµÄ²»Í¬ÊÇÊý¾Ý¿â´¥·¢Æ÷ºÍ¹ý³Ìº¯Êý²»ÔÙÐèÒªÓÉRethinkDBÀ´ÅäÖá£ËüµÄÊý¾Ý¿â±ä¸üʼþµÄ֪ͨÊÇÓÿÉÁ´½Ó£¨chainable£©µÄ²éѯÓïÑÔ
ReQL ʵÏֵġ£

ͼ8 »ùÓÚNodeJSºÍRethinkDBÊý¾Ý¿âµÄdWMVCÄ£ÐÍ×é¼þ¡£
ͼ9չʾÁËÓ¦ÓúÍÊý¾Ý¿â×é¼þ¼äµÄһϵÁн»»¥ºÍÏìÓ¦¡£

ͼ9 »ùÓÚNodeJSºÍRethinkDBÊý¾Ý¿â£¬Í¨¹ýһϵÁн»»¥ÎªÕýÔڲ鿴²©¿ÍÆÀÂÛµÄÓû§Ìṩʵʱ¸üС£
µ±·þÎñÆ÷¶Ë×é¼þblogApp.js ½ÓÊÕµ½Ò»¸ö»ùÓÚSSEµÄgetAllCommentsÇëÇóʱ£¬ËüÊ×ÏȰ´ÕÕÐÂÔöµÄÌØ¶¨HTTP±¨Í·×¼±¸ÏìÓ¦£¬ÈçÏÂËùʾ£¬Îª×î³õµÄÏìӦȥºÍdWMVC¿ØÖÆÆ÷ÎÕÊÖ¡£Õâʹ¸Ã¿ØÖÆÆ÷¿ÉÒÔ³ÖÐø¼àÌýºóÐøSSEÁ÷ʼþ¡£
function setUpSSE(res) { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Transfer-Encoding': 'chunked' }); res.write('\n'); }
|
½ÓÏÂÀ´£¬Ëüͨ¹ýBlogByRethinkDB.jsÖ´ÐÐÒ»¸ö¿ÉÁ´½ÓµÄReQL²éѯȥ֪ͨ¸ÃÊý¾Ý¿â£¬ËüÏëÒª¹Û²ìºÍ½ÓÊÕ¸ÃÊý¾Ý¼Ç¼µÄδÀ´Èκεıä¸ü¡£ÕâÌõ¿É¹Û²ìµÄ²éѯʹ¸ÃÊý¾Ý¿âÒ»·¢Éú±ä¸ü¾Í½«¸Ã±ä¸ü
¶èÐÔÁ÷»¯ ·¢»Ø¸øÓ¦ÓÃ×é¼þ¡£
BlogByRethinkDB.prototype.observeComments = function(process) { connectToRDB(function(err, rdbConn) { if(err) { if(rdbConn) rdbConn.close(); return console.error(err); }
//Listen for blog comment change events
r.table(config.wmvcBlog.dbTable).filter(r.row('topicId').eq(config.wmvcBlog.wmvcBlogTopicId))
.changes({includeInitial: false}).run(rdbConn,
function(err, cursor)
{
if(err)
{
if(rdbConn) rdbConn.close();
return console.error(err);
}
//Retrieve all the comments in an array.
cursor.each(function(err, row)
{
if(err)
{
if(rdbConn) rdbConn.close();
return console.error(err);
}
if(row)
{
return process(null, row.new_val);
}
})
})
})
}; |
È»ºó£¬ËüÖØÐ»ñÈ¡¸ÃÇëÇóÖ÷ÌâµÄËùÓÐÒÑÓÐÆÀÂÛ¡£
BlogByRethinkDB.prototype.getAllComments = function(process) { connectToRDB(function(err, rdbConn) { if(err) { if(rdbConn) rdbConn.close(); return console.error(err); }
//Query for comments
r.table(config.wmvcBlog.dbTable).filter(r.row('topicId').eq(config.wmvcBlog.wmvcBlogTopicId))
.run(rdbConn, function(err, cursor)
if(rdbConn) rdbConn.close();
if(err)
{
return console.error(err);
}
//Retrieve all the comments in
an array.
cursor.toArray(function(err,
result)
{
if(err)
{
return console.error(err);
}
if(result && result.length
> 0)
{
return process(null, result[0]);
}
else
{
return process(null, null)
}
});
});
});
}; |
×îºó£¬¸Ã·þÎñÆ÷¶Ë¶ÔÏó×éÖ¯²¢·µ»ØÒ»¸öHTTPÏìÓ¦£¬Ëü´øÓÐSEE¼æÈݸñʽµÄÊý¾Ý¡£
function
handleSSEResponse(err, blogComments, res, next)
{
if(err)
{
return next(err);
}
if(blogComments) {
var id = new Date().getTime();
res.write('id: ' + id + '\n');
res.write('data: ' + JSON.stringify(blogComments)
+ '\n\n');
}
else
{
var empty = new Array();
var noResult = { comments: empty
};
var id = new Date().getTime();
res.write('id: ' + id + '\n');
res.write('data: ' + JSON.stringify(noResult)
+ '\n\n');
}
}
|
Æäºó£¬µ±ÐÂµÄÆÀÂÛÌí¼Óµ½ÏµÍ³ÖÐʱ£¬observeComments½«Òì²½ÏìÓ¦ËüµÄÊý¾Ý¿â±ä¸üʼþ£¬²¢¹ã²¥¸Ã¸üÐÂµÄÆÀÂÛ¸øËùÓÐÕýÔڲ鿴µÄÓû§£¬Èçͼ9Ëùʾ¡£
pWMVCµã¶ÔµãWebÓ¦ÓÃ
pWMVC¼Ü¹¹·½°¸µÄÖ§ÖùÊÇWebRTCÐÒé¡£ÌØ±ðÊÇÆäÖ÷Òª×é¼þÖеÄRTCDataChannelÒÑÔڴ˲©¿ÍÓ¦ÓõÄʵÏÖÖеõ½ÁËÓ¦Ó᣸Ã×é¼þÌṩÁËÔÚä¯ÀÀÆ÷¼äË«Ïòµã¶ÔµãÊý¾Ý´«ÊäµÄÄÜÁ¦£¬¶ø²»Ðè°²×°¶îÍâµÄ²å¼þ¡£
DataChannelJS ÊÇÒ»¸öÕë¶ÔRTCDataChannelµÄJavaScript°ü×°Æ÷Àà¿â£¬ÓÃËü¿ÉÒÔʹµ×²ã²»±ØÌ«¹ý¸´ÔÓ£¬´Ó¶ø¼ò»¯ÊµÏÖ¡£³öÏÖͬÑùµÄÄ¿µÄ£¬
PusherJS ±»Ñ¡ÔñÀ´ÌṩÐźŷþÎñ¡£WebRTC-awareµÄÓ¦ÓÃÐèÒªÒ»¸öÐźÅͨµÀ£¬ÓÃÓÚÌØ¶¨¿Í»§¶Ë½»»»»áÒéÃèÊöºÍÍøÂç¿É´ïÐÔµÄÐÅÏ¢¡£Õû¸öÓ¦ÓÃÕûºÏ²¿ÊðΪһ¸öNodeJS
Web·þÎñÆ÷¡£
ÐèҪעÒâµÄÊÇ£¬NodeJS·þÎñÆ÷ºÍPusherJSÐźÅ×°Öö¼²»±£Áôä¯ÀÀÆ÷¼äµÄÈκÎÊý¾Ý½»»»¡£Èçͼ10Ëùʾ£¬²ÎÓëÆäÖеÄÐÅÏ¢½»»»±£´æÔÚÿ¸öÓû§µÄä¯ÀÀÆ÷
±¾µØ´æ´¢ ÖС£Á¬Í¬ÕâЩ±¾µØ´æ´¢Ò»Æð£¬ËùÓÐÖ÷ÒªÓ¦ÓÃ×é¼þÒ²¶¼Î»ÓÚä¯ÀÀÆ÷¶Ë£¬²¢ÔÚÔËÐÐÆÚÖ´ÐС£¸ÃNodeJS×é¼þÖ»ÔÚä¯ÀÀÆ÷¼äת²¥²©¿ÍÆÀÂÛ£¬Î¬»¤×éÁ¬½Ó״̬£¬±£³ÖͨÐÅͨµÀµÄ¿ªÍ¨¡£

ͼ10 ¸Ã²©¿ÍÓ¦ÓÃpWMVCʵÏֵļܹ¹Í¼¡£ËùÓÐÓ¦ÓÃÆÀÂÛºÍÊý¾Ý´æ´¢¶¼ÔÚÓû§ä¯ÀÀÆ÷ÉÏ¡£
Node.jsµÄÖ÷ÒªÖ°ÔðÊǸºÔðËùÓвÎÓëÕßä¯ÀÀÆ÷¼äµÄÐźŴ«Êä¡£
ͼ11 ²ûÊöÁËÁ½¸öÓû§Ö®¼ä½¨Á¢ºÍÐγɲ©¿ÍÖ÷Ìâ×éµÄ˳Ðò¹ý³ÌÁ÷¡£µÚÒ»¸öÓû§ÔÚËûµÄä¯ÀÀÆ÷ÉÏ·ÃÎʺͳõʼ»¯pWMVCÓ¦Óã¬p2pControllerͨ¹ýÈô¸É²½Öè´ò¿ªÁËÒ»¸öDataChannelJSʵÀý£¬°ó¶¨µ½Ò»¸öPusherJSÐÅºÅÆµµÀ£¬²¢¿ªÊ¼·¢ËÍͨÐÅÐźš£´Ëʱ£¬ÓÉÓÚûÓÐÆäËûͬÑùµÄ²ÎÓëÕߣ¬¸ÃÓ¦ÓÃΪÊ׸öÓû§ÏÔʾһ¸öĬÈÏÒ³Ãæ¡£½ÓÏÂÀ´£¬ÁíһλÓû§´ò¿ª¸Ã²©¿ÍWebÒ³Ãæ¡£p2pCcontroller¼ì²â¸Ã²©¿Í×éÒѾ´ò¿ª£¬ÓÚÊÇËü¾ÍÖ±½ÓÁ¬½ÓÕâµÚ¶þλÓû§µÄDataChannelJS²¢°ó¶¨Ëüµ½PusherJSÐźÅ×°Öá£È»ºó£¬ÕâÁ½¸öä¯ÀÀÆ÷½øÐÐһϵÁÐ
ICE £¨½»»¥Ê½Á¬½Ó½¨Á¢£©Í¨ÐŲ¢ÐÉÌÍê³ÉÒ»´Îp2pÎÕÊÖ¡£Õâ¸ö¹ý³ÌÔÚä¯ÀÀÆ÷¿ØÖÆÌ¨´°¿Úͨ¹ýÒ»¿é½ÓÒ»¿éµÄ·½Ê½À´±íʾ£¬¶ø³öÓÚ¼ò½à¿¼ÂǾͲ»ÏÔʾϸ½ÚÁË¡£ÔÚÎÕÊÖÖ®ºó£¬ÕâÁ½¸öÓû§×¼±¸ºÃ˽Ͻ»»»ÐÅÏ¢ÁË£¬´Ë½öÏÞÓÚÏÖÔÚÔÚËüÃÇÖ®¼ä¿ªÍ¨µÄDataChannelJS¡£

ͼ11 »ùÓÚPusher.js¡¢DataChannel.jsºÍNode.js£¨ÑÓÐøÍ¼10£©£¬
Á½¸öÓû§ä¯ÀÀÆ÷Ö®¼ä½¨Á¢»ùÓÚWebRTCͨÐŵÄһϵÁн»»¥
Ò»µ©Á½¸öÓû§Ö®¼ä¿ªÍ¨ÁËDataChannelJS£¨Èçͼ12£©£¬¸ÃÓ¦ÓþͻáÊ×ÏÈ´Ó¸Ãä¯ÀÀÆ÷±¾µØ´æ´¢½ÓÊÕºÍÏÔʾ¸ÃÖ÷ÌâÒÑÓÐµÄÆÀÂÛ£¨Èç¹ûÓеϰ£©£¬ÒÔ±ãËûÃÇÁ˽âÉϴν»Á÷ÖÁ½ñ´í¹ýµÄÄÚÈÝ¡£
webRTCDatachannel.onopen
= function (userId)
{
p2pModel.getAllComments(groupName)
.success(function(updatedComments)
{
if(updatedComments === null)
{
updatedComments = { comments:
new Array() };
}
$scope.comments = updatedComments;
})
.error(function(error)
{
alert('Failed to save the new
comment' + error);
});
}
getAllComments: function (groupName)
{
var savedComments = $window.localStorage.getItem(groupName);
if(savedComments !== null)
{
savedComments = JSON.parse(savedComments);
}
var updatedComments = aggregateComments("",
null, savedComments);
return handlePromise($q, updatedComments);
} |
ͼ12 ÑÓÐøÍ¼11£¬»ùÓÚPusher.js¡¢DataChannel.jsºÍNode.js£¬Á½¸öÓû§ä¯ÀÀÆ÷½»»»»ùÓÚWebRTCÐÅÏ¢µÄһϵÁн»»¥¡£¸ÃÐÅÏ¢±£´æÔÚ¸öÈËÓû§ä¯ÀÀÆ÷µÄ±¾µØ´æ´¢ÖС£
ÔÚÕâЩÓû§²é¿´ÆÀÂÛµÄͬʱ£¬ËûÃǵÄä¯ÀÀÆ÷»á¼ÌÐø¸ø±Ë´Ë·¢ÐźÅÒÔ±£³ÖͨÐÅͨµÀµÄ¿ªÍ¨¡£Òò´Ë£¬Óû§¿ÉÒÔ·¢±íÆäËûÐÂµÄÆÀÂÛ£¬Èçͼ12¼°ÏÂÃæµÄ´úÂëÆ¬¶ÎËùʾ¡£
$scope.addComment = function () { var newInput = $scope.newComment.trim(); if (!newInput.length) { return;
}
var currentComments = $scope.comments;
p2pModel.aggregateAndStoreComments(groupName, newInput, currentComments) .success(function(updatedComments) { webRTCDatachannel.send(updatedComments); $scope.comments = updatedComments; }) .error(function(error) { alert('Failed to save the new comment' + error); });
$scope.newComment = '';
} |
µ±ÐÂµÄÆÀÂÛ·¢±í³öÀ´Ê±£¬p2pControllerÊ×ÏÈʹÓÃp2pModelÕë¶Ô¸ÃÖ÷Ìâ¾Û¼¯ºÍ¸üб¾µØ´æ´¢£¨ÈçÏÂËùʾ£©¡£È»ºó£¬Í¨¹ýDataChannelJS½«¸üÐÂµÄÆÀÂÛ·¢Ë͸øÆäËû²ÎÓëÕß¡£
aggregateAndStoreComments: function (groupName, comment, currentComments) { var savedComments = $window.localStorage.getItem(groupName);
if(savedComments !== null)
{
savedComments = JSON.parse(savedComments);
}
var updatedComments = aggregateComments(comment,
currentComments, savedComments);
storeComments(groupName, updatedComments, $window);
return handlePromise($q, updatedComments);
} |
µ±ÆäËû²ÎÓëÕß½ÓÊÕµ½¸üÐÂµÄÆÀÂÛʱ£¬ÆÀÂÛ±»ÏÔʾÔÚ¸ÃWebÒ³ÃæÉÏ£¬²¢±£´æµ½ËûÃǵı¾µØ´æ´¢ÖС£
webRTCDatachannel.onmessage = function (newInput, userId) { p2pModel.aggregateAndStoreComments(groupName, "", newInput) .success(function(updatedComments) { $scope.comments = updatedComments; }) .error(function(error) { alert('Failed to save the new comment' + error); }); }
|
×ܽá
¾¡¹ÜMVC¼Ü¹¹·½·¨µÄ½»»¥ºÍÏìÓ¦µÄµä·¶ÔÚÍòÎ¬ÍøÇ°¶þÊ®ÄêÆÚ¼äµÄWebÓ¦ÓÃÁìÓòµÄÓ¦ÓüõÈõÁË£¬µ«½üÆÚµÄ½ø²½ÓÖʹ¸Ã»ù´¡ÀíÂÛµÃÒÔÔÚWeb¿ª·¢ÉçÇø¸´ÐË¡£±ê׼ͨÐÅÐÒé¼°ÆäÌØÓеÄInSoRÄÜÁ¦Ê¹ÐÅÏ¢±ä¸üʼþ¿ÉÒÔʵʱµØ¶¯Ì¬ºÍÒ첽ѻ·±éÀúWebÓ¦ÓÃϵͳµÄ±ß½ç¡£ÕâЩʹÏÖ´úWebÓ¦Óÿª·¢ÈËÔ±¿ÉÒÔÀûÓÃdWMVCºÍpWMVC¼Ü¹¹·¶Ê½È¥Íê³ÉMVC-esqueʵʱ±ä¸ü¹Û²ì¡°Ê¼þÑ»·¡±£¬°´¶à±äµÄÁ÷ÐзçÉд´½¨ÎÞ·ì¡¢¸ßЧµÄÏìӦʽӦÓÃÐÐΪ¡£ÕâЩ¹¤¾ß²»½ö¿ÉÓ¦ÓÃÓÚÏÖ´úеķþÎñÆ÷¶ËÔËÐÐÆÚ»·¾³£¬Ò²¿ÉÒÔÓÃÓÚ´«Í³µÄÖмä¼þ¼Ü¹¹¡£
|