You need to enable JavaScript to run this app.
导航
开发方法(Web 应用)
最近更新时间:2024.09.06 19:27:17首次发布时间:2022.06.10 16:25:10

对于 Web 应用,您的程序是一个运行在 Native 运行时中的 HTTP Server 服务。本文为您介绍本地开发该 HTTP Server 需要遵循的规范和方法。

服务规范

在本地开发 HTTP Server 时需要遵循以下规范:

  • 服务本身必须是无状态的
    如需状态可存储至远端 redis 或 mysql,服务启动不依赖本地内存 cache 或落盘的数据。

  • 启动命令
    服务需要提供启动命令(后续在控制台 创建 Web 应用 时配置启动命令)。

    • 对于简单的启动命令,在控制台直接配置即可。
    • 如果启动过程比较复杂,支持在项目中提供一个启动脚本,并在控制台配置启动命令时指定该启动脚本。

    注意

    启动脚本必须具有相应的可执行权限,否则发布服务时会出现 permission denied 报错。可参考以下命令给 Linux 系统所有的用户组添加脚本的执行权限。

    # 注意是在本地执行该命令
    # 假设启动脚本为 run.sh
    chmod a+x run.sh
    
  • 监听端口
    服务需要监听端口。

    • 请确保服务代码监听的端口与控制台配置的端口一致(后续在控制台 创建 Web 应用 时配置监听端口),否则会导致函数发布时冷启动失败并出现以下错误。
    function_start_timeout
    
    function process start timeout: function start timed out (120s) with command `./main --port 3000`, function may not listen on address *:8000, logs: current working directory: /opt/bytefaas Server listen on address: :3000. Please check your log(runtime_log)/code and then retry, or contact oncall
    
    • 在服务中可以通过环境变量 _FAAS_RUNTIME_PORT 来获取指定的监听端口。
  • 不鼓励后台进程
    不鼓励服务启动后台进程或线程(即请求已经返回了 Response,但后台仍有任务异步执行)。函数服务根据请求量对后端实例进行动态扩缩容,无法保证后台进程、线程的存活性。

HTTP Server 代码示例

在 Native 运行时中,函数服务会将请求 Method、Path、Body、Query 以及 Headers 转发给您的 HTTP Server。您可以直接使用入参请求头(Headers)和请求体(Body)来编写函数的业务逻辑。

以 Golang 语言为例,一个完整的 HTTP Server 代码示例如下:

package main

import (
        "flag"
        "fmt"
        "log"
        "net/http"
        "os"
        "os/signal"
        "syscall"
)

func hello(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("Hello World from VeFaaS"))
}

func main() {
        // 从命令行参数或者环境变量中获取监听端口
        var port string
        flag.StringVar(&port, "port", "", "http server listen port")
        flag.Parse()

        if port == "" {
                port = os.Getenv("_FAAS_RUNTIME_PORT")
        }

        // 配置 HTTP Server 的请求处理 handler
        http.HandleFunc("/v1/hello", hello)

        addr := fmt.Sprintf("0.0.0.0:%s", port)
        srv := http.Server{Addr: addr}

        // 启动 HTTP Server
        go func() {
                log.Printf("Start HTTP Server at: %s", addr)
                if err := srv.ListenAndServe(); err != nil {
                        log.Println("ListenAndServe: ", err)
                }
        }()

        // 监听 SIGINT, SIGTERM 信号
        quit := make(chan os.Signal, 1)
        signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
        <-quit

        // 收到信号后进行优雅退出
        log.Println("Shutting down server...")
        // 执行您的 Graceful Shutdown 逻辑
        log.Println("Doing your graceful shutdown logic")
        if err := srv.Shutdown(nil); err != nil {
                log.Fatal("Server shutdown error: ", err)
        }

        log.Println("Server gracefully stopped")
}

服务的监听端口与启动

您可以通过命令行参数传递端口,或者通过环境变量 _FAAS_RUNTIME_PORT 来获取配置的监听端口(默认为 8000),以下代码展示了从命令参数以及环境变量中获取端口号的逻辑。

注意

服务监听的端口必须与您在控制台配置的监听端口相匹配。

var port string
flag.StringVar(&port, "port", "", "http server listen port")
flag.Parse()

if port == "" {
        port = os.Getenv("_FAAS_RUNTIME_PORT")
}

得到监听端口号 port 后,您需要保证启动的 HTTP Server 监听 0.0.0.0:port,否则会导致部署失败。以下代码展示了拼接服务监听地址并启动 HTTP Server 的逻辑。

// 拼接 0.0.0.0:port 地址
addr := fmt.Sprintf("0.0.0.0:%s", port)
srv := http.Server{Addr: addr}

// 启动 HTTP Server
go func() {
        log.Printf("Start HTTP Server at: %s", addr)
        if err := srv.ListenAndServe(); err != nil {
                log.Println("ListenAndServe: ", err)
        }
}()

请求处理

针对 Native 运行时的 HTTP Server 而言,由于您本身已经实现了一个 HTTP Server,因此您可以在 HTTP Server 的逻辑中对您的请求处理程序进行自定义处理。以下代码展示了简单的请求处理接口。

func hello(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("Hello World from VeFaaS"))
}

以下代码展示了请求处理的路由。

// 配置 HTTP Server 的请求处理 handler
http.HandleFunc("/v1/hello", hello)

服务的优雅退出

在函数实例退出的时候,函数服务会给您的 HTTP Server 服务发送 SIGINT 信号,您可以捕获该信号并执行自定义的优雅退出逻辑。以下代码展示了从信号捕获到 HTTP Server 关闭的流程。

// 监听 SIGINT, SIGTERM 信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit

// 收到信号后进行优雅退出
log.Println("Shutting down server...")
// 执行您的 Graceful Shutdown 逻辑
log.Println("Doing your graceful shutdown logic")
if err := srv.Shutdown(nil); err != nil {
        log.Fatal("Server shutdown error: ", err)
}

log.Println("Server gracefully stopped")

配置启动命令

完成代码开发和编译后,您需要在控制台 创建 Web 应用 时配置启动命令与监听端口。下图假设服务二进制名为http-server,监听端口为 3000。

使用环境变量

您可以在控制台配置函数运行时所需的环境变量,并在代码中读取对应的环境变量,用于函数处理逻辑。对于所配置的环境变量,veFaaS 会将其注入到函数运行所在的容器中,程序可以通过语言的内置库进行读取。 例如:若您函数配置中环境变量的键(key)为 envKey,运行环境读取该环境变量的代码示例如下。

import "os"
envValue := os.Getenv("envKey")