Compare commits

...

8 Commits

Author SHA1 Message Date
8a4b8e69bc update k8s 2025-08-29 22:08:50 +08:00
d37d099619 补充prometheus 2025-08-24 16:55:43 +08:00
e3a6e821d9 补充架构图 2025-08-24 16:29:20 +08:00
bf80400b7a 处理包之间的引用 2025-08-24 15:58:47 +08:00
4a0ed93617 补充grpc认证 2025-08-24 14:51:13 +08:00
afa7743e82 update stream req 2025-08-24 14:34:22 +08:00
a6725de634 完善grpc 2025-08-24 14:15:16 +08:00
47b735fea1 补充 service 2025-08-24 14:14:55 +08:00
20 changed files with 1020 additions and 48 deletions

10
.vscode/settings.json vendored
View File

@ -4,5 +4,13 @@
"CONFIG_PATH": "${workspaceFolder}/application.yaml" "CONFIG_PATH": "${workspaceFolder}/application.yaml"
}, },
"go.testEnvFile": "${workspaceFolder}/etc/unit_test.env", "go.testEnvFile": "${workspaceFolder}/etc/unit_test.env",
"terminal.integrated.suggest.enabled": true "terminal.integrated.suggest.enabled": true,
"protoc": {
"path": "/usr/local/bin/protoc",
"compile_on_save": false,
"options": [
"--proto_path=.",
"--proto_path=/usr/local/include",
]
}
} }

View File

@ -28,8 +28,10 @@ func (c *consumer) Run(ctx context.Context) error {
// 发送的数据时Json格式, 接收用的JSON, 发送也需要使用JSON // 发送的数据时Json格式, 接收用的JSON, 发送也需要使用JSON
err = e.Load(m.Value) err = e.Load(m.Value)
if err == nil { if err == nil {
// 保存日志, 保持失败的次数统计起来,披露给外部, 编写一个采集器,来采统计失败次数
if err := event.GetService().SaveEvent(ctx, types.NewSet[*event.Event]().Add(e)); err != nil { if err := event.GetService().SaveEvent(ctx, types.NewSet[*event.Event]().Add(e)); err != nil {
c.log.Error().Msgf("save event error, %s", err) c.log.Error().Msgf("save event error, %s", err)
c.collector.Inc()
} }
} }

View File

@ -6,6 +6,7 @@ import (
"122.51.31.227/go-course/go18/devcloud/audit/apps/event" "122.51.31.227/go-course/go18/devcloud/audit/apps/event"
"github.com/infraboard/mcube/v2/ioc" "github.com/infraboard/mcube/v2/ioc"
"github.com/infraboard/mcube/v2/ioc/config/log" "github.com/infraboard/mcube/v2/ioc/config/log"
"github.com/prometheus/client_golang/prometheus"
"github.com/rs/zerolog" "github.com/rs/zerolog"
ioc_kafka "github.com/infraboard/mcube/v2/ioc/config/kafka" ioc_kafka "github.com/infraboard/mcube/v2/ioc/config/kafka"
@ -37,6 +38,9 @@ type consumer struct {
GroupId string `toml:"group_id" json:"group_id" yaml:"group_id" env:"GROUP_ID"` GroupId string `toml:"group_id" json:"group_id" yaml:"group_id" env:"GROUP_ID"`
// 当前这个消费者 配置的topic // 当前这个消费者 配置的topic
Topics []string `toml:"topic" json:"topic" yaml:"topic" env:"TOPIC"` Topics []string `toml:"topic" json:"topic" yaml:"topic" env:"TOPIC"`
// 采集器
collector *EventCollector
} }
// 对象名称 // 对象名称
@ -48,6 +52,11 @@ func (i *consumer) Name() string {
func (i *consumer) Init() error { func (i *consumer) Init() error {
// 对象 // 对象
i.log = log.Sub(i.Name()) i.log = log.Sub(i.Name())
// 准备好采集器, 注册给Prometheus
i.collector = NewEventCollector()
prometheus.MustRegister(i.collector)
i.reader = ioc_kafka.ConsumerGroup(i.GroupId, i.Topics) i.reader = ioc_kafka.ConsumerGroup(i.GroupId, i.Topics)
go i.Run(i.ctx) go i.Run(i.ctx)

View File

@ -0,0 +1,38 @@
package consumer
import "github.com/prometheus/client_golang/prometheus"
// # HELP save_event_error_count 事件入库失败个数统计
// # TYPE save_event_error_count gauge
// save_event_error_count{service="devcloud"} 0
func NewEventCollector() *EventCollector {
return &EventCollector{
errCountDesc: prometheus.NewDesc(
"save_event_error_count",
"事件入库失败个数统计",
[]string{},
prometheus.Labels{"service": "devcloud"},
),
}
}
// 收集事件指标的采集器
type EventCollector struct {
errCountDesc *prometheus.Desc
// 需要自己根据实践情况来维护这个变量
errCount int
}
func (c *EventCollector) Inc() {
c.errCount++
}
// 指标元数据注册
func (c *EventCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.errCountDesc
}
// 指标的值的采集
func (c *EventCollector) Collect(ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(c.errCountDesc, prometheus.GaugeValue, float64(c.errCount))
}

View File

@ -0,0 +1,16 @@
# k8s 集群管理
https://kubernetes.io/
k8s UI工具: Rancher, KubeSphere, Dashbaord k8s 集群管理工具(运维人员)
+ Deployment,StatfulSet, DaesonSet, Pod, Service, ...
我们要做的系统: 是做个 k8s的集群管理工具吗 给软件研发人员使用的(应用的发布与部署)
依赖于K8s提供的能力来实现一套发布系统
## 概念的定义

View File

@ -0,0 +1,87 @@
<mxfile host="65bd71144e">
<diagram id="2lwm4o9OGl1Gjpf4ACLQ" name="第 1 页">
<mxGraphModel dx="1115" dy="631" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="4" value="查询制品" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="5" target="9">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" value="artifact&lt;div&gt;制品库&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="260" y="100" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="6" style="edgeStyle=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="7" target="5">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="23" value="制品信息上报" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="6">
<mxGeometry x="-0.1818" y="-1" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="7" value="CI工具" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="30" y="100" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="8" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="9" target="13">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="9" value="deploy&lt;div&gt;应用部署&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="260" y="250" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="10" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="12" target="9">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="11" value="选择适合的制品格式&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="10">
<mxGeometry x="0.2278" y="-3" relative="1" as="geometry">
<mxPoint x="29" y="-17" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="12" value="cluster&lt;div&gt;应用集群&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="590" y="250" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="13" value="k8s 集群" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="260" y="430" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="14" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="18" target="13">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="15" value="watch" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="14">
<mxGeometry x="0.2242" y="-1" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="16" style="edgeStyle=none;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="18" target="12">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="17" value="实时更新集群状态" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="16">
<mxGeometry x="0.0236" y="-4" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="18" value="sync&lt;div&gt;部署同步&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="590" y="430" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="19" value="首次部署使用模版渲染" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="20" target="12">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="20" value="temlate&lt;div&gt;部署模版&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="590" y="100" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="21" style="edgeStyle=none;html=1;exitX=0;exitY=0.3333333333333333;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="22" target="12">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="22" value="Actor" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" vertex="1" parent="1">
<mxGeometry x="770" y="260" width="30" height="60" as="geometry"/>
</mxCell>
<mxCell id="24" value="应用部署 (应用发布流中最核心)" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="440" y="30" width="200" height="30" as="geometry"/>
</mxCell>
<mxCell id="25" value="承载应用运行的实例" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="485" y="330" width="110" height="30" as="geometry"/>
</mxCell>
<mxCell id="26" value="部署引擎" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="200" y="320" width="60" height="30" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

4
go.mod
View File

@ -19,6 +19,8 @@ require (
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/lighthouse v1.0.1134 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/lighthouse v1.0.1134
go.mongodb.org/mongo-driver v1.17.3 go.mongodb.org/mongo-driver v1.17.3
golang.org/x/crypto v0.38.0 golang.org/x/crypto v0.38.0
google.golang.org/grpc v1.72.0
google.golang.org/protobuf v1.36.6
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
gorm.io/datatypes v1.2.5 gorm.io/datatypes v1.2.5
gorm.io/driver/mysql v1.5.7 gorm.io/driver/mysql v1.5.7
@ -112,8 +114,6 @@ require (
golang.org/x/text v0.25.0 // indirect golang.org/x/text v0.25.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
google.golang.org/grpc v1.72.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gorm.io/driver/postgres v1.5.11 // indirect gorm.io/driver/postgres v1.5.11 // indirect
modernc.org/libc v1.22.5 // indirect modernc.org/libc v1.22.5 // indirect

3
skills/metric/README.md Normal file
View File

@ -0,0 +1,3 @@
# Metric
https://www.mcube.top/guide/config/metric.html

View File

@ -1,59 +1,108 @@
<mxfile host="65bd71144e"> <mxfile host="65bd71144e">
<diagram id="yflk6MuM42xHFq2jSyhu" name="第 1 页"> <diagram id="yflk6MuM42xHFq2jSyhu" name="第 1 页">
<mxGraphModel dx="1333" dy="710" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0"> <mxGraphModel dx="1115" dy="597" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root> <root>
<mxCell id="0"/> <mxCell id="0"/>
<mxCell id="1" parent="0"/> <mxCell id="1" parent="0"/>
<mxCell id="2" value="server a&lt;div&gt;fn&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="2" value="server a&lt;div&gt;fn&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="170" y="170" width="120" height="60" as="geometry"/> <mxGeometry x="170" y="170" width="120" height="60" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="6" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="5" target="2"> <mxCell id="6" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="5" target="2" edge="1">
<mxGeometry relative="1" as="geometry"/> <mxGeometry relative="1" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="7" value="servier a: fn" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="6"> <mxCell id="7" value="servier a: fn" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="6" vertex="1" connectable="0">
<mxGeometry x="0.1941" y="-1" relative="1" as="geometry"> <mxGeometry x="0.1941" y="-1" relative="1" as="geometry">
<mxPoint as="offset"/> <mxPoint as="offset"/>
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="5" value="server b" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="5" value="server b" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="630" y="170" width="120" height="60" as="geometry"/> <mxGeometry x="630" y="170" width="120" height="60" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="8" value="rpc" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1"> <mxCell id="8" value="rpc" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="400" y="100" width="60" height="30" as="geometry"/> <mxGeometry x="400" y="100" width="60" height="30" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="9" value="fn: (my_name) {hello, my_name}" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1"> <mxCell id="9" value="fn: (my_name) {hello, my_name}" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="350" y="250" width="230" height="30" as="geometry"/> <mxGeometry x="350" y="250" width="230" height="30" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="10" value="service c&lt;div&gt;python&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="10" value="service c&lt;div&gt;python&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="630" y="390" width="120" height="60" as="geometry"/> <mxGeometry x="630" y="390" width="120" height="60" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="11" value="service d&lt;div&gt;java&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="11" value="service d&lt;div&gt;java&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="390" y="390" width="120" height="60" as="geometry"/> <mxGeometry x="390" y="390" width="120" height="60" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="12" value="server b&lt;div&gt;go&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="12" value="server b&lt;div&gt;go&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="150" y="390" width="120" height="60" as="geometry"/> <mxGeometry x="150" y="390" width="120" height="60" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="19" style="edgeStyle=none;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="13" target="16"> <mxCell id="19" style="edgeStyle=none;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" parent="1" source="13" target="16" edge="1">
<mxGeometry relative="1" as="geometry"/> <mxGeometry relative="1" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="20" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="13" target="17"> <mxCell id="20" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="13" target="17" edge="1">
<mxGeometry relative="1" as="geometry"/> <mxGeometry relative="1" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="13" value="protofuf" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="13" value="protofuf" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="390" y="570" width="120" height="60" as="geometry"/> <mxGeometry x="390" y="570" width="120" height="60" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="15" value="interface(struct)" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1"> <mxCell id="15" value="interface(struct)" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="160" y="480" width="100" height="30" as="geometry"/> <mxGeometry x="160" y="480" width="100" height="30" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="16" value="interface(class)" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1"> <mxCell id="16" value="interface(class)" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="400" y="480" width="100" height="30" as="geometry"/> <mxGeometry x="400" y="480" width="100" height="30" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="17" value="class" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1"> <mxCell id="17" value="class" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="670" y="480" width="60" height="30" as="geometry"/> <mxGeometry x="670" y="480" width="60" height="30" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="18" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.43;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="13" target="15"> <mxCell id="18" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.43;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="13" target="15" edge="1">
<mxGeometry relative="1" as="geometry"/> <mxGeometry relative="1" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="30" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="21" target="22">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="31" value="GRPC" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="30">
<mxGeometry x="-0.2941" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="32" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="21" target="25">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="21" value="业务网关(Restful API grpc-rest)" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="190" y="920" width="530" height="60" as="geometry"/>
</mxCell>
<mxCell id="22" value="grpc service&lt;div&gt;app1&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="190" y="1050" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="23" value="&lt;span style=&quot;color: rgb(0, 0, 0);&quot;&gt;grpc service&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(0, 0, 0);&quot;&gt;app2&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="340" y="1050" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="24" value="&lt;span style=&quot;color: rgb(0, 0, 0);&quot;&gt;grpc service&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(0, 0, 0);&quot;&gt;app3&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="480" y="1050" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="25" value="&lt;span style=&quot;color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;&quot;&gt;grpc service&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;&quot;&gt;app3&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="630" y="1050" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="28" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="27" target="21">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="29" value="Restful API" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="28">
<mxGeometry x="-0.15" y="-4" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="27" value="Browser" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="190" y="800" width="530" height="60" as="geometry"/>
</mxCell>
<mxCell id="33" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.425;entryY=1.033;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="23" target="22">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="330" y="1150"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="34" value="GRPC" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="33">
<mxGeometry x="-0.2337" y="-2" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
</root> </root>
</mxGraphModel> </mxGraphModel>
</diagram> </diagram>

View File

@ -10,6 +10,8 @@ type HelloService interface {
+ RPC方法的定义 + RPC方法的定义
+ PRC数据结构的定义 + PRC数据结构的定义
安装vsocde的 proto语法高亮插件: vscode-proto3
## 插件安装 ## 插件安装
```sh ```sh
@ -105,7 +107,6 @@ service HelloService {
protoc -I=. --go-grpc_out=. --go-grpc_opt=module="122.51.31.227/go-course/go18" skills/rpc/protobuf/hello_service/interface.proto protoc -I=. --go-grpc_out=. --go-grpc_opt=module="122.51.31.227/go-course/go18" skills/rpc/protobuf/hello_service/interface.proto
``` ```
interface_grpc.pb.go interface_grpc.pb.go
```go ```go
// 服务端接口 // 服务端接口
@ -155,4 +156,11 @@ func (c *helloServiceClient) Hello(ctx context.Context, in *Request, opts ...grp
## 其他服务 使用生成的client调用grpc服务的方法 ## 其他服务 使用生成的client调用grpc服务的方法
## 包
```sh
protoc -I=. --go_out=. --go_opt=module="122.51.31.227/go-course/go18" skills/rpc/protobuf/app_service/interface.proto
```

View File

@ -0,0 +1,165 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.8
// protoc v6.32.0
// source: skills/rpc/protobuf/app_service/interface.proto
package app_service
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
hello_service "122.51.31.227/go-course/go18/skills/rpc/protobuf/hello_service"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type App struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
// 通过包名引用其他包里面定义
Request *hello_service.Request `protobuf:"bytes,4,opt,name=request,proto3" json:"request,omitempty"`
Response *hello_service.Response `protobuf:"bytes,5,opt,name=response,proto3" json:"response,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *App) Reset() {
*x = App{}
mi := &file_skills_rpc_protobuf_app_service_interface_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *App) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*App) ProtoMessage() {}
func (x *App) ProtoReflect() protoreflect.Message {
mi := &file_skills_rpc_protobuf_app_service_interface_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use App.ProtoReflect.Descriptor instead.
func (*App) Descriptor() ([]byte, []int) {
return file_skills_rpc_protobuf_app_service_interface_proto_rawDescGZIP(), []int{0}
}
func (x *App) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *App) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *App) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *App) GetRequest() *hello_service.Request {
if x != nil {
return x.Request
}
return nil
}
func (x *App) GetResponse() *hello_service.Response {
if x != nil {
return x.Response
}
return nil
}
var File_skills_rpc_protobuf_app_service_interface_proto protoreflect.FileDescriptor
const file_skills_rpc_protobuf_app_service_interface_proto_rawDesc = "" +
"\n" +
"/skills/rpc/protobuf/app_service/interface.proto\x12\x03app\x1a1skills/rpc/protobuf/hello_service/interface.proto\"\xa2\x01\n" +
"\x03App\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12 \n" +
"\vdescription\x18\x03 \x01(\tR\vdescription\x12(\n" +
"\arequest\x18\x04 \x01(\v2\x0e.hello.RequestR\arequest\x12+\n" +
"\bresponse\x18\x05 \x01(\v2\x0f.hello.ResponseR\bresponseB>Z<122.51.31.227/go-course/go18/skills/rpc/protobuf/app_serviceb\x06proto3"
var (
file_skills_rpc_protobuf_app_service_interface_proto_rawDescOnce sync.Once
file_skills_rpc_protobuf_app_service_interface_proto_rawDescData []byte
)
func file_skills_rpc_protobuf_app_service_interface_proto_rawDescGZIP() []byte {
file_skills_rpc_protobuf_app_service_interface_proto_rawDescOnce.Do(func() {
file_skills_rpc_protobuf_app_service_interface_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_skills_rpc_protobuf_app_service_interface_proto_rawDesc), len(file_skills_rpc_protobuf_app_service_interface_proto_rawDesc)))
})
return file_skills_rpc_protobuf_app_service_interface_proto_rawDescData
}
var file_skills_rpc_protobuf_app_service_interface_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_skills_rpc_protobuf_app_service_interface_proto_goTypes = []any{
(*App)(nil), // 0: app.App
(*hello_service.Request)(nil), // 1: hello.Request
(*hello_service.Response)(nil), // 2: hello.Response
}
var file_skills_rpc_protobuf_app_service_interface_proto_depIdxs = []int32{
1, // 0: app.App.request:type_name -> hello.Request
2, // 1: app.App.response:type_name -> hello.Response
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_skills_rpc_protobuf_app_service_interface_proto_init() }
func file_skills_rpc_protobuf_app_service_interface_proto_init() {
if File_skills_rpc_protobuf_app_service_interface_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_skills_rpc_protobuf_app_service_interface_proto_rawDesc), len(file_skills_rpc_protobuf_app_service_interface_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_skills_rpc_protobuf_app_service_interface_proto_goTypes,
DependencyIndexes: file_skills_rpc_protobuf_app_service_interface_proto_depIdxs,
MessageInfos: file_skills_rpc_protobuf_app_service_interface_proto_msgTypes,
}.Build()
File_skills_rpc_protobuf_app_service_interface_proto = out.File
file_skills_rpc_protobuf_app_service_interface_proto_goTypes = nil
file_skills_rpc_protobuf_app_service_interface_proto_depIdxs = nil
}

View File

@ -0,0 +1,16 @@
syntax = "proto3";
package app;
option go_package="122.51.31.227/go-course/go18/skills/rpc/protobuf/app_service";
//
import "skills/rpc/protobuf/hello_service/interface.proto";
message App {
string id = 1;
string name = 2;
string description = 3;
//
hello.Request request = 4;
hello.Response response = 5;
}

View File

@ -0,0 +1,56 @@
package hello_service
import context "context"
// PerRPCCredentials defines the common interface for the credentials which need to
// attach security information to every RPC (e.g., oauth2).
// type PerRPCCredentials interface {
// // GetRequestMetadata gets the current request metadata, refreshing tokens
// // if required. This should be called by the transport layer on each
// // request, and the data should be populated in headers or other
// // context. If a status code is returned, it will be used as the status for
// // the RPC (restricted to an allowable set of codes as defined by gRFC
// // A54). uri is the URI of the entry point for the request. When supported
// // by the underlying implementation, ctx can be used for timeout and
// // cancellation. Additionally, RequestInfo data will be available via ctx
// // to this call. TODO(zhaoq): Define the set of the qualified keys instead
// // of leaving it as an arbitrary string.
// GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
// // RequireTransportSecurity indicates whether the credentials requires
// // transport security.
// RequireTransportSecurity() bool
// }
func NewClientAuthentication(clientId, clientSecret string) *Authentication {
return &Authentication{
clientID: clientId,
clientSecret: clientSecret,
}
}
// Authentication todo
type Authentication struct {
clientID string
clientSecret string
}
// WithClientCredentials todo
func (a *Authentication) WithClientCredentials(clientID, clientSecret string) {
a.clientID = clientID
a.clientSecret = clientSecret
}
// GetRequestMetadata todo
func (a *Authentication) GetRequestMetadata(context.Context, ...string) (
map[string]string, error,
) {
return map[string]string{
ClientHeaderKey: a.clientID,
ClientSecretKey: a.clientSecret,
}, nil
}
// RequireTransportSecurity todo
func (a *Authentication) RequireTransportSecurity() bool {
return false
}

View File

@ -9,6 +9,7 @@ package hello_service
import ( import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
anypb "google.golang.org/protobuf/types/known/anypb"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe" unsafe "unsafe"
@ -21,9 +22,72 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
type Corpus int32
const (
Corpus_UNIVERSAL Corpus = 0
Corpus_WEB Corpus = 1
Corpus_IMAGES Corpus = 2
Corpus_LOCAL Corpus = 3
Corpus_NEWS Corpus = 4
Corpus_PRODUCTS Corpus = 5
Corpus_VIDEO Corpus = 6
)
// Enum value maps for Corpus.
var (
Corpus_name = map[int32]string{
0: "UNIVERSAL",
1: "WEB",
2: "IMAGES",
3: "LOCAL",
4: "NEWS",
5: "PRODUCTS",
6: "VIDEO",
}
Corpus_value = map[string]int32{
"UNIVERSAL": 0,
"WEB": 1,
"IMAGES": 2,
"LOCAL": 3,
"NEWS": 4,
"PRODUCTS": 5,
"VIDEO": 6,
}
)
func (x Corpus) Enum() *Corpus {
p := new(Corpus)
*p = x
return p
}
func (x Corpus) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Corpus) Descriptor() protoreflect.EnumDescriptor {
return file_skills_rpc_protobuf_hello_service_interface_proto_enumTypes[0].Descriptor()
}
func (Corpus) Type() protoreflect.EnumType {
return &file_skills_rpc_protobuf_hello_service_interface_proto_enumTypes[0]
}
func (x Corpus) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Corpus.Descriptor instead.
func (Corpus) EnumDescriptor() ([]byte, []int) {
return file_skills_rpc_protobuf_hello_service_interface_proto_rawDescGZIP(), []int{0}
}
type Request struct { type Request struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
// map
Extras map[string]string `protobuf:"bytes,2,rep,name=extras,proto3" json:"extras,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -65,6 +129,66 @@ func (x *Request) GetValue() string {
return "" return ""
} }
func (x *Request) GetExtras() map[string]string {
if x != nil {
return x.Extras
}
return nil
}
type RequestSet struct {
state protoimpl.MessageState `protogen:"open.v1"`
Total int64 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"`
// repeated 来描述一个数组
Items []*Request `protobuf:"bytes,2,rep,name=items,proto3" json:"items,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RequestSet) Reset() {
*x = RequestSet{}
mi := &file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RequestSet) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RequestSet) ProtoMessage() {}
func (x *RequestSet) ProtoReflect() protoreflect.Message {
mi := &file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RequestSet.ProtoReflect.Descriptor instead.
func (*RequestSet) Descriptor() ([]byte, []int) {
return file_skills_rpc_protobuf_hello_service_interface_proto_rawDescGZIP(), []int{1}
}
func (x *RequestSet) GetTotal() int64 {
if x != nil {
return x.Total
}
return 0
}
func (x *RequestSet) GetItems() []*Request {
if x != nil {
return x.Items
}
return nil
}
type Response struct { type Response struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
@ -74,7 +198,7 @@ type Response struct {
func (x *Response) Reset() { func (x *Response) Reset() {
*x = Response{} *x = Response{}
mi := &file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes[1] mi := &file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -86,7 +210,7 @@ func (x *Response) String() string {
func (*Response) ProtoMessage() {} func (*Response) ProtoMessage() {}
func (x *Response) ProtoReflect() protoreflect.Message { func (x *Response) ProtoReflect() protoreflect.Message {
mi := &file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes[1] mi := &file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes[2]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -99,7 +223,7 @@ func (x *Response) ProtoReflect() protoreflect.Message {
// Deprecated: Use Response.ProtoReflect.Descriptor instead. // Deprecated: Use Response.ProtoReflect.Descriptor instead.
func (*Response) Descriptor() ([]byte, []int) { func (*Response) Descriptor() ([]byte, []int) {
return file_skills_rpc_protobuf_hello_service_interface_proto_rawDescGZIP(), []int{1} return file_skills_rpc_protobuf_hello_service_interface_proto_rawDescGZIP(), []int{2}
} }
func (x *Response) GetValue() string { func (x *Response) GetValue() string {
@ -109,17 +233,90 @@ func (x *Response) GetValue() string {
return "" return ""
} }
type ErrorStatus struct {
state protoimpl.MessageState `protogen:"open.v1"`
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
Details []*anypb.Any `protobuf:"bytes,2,rep,name=details,proto3" json:"details,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ErrorStatus) Reset() {
*x = ErrorStatus{}
mi := &file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ErrorStatus) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ErrorStatus) ProtoMessage() {}
func (x *ErrorStatus) ProtoReflect() protoreflect.Message {
mi := &file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ErrorStatus.ProtoReflect.Descriptor instead.
func (*ErrorStatus) Descriptor() ([]byte, []int) {
return file_skills_rpc_protobuf_hello_service_interface_proto_rawDescGZIP(), []int{3}
}
func (x *ErrorStatus) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *ErrorStatus) GetDetails() []*anypb.Any {
if x != nil {
return x.Details
}
return nil
}
var File_skills_rpc_protobuf_hello_service_interface_proto protoreflect.FileDescriptor var File_skills_rpc_protobuf_hello_service_interface_proto protoreflect.FileDescriptor
const file_skills_rpc_protobuf_hello_service_interface_proto_rawDesc = "" + const file_skills_rpc_protobuf_hello_service_interface_proto_rawDesc = "" +
"\n" + "\n" +
"1skills/rpc/protobuf/hello_service/interface.proto\x12\x05hello\"\x1f\n" + "1skills/rpc/protobuf/hello_service/interface.proto\x12\x05hello\x1a\x19google/protobuf/any.proto\"\x8e\x01\n" +
"\aRequest\x12\x14\n" + "\aRequest\x12\x14\n" +
"\x05value\x18\x01 \x01(\tR\x05value\" \n" + "\x05value\x18\x01 \x01(\tR\x05value\x122\n" +
"\x06extras\x18\x02 \x03(\v2\x1a.hello.Request.ExtrasEntryR\x06extras\x1a9\n" +
"\vExtrasEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"H\n" +
"\n" +
"RequestSet\x12\x14\n" +
"\x05total\x18\x01 \x01(\x03R\x05total\x12$\n" +
"\x05items\x18\x02 \x03(\v2\x0e.hello.RequestR\x05items\" \n" +
"\bResponse\x12\x14\n" + "\bResponse\x12\x14\n" +
"\x05value\x18\x01 \x01(\tR\x05value28\n" + "\x05value\x18\x01 \x01(\tR\x05value\"W\n" +
"\vErrorStatus\x12\x18\n" +
"\amessage\x18\x01 \x01(\tR\amessage\x12.\n" +
"\adetails\x18\x02 \x03(\v2\x14.google.protobuf.AnyR\adetails*Z\n" +
"\x06Corpus\x12\r\n" +
"\tUNIVERSAL\x10\x00\x12\a\n" +
"\x03WEB\x10\x01\x12\n" +
"\n" +
"\x06IMAGES\x10\x02\x12\t\n" +
"\x05LOCAL\x10\x03\x12\b\n" +
"\x04NEWS\x10\x04\x12\f\n" +
"\bPRODUCTS\x10\x05\x12\t\n" +
"\x05VIDEO\x10\x062j\n" +
"\fHelloService\x12(\n" + "\fHelloService\x12(\n" +
"\x05Hello\x12\x0e.hello.Request\x1a\x0f.hello.ResponseB@Z>122.51.31.227/go-course/go18/skills/rpc/protobuf/hello_serviceb\x06proto3" "\x05Hello\x12\x0e.hello.Request\x1a\x0f.hello.Response\x120\n" +
"\aChannel\x12\x0e.hello.Request\x1a\x0f.hello.Response\"\x00(\x010\x01B@Z>122.51.31.227/go-course/go18/skills/rpc/protobuf/hello_serviceb\x06proto3"
var ( var (
file_skills_rpc_protobuf_hello_service_interface_proto_rawDescOnce sync.Once file_skills_rpc_protobuf_hello_service_interface_proto_rawDescOnce sync.Once
@ -133,19 +330,30 @@ func file_skills_rpc_protobuf_hello_service_interface_proto_rawDescGZIP() []byte
return file_skills_rpc_protobuf_hello_service_interface_proto_rawDescData return file_skills_rpc_protobuf_hello_service_interface_proto_rawDescData
} }
var file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_skills_rpc_protobuf_hello_service_interface_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_skills_rpc_protobuf_hello_service_interface_proto_goTypes = []any{ var file_skills_rpc_protobuf_hello_service_interface_proto_goTypes = []any{
(*Request)(nil), // 0: hello.Request (Corpus)(0), // 0: hello.Corpus
(*Response)(nil), // 1: hello.Response (*Request)(nil), // 1: hello.Request
(*RequestSet)(nil), // 2: hello.RequestSet
(*Response)(nil), // 3: hello.Response
(*ErrorStatus)(nil), // 4: hello.ErrorStatus
nil, // 5: hello.Request.ExtrasEntry
(*anypb.Any)(nil), // 6: google.protobuf.Any
} }
var file_skills_rpc_protobuf_hello_service_interface_proto_depIdxs = []int32{ var file_skills_rpc_protobuf_hello_service_interface_proto_depIdxs = []int32{
0, // 0: hello.HelloService.Hello:input_type -> hello.Request 5, // 0: hello.Request.extras:type_name -> hello.Request.ExtrasEntry
1, // 1: hello.HelloService.Hello:output_type -> hello.Response 1, // 1: hello.RequestSet.items:type_name -> hello.Request
1, // [1:2] is the sub-list for method output_type 6, // 2: hello.ErrorStatus.details:type_name -> google.protobuf.Any
0, // [0:1] is the sub-list for method input_type 1, // 3: hello.HelloService.Hello:input_type -> hello.Request
0, // [0:0] is the sub-list for extension type_name 1, // 4: hello.HelloService.Channel:input_type -> hello.Request
0, // [0:0] is the sub-list for extension extendee 3, // 5: hello.HelloService.Hello:output_type -> hello.Response
0, // [0:0] is the sub-list for field type_name 3, // 6: hello.HelloService.Channel:output_type -> hello.Response
5, // [5:7] is the sub-list for method output_type
3, // [3:5] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
} }
func init() { file_skills_rpc_protobuf_hello_service_interface_proto_init() } func init() { file_skills_rpc_protobuf_hello_service_interface_proto_init() }
@ -158,13 +366,14 @@ func file_skills_rpc_protobuf_hello_service_interface_proto_init() {
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_skills_rpc_protobuf_hello_service_interface_proto_rawDesc), len(file_skills_rpc_protobuf_hello_service_interface_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_skills_rpc_protobuf_hello_service_interface_proto_rawDesc), len(file_skills_rpc_protobuf_hello_service_interface_proto_rawDesc)),
NumEnums: 0, NumEnums: 1,
NumMessages: 2, NumMessages: 5,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },
GoTypes: file_skills_rpc_protobuf_hello_service_interface_proto_goTypes, GoTypes: file_skills_rpc_protobuf_hello_service_interface_proto_goTypes,
DependencyIndexes: file_skills_rpc_protobuf_hello_service_interface_proto_depIdxs, DependencyIndexes: file_skills_rpc_protobuf_hello_service_interface_proto_depIdxs,
EnumInfos: file_skills_rpc_protobuf_hello_service_interface_proto_enumTypes,
MessageInfos: file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes, MessageInfos: file_skills_rpc_protobuf_hello_service_interface_proto_msgTypes,
}.Build() }.Build()
File_skills_rpc_protobuf_hello_service_interface_proto = out.File File_skills_rpc_protobuf_hello_service_interface_proto = out.File

View File

@ -3,16 +3,44 @@ syntax = "proto3";
package hello; package hello;
option go_package="122.51.31.227/go-course/go18/skills/rpc/protobuf/hello_service"; option go_package="122.51.31.227/go-course/go18/skills/rpc/protobuf/hello_service";
// proto文件, ipmort用法
import "google/protobuf/any.proto";
// The HelloService service definition. // The HelloService service definition.
service HelloService { service HelloService {
// rpc // rpc
rpc Hello (Request) returns (Response); rpc Hello (Request) returns (Response);
rpc Channel (stream Request) returns (stream Response) {}
} }
message Request { message Request {
string value = 1; string value = 1;
// map
map<string, string> extras = 2;
}
message RequestSet {
int64 total = 1;
// repeated
repeated Request items = 2;
} }
message Response { message Response {
string value = 1; string value = 1;
}
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
} }

View File

@ -8,7 +8,6 @@ package hello_service
import ( import (
context "context" context "context"
grpc "google.golang.org/grpc" grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes" codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status" status "google.golang.org/grpc/status"
@ -20,7 +19,8 @@ import (
const _ = grpc.SupportPackageIsVersion9 const _ = grpc.SupportPackageIsVersion9
const ( const (
HelloService_Hello_FullMethodName = "/hello.HelloService/Hello" HelloService_Hello_FullMethodName = "/hello.HelloService/Hello"
HelloService_Channel_FullMethodName = "/hello.HelloService/Channel"
) )
// HelloServiceClient is the client API for HelloService service. // HelloServiceClient is the client API for HelloService service.
@ -31,6 +31,7 @@ const (
type HelloServiceClient interface { type HelloServiceClient interface {
// rpc 声明接口 // rpc 声明接口
Hello(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) Hello(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
Channel(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Request, Response], error)
} }
type helloServiceClient struct { type helloServiceClient struct {
@ -51,6 +52,19 @@ func (c *helloServiceClient) Hello(ctx context.Context, in *Request, opts ...grp
return out, nil return out, nil
} }
func (c *helloServiceClient) Channel(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Request, Response], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &HelloService_ServiceDesc.Streams[0], HelloService_Channel_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[Request, Response]{ClientStream: stream}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type HelloService_ChannelClient = grpc.BidiStreamingClient[Request, Response]
// HelloServiceServer is the server API for HelloService service. // HelloServiceServer is the server API for HelloService service.
// All implementations must embed UnimplementedHelloServiceServer // All implementations must embed UnimplementedHelloServiceServer
// for forward compatibility. // for forward compatibility.
@ -59,6 +73,7 @@ func (c *helloServiceClient) Hello(ctx context.Context, in *Request, opts ...grp
type HelloServiceServer interface { type HelloServiceServer interface {
// rpc 声明接口 // rpc 声明接口
Hello(context.Context, *Request) (*Response, error) Hello(context.Context, *Request) (*Response, error)
Channel(grpc.BidiStreamingServer[Request, Response]) error
mustEmbedUnimplementedHelloServiceServer() mustEmbedUnimplementedHelloServiceServer()
} }
@ -72,6 +87,9 @@ type UnimplementedHelloServiceServer struct{}
func (UnimplementedHelloServiceServer) Hello(context.Context, *Request) (*Response, error) { func (UnimplementedHelloServiceServer) Hello(context.Context, *Request) (*Response, error) {
return nil, status.Errorf(codes.Unimplemented, "method Hello not implemented") return nil, status.Errorf(codes.Unimplemented, "method Hello not implemented")
} }
func (UnimplementedHelloServiceServer) Channel(grpc.BidiStreamingServer[Request, Response]) error {
return status.Errorf(codes.Unimplemented, "method Channel not implemented")
}
func (UnimplementedHelloServiceServer) mustEmbedUnimplementedHelloServiceServer() {} func (UnimplementedHelloServiceServer) mustEmbedUnimplementedHelloServiceServer() {}
func (UnimplementedHelloServiceServer) testEmbeddedByValue() {} func (UnimplementedHelloServiceServer) testEmbeddedByValue() {}
@ -111,6 +129,13 @@ func _HelloService_Hello_Handler(srv interface{}, ctx context.Context, dec func(
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _HelloService_Channel_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(HelloServiceServer).Channel(&grpc.GenericServerStream[Request, Response]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type HelloService_ChannelServer = grpc.BidiStreamingServer[Request, Response]
// HelloService_ServiceDesc is the grpc.ServiceDesc for HelloService service. // HelloService_ServiceDesc is the grpc.ServiceDesc for HelloService service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@ -123,6 +148,13 @@ var HelloService_ServiceDesc = grpc.ServiceDesc{
Handler: _HelloService_Hello_Handler, Handler: _HelloService_Hello_Handler,
}, },
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{
{
StreamName: "Channel",
Handler: _HelloService_Channel_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "skills/rpc/protobuf/hello_service/interface.proto", Metadata: "skills/rpc/protobuf/hello_service/interface.proto",
} }

View File

@ -0,0 +1,35 @@
package hello_service_test
import (
"testing"
"122.51.31.227/go-course/go18/skills/rpc/protobuf/hello_service"
"google.golang.org/protobuf/types/known/anypb"
)
func TestAny(t *testing.T) {
status := hello_service.ErrorStatus{
Details: []*anypb.Any{},
}
d1 := hello_service.Request{Value: "test"}
any1, err := anypb.New(&d1)
if err != nil {
t.Fatal(err)
}
d2 := hello_service.Response{Value: "test"}
any2, err := anypb.New(&d2)
if err != nil {
t.Fatal(err)
}
status.Details = append(status.Details, any1, any2)
t.Log(status)
target := hello_service.Request{}
if err := status.Details[0].UnmarshalTo(&target); err != nil {
t.Fatal(err)
}
t.Log(target.Value)
}

View File

@ -0,0 +1,90 @@
package hello_service
import (
context "context"
"fmt"
"github.com/infraboard/mcube/v2/ioc/config/log"
"github.com/rs/zerolog"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
status "google.golang.org/grpc/status"
)
const (
ClientHeaderKey = "client-id"
ClientSecretKey = "client-secret"
)
// GrpcAuthUnaryServerInterceptor returns a new unary server interceptor for auth.
func GrpcAuthUnaryServerInterceptor() grpc.UnaryServerInterceptor {
return newGrpcAuther().Auth
}
func newGrpcAuther() *grpcAuther {
return &grpcAuther{
log: log.Sub("Grpc Auther"),
}
}
// UnaryServerInterceptor provides a hook to intercept the execution of a unary RPC on the server. info
// contains all the information of this RPC the interceptor can operate on. And handler is the wrapper
// of the service method implementation. It is the responsibility of the interceptor to invoke handler
// to complete the RPC.
// type UnaryServerInterceptor func(ctx context.Context, req any, info *UnaryServerInfo, handler UnaryHandler) (resp any, err error)
// internal todo
type grpcAuther struct {
log *zerolog.Logger
}
func (a *grpcAuther) Auth(
ctx context.Context, req any,
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (resp any, err error) {
// http2 header -> metadata
// 重上下文中获取认证信息
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, fmt.Errorf("ctx is not an grpc incoming context")
}
fmt.Println("gprc header info: ", md)
clientId, clientSecret := a.GetClientCredentialsFromMeta(md)
// 校验调用的客户端凭证是否有效
if err := a.validateServiceCredential(clientId, clientSecret); err != nil {
return nil, err
}
// 把请求交给后续的Handler处理
resp, err = handler(ctx, req)
return resp, err
}
func (a *grpcAuther) GetClientCredentialsFromMeta(md metadata.MD) (
clientId, clientSecret string) {
cids := md.Get(ClientHeaderKey)
sids := md.Get(ClientSecretKey)
if len(cids) > 0 {
clientId = cids[0]
}
if len(sids) > 0 {
clientSecret = sids[0]
}
return
}
func (a *grpcAuther) validateServiceCredential(clientId, clientSecret string) error {
if clientId == "" && clientSecret == "" {
return status.Errorf(codes.Unauthenticated, "client_id or client_secret is \"\"")
}
if !(clientId == "admin" && clientSecret == "123456") {
return status.Errorf(codes.Unauthenticated, "client_id or client_secret invalidate")
}
return nil
}

View File

@ -1,3 +1,63 @@
package main package main
func main() {} import (
"context"
"io"
"log"
"net"
"122.51.31.227/go-course/go18/skills/rpc/protobuf/hello_service"
"google.golang.org/grpc"
)
func main() {
// 首先是通过grpc.NewServer()构造一个gRPC服务对象
// 补充了全局的认证中间件
grpcServer := grpc.NewServer(grpc.ChainUnaryInterceptor(hello_service.GrpcAuthUnaryServerInterceptor()))
// SDK 提供 服务实现对象的注册
hello_service.RegisterHelloServiceServer(grpcServer, &HelloService{})
lis, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal(err)
}
// 然后通过grpcServer.Serve(lis)在一个监听端口上提供gRPC服务
grpcServer.Serve(lis)
}
var _ hello_service.HelloServiceServer = (*HelloService)(nil)
// 实现一个GRPC的对象, 并行实现了 HelloServiceServer 接口
// 该对象就可以注册给GRPC框架
type HelloService struct {
hello_service.UnimplementedHelloServiceServer
}
func (h *HelloService) Hello(ctx context.Context, req *hello_service.Request) (*hello_service.Response, error) {
return &hello_service.Response{
Value: "Hello " + req.Value,
}, nil
}
// 接收来自客户端的流式请求,然后不断返回
func (h *HelloService) Channel(stream grpc.BidiStreamingServer[hello_service.Request, hello_service.Response]) error {
for {
// 读取客户端发送过来的数据
msg, err := stream.Recv()
if err != nil {
// 如果遇到io.EOF表示客户端流被关闭
if err == io.EOF {
return nil
}
return err
}
// 处理消息并返回响应
if err := stream.Send(&hello_service.Response{
Value: "Hello " + msg.Value,
}); err != nil {
return err
}
}
}

View File

@ -1,5 +1,66 @@
package main package main
func main() { import (
"context"
"fmt"
"io"
"log"
"time"
"122.51.31.227/go-course/go18/skills/rpc/protobuf/hello_service"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
// grpc.Dial负责和gRPC服务建立链接
conn, err := grpc.NewClient("localhost:1234",
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithPerRPCCredentials(hello_service.NewClientAuthentication(
"admin",
"123456",
)))
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 使用SDK调用远程函数
helloServiceClient := hello_service.NewHelloServiceClient(conn)
resp, err := helloServiceClient.Hello(context.Background(), &hello_service.Request{
Value: "bob",
})
if err != nil {
panic(err)
}
fmt.Println(resp.Value)
stream, err := helloServiceClient.Channel(context.Background())
if err != nil {
panic(err)
}
// 首先是向服务端发送数据
go func() {
count := 1
for {
if err := stream.Send(&hello_service.Request{Value: fmt.Sprintf("[%d] hi", count)}); err != nil {
log.Fatal(err)
}
count++
time.Sleep(time.Second)
}
}()
// 然后在循环中接收服务端返回的数据
for {
reply, err := stream.Recv()
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
fmt.Println(reply.GetValue())
}
} }