Worktile自上線兩年多以來,以良好的用戶體驗和穩定的服務,得到了用戶的承認和喜好。截止筆者寫這篇文章的時候,已經有超過10萬家團隊在使用Worktile。做爲團隊協做工具,從技術上分析首先要解決以下幾個問題:javascript
那麼Worktile是如何作到這幾點的?今天筆者在這篇文章裏一一爲你們揭祕。前端
先來講說Worktile中SPA(單頁應用程序)設計,做爲團隊協做工具,須要儘量減小用戶在不一樣頁面之間的跳轉,因此從一開始咱們就決定Worktile必須是單頁應用程序,當時面臨的選擇有不少,首先咱們考慮使用大名鼎鼎的Backbone.js,可是很快又拋棄了,由於在實際使用中Backbone.js太複雜,另外一方面開發效率過低,最終咱們選擇了Google出品的AngularJs,下面這幅圖是AngularJS的結構圖:java
選擇它主要基於如下幾點考慮:mysql
<div class="entry-task-main" ng-class="{1:'task-completed-style'}[task.completed]"> <a class="entry-task-check" id="task_check_{{ task.tid }}" wt-click="js_complete_task($event, entry, task)"> <i ng-class="{0: 'icon-check-empty', 1: 'icon-check-sign'}[task.completed]"></i> </a> <a class="entry-task-title" href="javascript:;">{{task.name}}</a> </div>
語義化標籤,AngularJS在設計之初信奉的理念就是:當編寫UI的同時又須要編寫業務邏輯時,聲明式的代碼遠比命令式代碼要好,命令式的代碼更適合寫業務邏輯,AngularJS在設計上就經過語義化的標籤把對DOM元素的操做和邏輯代碼分離,如咱們須要展示一個任務列表,只須要下面這段代碼便可:web
<div ng-repeat="task in tasks"> <wt-task view-type="item" show-project="false" class="slide-trigger" hide_action="true" ng-click="locator.openTask(task.pid, task.tid)"> </wt-task> </div>
模塊化設計,AngularJS堪稱模塊化設計方面的典範,經過模塊化設計咱們能夠很是好的實現Worktile的工程化,在Worktile中涉及的元素很是多,若有項目、任務、日程、文件、話題、文檔等等,而這每個元素均可以設計爲一個模塊,以下所示:redis
(function () { 'use strict'; angular.module('wtApp', [ 'wt.project.ctrl', 'wt.team.ctrl', 'wt.task.ctrl', 'wt.event.ctrl', 'wt.post.ctrl', 'wt.file.ctrl', 'wt.page.ctrl', 'wt.mail.ctrl' ]); }());
引入依賴注入,依賴注入是面向對象中比較成熟的設計模式之一,爲了解決面向對象中依賴問題,獲得了普遍的應用,AngularJS中大膽使用了依賴注入,極大的減小了各個模塊之間的依賴問題:sql
taskListCtrl.$inject = ['$scope', '$stateParams', '$rootScope', '$popbox', '$location', '$timeout', 'bus', 'globalDataContext', 'locator'];
結合以上特色,咱們最終決定了前端框架使用AngularJS來實現,從Worktile上線兩年多的表現來看,咱們的選擇無疑是正確的。固然AngularJS也有一些缺點,在實際使用中仍是要根據具體的產品類型來選擇使用,另外AngularJS 2.0也已經初見端倪,和AngularJS 1.0有很大的不一樣,感興趣的同窗能夠先去嚐鮮一下。mongodb
咱們再來看看Worktile的後臺服務設計,Worktile的總體服務架構設計以下圖所示:數據庫
其中前端部分在上面的SPA一節中咱們已經說過了,下面一一分析下其餘的服務:後端
消息推送服務是Worktile最核心的服務之一,前面提到過做爲一款團隊協做工具,要可以實現很是好的實時性,任何數據的變化都須要及時變動到團隊全部成員當前所在的視圖,以下面這幅圖,是一個典型的任務看板,團隊全部成員可能同時在操做當前項目中的任務,每一個操做引發看板的變化都會實時更新,不須要用戶作任何刷新操做:
爲了達到這種效果,須要在Web客戶端和服務器之間維持一個長鏈接,當有任何改變發生時,給客戶端發送不一樣的消息,告知客戶端哪些數據發生了變化,以下面是咱們爲任務定義的消息中的其中幾個:
on_task_trash : "on_task_trash", on_task_complete : "on_task_complete", on_task_move : "on_task_move", on_task_update : "on_task_update", on_task_comment : "on_task_comment", on_task_badges_file : "on_task_badges_file", on_task_unarchived : "on_task_unarchived", on_task_badges_check : "on_task_badges_check"
實現實時消息推送,有如下幾種方式可供選擇:
在Worktile一開始咱們選用了Socket.IO做爲消息服務,可是隨着訪問量的增大,須要作集羣化的時候感受到力不從心,尤爲對於Socket.IO狀態數據的存儲,因爲並無官方的解決方案,當時咱們採用了一個第三方的開源項目,使用Redis來存儲,引發了一些性能上的問題,在後來重構時選用了基於Erlang語言的開源XMPP服務ejabberd做爲咱們的消息服務。
ejabberd是xmpp協議的一種實現, xmpp普遍應用於即時通訊領域。Xmpp協議的實現有不少種,好比java的openfire,但相較其餘實現,ejabberd的併發性能無疑使最優秀的。Xmpp協議的前身是jabber協議,早期的jabber協議主要包括在線狀態(presence)、好友花名冊(roster)、IQ(Info/Query)幾個部分。如今jabber已經成爲rfc的官方標準,如rfc2799, rfc4622, rfc6121,以及xmpp的擴展協議(xep)。Worktile就是基於XEP-012四、XEP-0206定義的BOSH擴展協議。
因爲自身業務的須要,咱們對ejabberd的用戶認證和好友列表模塊的源碼進行修改,經過redis保存用戶的在線狀態,而不是mnesia和mysql。另外好友這塊咱們是從已有的數據庫中(mongodb)中獲取Worktile中項目或團隊的成員。Web端經過strophe.js來鏈接(http-bind),strophe.js能夠以長輪詢和websocket兩種方式來鏈接,因爲ejabberd尚未好的websocket的實現,就採用了BOSH的方式模擬長鏈接。整個系統的結構以下:
關於Worktile整個的技術架構就揭祕到這裏,經過上面的介紹,相信你們也能看到Worktile自己就是典型的MEAN(MongoDB、Express、AngularJS、NodeJS)架構,外加上ejabberd做爲實時消息推送服務,建議你們在本身的產品中,根據產品自身的特色和團隊的技術背景,來選擇具體使用哪一種技術。
李會軍,Worktile聯合創始人&CTO,關注團隊協做領域,致力於用工具解決中小團隊的協做問題。
您能夠點擊Worktile技術博客查看更多幹貨內容,歡迎訪問交流技術問題。
文章轉載請註明出處。