前言
首先介紹下在本文出現的幾個比較重要的概念:
函數計算(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 現已內建到 VSCode、IDEA、Pycharm 等 IDE 的圖形化插件中,相較于指令行,這些插件往往通過圖形化的方式帶來更好的使用體驗,是以:
- 如果您是 VSCode 使用者,我們強烈推薦您直接使用 VSCode 插件
- 如果您是 IDEA 或 Pycharm 使用者,我們強烈推薦您直接使用 Cloud Toolkit 插件。
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 不同于解釋型的語言,在作為函數運作前,需要先編譯。在我們的例子中,可以直接使用
fun build 函數名
的方式,在我們的例子中,函數名為
java8
,是以編譯指令如下:
fun build java8
可以看到 log:
using template: template.yml
start building function dependencies without docker
building localdemo/java8
running task flow MavenTaskFlow
running task: MavenCompileTask
running task: MavenCopyDependencies
running task: CopyMavenArtifacts
Build Success
Built artifacts: .fun/build/artifacts
Built template: .fun/build/artifacts/template.yml
Tips for next step
======================
* Invoke Event Function: fun local invoke
* Invoke Http Function: fun local start
* Deploy Resources: fun deploy
編譯成功後,可以直接使用以下指令運作函數:
fun local invoke java8
運作結果如下:
以下是一個運作 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 編輯器側邊欄單擊設定斷點,然後點選“開始調試”按鈕,即可開始調試。
以下是一個 nodejs8 函數本地單步調試的流程例子:
本地調試 java 類型的函數
調試 java 函數的過程和 nodejs、python 是類似的。但由于 java 程式員通常喜歡用 IDEA、Eclipse 這樣的 IDE,是以我們單獨拿出來說一下。
使用 VSCode 調試 java
使用 VSCode 調試 java 時,需要安裝兩個插件:
Language Support for Java(TM) by Red Hat
、
Debugger for Java
。利用 VSCode 的插件市場安裝插件很簡單,可以
以下是一個使用 VSCode 調試 java 類型函數的例子:
使用 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
屬性會與線上行為一緻,當函數運作時,會首先運作 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 目錄。