1. 程式人生 > >開發函數計算的正確姿勢 —— 使用 Fun Local 本地運行與調試

開發函數計算的正確姿勢 —— 使用 Fun Local 本地運行與調試

運行 evel 相對 環境 文件名 阿裏 max n) 管理服務器

前言
首先介紹下在本文出現的幾個比較重要的概念:

函數計算(Function Compute): 函數計算是一個事件驅動的服務,通過函數計算,用戶無需管理服務器等運行情況,只需編寫代碼並上傳。函數計算準備計算資源,並以彈性伸縮的方式運行用戶代碼,而用戶只需根據實際代碼運行所消耗的資源進行付費。函數計算更多信息 參考。

Fun: Fun 是一個用於支持 Serverless 應用部署的工具,能幫助您便捷地管理函數計算、API 網關、日誌服務等資源。它通過一個資源配置文件(template.yml),協助您進行開發、構建、部署操作。Fun 的更多文檔 參考。

2.0 版本的 Fun,在部署這一塊做了很多努力,並提供了比較完善的功能,能夠做到將雲資源方便、平滑地部署到雲端。但該版本,在本地開發上的體驗,還有較多的工作要做。於是,我們決定推出 Fun Local 彌補這一處短板。

Fun Local: Fun Local 作為 Fun 的一個子命令存在,只要 Fun 的版本大於等於 2.6.0,即可以直接通過 fun local 命令使用。Fun Local 工具可以將函數計算中的函數在本地完全模擬運行,並提供單步調試的功能,旨在彌補函數計算相對於傳統應用開發體驗上的短板,並為用戶提供一種解決函數計算問題排查的新途徑。

《開發函數計算的正確姿勢》系列除本篇是為用戶介紹 fun local 的使用方法外,其他幾篇都會向用戶展示 Fun Local 對於函數計算開發所帶來的效率上的巨大提升。

Fun Local 命令格式
使用 fun local invoke -h 可以查看 fun local invoke 的幫助信息:

$ fun local invoke -h
Usage: invoke [options] <[service/]function>

Run your serverless application locally for quick development & testing.

Options:

-d, --debug-port <port>  used for local debugging
-c, --config <ide>       print out ide debug configuration. Options are VSCode
-e, --event <path>       event file containing event data passed to the function
-h, --help               output usage information

本地運行函數
運行函數的命令格式為:

fun local invoke [options] <[service/]function>
其中 options、service 都是可以省略的。
從調用方式上,可以理解為,fun local invoke 支持通過 函數名 調用,或者 服務名/函數名 的方式調用,即

fun local invoke function
fun local invoke service/function
比如,如果要運行名為 php72 的函數,可以直接通過以下命令完成:

fun local invoke php72
調用結果為:

技術分享圖片

再比如,要運行名為 nodejs8 的函數,可以使用:

fun local invoke nodejs8
會得到如下結果:

技術分享圖片

如果 template.yml 中包含多個服務,而多個服務中包含相同名稱的函數時,通過函數名的方式調用 fun 只會運行第一個名稱匹配的函數。

如果想要精準匹配,可以使用 服務名/函數名 的方式。

比如想要調用 localdemo 下的 php72,可以使用:

fun local invoke localdemo/php72
在本例中,會得到和 fun local invoke php72 一致的結果。

以下是一個運行 nodejs8 函數的演示:

技術分享圖片

本地運行 java 類型的函數
java 不同於解釋型的語言,在作為函數運行前,需要先編譯。在我們的例子中,可以進入到 demo 中的 java8 目錄,然後執行:

mvn package
可以看到 log:

[INFO] skip non existing resourceDirectory /Users/tan/code/fun/examples/local/java8/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ demo ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ demo ---
[INFO] No tests to run.
[INFO]
[INFO] --- maven-dependency-plugin:2.8:copy-dependencies (copy-dependencies) @ demo ---
[INFO] fc-java-core-1.0.0.jar already exists in destination.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ demo ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.223 s
[INFO] Finished at: 2018-11-22T10:45:14+08:00
[INFO] Final Memory: 15M/309M
[INFO] ------------------------------------------------------------------------
該命令會在 java8/target 目錄下生成 demo-1.0-SNAPSHOT.jar 文件。

由於我們在 template.yml 中配置的 CodeUri 為 java8/target/demo-1.0-SNAPSHOT.jar,因此不需要任何改動,可以直接通過以下命令運行:

fun local invoke java8
運行結果如下:

技術分享圖片

本地調試
fun local invoke 支持 -d, --debug-port <port> 選項,可以對函數進行本地單步調試。本文檔只介紹如何配置調試,並不涉及調試技巧,更多文章,請參考。

備註:Fun Local 涉及到的 debugging 技術全部都基於各個語言通用的調試協議實現的,因此無論什麽語言的開發者,即使不喜歡用 VSCode,只要使用對應語言的 remote debugging 方法都可以進行調試。

本地調試 nodejs、python 類型的函數
對於 nodejs6、nodejs8、python2.7、python3、java8 類型的函數,調試方法基本一致。下面拿 nodejs8 舉例。

我們上面演示了可以通過 fun local invoke nodejs8 來運行名稱為 nodejs8 的函數,如果想對該函數進行調試,只需要使用 -d 參數,並配置相應的端口號即可。

比如我們以調試方式運行函數,並將調試端口設定在 3000,可以通過下面的命令:

fun local invoke -d 3000 nodejs8
另外,推薦添加 --config 參數,在調試的同時,可以輸出用來調試的 IDE 的配置信息:

fun local invoke -d 3000 --config VSCode nodejs8
命令執行結果如下:

skip pulling images ...
you can paste these config to .vscode/launch.json, and then attach to your running function
///////////////// config begin /////////////////
{
"version": "0.2.0",
"configurations": [
{
"name": "fc/localdemo/nodejs8",
"type": "node",
"request": "attach",
"address": "localhost",
"port": 3000,
"localRoot": "/Users/tan/code/fun/examples/local/nodejs8",
"remoteRoot": "/code",
"protocol": "inspect",
"stopOnEntry": false
}
]
}
///////////////// config end /////////////////
Debugger listening on ws://0.0.0.0:3000/b65c288b-bd6a-4791-849b-b03e0d16b0ce
For help see https://nodejs.org/en/docs/inspector
程序會阻塞在這裏,並不會繼續往下執行。只有 IDE 的連接上來後,程序才會繼續執行。接下來,我們針對 VSCode 配置、VSCode 調試兩個方面分別進行講解。

其中 VSCode 配置只有在第一次對函數進行調試時才需要,如果已經配置過,則不需要再次配置。

VSCode 配置
創建 vscode launch.json 文件
技術分享圖片
復制日誌中的 config begin 與 config end 之間的配置到 launch.json 中。
技術分享圖片
完成上面配置後,在 Debug 視圖可以看到配置的函數列表。
技術分享圖片
至此,VSCode 配置完成。VSCode 更多配置知識可以參考官方文檔。

VSCode 調試
VSCode 配置成功後,只需要在 VSCode 編輯器側邊欄單擊設置斷點,然後點擊“開始調試”按鈕,即可開始調試。

技術分享圖片

本地調試 java 類型的函數
調試 java 函數的過程和 nodejs、python 是類似的。但由於 java 程序員通常喜歡用 IDEA、Eclipse 這樣的 IDE,所以我們單獨拿出來說一下。

使用 VSCode 調試 java
使用 VSCode 調試 java 時,需要安裝兩個插件:Language Support for Java(TM) by Red Hat、Debugger for Java。利用 VSCode 的插件市場安裝插件很簡單,可以 參考。

使用 IDEA 調試 java
IDEA 配置
IDEA 配置 remote debugging 還是比較簡單的,首先在菜單欄依次點擊 Run -> Edit Configurations...:

技術分享圖片

然後新建一個 Remote Debugging:

技術分享圖片

然後我們隨意輸出一個名字,並配置端口號為 3000.

技術分享圖片

以下是一個配置 IDEA remote debugging 的完整流程演示:

技術分享圖片

使用 IDEA 開始調試
首先將 java 函數以 debug 的方式運行起來:

fun local invoke -d 3000 java8
技術分享圖片

可以看到函數卡在這裏了,接著我們使用 IDEA 連接並開始調試。可以通過菜單欄上的 Run -> Debug... 或者工具欄直接點擊 Debug 按鈕,即可開始調試。

以下是一個用 IDEA 進行 remote debugging 的完整流程演示:

技術分享圖片

本地調試 php 類型的函數
php 的調試與其他類型的函數調試在流程上有一些不同。

首先,php 的運行通過 fun local invoke php72 命令完成,這與其他類型的函數一致。調試時,也像其他類型的函數一樣,通過 -d 參數以調試模式啟動函數:

fun local invoke -d 3000 --config VSCode php72
但不同的是,以 debug 方式運行 php 函數後,php 函數並沒有阻塞等待 vscode 調試器的連接,而是直接運行結束。

skip pulling images ...
you can paste these config to .vscode/launch.json, and then attach to your running function
///////////////// config begin /////////////////
{
"version": "0.2.0",
"configurations": [
{
"name": "fc/localdemo/php72",
"type": "php",
"request": "launch",
"port": 3000,
"stopOnEntry": false,
"pathMappings": {
"/code": "/Users/tan/code/fun/examples/local/php7.2"
},
"ignore": [
"/var/fc/runtime/**"
]
}
]
}
///////////////// config end /////////////////
FunctionCompute php7.2 runtime inited.
FC Invoke Start RequestId: 6e8f7ed7-653d-4a6a-94cc-1ef0d028e4b4
FC Invoke End RequestId: 6e8f7ed7-653d-4a6a-94cc-1ef0d028e4b4
hello world

RequestId: 6e8f7ed7-653d-4a6a-94cc-1ef0d028e4b4 Billed Duration: 48 ms Memory Size: 1998 MB Max Memory Used: 58 MB
這是因為,對於 php 程序,需要首先啟動 vscode 的調試器。

php 類型的函數啟動 VSCode 調試器的流程與其他類型的函數一致:復制上面日誌中的 vscode 配置到 launch.json,單擊“開始調試”即可。

然後在終端重新以調試模式啟動 php 函數即可開始調試:

fun local invoke -d 3000 php72
技術分享圖片

Event 事件源
函數計算提供了豐富的觸發器,包括但不局限於對象存儲觸發器、日誌服務觸發器、CDN 事件觸發器等。在本地無論是運行還是調試函數時,為了能夠完全模擬線上環境,通常需要構造觸發事件。

觸發事件可以是一段可讀的 json 配置,也可以是一段非可讀的二進制數據。這裏我們拿 json 舉例,假設觸發事件內容為:

{
"testKey": "testValue"
}
想要將這段事件內容傳給函數,可以通過以下三種途徑:

管道: echo ‘{"testKey": "testValue"}‘ | fun local invoke nodejs8
文件: 將的 json 內容寫入到文件,文件名隨意,比如 event.json。然後通過 -e 指定文件名:fun local invoke -e event.json nodejs8
重定向: fun local invoke nodejs8 < event.json 或者 fun local invoke nodejs8 <<< ‘{"testKey": "testValue"}‘ 等等。更多信息可以參考這篇文章。
環境變量
在 template.yml 中配置的 EnvironmentVariables 會與線上行為一致,當函數運行時,可以通過代碼獲取到。更多信息參考。

在本地運行函數時,除了 EnvironmentVariables 配置的環境變量,fun 還會額外提供一個 local=true 的環境變量,用來標識這是一個本地運行的函數。

通過這個環境變量,用戶可以區分是本地運行還是線上運行,以便於進行一些特定的邏輯處理。

Initializer
在 template.yml 中配置的 Initializer 屬性會與線上行為一致,當函數運行時,會首先運行 Initializer 指定的方法。Initializer 更多信息 參考。

Credentials
用戶可以通過 Credentials 中存儲的 ak 信息訪問阿裏雲的其他服務。Fun local 在本地運行函數時,會按照與 fun deploy 相同的 策略 尋找 ak 信息。

關於函數計算 Credentials 的描述,可以參考。

以下是一個根據本地、線上環境的不同,利用函數提供的 Credentials 配置 oss client 的例子:

local = bool(os.getenv(‘local‘, ""))
if (local):
print ‘thank you for running function in local!!!!!!‘
auth = oss2.Auth(creds.access_key_id,
creds.access_key_secret)
else:
auth = oss2.StsAuth(creds.access_key_id,
creds.access_key_secret,
creds.security_token)
附錄

代碼
本文講解涉及到的 demo 代碼,托管在 github 上。項目目錄結構如下:

.
├── java8
│ ├── pom.xml
│ ├── src
│ │ └── main
│ │ └── java
│ │ └── example
│ │ └── App.java
│ └── target
│ └── demo-1.0-SNAPSHOT.jar
├── nodejs6
│ └── index.js
├── nodejs8
│ └── index.js
├── php7.2
│ └── index.php
├── python2.7
│ └── index.py
├── python3
│ └── index.py
└── template.yml
template.yml 定義了函數計算模型,其中定義了一個名為 localdemo 的服務,並在該服務下,定義了 6 個函數,名稱分別是 nodejs6、nodejs8、php72、python27、python3、java8。它們對應的代碼目錄由 template 中的 CodeUri 定義,分別位於 nodejs6、nodejs8、php7.2、python2.7、python3、java8 目錄。

更多參考
Fun Repo
Fun specs
Fun examples
Fun 發布 2.0 新版本啦
函數計算工具鏈新成員 —— Fun Local 發布啦

開發函數計算的正確姿勢 —— 使用 Fun Local 本地運行與調試