Skip to content

Commit

Permalink
docs: add pluggable component design chinese doc
Browse files Browse the repository at this point in the history
  • Loading branch information
cyb0225 committed Oct 25, 2023
1 parent fe688de commit 6aed602
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 88 deletions.
Binary file added docs/img/pluggable/layotto.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/pluggable/layotto_datatflow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/zh/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
- [API插件](zh/design/api_plugin/design.md)
- [支持Dapr API](zh/design/api_plugin/dapr_api.md)
- [OSS API设计文档](zh/design/oss/design.md)
- [pluggable component 设计文档](zh/design/pluggable/design.md)
- 贡献指南
- [Layotto 贡献指南](zh/development/CONTRIBUTING.md)
- [新手攻略:从零开始成为 Layotto 贡献者](zh/development/start-from-zero.md)
Expand Down
35 changes: 35 additions & 0 deletions docs/zh/design/pluggable/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Pluggable Component 设计文档

## 背景

当前 Layotto 的 component 都是实现在 Layotto 的工程里面的,这要求用户要想使用新的 component,必须使用 golang 语言开发,同时必须在 Layotto 工程中实现,然后统一编译。
对于多语言用户来说非常不友好,因此 Layotto 需要提供pluggable components 的能力,允许用户可以通过任何语言实现自己的component,Layotto 通过 grpc 协议和外部的 component 进行通信。

## 方案

- 基于 uds(unix domain socket)实现本地跨语言组件服务发现,降低通信开销。
- 基于 proto 实现组件跨语言实现能力。

## 数据流架构

![](../../../img/pluggable/layotto_datatflow.png)

这是当前用户调用 sdk 开始的数据流向。虚线部分是与 pluggable component 主要参与的数据流。

### 组件发现

![](../../../img/pluggable/layotto.png)

如上图所示,用户自定义组件启动 socket 服务,并将 socket 文件放到指定目录中。 layotto 启动时,会读取该目录中的所有 socket 文件(跳过文件夹),并建立 socket 连接。

目前,layotto 向 dapr 对齐,不负责用户组件的生命周期,服务期间若用户组件下线,不会进行重连,该组件服务无法使用。
后面根据社区使用情况,决定 layotto 是否需要支持进程管理模块,或是使用一个单独的服务来管理。

由于 windows 对于 uds 的支持还不是很完善,且 layotto 本身取消了对 windows 的兼容,所以新特性采用的 uds 发现模式未对 windows 系统做兼容。

## 组件注册

如上面的数据流架构图所示,用户注册的组件需要实现 pluggable proto 定义的 grpc 服务。 layotto 会根据 grpc 接口,实现 go interface 接口,这里
对应于数据流图中的 wrap component。wrap component 与 build-in component 对 layotto runtime 来说没有任何区别,对于用户来说也没有特殊的感知。

layotto 通过 grpc reflect 库,获取到用户提供服务实现了哪些组件,注册到全局的组件注册中心,供用户使用。
139 changes: 51 additions & 88 deletions docs/zh/design/pluggable/usage.md
Original file line number Diff line number Diff line change
@@ -1,99 +1,62 @@
# pluggable component 使用文档
# Pluggable Component 使用文档

## 编写组件
该示例展示了如何通过 Layotto 提供的可插拔组件能力,用户实现并注册自己的组件。并通过 Layotto sdk 调用,来验证自己组件编写的正确性。

下面以 go 实现 hello 组件为例
## step1.编写并运行可插拔组件

`layotto/spec/proto/pluggable` 中找到对应组件的 proto 文件,生成对应实现语言的 grpc 文件。
接下来,运行已经编写好的代码

```shell
cd demo/pluggable/hello
go run .
```

打印如下结果表示服务启动成功
```shell

Check failure on line 15 in docs/zh/design/pluggable/usage.md

View workflow job for this annotation

GitHub Actions / Markdown Validation

Fenced code blocks should be surrounded by blank lines [Context: "```shell"]
start grpc server
```

若出现以下错误,代表 sock 文件已经存在,可能是上次启动服务时强制关闭导致的,使用 `rm /tmp/runtime/component-sockets/hello-grpc-demo.sock` 删除后重新启动即可。
```shell

Check failure on line 20 in docs/zh/design/pluggable/usage.md

View workflow job for this annotation

GitHub Actions / Markdown Validation

Fenced code blocks should be surrounded by blank lines [Context: "```shell"]
panic: listen unix /tmp/runtime/component-sockets/hello-grpc-demo.sock: bind: address already in use

goroutine 1 [running]:
main.main()
/home/cyb/project/ospp/layotto/demo/pluggable/hello/main.go:49 +0x236
exit status 2
```

> 1. 以 go 实现 hello 组件为例,在 `layotto/spec/proto/pluggable` 中找到对应组件的 proto 文件,生成对应实现语言的 grpc 文件。
go 语言的 pb 文件已经生成并放在了 `spec/proto/pluggable/v1` 下,用户在使用时直接引用即可。
> 2. 组件除了需要实现 protobuf 文件中定义的接口外,还需要使用 socket 方式启动文件并将 sock 文件存放在 `/tmp/runtime/component-sockets` 默认路径下,
也可以通过环境变量 `LAYOTTO_COMPONENTS_SOCKETS_FOLDER` 修改 sock 存储路径位置。
> 3. 除此之外,用户还需要注册 reflection 服务到 grpc server 中,用于 layotto 服务发现时获取该 grpc 服务具体实现接口的 spec。 具体代码可以参考 `demo/pluggable/hello/main.go`
## step2. 启动 Layotto

```go
package main

import (
"context"
"errors"
"fmt"
"github.com/golang/protobuf/ptypes/empty"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
pb "mosn.io/layotto/spec/proto/pluggable/v1/hello"
"net"
"os"
)

const (
AuthToken = "123456" // token 校验
TokenConfigKey = "token"
SocketFilePath = "/tmp/runtime/component-sockets/hello-grpc-demo.sock"
)

type HelloService struct {
pb.UnimplementedHelloServer
hello string
token string
}

func (h *HelloService) Init(ctx context.Context, config *pb.HelloConfig) (*empty.Empty, error) {
h.hello = config.GetHelloString()
h.token = config.Metadata[TokenConfigKey]
if h.token != AuthToken {
return nil, errors.New("auth failed")
}

return nil, nil
}

func (h *HelloService) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
res := &pb.HelloResponse{
HelloString: h.hello,
}
return res, nil
}

func main() {
listen, err := net.Listen("unix", SocketFilePath)
if err != nil {
panic(err)
}
defer os.RemoveAll(SocketFilePath)

server := grpc.NewServer()
srv := &HelloService{}
pb.RegisterHelloServer(server, srv)
reflection.Register(server)

fmt.Println("start grpc server")
if err := server.Serve(listen); err != nil && !errors.Is(err, net.ErrClosed) {
fmt.Println(err)
}
}
```shell
cd cmd/layotto
go build -o layotto .
./layotto start -c ../../configs/config_hello_component.json
```

1. 实现对应组件 proto 文件的 grpc 服务。
2. 启动 socket 服务,sock 文件需放置在 `/tmp/runtime/component-sockets` 下,也可以使用 `LAYOTTO_COMPONENTS_SOCKETS_FOLDER` 环境变量进行设置。
3. 注册 grpc 服务,除了注册 hello 服务外,还需要注册 reflection 服务。该服务用于 layotto 服务发现时,获取该 socket 服务具体实现了哪些 proto 文件定义的服务。
4. 启动服务

## 注册组件

填写配置文件,在对应组件下添加相关配置项,以上述的 hello 组件为例。

```json
"grpc_config": {
"hellos": {
"helloworld": {
"type": "hello-grpc-demo",
"hello": "hello",
"metadata": {
"token": "123456"
}
}
}
}
> 配置文件中填写组件的 type 为 `hello-grpc-demo`,由 socket 文件的前缀名决定。 配置项与注册普通 hello 组件一致。提供 metadata 项,便于用户设置自定义配置需求。
## step3. 组件校验

基于现有的组件测试代码,来测试用户实现的可插拔组件的正确性。

```shell
cd demo/hello/common
go run . -s helloworld
```

组件的 type 为 `hello-grpc-demo`,由 socket 文件的前缀名决定。
程序输出以下结果表示可插拔组件注册运行成功
```shell

Check failure on line 55 in docs/zh/design/pluggable/usage.md

View workflow job for this annotation

GitHub Actions / Markdown Validation

Fenced code blocks should be surrounded by blank lines [Context: "```shell"]
runtime client initializing for: 127.0.0.1:34904
hello
```

配置项与注册普通 hello 组件一致。提供 metadata 项,便于用户设置自定义配置需求。
## 了解 Layotto 可插拔组件的实现原理

如果您对实现原理感兴趣,或者想扩展一些功能,可以阅读[可插拔组件的设计文档](zh/design/pluggable/design.md)

0 comments on commit 6aed602

Please sign in to comment.