mirror of
https://github.com/criyle/go-judge.git
synced 2025-09-26 22:39:12 +08:00
docs: 加入中文文档
This commit is contained in:
parent
e2bddd3216
commit
222eb97b25
472
README.cn.md
Normal file
472
README.cn.md
Normal file
@ -0,0 +1,472 @@
|
||||
# go-judge
|
||||
|
||||
[](https://pkg.go.dev/github.com/criyle/go-judge) [](https://goreportcard.com/report/github.com/criyle/go-judge) [](https://github.com/criyle/go-judge/releases/latest) 
|
||||
|
||||
[English](README.md)
|
||||
|
||||
## 沙箱服务
|
||||
|
||||
快速,简单,安全
|
||||
|
||||
### 系统架构
|
||||
|
||||
```text
|
||||
+------------------------------------------------------------------------+
|
||||
| 传输层 (HTTP / WebSocket / FFI / ...) |
|
||||
+------------------------------------------------------------------------+
|
||||
| 工作协程 (环境池 和 环境生产者 ) |
|
||||
+-----------------------------------------------------------+------------+
|
||||
| 运行环境 | 文件存储 |
|
||||
+--------------------+----------------+---------------------+------+-----+
|
||||
| Linux (go-sandbox) | Windows (winc) | macOS (app sandbox) | 内存 | 磁盘 |
|
||||
+--------------------+----------------+---------------------+------+-----+
|
||||
```
|
||||
|
||||
### REST API 接口
|
||||
|
||||
沙箱服务提供 REST API 接口来在受限制的环境中运行程序。本质是 `envexec` 的简单封装。
|
||||
|
||||
- /run POST 在受限制的环境中运行程序(下面有例子)
|
||||
- /file GET 得到所有在文件存储中的文件列表
|
||||
- /file POST 上传一个文件到文件存储,返回一个文件 ID 用于提供给 /run 接口
|
||||
- /file/:fileId GET 下载文件 ID 指定的文件
|
||||
- /file/:fileId DELETE 删除文件 ID 指定的文件
|
||||
- /ws /run 接口的 WebSocket 版
|
||||
- /metrics 提供 prometheus 版监控 (使用 `ES_ENABLE_METRICS=1` 环境变量开启)
|
||||
- /debug 提供 go 语音调试接口 (使用 `ES_ENABLE_DEBUG=1` 环境变量开启)
|
||||
- /version 得到本程序编译版本和 go 语音运行时版本
|
||||
|
||||
### 命令行参数
|
||||
|
||||
服务相关:
|
||||
|
||||
- 默认监听地址是 `:5050`,使用 `-http-addr` 指定
|
||||
- 默认 gRPC 接口处于关闭状态,使用 `-enable-grpc` 开启
|
||||
- 默认 gRPC 监听地址是 `:5051` ,使用 `-grpc-addr` 指定
|
||||
- 默认日志等级是 debug ,使用 `-silent` 关闭 或 使用 `-release` 开启 release 级别日志
|
||||
- 默认没有开启鉴权,使用 `-auth-token` 指定令牌鉴权
|
||||
- 默认没有开启 go 语音调试接口,使用 `-enable-debug` 开启
|
||||
- 默认没有开启监控接口,使用 `-enable-metrics` 开启
|
||||
|
||||
沙箱相关:
|
||||
|
||||
- 默认同时运行任务数为 `4` ,使用 `-parallelism` 指定
|
||||
- 默认文件存储在内存里,使用 `-dir` 指定本地目录为文件存储
|
||||
- 默认 cgroup 的前缀为 `executor_server` ,使用 `-cgroup-prefix` 指定
|
||||
- 默认没有磁盘文件复制限制,使用 `-src-prefix` 限制 copyIn 操作文件目录前缀(需要绝对路径)
|
||||
- 默认时间和内存使用检查周期为 100 毫秒(`100ms`),使用 `-time-limit-checker-interval` 指定
|
||||
- 默认最大输出限制为 `256MiB`,使用 `-output-limit` 指定
|
||||
- 默认最大额外内存使用为 `16KiB` ,使用 `-extra-memory-limit` 指定
|
||||
- 默认最大 `copyOut` 文件大小为 `64MiB` ,使用 `-copy-out-limit` 指定
|
||||
- 使用 `-cpuset` 指定 `cpuset.cpus` (仅 Linux)
|
||||
- 默认容器用户开始区间为 10000 使用 `-container-cred-start` 指定(仅 Linux)
|
||||
- 举例,默认情况下第 0 个容器使用 10001 作为容器用户。第 1 个容器使用 10002 作为容器用户,以此类推
|
||||
- 使用 `-enable-cpu-rate` 开启 `cpu` cgroup 来启用 `cpuRate` 控制(仅 Linux)
|
||||
- 使用 `-cpu-cfs-period` 指定 cfs_period if cpu rate is enabled (default 100ms) (valid value: \[1ms, 1s\])
|
||||
- 使用 `-seccomp-conf` 指定 `seecomp` 过滤器(需要编译标志 `seccomp`,默认不开启)(仅 Linux)
|
||||
- 使用 `-pre-fork` 指定启动时创建的容器数量
|
||||
- 使用 `-tmp-fs-param` 指定容器内 `tmpfs` 的挂载参数(仅 Linux)
|
||||
|
||||
### 环境变量
|
||||
|
||||
所以命令行参数都可以通过环境变量的形式来指定,(类似 `ES_HTTP_ADDR` 来指定 `-http-addr`)。使用 `executorserver --help` 查看所以环境变量
|
||||
|
||||
### 安装和运行
|
||||
|
||||
下载预编译二进制文件 [Release](https://github.com/criyle/go-judge/releases) 并在终端开启
|
||||
|
||||
或者使用 docker
|
||||
|
||||
```bash
|
||||
docker run -it --rm --privileged -p 5050:5050 criyle/executorserver
|
||||
```
|
||||
|
||||
#### 编译 docker
|
||||
|
||||
终端中运行 `docker build -t executorserver -f Dockerfile.exec .`
|
||||
|
||||
沙箱服务需要特权级别 docker 来创建子容器和提供 cgroup 资源限制。
|
||||
|
||||
### 编译沙箱终端
|
||||
|
||||
编译 `go build ./cmd/executorshell`
|
||||
|
||||
运行 `./executorshell`,需要打开 gRPC 接口来使用。提供一个沙箱内的终端环境。
|
||||
|
||||
### /run 接口返回状态
|
||||
|
||||
- Accepted: 程序在资源限制内正常退出
|
||||
- Memory Limit Exceeded: 超出内存限制
|
||||
- Time Limit Exceeded:
|
||||
- 超出 `timeLimit` 时间限制
|
||||
- 或者超过 `clockLimit` 等待时间限制
|
||||
- Output Limit Exceeded:
|
||||
- 超出 `pipeCollector` 限制
|
||||
- 或者超出 `-output-limit` 最大输出限制
|
||||
- File Error:
|
||||
- `copyIn` 指定文件不存在
|
||||
- 或者 `copyIn` 指定文件大小超出沙箱文件系统限制
|
||||
- 或者 `copyOut` 指定文件不存在
|
||||
- Non Zero Exit Status: 程序用非 0 返回值退出
|
||||
- Signalled: 程序收到结束信号而退出(例如 `SIGSEGV`)
|
||||
- Dangerous Syscall: 程序被 `seccomp` 过滤器结束
|
||||
- Internal Error:
|
||||
- 指定程序路径不存在
|
||||
- 或者容器创建失败
|
||||
- 比如使用非特权 docker
|
||||
- 或者在个人目录下以 root 权限运行
|
||||
- 或者其他错误
|
||||
|
||||
### 容器的文件系统
|
||||
|
||||
在 Linux 平台,默认只读挂载点包括主机的 `/lib`, `/lib64`, `/usr`, `/bin`, `/etc/alternatives`, `/etc/fpc.cfg`, `/dev/null`, `/dev/urandom` 和临时文件系统 `/w`, `/tmp` 以及 `/proc`。
|
||||
|
||||
使用 `mount.yaml` 定制容器文件系统。
|
||||
|
||||
### 包
|
||||
|
||||
- envexec: 核心逻辑包,在提供的环境中运行一个或多个程序
|
||||
- env: 环境的标准实现
|
||||
|
||||
### 注意
|
||||
|
||||
#### CentOS 7
|
||||
|
||||
需要开启 user 命名空间来使用 [stack overflow](https://superuser.com/questions/1294215/is-it-safe-to-enable-user-namespaces-in-centos-7-4-and-how-to-do-it/1294246#1294246)
|
||||
|
||||
```bash
|
||||
echo user.max_user_namespaces=10000 >> /etc/sysctl.d/98-userns.conf
|
||||
sysctl -p
|
||||
```
|
||||
|
||||
### 压力测试
|
||||
|
||||
使用 `wrk` 和 `t.lua`: `wrk -s t.lua -c 1 -t 1 -d 30s --latency http://localhost:5050/run`.
|
||||
|
||||
注意,这些结果只是极限情况下的表现,实际情况和使用方式相关。通常沙箱服务相比于直接运行程序,通常有 1 毫秒左右额外延迟。
|
||||
|
||||
```lua
|
||||
wrk.method = "POST"
|
||||
wrk.body = '{"cmd":[{"args":["/bin/cat","a.hs"],"env":["PATH=/usr/bin:/bin"],"files":[{"content":""},{"name":"stdout","max":10240},{"name":"stderr","max":10240}],"cpuLimit":10000000000,"memoryLimit":104857600,"procLimit":50,"copyIn":{"a.hs":{"content":"main = putStrLn \\"Hello, World!\\""},"b":{"content":"TEST"}}}]}'
|
||||
wrk.headers["Content-Type"] = "application/json;charset=UTF-8"
|
||||
```
|
||||
|
||||
- 单线程 ~400-460 op/s Windows 10 WSL2 @ 5800X
|
||||
- 多线程 ~1800-2000 op/s Windows 10 WSL2 @ 5800X
|
||||
|
||||
单线程:
|
||||
|
||||
```text
|
||||
Running 30s test @ http://localhost:5050/run
|
||||
1 threads and 1 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 1.16ms 132.89us 6.20ms 90.15%
|
||||
Req/Sec 0.87k 19.33 0.91k 85.33%
|
||||
Latency Distribution
|
||||
50% 1.13ms
|
||||
75% 1.18ms
|
||||
90% 1.27ms
|
||||
99% 1.61ms
|
||||
25956 requests in 30.01s, 6.88MB read
|
||||
Requests/sec: 864.88
|
||||
Transfer/sec: 234.68KB
|
||||
```
|
||||
|
||||
### REST API 接口定义
|
||||
|
||||
```typescript
|
||||
interface LocalFile {
|
||||
src: string; // 文件绝对路径
|
||||
}
|
||||
|
||||
interface MemoryFile {
|
||||
content: string | Buffer; // 文件内容
|
||||
}
|
||||
|
||||
interface PreparedFile {
|
||||
fileId: string; // 文件 id
|
||||
}
|
||||
|
||||
interface Pipe {
|
||||
name: string; // copyOut 文件名
|
||||
max: number; // 最大大小限制
|
||||
}
|
||||
|
||||
interface Cmd {
|
||||
args: string[]; // 程序命令行参数
|
||||
env?: string[]; // 程序环境变量
|
||||
|
||||
// 指定 标准输入、标准输出和标准错误的文件
|
||||
files?: (LocalFile | MemoryFile | PreparedFile | Pipe | null)[];
|
||||
tty?: boolean; // 开启 TTY (需要保证标准输出和标准错误为同一文件)同时需要指定 TERM 环境变量 (例如 TERM=xterm)
|
||||
|
||||
// 资源限制
|
||||
cpuLimit?: number; // CPU时间限制,单位纳秒
|
||||
clockLimit?: number; // 等待时间限制,单位纳秒 (通常为 cpuLimit 两倍)
|
||||
memoryLimit?: number; // 内存限制,单位 byte
|
||||
stackLimit?: number; // 栈内存限制,单位 byte
|
||||
procLimit?: number; // 线程数量限制
|
||||
strictMemoryLimit?: boolean; // 开启严格内存限制 (仅 Linux,设置 rlimit 内存限制)
|
||||
|
||||
// 在执行程序之前复制进容器的文件列表
|
||||
copyIn?: {[dst:string]:LocalFile | MemoryFile | PreparedFile};
|
||||
|
||||
// 在执行程序后从容器文件系统中复制出来的文件列表
|
||||
copyOut?: string[];
|
||||
// 和 copyOut 相同,不过文件不返回内容,而是返回一个对应文件 ID ,内容可以通过 /file/:fileId 接口下载
|
||||
copyOutCached?: string[];
|
||||
// 指定 copyOut 复制文件大小限制,单位 byte
|
||||
copyOutMax: number;
|
||||
}
|
||||
|
||||
enum Status {
|
||||
Accepted, // normal
|
||||
MemoryLimitExceeded, // mle
|
||||
TimeLimitExceeded, // tle
|
||||
OutputLimitExceeded, // ole
|
||||
FileError, // fe
|
||||
RuntimeError, // re
|
||||
DangerousSyscall, // dgs
|
||||
InternalError, // system error
|
||||
}
|
||||
|
||||
interface PipeIndex {
|
||||
index: number; // cmd 的下标
|
||||
fd: number; // cmd 的 fd
|
||||
}
|
||||
|
||||
interface PipeMap {
|
||||
in: PipeIndex; // 管道的输入端
|
||||
out: PipeIndex; // 管道的输出端
|
||||
}
|
||||
|
||||
interface Request {
|
||||
requestId?: string; // 给 WebSocket 使用
|
||||
cmd: Cmd[];
|
||||
pipeMapping: PipeMap[];
|
||||
}
|
||||
|
||||
interface Result {
|
||||
status: Status;
|
||||
error?: string; // 详细错误信息
|
||||
exitStatus: number; // 程序返回值
|
||||
time: number; // 程序运行 CPU 时间,单位纳秒
|
||||
memory: number; // 程序运行内存,单位 byte
|
||||
runTime: number; // 程序运行现实时间,单位纳秒
|
||||
// copyOut 和 pipeCollector 指定的文件内容
|
||||
files?: {[name:string]:string};
|
||||
// copyFileCached 指定的文件 id
|
||||
fileIds?: {[name:string]:string};
|
||||
}
|
||||
|
||||
// WebSocket 结果
|
||||
interface WSResult {
|
||||
requestId: string;
|
||||
results: []Result;
|
||||
error?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 示例
|
||||
|
||||
单个文件编译运行(这个例子需要安装 `g++` 例如 `apt install g++` ):
|
||||
|
||||
```json
|
||||
{
|
||||
"cmd": [{
|
||||
"args": ["/usr/bin/g++", "a.cc", "-o", "a"],
|
||||
"env": ["PATH=/usr/bin:/bin"],
|
||||
"files": [{
|
||||
"content": ""
|
||||
}, {
|
||||
"name": "stdout",
|
||||
"max": 10240
|
||||
}, {
|
||||
"name": "stderr",
|
||||
"max": 10240
|
||||
}],
|
||||
"cpuLimit": 10000000000,
|
||||
"memoryLimit": 104857600,
|
||||
"procLimit": 50,
|
||||
"copyIn": {
|
||||
"a.cc": {
|
||||
"content": "#include <iostream>\nusing namespace std;\nint main() {\nint a, b;\ncin >> a >> b;\ncout << a + b << endl;\n}"
|
||||
}
|
||||
},
|
||||
"copyOut": ["stdout", "stderr"],
|
||||
"copyOutCached": ["a.cc", "a"],
|
||||
"copyOutDir": "1"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"status": "Accepted",
|
||||
"exitStatus": 0,
|
||||
"time": 303225231,
|
||||
"memory": 32243712,
|
||||
"runTime": 524177700,
|
||||
"files": {
|
||||
"stderr": "",
|
||||
"stdout": ""
|
||||
},
|
||||
"fileIds": {
|
||||
"a": "5LWIZAA45JHX4Y4Z",
|
||||
"a.cc": "NOHPGGDTYQUFRSLJ"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"cmd": [{
|
||||
"args": ["a"],
|
||||
"env": ["PATH=/usr/bin:/bin"],
|
||||
"files": [{
|
||||
"content": "1 1"
|
||||
}, {
|
||||
"name": "stdout",
|
||||
"max": 10240
|
||||
}, {
|
||||
"name": "stderr",
|
||||
"max": 10240
|
||||
}],
|
||||
"cpuLimit": 10000000000,
|
||||
"memoryLimit": 104857600,
|
||||
"procLimit": 50,
|
||||
"strictMemoryLimit": false,
|
||||
"copyIn": {
|
||||
"a": {
|
||||
"fileId": "5LWIZAA45JHX4Y4Z"
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"status": "Accepted",
|
||||
"exitStatus": 0,
|
||||
"time": 1173000,
|
||||
"memory": 10637312,
|
||||
"runTime": 1100200,
|
||||
"files": {
|
||||
"stderr": "",
|
||||
"stdout": "2\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
多个程序(例如交互题):
|
||||
|
||||
```json
|
||||
{
|
||||
"cmd": [{
|
||||
"args": ["/bin/cat", "1"],
|
||||
"env": ["PATH=/usr/bin:/bin"],
|
||||
"files": [{
|
||||
"content": ""
|
||||
}, null, {
|
||||
"name": "stderr",
|
||||
"max": 10240
|
||||
}],
|
||||
"cpuLimit": 1000000000,
|
||||
"memoryLimit": 1048576,
|
||||
"procLimit": 50,
|
||||
"copyIn": {
|
||||
"1": { "content": "TEST 1" }
|
||||
},
|
||||
"copyOut": ["stderr"]
|
||||
},
|
||||
{
|
||||
"args": ["/bin/cat"],
|
||||
"env": ["PATH=/usr/bin:/bin"],
|
||||
"files": [null, {
|
||||
"name": "stdout",
|
||||
"max": 10240
|
||||
}, {
|
||||
"name": "stderr",
|
||||
"max": 10240
|
||||
}],
|
||||
"cpuLimit": 1000000000,
|
||||
"memoryLimit": 1048576,
|
||||
"procLimit": 50,
|
||||
"copyOut": ["stdout", "stderr"]
|
||||
}],
|
||||
"pipeMapping": [{
|
||||
"in" : {"index": 0, "fd": 1 },
|
||||
"out" : {"index": 1, "fd" : 0 }
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"status": "Accepted",
|
||||
"exitStatus": 0,
|
||||
"time": 1545123,
|
||||
"memory": 253952,
|
||||
"runTime": 4148800,
|
||||
"files": {
|
||||
"stderr": ""
|
||||
},
|
||||
"fileIds": {}
|
||||
},
|
||||
{
|
||||
"status": "Accepted",
|
||||
"exitStatus": 0,
|
||||
"time": 1501463,
|
||||
"memory": 253952,
|
||||
"runTime": 5897700,
|
||||
"files": {
|
||||
"stderr": "",
|
||||
"stdout": "TEST 1"
|
||||
},
|
||||
"fileIds": {}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
开启 CPURate 限制的死循环:
|
||||
|
||||
```json
|
||||
{
|
||||
"cmd": [{
|
||||
"args": ["/usr/bin/python3", "1.py"],
|
||||
"env": ["PATH=/usr/bin:/bin"],
|
||||
"files": [{"content": ""}, {"name": "stdout","max": 10240}, {"name": "stderr","max": 10240}],
|
||||
"cpuLimit": 3000000000,
|
||||
"clockLimit": 4000000000,
|
||||
"memoryLimit": 104857600,
|
||||
"procLimit": 50,
|
||||
"cpuRate": 0.1,
|
||||
"copyIn": {
|
||||
"1.py": {
|
||||
"content": "while True:\n pass"
|
||||
}
|
||||
}}]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"status": "Time Limit Exceeded",
|
||||
"exitStatus": 9,
|
||||
"time": 414803599,
|
||||
"memory": 3657728,
|
||||
"runTime": 4046054900,
|
||||
"files": {
|
||||
"stderr": "",
|
||||
"stdout": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
84
README.md
84
README.md
@ -2,8 +2,12 @@
|
||||
|
||||
[](https://pkg.go.dev/github.com/criyle/go-judge) [](https://goreportcard.com/report/github.com/criyle/go-judge) [](https://github.com/criyle/go-judge/releases/latest) 
|
||||
|
||||
[中文文档](README.cn.md)
|
||||
|
||||
## Executor Service
|
||||
|
||||
Fast, Simple, Secure
|
||||
|
||||
### Architecture
|
||||
|
||||
```text
|
||||
@ -54,17 +58,17 @@ Sandbox:
|
||||
- `-output-limit` specifies size limit of POSIX rlimit of output (default 256MiB)
|
||||
- `-extra-memory-limit` specifies the additional memory limit to check memory limit exceeded (default 16KiB)
|
||||
- `-copy-out-limit` specifies the default file copy out max (default 64MiB)
|
||||
- `-cpuset` specifies `cpuset.cpus` cgroup for each container
|
||||
- `-container-cred-start` specifies container `setuid` / `setgid` credential start point (default: 10000)
|
||||
- `-cpuset` specifies `cpuset.cpus` cgroup for each container (Linux only)
|
||||
- `-container-cred-start` specifies container `setuid` / `setgid` credential start point (default: 10000) (Linux only)
|
||||
- for example, by default container 0 will run with 10001 uid & gid and container 1 will run with 10002 uid & gid...
|
||||
- `-enable-cpu-rate` enabled `cpu` cgroup to control cpu rate using cfs_quota & cfs_period control
|
||||
- `-cpu-cfs-period` specifies cfs_period if cpu rate is enabled (default 100ms) (valid value: \[1ms, 1s\])
|
||||
- `-seccomp-conf` specifies `seecomp` filter setting to load when running program (need build tag `seccomp`)
|
||||
- `-enable-cpu-rate` enabled `cpu` cgroup to control cpu rate using cfs_quota & cfs_period control (Linux only)
|
||||
- `-cpu-cfs-period` specifies cfs_period if cpu rate is enabled (default 100ms) (valid value: \[1ms, 1s\])
|
||||
- `-seccomp-conf` specifies `seecomp` filter setting to load when running program (need build tag `seccomp`) (Linux only)
|
||||
- for example, by `strace -c prog` to get all `syscall` needed and restrict to that sub set
|
||||
- however, the `syscall` count in one platform(e.g. x86_64) is not suitable for all platform, so this option is not recommended
|
||||
- the program killed by seccomp filter will have status `Dangerous Syscall`
|
||||
- `-pre-fork` specifies number of container to create when server starts
|
||||
- `-tmp-fs-param` specifies the tmpfs parameter for `/w` and `/tmp` when using default mounting
|
||||
- `-tmp-fs-param` specifies the tmpfs parameter for `/w` and `/tmp` when using default mounting (Linux only)
|
||||
|
||||
### Environment Variables
|
||||
|
||||
@ -77,7 +81,7 @@ Download compiled executable from [Release](https://github.com/criyle/go-judge/r
|
||||
Or, by docker
|
||||
|
||||
```bash
|
||||
docker run -it --rm --privileged -p 5050:5050 criyle/executorserver:demo
|
||||
docker run -it --rm --privileged -p 5050:5050 criyle/executorserver
|
||||
```
|
||||
|
||||
#### Build Executor Server
|
||||
@ -188,8 +192,8 @@ wrk.body = '{"cmd":[{"args":["/bin/cat","a.hs"],"env":["PATH=/usr/bin:/bin"],"
|
||||
wrk.headers["Content-Type"] = "application/json;charset=UTF-8"
|
||||
```
|
||||
|
||||
- Single thread ~400-460 op/s Windows 10 WSL2
|
||||
- Multi thread ~1100-1200 op/s Windows 10 WSL2
|
||||
- Single thread ~800-860 op/s Windows 10 WSL2 @ 5800X
|
||||
- Multi thread ~1800-2000 op/s Windows 10 WSL2 @ 5800X
|
||||
|
||||
Single thread:
|
||||
|
||||
@ -197,16 +201,16 @@ Single thread:
|
||||
Running 30s test @ http://localhost:5050/run
|
||||
1 threads and 1 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 2.23ms 287.79us 14.29ms 86.51%
|
||||
Req/Sec 451.05 17.60 494.00 76.00%
|
||||
Latency 1.16ms 132.89us 6.20ms 90.15%
|
||||
Req/Sec 0.87k 19.33 0.91k 85.33%
|
||||
Latency Distribution
|
||||
50% 2.19ms
|
||||
75% 2.33ms
|
||||
90% 2.50ms
|
||||
99% 2.93ms
|
||||
13482 requests in 30.02s, 3.58MB read
|
||||
Requests/sec: 449.15
|
||||
Transfer/sec: 121.98KB
|
||||
50% 1.13ms
|
||||
75% 1.18ms
|
||||
90% 1.27ms
|
||||
99% 1.61ms
|
||||
25956 requests in 30.01s, 6.88MB read
|
||||
Requests/sec: 864.88
|
||||
Transfer/sec: 234.68KB
|
||||
```
|
||||
|
||||
### REST API Interface
|
||||
@ -290,6 +294,7 @@ interface Request {
|
||||
interface Result {
|
||||
status: Status;
|
||||
error?: string; // potential system error message
|
||||
exitStatus: number;
|
||||
time: number; // ns (cgroup recorded time)
|
||||
memory: number; // byte
|
||||
runTime: number; // ns (wall clock time)
|
||||
@ -488,6 +493,49 @@ Single (this example require `apt install g++` inside the container):
|
||||
]
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"cmd": [{
|
||||
"args": ["a"],
|
||||
"env": ["PATH=/usr/bin:/bin"],
|
||||
"files": [{
|
||||
"content": "1 1"
|
||||
}, {
|
||||
"name": "stdout",
|
||||
"max": 10240
|
||||
}, {
|
||||
"name": "stderr",
|
||||
"max": 10240
|
||||
}],
|
||||
"cpuLimit": 10000000000,
|
||||
"memoryLimit": 104857600,
|
||||
"procLimit": 50,
|
||||
"strictMemoryLimit": false,
|
||||
"copyIn": {
|
||||
"a": {
|
||||
"fileId": "5LWIZAA45JHX4Y4Z"
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"status": "Accepted",
|
||||
"exitStatus": 0,
|
||||
"time": 1173000,
|
||||
"memory": 10637312,
|
||||
"runTime": 1100200,
|
||||
"files": {
|
||||
"stderr": "",
|
||||
"stdout": "2\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Multiple (interaction problem):
|
||||
|
||||
```json
|
||||
|
17
go.mod
17
go.mod
@ -3,7 +3,7 @@ module github.com/criyle/go-judge
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.80.0 // indirect
|
||||
cloud.google.com/go v0.81.0 // indirect
|
||||
github.com/creack/pty v1.1.11
|
||||
github.com/criyle/go-sandbox v0.7.2
|
||||
github.com/elastic/go-seccomp-bpf v1.1.0
|
||||
@ -13,7 +13,7 @@ require (
|
||||
github.com/gin-contrib/pprof v1.3.0
|
||||
github.com/gin-contrib/zap v0.0.1
|
||||
github.com/gin-gonic/gin v1.6.3
|
||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.4.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
@ -30,14 +30,15 @@ require (
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.16.0
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||
golang.org/x/net v0.0.0-20210330230544-e57232859fb2
|
||||
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 // indirect
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57
|
||||
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210330181207-2295ebbda0c6 // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210405174219-a39eb2f71cb9 // indirect
|
||||
google.golang.org/grpc v1.36.1
|
||||
google.golang.org/grpc/examples v0.0.0-20210326170912-4a19753e9dfd // indirect
|
||||
google.golang.org/grpc/examples v0.0.0-20210405205600-8892a7b247c0 // indirect
|
||||
google.golang.org/protobuf v1.26.0
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
@ -46,7 +47,7 @@ require (
|
||||
|
||||
retract (
|
||||
// File descripter leak when multiple container fork at the same time
|
||||
[v0.9.5, v1.1.3]
|
||||
[v0.9.5, v1.1.4]
|
||||
// Old version, don't use
|
||||
[v0.0.1, v0.9.4]
|
||||
)
|
||||
|
36
go.sum
36
go.sum
@ -17,8 +17,8 @@ cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKP
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.80.0 h1:kAdyAMrj9CjqOSGiluseVjIgAyQ3uxADYtUYR6MwYeY=
|
||||
cloud.google.com/go v0.80.0/go.mod h1:fqpb6QRi1CFGAMXDoE72G+b+Ybv7dMB/T1tbExDHktI=
|
||||
cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
@ -147,8 +147,8 @@ github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEK
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-playground/validator/v10 v10.4.2 h1:RqFe5MzGf2UOFhxQYnjHabHOT6CLbYWkeXOfcXB7fsM=
|
||||
github.com/go-playground/validator/v10 v10.4.2/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
@ -549,8 +549,9 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210330230544-e57232859fb2 h1:nGCZOty+lVDsc4H2qPFksI5Se296+V+GhMiL/TzmYNk=
|
||||
golang.org/x/net v0.0.0-20210330230544-e57232859fb2/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -562,8 +563,8 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 h1:D7nTwh4J0i+5mW4Zjzn5omvlr6YBcWywE6KOcatyNxY=
|
||||
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc=
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -629,11 +630,11 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210314195730-07df6a141424/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 h1:EC6+IGYTjPpRfv9a2b/6Puw0W+hLtAhkV1tPsXhutqs=
|
||||
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@ -643,8 +644,9 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@ -730,7 +732,7 @@ google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.42.0/go.mod h1:+Oj4s6ch2SEGtPjGqfUfZonBH0GjQH89gTeKKAEGZKI=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@ -780,10 +782,10 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210312152112-fc591d9ea70f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210323160006-e668133fea6a/go.mod h1:f2Bd7+2PlaVKmvKQ52aspJZXIDaRQBVdOOBfJ5i8OEs=
|
||||
google.golang.org/genproto v0.0.0-20210330181207-2295ebbda0c6 h1:F+kz69Q2lpqBTskKa73+uBMwi7PBQRQEUBqHt0oc1K8=
|
||||
google.golang.org/genproto v0.0.0-20210330181207-2295ebbda0c6/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210405174219-a39eb2f71cb9 h1:Uga0hMhZnzB159cnlmhVPgEmZTwoZqwOb7e0pu+rlNI=
|
||||
google.golang.org/genproto v0.0.0-20210405174219-a39eb2f71cb9/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
@ -808,8 +810,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1 h1:cmUfbeGKnz9+2DD/UYsMQXeqbHZqZDs4eQwW0sFOpBY=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc/examples v0.0.0-20210326170912-4a19753e9dfd h1:UdBI3NZesDI4AuP02aKqpg73FiB0RO728xXQjlDfWk8=
|
||||
google.golang.org/grpc/examples v0.0.0-20210326170912-4a19753e9dfd/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE=
|
||||
google.golang.org/grpc/examples v0.0.0-20210405205600-8892a7b247c0 h1:T9WEcYHefnWQ9Uj+8SQ3tFS4RfH3u8ONRtRQM7BhDvo=
|
||||
google.golang.org/grpc/examples v0.0.0-20210405205600-8892a7b247c0/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
Loading…
Reference in New Issue
Block a user