96SEO 2026-02-19 21:25 0
grpc进阶之protobuf参考文档定义一个消息类型指定字段类型分配标识号指定字段规则添加更多消息类型添加注释

PATH$PATH:$GOROOT/bin:$GPPATH/bin编辑保存并退出vim后记得把这些环境载⼊source
GOPROXYhttps://goproxy.io,direct
代码规范不是强制的也就是你不遵循代码规范写出来的代码运⾏也是完全没有问题的代码规范⽬的是⽅便团队形成⼀个统⼀的代码⻛格提⾼代码的可读性规范性和统⼀性。
本规范将从命名规范注释规范代码⻛格和
语⾔提供的常⽤的⼯具这⼏个⽅⾯做⼀个说明。
规范并不是唯⼀的也就是说理论上每个公司都可以制定⾃⼰的规范不过⼀般来说整体上规范差异不会很⼤。
命名是代码规范中很重要的⼀部分统⼀的命名规则有利于提⾼的代码的可读性好的命名仅仅通过命名就可以获取到⾜够多的信息。
当命名包括常量、变量、类型、函数名、结构字段等等以⼀个⼤写字⺟开头如Group1那么使⽤这种形式的标识符的对象就可以被外部包的代码所使⽤客户端程序需要先导⼊这个包这被称为导出像⾯向对象语⾔中的
命名如果以⼩写字⺟开头则对包外是不可⻅的但是他们在整个包的内部是可⻅并且可⽤的像⾯向对象语⾔中的
的名字和⽬录保持⼀致尽量采取有意义的包名简短有意义尽量和标准库不要冲突。
包名应该为⼩写单词不要使⽤下划线或者混合⼤⼩写。
尽量采取有意义的⽂件名简短有意义应该为⼩写单词使⽤下划线分隔各个单词。
和结构体类似变量名称⼀般遵循驼峰法⾸字⺟根据访问控制原则⼤写或者⼩写但遇到特有名词时需要遵循以下规则
⾏注释。
⾏注释是常态块注释主要显示为包注释但在表达式中很有⽤或禁⽤⼤量代码。
结尾且不可以嵌套使⽤多⾏注释⼀般⽤于包的⽂档描述或注释成块的代码⽚段
⼯具可以根据注释⽣成⽂档⽣成可以⾃动⽣成对应的⽹站golang.org
godoc⼯具直接⽣成的注释的质量决定了⽣成的⽂档的质量。
每个包都应该有⼀个包注释在
⼦句之前有⼀个块注释。
对于多⽂件包包注释只需要存在于⼀个⽂件中任何⼀个都可以。
包评论应该介绍包并提供与整个包相关的信息。
它将⾸先出现在
每个⾃定义的结构体或者接⼝都应该有注释说明该注释对结构进⾏简要介绍放在结构体定义的前⼀⾏格式为
结构体说明。
同时结构体内的每个成员变量都要有说明该说明放在成员变量的后⾯注意对⻬实例如下
每个函数或者⽅法结构体或者接⼝下的函数称为⽅法都应该有注释说明函数的注释应该包括三个⽅⾯严格按照此顺序撰写
对于⼀些关键位置的代码逻辑或者局部较为复杂的逻辑需要有相应的逻辑说明⽅便其他开发者阅读该段代码实例如下
这个不仅仅是中⽂和英⽂之间英⽂和中⽂标点之间也都要使⽤空格分隔例如
如果你的包引⼊了三种类型的包标准库包程序内部包第三⽅包建议采⽤如下⽅式进⾏组织你的包
(encoding/jsonstringsmyproject/modelsmyproject/controllermyproject/utilsgithub.com/astaxie/beegogithub.com/go-sql-driver/mysql
有顺序的引⼊包不同的类型采⽤空格分离第⼀种实标准库第⼆是项⽬包第三是第三⽅包。
在项⽬中不要使⽤相对路径引⼊包
“github.com/repo/proj/src/net”但是如果是引⼊本项⽬中的其他包最好使⽤相对路径。
panic除⾮你知道你在做什么错误描述如果是英⽂必须为⼩写不需要标点结尾采⽤独⽴的错误流进⾏处理
在远程调⽤时我们需要执⾏的函数体是在远程的机器上的也就是说add
add编译器就⾃动帮我们调⽤它相应的函数指针。
但是在远程调⽤中函数指针是不⾏的因为两个进程的地址空间是完全不⼀样的。
所以在
在所有进程中都是唯⼀确定的。
客户端在做远程过程调⽤时必须附上这个
必须相同。
当客户端需要进⾏远程调⽤时它就查⼀下这个表找出相应的
ID然后把它传给服务端服务端也通过查表来确定客户端需要调⽤的函数然后执⾏相应函数的代码。
序列化和反序列化。
客户端怎么把参数值传给远程的函数呢在本地调⽤中我们只需要把参数压到栈⾥然后让函数⾃⼰去栈⾥读就⾏。
但是在远程过程调⽤时客户端跟服务端是不同的进程不能通过内存来传递参数。
甚⾄有时候客户端和服务端使⽤的都不是同⼀种语⾔⽐如服务端⽤
或者Python。
这时候就需要客户端把参数先转成⼀个字节流传给服务端后再把字节流转成⾃⼰能读取的格式。
这个过程叫序列化和反序列化。
同理从服务端返回的值也需要序列化反序列化的过程。
⽹络传输。
远程调⽤往往⽤在⽹络上客户端和服务端是通过⽹络连接的。
所有的数据都需要通过⽹络传输因此就需要有⼀个⽹络传输层。
⽹络传输层需要把
和序列化后的参数字节流传给服务端然后再把序列化后的调⽤结果传回客户端。
只要能完成这两者的都可以作为传输层使⽤。
因此它所使⽤的协议其实是不限的能完成传输就⾏。
尽管⼤部分
IDa和b序列化。
可以直接将它们的值以⼆进制形式打包把2中得到的数据包发送给ServerAddr这需要使⽤⽹络传输层等待服务器返回结果如果服务器调⽤成功那么就将结果反序列化并赋给total
ID到函数指针的映射call_id_map可以⽤dict完成等待请求包括多线程的并发处理能⼒得到⼀个请求后将其数据包反序列化得到Call
ID通过在call_id_map中查找得到相应的函数指针将a和rb反序列化后在本地调⽤add函数得到结果将结果序列化后通过⽹络返回给Client
ID。
映射表⼀般就是⼀个哈希表。
序列化反序列化可以⾃⼰写也可以使⽤
实际上真正的开发过程中除了上⾯的基本功能以外还需要更多的细节⽹络错误、流量控制、超时和重试等。
的简写中⽂描述表述性状态传递是指某个瞬间状态的资源数据的快照包括资源数据的内容、表述格式
接⼝只关注服务提供⽅对于客户端怎么调⽤并不关⼼。
接⼝只要保证有客户端调⽤时返回对应的数据就⾏了。
⽽
是服务端把⽅法写好客户端并不知道具体⽅法。
客户端只想获取资源所以发起
是服务端提供好⽅法给客户端调⽤客户端需要知道服务端的具体类具体⽅法然后像调⽤本地⽅法⼀样直接调⽤它。
协议进⾏传输的。
常⻅的序列化协议有json、xml、hession、protobuf、thrift、text、bytes
⾯向资源更注重接⼝的规范因为要保证通⽤性更强所以对外最好通过REST。
⽽
⾯向⽅法主要⽤于函数⽅法的调⽤可以适合更复杂通信需求的场景。
RESTful
请求时客户端需要等待服务端的响应。
当然对于这⼀点是可以通过⼀些技术来实现异步的机制的。
采⽤
API客户端与服务端之间虽然可以独⽴开发但还是存在耦合。
⽐如客户端在发送请求的时必须知道服务器的地址且必须保证服务器正常⼯作。
⽽
REST。
⽽组件内部的各个模块可以选择RPC⼀个是不⽤耗费太多精⼒去开发和维护多套的
提供的功能过多导致在⽹络传输时需要携带的信息更多从性能⻆度上讲较为低效。
⽽
服务⽹络传输上仅传输与业务内容相关的数据传输数据更⼩性能更⾼。
定义的协议就⾏了这对于开发服务来说⾮常有⽤⼀般这种协议的价值在于我们⾃⼰开发的服务之间需要。
通信的时候
技术在架构设计上有四部分组成分别是客户端、客户端存根、服务端、服务端存根。
该程序运⾏在客户端所在的计算机机器上主要⽤来存储要调⽤的服务器的地址另外该程序还负责将客户端请求远端服务器程序的数据信息打包成数据包通过⽹络发送给服务端Stub
程序通过⽹络发送的请求消息数据包并调⽤服务端中真正的程序功能⽅法完成功能调⽤其次将服务端执⾏调⽤的结果进⾏数据处理打包发送给客户端
技术的组成结构我们来看⼀下具体是如何实现客户端到服务端的调⽤的。
实际上如果我们想要在⽹络中的任意两台计算机上实现远程调⽤过程要解决很多问题⽐如
两台物理机器在⽹络中要建⽴稳定可靠的通信连接。
两台服务器的通信协议的定义问题即两台服务器上的程序如何识别对⽅的请求和返回结果。
也就是说两台计算机必须都能够识别对⽅发来的信息并且能够识别出其中的请求含义和返回含义然后才能进⾏处理。
这其实就是通信协议所要完成的⼯作。
程序接收到了客户端的功能调⽤请求将客户端请求调⽤的⽅法名携带的参数等信息做序列化操作并打包成数据包。
程序接收到客户端发送的数据包信息并通过约定好的协议将数据进⾏反序列化得到请求的⽅法名和请求参数等信息。
6、服务端程序根据已有业务逻辑执⾏调⽤过程待业务执⾏结束将执⾏结果返回给服务端
是⼀系列操作的集合其中涉及到很多对数据的操作以及⽹络通信。
因此我们对
程序在具体的编码和开发实践过程中都是使⽤动态代理技术⾃动⽣成的⼀段程序。
调⽤的过程中我们可以看到数据需要在⼀台机器上传输到另外⼀台机器上。
在互联⽹上所有的数据都是以字节的形式进⾏传输的。
⽽我们在编程的过程中往往都是使⽤数据对象因此想要在⽹络上将数据对象和相关变量进⾏传输就需要对数据对象做序列化和反序列化的操作。
把字节序列恢复为对象的过程称为对象的反序列化也就是解码的过程。
等相关框架都可以对数据做序列化和反序列化编解码操作。
后⾯我们要学习的
{panic(建⽴链接失败)}rpc.ServeConn(conn)
只能有两个可序列化的参数其中第⼆个参数是指针类型并且返回⼀个error类型同时必须是公开的⽅法
client.Call(HelloService.Hello,
{log.Fatal(err)}fmt.Println(reply)
第⼀个参数是⽤点号链接的RPC服务名字和⽅法名字第⼆和第三个参数分别我们定义RPC⽅法的两个参数
github.com/golang/protobuf/protoc-gen-go如果是新版本
google.golang.org/protobuf/cmd/protoc-gen-gov1.26
google.golang.org/grpc/cmd/protoc-gen-go-grpcv1.1注意安装过程中会提示说
#如果⼤家使⽤最新的protoc此处可能会报错说不⽀持这种⽤法所以可以使⽤下⾯的语句⽣成
(contextfmtgoogle.golang.org/grpcgrpc_demo/hellonet
*hello.HelloRequest)(*hello.HelloReply,error){return
Server{}hello.RegisterGreeterServer(g,s)lis,
(contextfmtgoogle.golang.org/grpcgrpc_demo/proto
grpc.Dial(127.0.0.1:8080,grpc.WithInsecure())if
hello.NewGreeterClient(conn)r,err
c.SayHello(context.Background(),hello.HelloRequest{Name:bobby})if
err!nil{panic(err)}fmt.Println(r.Message)
先来看一个非常简单的例子。
假设你想定义一个“搜索请求”的消息格式每一个请求含有一个查询字符串、你感兴趣的查询结果所在的页数以及每一页多少条查询结果。
可以采用如下的方式来定义消息类型的.proto文件了
}文件的第一行指定了你正在使用proto3语法如果你没有指定这个编译器会使用proto2。
这个指定语法行必须是文件的非空非注释的第一个行。
SearchRequest消息格式有3个字段在消息中承载的数据分别对应于每一个字段。
其中每个字段都有一个名字和一种类型。
在上面的例子中所有字段都是标量类型两个整型page_number和result_per_page一个string类型query。
当然你也可以为字段指定其他的合成类型包括枚举enumerations或其他消息类型。
正如你所见在消息定义中每个字段都有唯一的一个数字标识符。
这些标识符是用来在消息的二进制格式中识别各个字段的一旦开始使用就不能够再改变。
注[1,15]之内的标识号在编码的时候会占用一个字节。
[16,2047]之内的标识号则占用2个字节。
所以应该为那些频繁出现的消息元素保留
[1,15]之内的标识号。
切记要为将来有可能添加的、频繁出现的标识号预留一些标识号。
536,870,911。
不可以使用其中的[1900019999]
(从FieldDescriptor::kFirstReservedNumber
到FieldDescriptor::kLastReservedNumber)
Protobuf协议实现中对这些进行了预留。
如果非要在.proto文件中使用这些预留标识号编译时就会报警。
同样你也不能使用早期保留的标识号。
singular一个格式良好的消息应该有0个或者1个这种字段但是不能超过1个。
repeated在一个格式良好的消息中这种字段可以重复任意多次包括0次。
重复的值的顺序会被保留。
在proto3中repeated的标量域默认情况虾使用packed。
在一个.proto文件中可以定义多个消息类型。
在定义多个相关的消息的时候这一点特别有用——例如如果想定义与SearchResponse消息类型对应的回复消息格式的话你可以将它添加到相同的.proto文件中如
向.proto文件添加注释可以使用C/C/java风格的双斜杠//
类型实际上是mapkey是stringvalue是string类型的slice。
创建的时候可以像创建普通的map类型一样使用new关键字进行创建
metadata.New(map[string]string{key1:
metadata.NewOutgoingContext(context.Background(),
metadata.FromIncomingContext(ctx)
(OldPackageTest/grpc_test/protocontextfmtgoogle.golang.org/grpcgoogle.golang.org/grpc/metadata
proto.NewGreeterClient(conn)//md
time.Now().Format(timestampFormat))md
metadata.New(map[string]string{name:bobby,pasword:imooc,})ctx
metadata.NewOutgoingContext(context.Background(),
proto.HelloRequest{Name:bobby})if
{panic(err)}fmt.Println(r.Message)
(contextfmtgoogle.golang.org/grpc/metadatanetgoogle.golang.org/grpcOldPackageTest/grpc_test/proto
metadata.FromIncomingContext(ctx)if
grpc.NewServer()proto.RegisterGreeterServer(g,
(contextfmtgoogle.golang.org/grpctimestart/grpc_interceptor/proto
grpc.WithUnaryInterceptor(interceptor))
c.SayHello(context.Background(),
proto.HelloRequest{Name:bobby})
(contextfmtnetgoogle.golang.org/grpcstart/grpc_interceptor/proto
grpc.UnaryInterceptor(interceptor))g
grpc.NewServer(opts...)proto.RegisterGreeterServer(g,
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback