二十分鐘教你如何將區塊鏈應用與函式計算相結合
前言
本篇文章適合對區塊鏈應用感興趣或是想要通過函式計算服務進一步開發區塊鏈應用的新人。本文將結合阿里雲區塊鏈服務、阿里雲函式計算服務、阿里雲日誌服務 以及社群應用 Marbles,手把手教大家如何將阿里雲區塊鏈服務與阿里雲函式計算服務相結合,並進一步提供業務上的結合場景,供大家開拓思路。
本文分為以下幾部分:
- 函式計算與區塊鏈
- Marbles 區塊鏈應用介紹
- Marbles 區塊鏈應用結合函式計算進行擴充套件示例
- 區塊鏈應用與函式計算在業務上結合的場景與價值探討
函式計算與區塊鏈
函式計算
函式計算是事件驅動的全託管計算服務。使用函式計算,無需採購與管理伺服器等基礎設施,只需編寫並上傳程式碼。函式計算為使用者準備好計算資源,彈性地可靠地執行任務,並提供日誌查詢、效能監控和報警等功能。藉助函式計算,可以快速構建任何型別的應用和服務,並且只需為任務實際消耗的資源付費。
下圖為函式計算工作流程:
區塊鏈
區塊鏈可以理解為去中心的分散式記賬系統,其是一種 分散式、去中心化的計算與儲存架構 。區塊鏈通過某種方式來記錄資料,使使用者可以信任區塊鏈系統記錄的資料。區塊鏈中的記賬節點會按照一致性協議記賬。記賬節點願意按照一致性協議記賬,是因為在一致性協議的設計中,誠實的記賬節點會得到相應的獎賞,且誠實的記錄比惡意篡改記錄的收益更大。
依託於區塊鏈網路的可信度,衍生出了智慧合約的概念。什麼是智慧合約呢?現實生活中,買家與賣家要進行一筆交易,為了保證交易的順利進行,雙方會簽訂一份合約,合約中會宣告雙方各自的身份、權利以及義務。當交易出現糾紛時,買家與賣家根據當時簽訂的合約通過法律的手段解決糾紛。這種方式的不足之處在於解決糾紛的過程需要第三方權威來仲裁以及需要大量時間。那麼,假使我們現在有一位可信公正的交易代理人。賣家將商品交給代理人,買家與代理人雙方之間一手交錢一手交貨。若買家拒絕購買,代理人會將商品歸還給買家。買家也不會付了錢拿不到商品。智慧合約就可以充當這樣的代理人,其為區塊鏈上一個包含合約程式碼和儲存空間的虛擬賬戶,合約的程式碼控制智慧合約的行為,合約的賬戶儲存合約的狀態。
由於有了智慧合約,DApp (Decentralized Application 即去中心化應用)也應運而生。DApp 是執行在區塊鏈網路上的應用軟體,其上執行的程式碼我們稱之為智慧合約。
Marbles 區塊鏈應用介紹
Marbles 區塊鏈應用是一個 資產轉移 應用演示。在 Marbles 區塊鏈應用中多個使用者可以建立並相互轉移彈珠。 ( 即彈珠就是資產轉移中的資產 )
上圖中:
- Amy、Alice、Ava 所在的小長方形是她們每個人的賬戶
- 小長方形中的圓形彈珠是每個人賬戶中的資產,彈珠的顏色和大小是資產的屬性
- 點選小長方形中的加號是為某個賬戶建立彈珠(資產)
- 將某個小長方形中的彈珠拖拽到右上方的垃圾桶中,是為某個賬戶刪除彈珠(資產)
- 將某個彈珠從一個小長方形拖拽到到另一個小長方形,是彈珠(資產)的轉移
- 每一步的操作會在下方的 BLOCKS 建立一個新的小正方形,這個小正方形就代表包含交易內容的區塊
Marbles 區塊鏈應用程式碼分成三部分:
- 鏈碼 - 區塊鏈網路中,對等節點所執行的程式碼。鏈碼在此次介紹的 Marbles 應用中的主要作用是處理建立以及轉移彈珠的邏輯。
- 客戶端 - 瀏覽器中執行的程式碼,負責 Marbles 應用頁面的渲染與互動。
- 服務端 - 伺服器中執行的程式碼,充當 Marbles 應用與區塊鏈網路之間的橋樑,其與客戶端以及區塊鏈網路中執行著鏈碼的節點進行通訊。
在 Marbles 應用中,當客戶端傳送訊息給服務端,服務端與區塊鏈網路通訊的時序圖大體上如下圖所示:( 讀者對詳細過程感興趣,可以閱讀 Github IBM-Blockchain/marbles )
其中,Peer 節點(對等節點) 存在於區塊鏈網路中,擁有賬本並且可安裝鏈碼。Orderer 節點負責接收包含簽名的交易,對未打包的交易進行排序生成區塊,廣播給 Peer 節點。
上圖中:Client 以及 Server 是上文中所說的客戶端以及服務端。
- 當用戶建立或轉移彈珠時,Client 客戶端觸發相應事件,並向 Server 服務端發起請求。
- Server 接收到事件資訊後,首先會構建提案(也就是交易),提案是將事件資訊進行封裝,比如:此次交易的兩方以及交易內容是什麼。
- Server 將構建好的提案發送給區塊鏈網路中的一個 Peer 節點,由 Peer 節點來對提案進行模擬,校驗其合法性。為什麼要這麼做呢?因為鏈碼的作用就是處理交易邏輯,而鏈碼安裝在 Peer 節點上,並沒有安裝在 Server 上。
- 如果 Peer 節點模擬提案成功,認為其合法,則會對提案進行背書,並向 Server 返回背書後的提案。背書可以理解為,當我們去購買東西並忘記帶現金,付給對方一張 別人 給的支票。對方懷疑支票不可兌現的時候,我們在支票上簽字並表明若這張 他人 給的支票不能兌換,則可以來找我們要現金。這個簽字的動作就是背書,宣告對事物或被認可人的支援。
- Server 將背書後的提案發送給 Orderer 節點。
- Orderer 節點對提案進行排序並打包進區塊,將區塊廣播給區塊鏈網路中的所有 Peer 節點。
Marbles 區塊鏈應用結合函式計算進行擴充套件示例
假設說,現在有這麼一個業務場景,需要在每次 Marbles 應用有事件發生時,要對事件進行相應處理,且這部分的處理程式碼會 經常迭代 。那麼,在區塊鏈應用更新需要較多時間的情況下,通過函式計算來處理是較為方便的。( 具體緣由在下一節將會繼續探討 )
接下來,筆者將帶著大家模擬這個業務場景,擴充套件 Marbles Server 端程式碼。在每次事件發生時,將事件資訊打包並通過函式計算的 HTTP 觸發器,由函式計算來對事件資訊做相應處理。
1. 準備工作
開通阿里雲日誌服務、函式計算服務、區塊鏈服務
2. 在阿里雲區塊鏈服務中建立組織、建立聯盟
3. 在阿里雲區塊鏈服務中建立通道
- 點選相應組織
- 點選通道
- 點選新增通道,填寫名稱與組織,並建立
- 點選新建立的通道右側的待審批連結,同意審批
4. 部署 Marbles 應用以及鏈碼
參考 阿里雲區塊鏈服務開發指南
注意事項:nodejs 版本為 v8
5. 下載並配置阿里雲函式計算開發工具 fun
- fun 是一個 Node.js 編寫的命令列工具,通過 npm 進行安裝:
$ npm install @alicloud/fun -g
- 通過在命令列輸入
fun config
,根據提示依次配置Account ID
、Access Key Id
、Access Key Secret
以及Default Region Name
。可參考:服務地址 、 建立 AccessKey - 配置 template.yml
在專案根目錄下建立一個 template.yml 檔案:
ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
marblesFC: # 服務名稱
Type: 'Aliyun::Serverless::Service'
Properties:
Description: 'fc test'
LogConfig: # 日誌配置
Project: test-log-project # 日誌 Project
Logstore: test-log-store # 日誌 LogStore
processEvent: # 函式名
Type: 'Aliyun::Serverless::Function'
Properties:
Handler: httpTrigger.handler # 檔名.方法名
Runtime: nodejs8
CodeUri: './'
Timeout: 60
Events:
http-test: # 觸發器名
Type: HTTP # 觸發器型別
Properties:
AuthType: ANONYMOUS
Methods: ['GET', 'POST', 'PUT']
test-log-project: # LogProject 名稱
Type: 'Aliyun::Serverless::Log'
Properties:
Description: 'just for test'
test-log-store: # LogStore 名稱
Type: 'Aliyun::Serverless::Log::Logstore'
Properties:
TTL: 10
ShardCount: 1
上述檔案做了如下事項:
- 在函式計算中建立了
marblesFC
服務 - 為
marblesFC
服務配置了test-log-project
日誌 Project 以及test-log-store
日誌 LogStore - 在
marblesFC
服務中建立了processEvent
函式,併為其設定入口函式為httpTrigger.js
檔案中的handler
方法 - 為
processEvent
函式配置了 HTTP 觸發器,觸發器名為http-test
- 建立了
test-log-project
日誌 Project 以及test-log-store
日誌 LogStore
- 在專案根目錄下建立 httpTrigger.js 檔案
var getRawBody = require('raw-body')
module.exports.handler = function (request, response, context) {
// get request info
getRawBody(request, function (err, data) {
var params = {
path: request.path,
queries: request.queries,
headers: request.headers,
method: request.method,
body: data,
url: request.url,
clientIP: request.clientIP,
}
// you can deal with your own logic here
console.log(JSON.stringify(params.queries))
// set response
var respBody = new Buffer.from(JSON.stringify(params));
// var respBody = new Buffer( )
response.setStatusCode(200)
response.setHeader('content-type', 'application/json')
response.send(respBody)
})
};
- 在專案根目錄下執行
fun deploy
進行部署 - 在阿里雲函式計算控制檯中,進入到
marblesFC
服務下的processEvent
函式,在程式碼執行頁面記錄下呼叫 HTTP 觸發器的地址
6. 修改 Marbles Server 端程式碼
- 開啟 Marbles 原始碼根目錄資料夾下的
app.js
檔案 - 新增
var https = require('https');
https模組 - 修改
setupWebSocket
函式
function setupWebSocket() {
console.log('------------------------------------------ Websocket Up ------------------------------------------');
wss = new ws.Server({ server: server }); // start the websocket now
wss.on('connection', function connection(ws) {
// -- Process all websocket messages -- //
ws.on('message', function incoming(message) {
console.log(' ');
console.log('-------------------------------- Incoming WS Msg --------------------------------');
logger.debug('[ws] received ws msg:', message);
var data = null;
try {
data = JSON.parse(message); // it better be json
} catch (e) {
logger.debug('[ws] message error', message, e.stack);
}
// --- [5] Process the ws message --- //
if (data && data.type == 'setup') { // its a setup request, enter the setup code
logger.debug('[ws] setup message', data);
startup_lib.setup_ws_steps(data); // <-- open startup_lib.js to view the rest of the start up code
} else if (data) { // its a normal marble request, pass it to the lib for processing
https.get("此處填寫觸發 HTTP 觸發器的 URL 地址?type="+data.type, function(res){
console.log('test http trigger');
});
ws_server.process_msg(ws, data); // <-- the interesting "blockchainy" code is this way (websocket_server_side.js)
}
});
// log web socket errors
ws.on('error', function (e) { logger.debug('[ws] error', e); });
// log web socket connection disconnects (typically client closed browser)
ws.on('close', function () { logger.debug('[ws] closed'); });
// whenever someone connects, tell them our app's state
ws.send(JSON.stringify(ws_server.build_state_msg())); // tell client our app state
});
// --- Send a message to all connected clients --- //
wss.broadcast = function broadcast(data) {
var i = 0;
wss.clients.forEach(function each(client) { // iter on each connection
try {
logger.debug('[ws] broadcasting to clients. ', (++i), data.msg);
client.send(JSON.stringify(data)); // BAM, send the data
} catch (e) {
logger.debug('[ws] error broadcast ws', e);
}
});
};
ws_server.setup(wss, null);
}
7. 啟動 Marbles
在控制檯中進入 Marbles 專案,通過 gulp marbles_baas
啟動 Marbles 應用
8. 建立彈珠或轉移彈珠
試著在瀏覽器中建立彈珠或者用滑鼠拖拽轉移彈珠
9. 檢視日誌
對 Marbles 應用做了相應操作後,進入阿里雲日誌服務 test-log-project
Project 下的 test-log-store
LogStore,檢視日誌
我們看到當 Marbles Server 接收到事件後,會觸發 HTTP 觸發器,由函式計算 FC 來對事件做相應處理。( 簡單起見,demo 的處理目前僅僅是記錄日誌,讀者們可以自行擴充套件 )
區塊鏈應用與函式計算在業務上結合的場景與價值探討
通過製作上面的 demo,相信大家現在對於如何將區塊鏈應用與函式計算相結合有了一定的認識。接下來,就讓我們一起探討下,區塊鏈應用與函式計算在業務上有哪些結合的場景與價值。
下圖是最原始的 Fabric SDK 時序圖
為了讓讀者方便理解,我們仍舊使用 Marbles 應用的時序圖,讀者可以將 Marbles 應用中的 Server 理解為 Fabric SDK 時序圖中的 Application,
在筆者看來,函式計算可以與區塊鏈應用相結合的場景主要有以下三點:
1. 處理事件
處理事件的場景剛剛在 demo 中各位讀者想必已經體驗過了。那麼,為什麼筆者倡導用函式計算來處理事件呢?
通過函式計算可以將每一次的事件進行相應的處理,處理完成後傳送給日誌服務。同時,還可以在函式計算中設定定時觸發器,在指定時間內,再次統計事件的資訊,由此對區塊鏈狀態進行一個數據分析。
而關於這種方式的統計邏輯,很有可能是需要經常迭代的。因此,不適合將其邏輯放入區塊鏈應用中,而是更適合放在小巧易迭代的函式計算場景中。
2. 附加業務
考慮一個這樣的場景:現在 X 公司推出了一款新的支付 App,為了鼓勵使用者使用該公司的 App,該公司對外宣傳當用戶用該公司的 App 成功完成交易後,該公司會送大量優惠券以及積分。若該 App 是一款區塊鏈應用,那麼把優惠活動的業務邏輯放在哪裡最合適呢?
筆者目前覺得是放在 Orderer 將交易打包成區塊並廣播的時候最合適,因為優惠活動的業務邏輯是經常會變化的,這類業務邏輯可以統稱為附加業務,將附加業務抽象為一個個函式並放在函式計算中,容易更新迭代。
3. 驗證交易
Peer 節點在模擬提案時,若是有複雜多變的邏輯,可以放入函式計算中,由 Peer 節點來負責呼叫。
以上三點就是筆者對於如何將區塊鏈服務與函式計算相結合的思考,有不準確的地方,歡迎大家指出。