全部产品
云市场

使用 fun local 进行本地调试

更新时间:2020-02-20 15:39:33

fun local 作为 funcraft 的一个子命令存在,可以直接通过 fun local 命令使用。fun local 工具可以将函数计算中的函数在本地完全模拟运行,并提供单步调试的功能,旨在弥补函数计算相对于传统应用开发体验上的短板,并为用户提供一种解决函数计算问题排查的新途径。fun local 提供 fun local invokefun local start 两个子命令

Fun Local Invoke 支持本地运行事件函数,Fun Local Start 支持本地运行 Http 触发器函数以及事件函数。当使用 Fun Local Start Http 触发器函数时,允许通过浏览器直接触发函数运行,当使用 Fun Local Start 事件函数时,允许通过 InvokeFunction API 或者 SDK 进行调用了

注意:Fun Local 现已集成到 VSCode、IDEA、Pycharm 等 IDE 的图形化插件中,相较于命令行,这些插件往往通过图形化的方式带来更好的使用体验,因此:

  • 如果您是 VSCode 用户,我们强烈推荐您直接使用 VSCode 插件
  • 如果您是 IDEA 或 Pycharm 用户,我们强烈推荐您直接使用 Cloud Toolkit 插件

Fun Local Invoke

Fun Local Invoke 命令格式

使用 fun local invoke -h 可以查看 fun local invoke 的帮助信息:

  1. $ fun local invoke -h
  2. Usage: invoke [options] <[service/]function>
  3. Run your serverless application locally for quick development & testing.
  4. Options:
  5. -d, --debug-port <port> used for local debugging
  6. -c, --config <ide> print out ide debug configuration. Options are VSCode
  7. -e, --event <path> event file containing event data passed to the function
  8. -h, --help output usage information

本地运行函数

运行函数的命令格式为:

  1. fun local invoke [options] <[service/]function>

其中 options、service 都是可以省略的。从调用方式上,可以理解为,fun local invoke 支持通过函数名调用,或者服务名/函数名的方式调用,如下:

  1. fun local invoke function
  2. fun local invoke service/function

如果想要精准匹配,可以使用服务名/函数名的方式。

注意:如果 template.yml 中包含多个服务,而多个服务中包含相同名称的函数时,通过函数名的方式调用 fun 只会运行第一个名称匹配的函数。

本地运行 java 类型的函数

java 不同于解释型的语言,在作为函数运行前,需要先编译。在我们的例子中,可以直接使用 fun build 函数名的方式,例如函数名为 java8,因此编译命令如下:

  1. fun build java8

执行命令后可以看到log信息,如下:

  1. using template: template.yml
  2. start building function dependencies without docker
  3. building localdemo/java8
  4. running task flow MavenTaskFlow
  5. running task: MavenCompileTask
  6. running task: MavenCopyDependencies
  7. running task: CopyMavenArtifacts
  8. Build Success
  9. Built artifacts: .fun/build/artifacts
  10. Built template: .fun/build/artifacts/template.yml
  11. Tips for next step
  12. ======================
  13. * Invoke Event Function: fun local invoke
  14. * Invoke Http Function: fun local start
  15. * Deploy Resources: fun deploy

编译成功后,可以直接使用fun local invoke 命令运行函数。

本地调试

fun local invoke 支持 -d, —debug-port 选项,可以对函数进行本地单步调试。Fun Local 涉及到的 debugging 技术全部都基于各个语言通用的调试协议实现的,使用对应语言的 remote debugging 方法都可以进行调试。

本地调试 nodejs、python 类型的函数

对于 nodejs6、nodejs8、python2.7、python3、java8 类型的函数,调试方法基本一致。下面拿 nodejs8 举例。

我们上面演示了可以通过 fun local invoke nodejs8 来运行名称为 nodejs8 的函数,如果想对该函数进行调试,只需要使用 -d 参数,并配置相应的端口号即可。

比如我们以调试方式运行函数,并将调试端口设定在 3000,可以通过下面的命令:

  1. fun local invoke -d 3000 nodejs8

另外,推荐添加 —config 参数,在调试的同时,可以输出用来调试的 IDE 的配置信息:

  1. fun local invoke -d 3000 --config VSCode nodejs8

命令执行结果如下:

  1. skip pulling images ...
  2. you can paste these config to .vscode/launch.json, and then attach to your running function
  3. ///////////////// config begin /////////////////
  4. {
  5. "version": "0.2.0",
  6. "configurations": [
  7. {
  8. "name": "fc/localdemo/nodejs8",
  9. "type": "node",
  10. "request": "attach",
  11. "address": "localhost",
  12. "port": 3000,
  13. "localRoot": "/Users/tan/code/fun/examples/local/nodejs8",
  14. "remoteRoot": "/code",
  15. "protocol": "inspect",
  16. "stopOnEntry": false
  17. }
  18. ]
  19. }
  20. ///////////////// config end /////////////////
  21. Debugger listening on ws://0.0.0.0:3000/b65c288b-bd6a-4791-849b-b03e0d16b0ce
  22. For help see https://nodejs.org/en/docs/inspector

程序会阻塞在这里,并不会继续往下执行。只有 IDE 的连接上来后,程序才会继续执行。接下来,我们针对 VSCode 配置、VSCode 调试两个方面分别进行讲解。

其中 VSCode 配置只有在第一次对函数进行调试时才需要,如果已经配置过,则不需要再次配置。

VSCode 配置

1.创建 vscode launch.json 文件。

1

2.复制日志中的 config begin 与 config end 之间的配置到 launch.json 中。

2

3.完成上面配置后,在 Debug 视图可以看到配置的函数列表。

3

至此,VSCode 配置完成。VSCode 更多配置知识可以参考官方文档

VSCode 调试

VSCode 配置成功后,只需要在 VSCode 编辑器侧边栏单击设置断点,然后点击“开始调试”按钮,即可开始调试。

4

以下是一个 nodejs8 函数本地单步调试的流程例子:

5

本地调试 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 类型函数的例子:

6

使用 IDEA 调试 java

IDEA 配置

IDEA 配置 remote debugging ,首先在菜单栏依次点击 Run -> Edit Configurations…:7

1.新建一个 Remote Debugging:

11

2.我们输出一个名字,并配置端口号为 3000.

22

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

44

使用 IDEA 开始调试

先将 java 函数以 debug 的方式运行起来:

  1. fun local invoke -d 3000 java8

ee

可以看到函数停止在这里了,接着我们使用 IDEA 连接并开始调试。可以通过菜单栏上的 Run -> Debug… 或者工具栏直接点击 Debug 按钮,即可开始调试。

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

ff

本地调试 php 类型的函数

php 的调试与其他类型的函数调试在流程上有一些不同。

首先,php 的运行通过 fun local invoke php72 命令完成,这与其他类型的函数一致。调试时,也像其他类型的函数一样,通过 -d 参数以调试模式启动函数:

  1. fun local invoke -d 3000 --config VSCode php72

但不同的是,以 debug 方式运行 php 函数后,php 函数并没有阻塞等待 vscode 调试器的连接,而是直接运行结束。

  1. skip pulling images ...
  2. you can paste these config to .vscode/launch.json, and then attach to your running function
  3. ///////////////// config begin /////////////////
  4. {
  5. "version": "0.2.0",
  6. "configurations": [
  7. {
  8. "name": "fc/localdemo/php72",
  9. "type": "php",
  10. "request": "launch",
  11. "port": 3000,
  12. "stopOnEntry": false,
  13. "pathMappings": {
  14. "/code": "/Users/tan/code/fun/examples/local/php7.2"
  15. },
  16. "ignore": [
  17. "/var/fc/runtime/**"
  18. ]
  19. }
  20. ]
  21. }
  22. ///////////////// config end /////////////////
  23. FunctionCompute php7.2 runtime inited.
  24. FC Invoke Start RequestId: 6e8f7ed7-653d-4a6a-94cc-1ef0d028e4b4
  25. FC Invoke End RequestId: 6e8f7ed7-653d-4a6a-94cc-1ef0d028e4b4
  26. hello world
  27. 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 函数即可开始调试:

  1. fun local invoke -d 3000 php72

rr

Event 事件源

函数计算提供了丰富的触发器,包括但不局限于对象存储触发器、日志服务触发器、CDN 事件触发器等。在本地无论是运行还是调试函数时,为了能够完全模拟线上环境,通常需要构造触发事件。

触发事件可以是一段可读的 json 配置,也可以是一段非可读的二进制数据。这里我们拿 json 举例,假设触发事件内容为:

  1. {
  2. "testKey": "testValue"
  3. }

将这事件内容传给函数执行,可以通过以下三种途径:

  1. 管道: echo ‘{“testKey”: “testValue”}’ | fun local invoke nodejs8
  2. 文件: 将的 json 内容写入到文件,文件名随意,比如 event.json。然后通过 -e 指定文件名:fun local invoke -e event.json nodejs8
  3. 重定向: fun local invoke nodejs8 < event.json 或者 fun local invoke nodejs8 <<< ‘{“testKey”: “testValue”}’ 等等。更多信息可以参考这篇文章

Fun Local Start

Fun Local Start 命令格式

使用 fun local invoke -h 可以查看 fun local invoke 的帮助信息:

  1. Usage: fun local start [options]
  2. Allows you to run the Function Compute application locally for quick development & testing.
  3. It will start an http server locally to receive requests for http triggers and apis.
  4. It scans all functions in template.yml. If the resource type is HTTP, it will be registered to this http server, which can be triggered by the browser or some http tools.
  5. For other types of functions, they will be registered as apis, which can be called by sdk in each language or directly via api.
  6. Function Compute will look up the code by CodeUri in template.yml.
  7. For interpreted languages, such as node, python, php, the modified code will take effect immediately, without restarting the http server.
  8. For compiled languages ​​such as java, we recommend you set CodeUri to the compiled or packaged localtion.
  9. Once compiled or packaged result changed, the modified code will take effect without restarting the http server.
  10. Options:
  11. -d, --debug-port <port> specify the sandboxed container starting in debug mode, and exposing this port on localhost
  12. -c, --config <ide/debugger> output ide debug configuration. Options are vscode
  13. -h, --help output usage information

本地运行 Http Trigger 函数

运行命令格式为:

  1. fun local start [options]

其中 options 是可以省略的。

执行 fun local start 后,fun 会首先启动一个 http server,以提供 http 的服务。然后 fun 会扫描 template.yml 中描述的所有配置了 Http Trigger 的函数,注册到 http server 中。注册成功后,就可以通过浏览器或者其他的 http 工具访问了。

比如,对于在 local_http 下执行 fun local start 后,会显示所有的 http trigger 信息:1打开任意一个 url,即可通过 http trigger 的方式触发函数运行。

以下是一个访问 nodejs http trigger 的动态演示(其他类型的 runtime,比如 python、php 使用体验一致):2

本地调试 Http Trigger 函数

Http Trigger 本地调试的方法与使用事件触发函数的方法一致 ———— 通过 -d, —debug-port 选项。同时还支持 -c, —config ,支持在调试时,显示调试 ide 配置。

调试方法为,首先通过 fun local start —debug 3000 —config vscode 启动服务,然后会看到 template.yml 中声明的函数都被注册成功:3

根据服务名、函数名或者 http trigger 触发器名称选择合适的 url,使用浏览器打开会看到浏览器页面一直无响应,但在终端会看到日志输出:

  1. skip pulling image aliyunfc/runtime-python3.6:1.2.0...
  2. you can paste these config to .vscode/launch.json, and then attach to your running function
  3. ///////////////// config begin /////////////////
  4. {
  5. "version": "0.2.0",
  6. "configurations": [
  7. {
  8. "name": "fc/local-http-demo/python3",
  9. "type": "python",
  10. "request": "attach",
  11. "host": "localhost",
  12. "port": 3000,
  13. "pathMappings": [
  14. {
  15. "localRoot": "/Users/tan/fun_local_http_demo/python3",
  16. "remoteRoot": "/code"
  17. }
  18. ]
  19. }
  20. ]
  21. }
  22. ///////////////// config end /////////////////
  23. FunctionCompute python3 runtime inited.
  24. FC Invoke Start RequestId: 04c57fba-cbe9-4c1f-8c57-f8e0b539fa08

将日志中的配置信息复制到 vscode 调试器中,在代码中下好断点,单击开始调试即可。

以下是一个调试 python http trigger 的动态演示(其他类型的 runtime,比如 nodejs、php 使用体验基本一致):5

热加载

本地运行、调试 Http Trigger 时,支持热加载。

当通过 fun local start 启动本地服务后,即使修改了代码,也无需重启本地服务,可以直接刷新网页或者重新触发函数即可运行变更后的函数。

以下是一个 nodejs 热加载的动态演示(其他类型的 runtime,比如 php、python 类似)。

备注:请提前在函数目录执行 npm install,用于初始化函数依赖的 nodejs 模块。7

Api 本地运行调试

命令格式与 Http Trigger 一致,即:

  1. fun local start [options]

其中 options 是可以省略的。

执行 fun local start 后,fun 会首先启动一个 http server,以提供 http 的服务。然后 fun 会扫描 template.yml 中描述的所有的函数,并将所有没有配置 Http Trigger 的函数,注册到 http server 中。注册成功后,就可以通过 InvokeFunction API 或者 SDK 进行调用了。

直接通过 API 访问需要进行签名,需要实现 签名认证。这里推荐使用 SDK 进行调用。首先,我们通过 fun local start 将服务运行起来,执行日志如下:

  1. $ fun local start
  2. api localdemo/php72 was registered
  3. url: http://localhost:8000/2016-08-15/services/localdemo/functions/php72/invocations/
  4. api localdemo/python27 was registered
  5. url: http://localhost:8000/2016-08-15/services/localdemo/functions/python27/invocations/
  6. api localdemo/python3 was registered
  7. url: http://localhost:8000/2016-08-15/services/localdemo/functions/python3/invocations/
  8. api localdemo/nodejs6 was registered
  9. url: http://localhost:8000/2016-08-15/services/localdemo/functions/nodejs6/invocations/
  10. api localdemo/nodejs8 was registered
  11. url: http://localhost:8000/2016-08-15/services/localdemo/functions/nodejs8/invocations/
  12. api localdemo/java8 was registered
  13. url: http://localhost:8000/2016-08-15/services/localdemo/functions/java8/invocations/
  14. function compute app listening on port 8000!

启动服务后,我们就可以通过 python sdk 进行调用了。

首先安装 fc pytohn sdk

  1. pip install aliyun-fc2

然后编写代码:

  1. import fc2
  2. client = fc2.Client(endpoint='http://localhost:8000', accessKeyID='<your access key id>', accessKeySecret='your access key secret')
  3. resp = client.invoke_function('localdemo', 'php72')
  4. print resp.headers
  5. print resp.data

注意: sdk 代码中配置的 accessKeyId、accessKeySecret 要求与 fun 配置一致,否则调用时,会导致签名认证失败。

执行效果如下图所示:7

其他

fun local invoke 访问宿主机上的服务

如果您使用的是 Docker-for-Mac 或 Docker-for-Windows 18.03+,只需使用host.docker.internal作为 ip 即可访问您的本地服务。例如:

  1. client = fc2.Client(endpoint='http://host.docker.internal:8000', accessKeyID='xxx', accessKeySecret='xxx')
  2. resp = client.invoke_function('localdemo', 'nodejs8', json.dumps({'code': 123}))

如果 docker 版本小于 18.03,在宿主机上执行 ifconfig(Linux or Mac)或 ipconfig(Windows) 查看宿主机 ip,然后配置到 endpoint 即可。

环境变量

在 template.yml 中配置的 EnvironmentVariables 会与线上行为一致,当函数运行时,可以通过代码获取到。更多信息参考

在本地运行函数时,除了 EnvironmentVariables 配置的环境变量,fun 还会额外提供一个 local=true 的环境变量,用来标识这是一个本地运行的函数。

通过这个环境变量,用户可以区分是本地运行还是线上运行,以便于进行一些特定的逻辑处理。

Credentials

用户可以通过 Credentials 中存储的 ak 信息访问阿里云的其他服务。Fun local 在本地运行函数时,会按照与 fun deploy 相同的策略寻找 ak 信息。

关于函数计算 Credentials 的描述,可以参考。

以下是一个根据本地、线上环境的不同,利用函数提供的 Credentials 配置 oss client 的例子:

  1. local = bool(os.getenv('local', ""))
  2. if (local):
  3. print 'thank you for running function in local!!!!!!'
  4. auth = oss2.Auth(creds.access_key_id,
  5. creds.access_key_secret)
  6. else:
  7. auth = oss2.StsAuth(creds.access_key_id,
  8. creds.access_key_secret,
  9. creds.security_token)