New in production(UPDATED)

在網友@123 的幫助下,新的博客和域名成功投入使用,再次表示衷心的感謝!(鼓掌。。。。

就在一個月前,我第一次成為了商業產品的聯合創始人,與此同時,我也有了一個新的頭銜:“文化傳媒人”。何出此言?因為這個產品就是一個文化傳媒網站,而主題就是Cosplay
而看到這裡,你可能會這樣問:

Q: 這篇文章的標題是“New in production”,但你卻在說“The new production”。博主你也是一個標題黨?
A: 感謝你的提問!很可惜,不是的。我這篇只是一個印子,而真正的內容,是要從下一篇文章開始。

如果你接觸過甚至是十分熟悉Cosplay,你有可能會知道來自韓國Naver11區旗下的NHN Japan所投資的Cure及其國際站WorldCosplay
熟悉小問的朋友都知道,我是一個十足的動漫迷、技術人和攝影愛好者。我憑著一個Cosplay攝影師和Cosplay粉絲的經驗,我覺得上述的兩個網站的用戶體驗都接近於“Impatient”(以往11區的網站都有這樣的毛病,但近幾年有所改善)。作為一個技術人,則更不能容忍這樣的UX
於是乎在同是“漫宅+技術人+攝影師”的Mark的推動下,找來了我們的專業人員——狼子,開始籌畫“Koicos”這個東西。

而Koicos這個東西,正是與Cure或是WorldCosplay都存在巨大差別的產品。原因有以下幾點:
1. 我們是專業的技術工程師。相信你能夠來到這裡,你一定瞭解我的一些歷史。而我們負責前端頁面實現是在國內ACG技術宅圈子裏有一定名氣的卜卜口擔任前端頁面工程師(主要負責HTML[5]、CSS),而與我一齊負責前後端JavaScript開發的是芋頭(阿里花名 天祁),前淘寶網天貓**網的前端开发经理,以及來自佛山大學土匪(曾多次獲得國內各級ACM大賽獎項)擔任Python和數據工程師。
2. 我們也是用戶。在我們的團隊中,除了芋頭以外,其餘都是程度各不同Otaku。而除了土匪,其餘都是攝影愛好者(特別是狼子這貨,手中的是徠卡M-E徠卡M8和哈蘇500CM,純土豪一隻)。所以我們既是相關人員,也是目標用戶。身為用戶,我們最瞭解我們的用戶需要什麼,我們也能夠憑著我們的能力把這些需求實現出来。
3. 近乎“真空”的國內目標市場。為什麼我們有如此的信息來搞這個東西?因為我們已經做了大量的調查:在國內,上述的Cure和WorldCosplay佔有的市場份額,只有不到10%。而我們不貪求太多,我們只想要國內30%的市場份額,就已經足夠我們實現自負盈虧了。


再來說說Cosplay在國內的發展情況。

我們來看看我在截稿之前在淘寶指數得到的關於“Cosplay”的相關數據。

淘寶指數
從圖表中不難看出,國內Cosplay文化在近兩年內呈飛速發展趨勢。

再來看看,這些消費用戶中的消費水平,單件物品(如Cosplay服裝、假髮、道具等)的價格,普遍在100-300人民幣之間。這足以證明,我們的目標用戶的消費水平是十分高的。而瞭解Cosplay的你,一定知道其中原因。
人均消費

輕輕地打下廣告,我們廣州地區Cosplay圈子中有兩家Cosplay專營店是十分受Cosplay玩家歡迎的,它們分別是阿當當公主動漫星城點宵月家的范特西小羊窩。(為什麼要介紹它們,隨後你便瞭解。)


我們再談談Koicos所用到的技術。
你肯定知道小問我是一個十足的Noder(Node.js User)。固然我們Koicos所使用的主要技術也是Node.js啦~(謎之音:這什麼邏輯?..=。=)

好吧。。好久不更新了,以爲最近實在太忙:項目開發,考試,拍照(你明明是去玩吧!)等等壓在身上=。=好啦,今天抽空出來補坑吧。

認識小問的人都知道,我是在 Node.js 上開發了 webjs 而有所名聲的,而且 webjs 在前一段時間也開始進入了商用階段(因爲某種原因,所以不能向外公佈)。現在 webjs 也不知道爲啥,進入了停工階段,沒有什麼大的 Bug 需要去 fix,功能性方面因爲採用了較爲開放的模塊化開發方式,而且 API 也已經經過了好幾次變革,現在 webjs 也差不多進入 Stable 階段了。(雖然連版本都還沒有到v1.0_(:3」∠)_)

先繼續來說說 Koicos 在後端方面的開發情況。我們剛開始所採用的系統架構是這樣的:

v0.0.1 System

程序方面平臺方面:Node.js 採用 webjs 框架作爲 Web Server Cluster Platform (呆會會解釋爲什麼是 Cluster),Python 作爲 Data Analytics Worker Platform;數據庫方面:MongoDB 配合 Node.js 作爲頂層操作數據庫,PostgreSQL 配合 Python 作爲底層的數據固實、分析數據庫,Redis 配合 Node.js 作爲 Cache 和 Buffer Store。

而在 Web Server 中,我們有一個叫做 Cluster 的東西,這個也是我們在項目業務實現中,原創的一種架構技術:根據頁面結構,把頁面內容拆分成若干內容模塊。每一個內容模塊,我們都稱之爲一個 Packet,而我們在後端有若干個 Packet Server。當然並非一個 Packet 就對應擁有一個獨立的 Packet Server,而是同一類型的 Packet 就對應由一個 Packet Server 進行處理。這些 Packet Server 通過 HTTP 協議進行通訊。 而當時,我們總共有以下幾個 Packet Server:

  • Main,負責預處理所有來自客戶端的請求,並向 Assign 及其他 Packet Server 發送處理信號。
  • Assigin,Web Server Cluster 的神經中樞,負責通過分析來自 Main 的處理請求,創建隨機 Hash,並向其他 Packet Server 發送處理信號。
  • Photos,負責處理原子項目照片的相關寫入請求,並根據來自 Assign Server 的處理信號,生成所需的 UI。
  • Info,負責處理所有信息相關的寫入請求,並根據來自 Assign Server 的處理信號,生成所需的 UI。
  • Users,負責處理用戶系統的相關寫入請求,並根據來自 Assign Server 的處理信號,生成所需的 UI。
  • Relations,負責處理所有原子關係的相關寫入請求。

如此,當一個用戶在瀏覽器中輸入 http://koicos.com,並按下回車的時候,就經歷了如此技術過程:
1. Main Server 得到來自客戶端的請求
2. Main Server 向 Assign Server 發出經過預處理的請求
3. Assign Server 創建一個隨機 Hash,並向需要提供數據的 Packet Server 發出處理信號
4. 各 Packet Server 接收到信號後,根據信號中的參數,開始從 MongoDB 查找相關數據
5. 完成數據庫讀取以後,根據客戶端的相關信息,生成 HTML 或 JSON(P),並存到 Redis 中
6. 客戶端在得到 Layout 後就開始渲染 Layout DOM,並運行 JavaScript,客戶端異步向各 Packet Server 請求 UI
7. 各 Packet Server 得到來自客戶端的請求後,根據 Hash,將之前在 Redis 中準備好的數據返回給客戶端
8. 客戶端完成所有內容模塊的加載,UI 渲染完成

而後來發現,因爲模塊之間的通訊是採用 HTTP 的方式的,這樣一次頁面加載,服務器集羣就需要建立5次以上的 HTTP Connection,這樣的開銷相當大,無論從 CPU 或內存上都十分不划算。 於是我們想到了我們一直在使用的 Redis 有着一個很獨特的 Feature – Pub/Sub Mode,我們可以使用這個功能,把 Redis 看作是一個消息集散系統。我們先來看看官方的 Node.js DEMO。

Node.js Redis DEMO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var redis = require("redis"),
    client1 = redis.createClient(), client2 = redis.createClient(),
    msg_count = 0;

client1.on("subscribe", function (channel, count) {
    client2.publish("a nice channel", "I am sending a message.");
    client2.publish("a nice channel", "I am sending a second message.");
    client2.publish("a nice channel", "I am sending my last message.");
});

client1.on("message", function (channel, message) {
    console.log("client1 channel " + channel + ": " + message);
    msg_count += 1;
    if (msg_count === 3) {
        client1.unsubscribe();
        client1.end();
        client2.end();
    }
});

client1.incr("did a thing");
client1.subscribe("a nice channel");

但是因爲 Redis Sub/Pub Mode 自身的限制(有待研究),同一個 Redis Client 在 Subscribe 以後不能運行其他常規的命令,如 Publish, SET, GET等。
於是我們只能夠像這樣,建立三個 Client 以滿足需求。

Sub/Pub/Store Client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var redis = require('redis');
var eventproxy = require('eventproxy');

var subClient = redis.createClient();
var pubClient = redis.createClient();
var storeClient = redis.createClient();

var binder = eventproxy.create(
  'subClient', 'pubClient', 'storeClient',
  function() {
    // All clients are ready.
  }
);

subClient.on('ready', binder.done('subClient'));
pubClient.on('ready', binder.done('pubClient'));
storeClient.on('ready', binder.done('storeClient'));

這樣,我們就可以通過讓各 Packet Server 連接到同一個 Redis 服務器上,並讓需要的 Client 訂閱需要的 Channel:比如 Assign Server 需要向 Photos Server 發送圖片牆處理信號,這樣我就可以讓 Photos Server 的 subClient 訂閱 wall Channel。

Redis Pub/Sub Communication
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* Assign Server */
var pubClient = redis.createClient();
pubClient.publish('wall', JSON.stringify({
  /* ... */
}));

/* Photos */
var subClient = redis.createClient();
subClient.subscribe('wall');

/* Wall Signal Handler */
subClient.on('message', function(channel, message) {
  if (channel !== 'wall')
    return;

  var query = JSON.parse(message);
  /* Do something with query */
});

而且在數據庫方面,原先我們採用的桂林同學的 mongoskin 來作爲 MongoDB 的驅動器,但我們也發現,如果我們的業務需要對幾個表,也就是 MongoDB 中的 Collection 同時進行聯合查詢或 JOIN,這在 MongoDB 中是非常不科學的。因爲 MongoDB 在 Node.js 需要對每一個所需要用到的 Collection 進行實例化,也就是說我們若需要對幾個 Collection 進行 JOIN 的話,只能通過 JavaScript 的程序邏輯進行嵌套查詢。這樣每一次的查詢都要單獨發送一次查詢請求,如果業務複雜,程序的開銷相當龐大。而我們就改而將 Node.js 中的 MongoDB 直接改爲同 Python 一齊使用 PostgreSQL,也能節省服務器空間。
這樣之前複雜的程序邏輯,直接用我們所熟悉的 SQL 語句,一句話搞定了~

MongoDB Querying VS SQL

於是乎,我們的系統就成了這樣樣子:
v0.0.2 System


OK,今天就寫到這了。下次來分享一些純技術的=。=

Comments