自定义函数运行时

函数运行时是函数执行的载体,通过加载函数代码来调用函数,跟函数实现的语言强相关。比如python代码需要使用python runtime来调用。这里就涉及到多种语言的问题,为了统一调用接口和协议,我们最终选择了GRPC,借助其强大的跨语言IDL和高性能RPC通讯能力,打造可灵活扩展的函数计算框架。

协议约定

GRPC的消息和服务定义如下,开发者可直接用于生成各自编程语言的消息和服务实现,GRPC使用方法可参考make pb命令,也可参考GRPC官网的文档

  1. syntax = "proto3";
  2. package runtime;
  3. // The runtime definition.
  4. service Runtime {
  5. // Handle handles request
  6. rpc Handle (Message) returns (Message) {} // 消息处理接口
  7. }
  8. // The request message.
  9. message Message {
  10. uint32 QOS = 1; // MQTT消息的QOS
  11. string Topic = 2; // MQTT消息的主题
  12. bytes Payload = 3; // MQTT消息的内容
  13. string FunctionName = 11; // 被调用的函数名
  14. string FunctionInvokeID = 12; // 函数调用ID
  15. }

注意:Docker容器模式下,函数实例的资源限制不要低于50M内存,20个线程。

配置约定

函数配置的所有定义如下。对于自定义函数runtime,只需关注Function.Handler和Function.CodeDir的定义和使用,其他配置都是函数计算框架使用的配置。

  1. // Runtime runtime config
  2. type Runtime struct {
  3. // 模块哦诶只
  4. Module `yaml:",inline" json:",inline"`
  5. // 服务配置
  6. Server RuntimeServer `yaml:"server" json:"server"`
  7. //
  8. Function Function `yaml:"function" json:"function"`
  9. }
  10. // RuntimeServer function runtime server config
  11. type RuntimeServer struct {
  12. Address string `yaml:"address" json:"address" validate:"nonzero"`
  13. Timeout time.Duration `yaml:"timeout" json:"timeout" default:"30s"`
  14. Message struct {
  15. Length Length `yaml:"length" json:"length" default:"{\"max\":4194304}"`
  16. } `yaml:"message" json:"message"`
  17. }
  18. // Function function config
  19. type Function struct {
  20. // 函数ID,目前用于映射函数代码所在的目录
  21. ID string `yaml:"id" json:"id" validate:"regexp=^[a-zA-Z0-9_-]{1\\,140}$"`
  22. // 函数名,函数调用使用的名称
  23. Name string `yaml:"name" json:"name" validate:"regexp=^[a-zA-Z0-9_-]{1\\,140}$"`
  24. // 函数执行的入口
  25. Handler string `yaml:"handler" json:"handler" validate:"nonzero"`
  26. // 函数代码所在目录
  27. CodeDir string `yaml:"codedir" json:"codedir"`
  28. // 实例配置
  29. Instance Instance `yaml:"instance" json:"instance"`
  30. // 函数runtime,docker容器模式下是函数runtime的镜像,native进程模式下是函数runtime的可执行程序路径
  31. Entry string `yaml:"entry" json:"entry"`
  32. // 函数runtime启动进程时传入的环境变量
  33. Env map[string]string `yaml:"env" json:"env"`
  34. }
  35. // Instance instance config for function runtime module
  36. type Instance struct {
  37. // 最少实例数
  38. Min int `yaml:"min" json:"min" default:"0" validate:"min=0, max=100"`
  39. // 最多实例数
  40. Max int `yaml:"max" json:"max" default:"1" validate:"min=1, max=100"`
  41. // 实例最大空闲时间
  42. IdleTime time.Duration `yaml:"idletime" json:"idletime" default:"10m"`
  43. // 实例调用超时时间
  44. Timeout time.Duration `yaml:"timeout" json:"timeout" default:"5m"`
  45. // 实例的资源配置
  46. Resources Resources `yaml:",inline" json:",inline"`
  47. // 消息最大长度限制
  48. Message struct {
  49. Length Length `yaml:"length" json:"length" default:"{\"max\":4194304}"`
  50. } `yaml:"message" json:"message"`
  51. }

启动约定

函数runtime实际上也是一种模块,只是比较特殊,是被函数计算模块在运行过程中按需启动的模块,归为临时模块。其启动方式和普通模块启动的方式基本一致,只不过临时模块没有固定路径,不支持从配置文件中加载配置。因此约定通过传参的方式传入配置信息,使用JSON格式,比如-c "{\"name\":\"sayhi\", …}"。

函数runtime启动并加载配置后,首先根据Server配置初始化GRPC server,然后根据Function的配置加载函数代码入口,等待函数计算模块来调用,最后注意监听SIGTERM信号来优雅退出。完整的实现过程可参考python27 runtime

最后更新于 2018-12-28 10:23:09

原文: https://openedge.tech/docs/customize/How-to-develop-a-customize-runtime-for-function