<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>GRPC on Snow</title>
        <link>https://SymphonyW.github.io/tags/grpc/</link>
        <description>Recent content in GRPC on Snow</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en</language>
        <copyright>Snow</copyright>
        <lastBuildDate>Thu, 16 Apr 2026 20:04:52 +0800</lastBuildDate><atom:link href="https://SymphonyW.github.io/tags/grpc/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>About gRPC Interview</title>
        <link>https://SymphonyW.github.io/p/about-grpc-interview/</link>
        <pubDate>Thu, 16 Apr 2026 20:04:52 +0800</pubDate>
        
        <guid>https://SymphonyW.github.io/p/about-grpc-interview/</guid>
        <description>&lt;img src="https://SymphonyW.github.io/p/about-grpc-interview/gRPC.png" alt="Featured image of post About gRPC Interview" /&gt;&lt;p&gt;这份文档整理了 gRPC 面试中从基础概念到生产实践的全部高频考点，覆盖了初级、中级、高级面试的所有常见问题，帮助你全面准备 gRPC 相关的技术面试。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一基础概念篇入门必问&#34;&gt;一、基础概念篇（入门必问）
&lt;/h2&gt;&lt;h3 id=&#34;1-什么是-grpc它解决了什么问题&#34;&gt;1. 什么是 gRPC？它解决了什么问题？
&lt;/h3&gt;&lt;p&gt;gRPC(Google Remote Procedure Call) 是 Google 开源的高性能、通用的&lt;strong&gt;远程过程调用（RPC）框架&lt;/strong&gt;，基于 HTTP/2 协议传输，默认使用 Protocol Buffers（Protobuf）作为序列化格式。&lt;/p&gt;
&lt;p&gt;它主要解决了分布式系统中服务间通信的核心痛点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;跨语言通信&lt;/strong&gt;：支持 C++、Go、Java、Python、Ruby 等十余种语言，异构系统可以无缝集成&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;高性能传输&lt;/strong&gt;：相比传统的 REST+JSON，提供了 5-10 倍的性能提升&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;标准化接口&lt;/strong&gt;：通过 Protobuf 定义强类型接口，消除了接口文档与代码不一致的问题&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;流式通信&lt;/strong&gt;：支持双向流，满足实时通信、大数据传输等复杂场景&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;2-grpc-和-rest-api-有什么核心区别&#34;&gt;2. gRPC 和 REST API 有什么核心区别？
&lt;/h3&gt;&lt;p&gt;这是面试最高频的对比题，建议用表格清晰回答：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;对比维度&lt;/th&gt;
&lt;th&gt;REST API (HTTP/1.1 + JSON)&lt;/th&gt;
&lt;th&gt;gRPC (HTTP/2 + Protobuf)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;传输协议&lt;/td&gt;
&lt;td&gt;HTTP/1.1&lt;/td&gt;
&lt;td&gt;HTTP/2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;序列化格式&lt;/td&gt;
&lt;td&gt;JSON（文本格式）&lt;/td&gt;
&lt;td&gt;Protobuf（二进制格式）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;接口契约&lt;/td&gt;
&lt;td&gt;OpenAPI（可选，弱约束）&lt;/td&gt;
&lt;td&gt;.proto 文件（强制，强类型）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;通信模式&lt;/td&gt;
&lt;td&gt;仅支持简单请求 - 响应&lt;/td&gt;
&lt;td&gt;支持 4 种模式：一元、服务端流、客户端流、双向流&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;连接模型&lt;/td&gt;
&lt;td&gt;每个请求可能新建连接，队头阻塞&lt;/td&gt;
&lt;td&gt;单 TCP 连接多路复用，并发处理多个请求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;性能&lt;/td&gt;
&lt;td&gt;较低，文本解析慢、体积大&lt;/td&gt;
&lt;td&gt;高，二进制序列化 + 多路复用，性能提升 5-10 倍&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;浏览器支持&lt;/td&gt;
&lt;td&gt;原生支持&lt;/td&gt;
&lt;td&gt;需要 gRPC-Web 或网关转换&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;调试成本&lt;/td&gt;
&lt;td&gt;低，文本可读&lt;/td&gt;
&lt;td&gt;较高，二进制需要专用工具&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;补充：
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;虽然 gRPC 在性能和功能上有明显优势，但 REST API 由于其简单、易于调试和广泛的工具支持，在很多场景下仍然非常流行，特别是对外 API 和浏览器交互的场景。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;由于 gRPC 的调试成本较高，建议在开发阶段使用 gRPC 的调试工具，比如 BloomRPC、gRPCurl 等，或者在服务端实现一个 HTTP 网关，提供 REST API 供调试使用。等到生产环境后，再切换到 gRPC 的高性能通信。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-什么是-protocol-buffers为什么-grpc-选择它&#34;&gt;3. 什么是 Protocol Buffers？为什么 gRPC 选择它？
&lt;/h3&gt;&lt;p&gt;Protocol Buffers（简称 Protobuf）是 Google 推出的一种轻量级、高效的&lt;strong&gt;结构化数据序列化格式&lt;/strong&gt;，是一种接口定义语言（IDL）。&lt;/p&gt;
&lt;p&gt;gRPC 选择它的原因：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;极致的性能&lt;/strong&gt;：二进制序列化，比 JSON 小 3-10 倍，解析速度快 5-100 倍&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;跨语言兼容&lt;/strong&gt;：一份 proto 文件可以自动生成多语言的代码，保证接口一致性&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;强类型契约&lt;/strong&gt;：编译期就能检查接口错误，避免运行时的类型错误&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;向后兼容&lt;/strong&gt;：通过字段编号实现平滑的版本升级，不破坏老客户端&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;代码生成&lt;/strong&gt;：自动生成客户端和服务端的样板代码，减少开发工作量&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;4-protobuf-的序列化原理是什么为什么它比-json-小&#34;&gt;4. Protobuf 的序列化原理是什么？为什么它比 JSON 小？
&lt;/h3&gt;&lt;p&gt;Protobuf 提供了5种基本数据类型：int32、int64、uint32、uint64、bool、string 等，还有复合类型 message 和枚举 enum。&lt;/p&gt;
&lt;p&gt;Protobuf 的核心序列化逻辑是&lt;strong&gt;用字段编号代替字段名传输&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-protobuf&#34; data-lang=&#34;protobuf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;User&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kt&#34;&gt;int64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 1是字段编号，不是值！
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;序列化时，每个字段会被编码为：&lt;code&gt;(field_number &amp;lt;&amp;lt; 3 | wire_type) + value&lt;/code&gt;，将field_number左移3位，和wire_type（表示数据类型）进行按位或运算，得到一个唯一的标识符，然后紧跟着字段值的二进制编码。也就是说：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;不需要传输&lt;code&gt;&amp;quot;id&amp;quot;&lt;/code&gt;、&lt;code&gt;&amp;quot;name&amp;quot;&lt;/code&gt;这些字符串字段名，只传 1、2 这样的数字编号&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不需要 JSON 里的引号、冒号、逗号这些分隔符&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;数字类型直接存储二进制，不需要转成文本字符串&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以 Protobuf 的序列化结果体积远小于 JSON，而且解析速度更快。&lt;/p&gt;
&lt;h3 id=&#34;5-grpc-支持哪些编程语言如何保证跨语言兼容&#34;&gt;5. gRPC 支持哪些编程语言？如何保证跨语言兼容？
&lt;/h3&gt;&lt;p&gt;gRPC 官方支持 C++、Go、Java、Python、Ruby、C#、Objective-C、PHP、Dart、Rust 等十余种主流编程语言。&lt;/p&gt;
&lt;p&gt;跨语言兼容的核心是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;统一的接口定义&lt;/strong&gt;：所有语言都基于同一份.proto 文件定义接口&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;标准化的序列化&lt;/strong&gt;：Protobuf 定义了统一的二进制编码格式，不同语言都按照相同的规则序列化 / 反序列化&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;自动生成代码&lt;/strong&gt;：protoc 编译器会根据 proto 文件，为不同语言生成对应的客户端 / 服务端代码，保证接口的一致性&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二核心通信模式篇高频重点&#34;&gt;二、核心通信模式篇（高频重点）
&lt;/h2&gt;&lt;h3 id=&#34;1-grpc-支持哪四种通信模式分别的使用场景是什么&#34;&gt;1. gRPC 支持哪四种通信模式？分别的使用场景是什么？
&lt;/h3&gt;&lt;p&gt;这是面试必考题，必须完整说出四种模式，以及对应的场景：&lt;/p&gt;
&lt;h4 id=&#34;11-一元-rpcunary-rpc&#34;&gt;1.1 一元 RPC（Unary RPC）
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模式&lt;/strong&gt;：客户端发送 1 个请求，服务端返回 1 个响应，就是最普通的 RPC 调用&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;场景&lt;/strong&gt;：普通的查询、增删改查接口，比如获取用户信息、创建订单&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;类比&lt;/strong&gt;：普通的 HTTP POST 请求&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;12-服务端流-rpcserver-side-streaming-rpc&#34;&gt;1.2 服务端流 RPC（Server-side Streaming RPC）
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模式&lt;/strong&gt;：客户端发送 1 个请求，服务端返回多个响应的流&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;场景&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;大文件下载：服务端边读边发，不需要一次性把整个文件加载到内存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;实时数据推送：比如股票行情推送、监控数据推送&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;批量数据查询：比如查询百万级数据，服务端分批返回&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;proto 定义&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-protobuf&#34; data-lang=&#34;protobuf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;rpc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ListUsers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ListUsersRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;returns&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stream&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UserResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;13-客户端流-rpcclient-side-streaming-rpc&#34;&gt;1.3 客户端流 RPC（Client-side Streaming RPC）
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模式&lt;/strong&gt;：客户端发送多个请求的流，服务端返回 1 个响应&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;场景&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;大文件上传：客户端边读边发，不需要一次性上传整个文件&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;批量数据导入：比如批量上报监控数据、日志数据&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;proto 定义&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-protobuf&#34; data-lang=&#34;protobuf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;rpc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Upload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stream&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UploadRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;returns&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;UploadResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;14-双向流-rpcbidirectional-streaming-rpc&#34;&gt;1.4 双向流 RPC（Bidirectional Streaming RPC）
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模式&lt;/strong&gt;：客户端和服务端都可以独立发送多个消息的流，双方可以并发读写&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;场景&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;实时聊天：双方可以随时发消息&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;实时协作：比如在线文档的协同编辑&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;双向实时通信：比如游戏中的状态同步&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;proto 定义&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-protobuf&#34; data-lang=&#34;protobuf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;rpc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Chat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stream&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ChatMessage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;returns&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stream&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ChatMessage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;2-双向流是如何实现并发读写的&#34;&gt;2. 双向流是如何实现并发读写的？
&lt;/h3&gt;&lt;p&gt;双向流基于 HTTP/2 的全双工通信能力实现：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;HTTP/2 的单个 TCP 连接上，流是双向的，双方可以同时发送数据&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;gRPC 的流对象提供了独立的 Send 和 Recv 方法，两者互不阻塞&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;客户端和服务端可以在各自的 goroutine / 线程中独立处理读写，不需要等待对方&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;3-流模式下的流量控制flow-control是如何实现的&#34;&gt;3. 流模式下的流量控制（Flow Control）是如何实现的？
&lt;/h3&gt;&lt;p&gt;gRPC 的流控主要依赖底层 HTTP/2 的流量控制机制：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;窗口大小机制&lt;/strong&gt;：每个流和整个连接都有一个接收窗口，接收方通过&lt;code&gt;WINDOW_UPDATE&lt;/code&gt;帧告诉发送方自己还能接收多少数据&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;背压（Backpressure）&lt;/strong&gt;：当接收方处理不过来的时候，会减小窗口大小，发送方就会自动减慢发送速度，避免把接收方压垮&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;优先级调度&lt;/strong&gt;：HTTP/2 支持流的优先级，重要的流可以优先传输&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;4-流模式下如何优雅关闭流&#34;&gt;4. 流模式下如何优雅关闭流？
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;客户端 / 服务端完成发送后，调用&lt;code&gt;CloseSend()&lt;/code&gt;方法关闭发送方向的流&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;接收方读完所有消息后，会收到 EOF，标识流结束&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于错误场景，通过发送&lt;code&gt;RST_STREAM&lt;/code&gt;帧来重置流，中断整个流的通信&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;必须保证所有的消息都处理完成后再关闭，避免数据丢失&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三底层原理篇深入必问&#34;&gt;三、底层原理篇（深入必问）
&lt;/h2&gt;&lt;h3 id=&#34;1-为什么-grpc-选择-http2-而不是-http11&#34;&gt;1. 为什么 gRPC 选择 HTTP/2 而不是 HTTP/1.1？
&lt;/h3&gt;&lt;p&gt;HTTP/2 的特性是 gRPC 高性能的核心基础：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;多路复用（Multiplexing）&lt;/strong&gt;：解决了 HTTP/1.1 的队头阻塞问题，单个 TCP 连接可以同时处理多个请求 / 响应，请求和响应可以交错传输，不需要为每个请求新建 TCP 连接&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;二进制分帧&lt;/strong&gt;：把数据拆分为更小的帧，更高效的传输，天然支持流式数据&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;头部压缩（HPACK）&lt;/strong&gt;：对 HTTP 头部进行压缩，减少重复头部的传输开销，比如每次请求都带的认证 token、user-agent 这些&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;流控与优先级&lt;/strong&gt;：支持流量控制和流优先级，更好的管理资源&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务器推送&lt;/strong&gt;：服务端可以主动向客户端推送资源，支持双向通信&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;2-http2-的队头阻塞是什么grpc-是怎么解决的&#34;&gt;2. HTTP/2 的队头阻塞是什么？gRPC 是怎么解决的？
&lt;/h3&gt;&lt;p&gt;HTTP/1.1 中，同一个 TCP 连接上的请求必须排队，必须等前一个请求的响应完全返回后，才能处理下一个请求，如果前一个请求阻塞了，后面的所有请求都被阻塞，这就是&lt;strong&gt;队头阻塞（Head of Line Blocking）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;gRPC 基于 HTTP/2 的多路复用解决了这个问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;每个 RPC 调用对应一个独立的 Stream，每个 Stream 有唯一的 ID&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不同 Stream 的消息可以拆分成帧，交错传输，互不影响&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;即使某个 Stream 的消息阻塞了，其他 Stream 的消息依然可以正常传输&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这样单个 TCP 连接就可以同时处理成百上千个 RPC 请求，极大的提升了连接的利用率&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-grpchttp2stream-三者的关系是什么&#34;&gt;3. gRPC、HTTP/2、Stream 三者的关系是什么？
&lt;/h3&gt;&lt;p&gt;这三者是分层的关系：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HTTP/2&lt;/strong&gt;：是底层的传输协议，提供了多路复用、流控、二进制分帧这些基础能力&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stream&lt;/strong&gt;：是 HTTP/2 中的逻辑通道，每个 Stream 对应一个独立的 RPC 调用，多个 Stream 可以复用同一个 TCP 连接&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;gRPC&lt;/strong&gt;：是基于 HTTP/2 的 RPC 框架，它把每个 RPC 调用映射到一个 HTTP/2 的 Stream 上，利用 Stream 的双向传输能力实现了四种通信模式&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;简单来说：&lt;strong&gt;gRPC 利用 HTTP/2 的 Stream 机制，实现了 RPC 的调用和流式通信&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;4-http2-有哪些常见的帧类型各有什么作用&#34;&gt;4. HTTP/2 有哪些常见的帧类型？各有什么作用？
&lt;/h3&gt;&lt;p&gt;HTTP/2 以帧为最小传输单位，常见的帧类型：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DATA 帧&lt;/strong&gt;：传输应用层的业务数据&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HEADERS 帧&lt;/strong&gt;：传输 HTTP 头信息，gRPC 的 Metadata 就是通过这个帧传输的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PRIORITY 帧&lt;/strong&gt;：设置流的优先级&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;RST_STREAM 帧&lt;/strong&gt;：重置流，用于中断流、处理错误&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SETTINGS 帧&lt;/strong&gt;：配置连接的参数，比如窗口大小、最大帧大小&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PUSH_PROMISE 帧&lt;/strong&gt;：服务端推送的预告帧&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PING 帧&lt;/strong&gt;：心跳检测，测量往返时间&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GOAWAY 帧&lt;/strong&gt;：通知对端，连接即将关闭，不要再新建流了&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;WINDOW_UPDATE 帧&lt;/strong&gt;：更新流量控制的窗口大小&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;5-grpc-的高性能的原因是什么&#34;&gt;5. gRPC 的高性能的原因是什么？
&lt;/h3&gt;&lt;p&gt;这是高频题，总结起来有三点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;序列化层&lt;/strong&gt;：Protobuf 二进制序列化，体积小、解析快，比 JSON 高效很多&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;传输层&lt;/strong&gt;：HTTP/2 的多路复用，连接复用，减少 TCP 握手开销，解决队头阻塞&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;应用层&lt;/strong&gt;：强类型契约，编译期生成代码，没有运行时反射开销，异步非阻塞 IO 模型&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四核心功能与组件篇开发必问&#34;&gt;四、核心功能与组件篇（开发必问）
&lt;/h2&gt;&lt;h3 id=&#34;1-什么是拦截器interceptor作用是什么&#34;&gt;1. 什么是拦截器（Interceptor）？作用是什么？
&lt;/h3&gt;&lt;p&gt;拦截器是 gRPC 中用来拦截请求 / 响应的中间层，类似 Gin 的中间件，它可以在请求到达业务方法之前，或者响应返回之前，执行自定义的逻辑。&lt;/p&gt;
&lt;p&gt;拦截器分为两种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;一元拦截器&lt;/strong&gt;：处理普通的一元 RPC 调用&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;流拦截器&lt;/strong&gt;：处理流式 RPC 调用&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;常见的用途：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;认证与授权：统一验证 token，不需要每个业务方法都写&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;日志记录：统一记录请求日志、响应时间&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;链路追踪：添加 trace 信息，传递追踪上下文&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;监控 metrics：收集请求的 QPS、延迟、错误率&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;异常捕获：统一捕获 panic，转换成标准的错误响应&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求转换：统一修改请求头、请求参数&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-什么是-metadata它是用来做什么的&#34;&gt;2. 什么是 Metadata？它是用来做什么的？
&lt;/h3&gt;&lt;p&gt;Metadata 是 gRPC 中用来传递请求的附加元数据的键值对，它不会作为请求体的一部分，而是随请求一起传输，类似 HTTP 的 Header。&lt;/p&gt;
&lt;p&gt;它的作用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;传递认证信息：比如 JWT token、API Key&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;传递链路追踪信息：比如 Trace ID、Span ID&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;传递请求的附加信息：比如用户语言、客户端版本&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;传递调试信息：比如调试标记&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Metadata 是通过 HTTP/2 的 HEADERS 帧传输的，客户端和服务端都可以发送和接收 Metadata。&lt;/p&gt;
&lt;h3 id=&#34;3-什么是-channelchannel-和-connection-的关系是什么&#34;&gt;3. 什么是 Channel？Channel 和 Connection 的关系是什么？
&lt;/h3&gt;&lt;p&gt;Channel 是 gRPC 客户端的核心抽象，它代表了客户端到服务端的虚拟连接，封装了连接管理、负载均衡、服务发现等逻辑。&lt;/p&gt;
&lt;p&gt;Channel 和 Connection 的关系：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;一个 Channel 可以对应多个 TCP Connection&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在客户端负载均衡的模式下，Channel 会管理到多个服务端实例的连接&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Channel 是线程安全的，可以被多个 goroutine 并发使用，通常一个服务端的客户端只需要创建一个 Channel 就够了，不需要每次调用都新建&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Channel 会自动处理连接的重连、健康检查&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;4-什么是-resolver解析器常见的实现有哪些&#34;&gt;4. 什么是 Resolver（解析器）？常见的实现有哪些？
&lt;/h3&gt;&lt;p&gt;Resolver 是 gRPC 中负责服务发现的组件，它的作用是把服务名称解析成实际的服务端网络地址列表。&lt;/p&gt;
&lt;p&gt;常见的 Resolver 实现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DNS Resolver&lt;/strong&gt;：通过 DNS 解析服务地址&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;etcd Resolver&lt;/strong&gt;：从 etcd 中获取服务注册的地址，常用在微服务中&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consul/Nacos Resolver&lt;/strong&gt;：从 Consul/Nacos 这些服务注册中心获取地址&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;自定义 Resolver&lt;/strong&gt;：开发者可以自己实现解析逻辑，比如从配置中心获取地址&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resolver 会持续监听服务地址的变化，当服务端实例上下线时，自动更新客户端的地址列表，实现服务的动态发现。&lt;/p&gt;
&lt;h3 id=&#34;5-什么是-balancer负载均衡器常见的策略有哪些&#34;&gt;5. 什么是 Balancer（负载均衡器）？常见的策略有哪些？
&lt;/h3&gt;&lt;p&gt;Balancer 是 gRPC 中负责负载均衡的组件，当有多个服务端实例时，它负责选择一个实例来处理客户端的请求。&lt;/p&gt;
&lt;p&gt;常见的负载均衡策略：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;pick_first&lt;/strong&gt;：默认策略，选择第一个可用的地址，所有请求都发往这个地址&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;round_robin&lt;/strong&gt;：轮询，按顺序把请求分发到不同的实例&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;weighted_round_robin&lt;/strong&gt;：加权轮询，根据实例的权重分发请求&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;least_request&lt;/strong&gt;：最小请求数，选择当前正在处理的请求最少的实例&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;p2c（Power of Two Choices）&lt;/strong&gt;：随机选两个实例，选择其中负载更低的那个，go-zero 默认用的就是这个，性能很好&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;6-什么是-deadlinetimeout它的作用是什么&#34;&gt;6. 什么是 Deadline/Timeout？它的作用是什么？
&lt;/h3&gt;&lt;p&gt;Deadline 是 gRPC 中用来设置请求的截止时间的机制，它表示这个请求必须在某个时间点之前完成，否则就会超时返回&lt;code&gt;DEADLINE_EXCEEDED&lt;/code&gt;错误。&lt;/p&gt;
&lt;p&gt;它的核心作用：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;防止资源泄露&lt;/strong&gt;：避免请求因为网络问题一直挂起，占用服务端的资源&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;防止服务雪崩&lt;/strong&gt;：当服务端响应慢的时候，客户端不会一直等待，快速失败，避免把服务端压垮&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;链路传递&lt;/strong&gt;：Deadline 会随着 context 在整个微服务调用链中传递，整个调用链共享同一个截止时间，比如网关设置了 3s 的超时，那么下游的所有服务都会自动感知到剩余的时间，不会做无效的处理&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：Deadline 是绝对时间，不是相对时间，会跨服务传递，所以不需要每个服务都单独设置超时。&lt;/p&gt;
&lt;h3 id=&#34;7-grpc-的重试策略有哪些&#34;&gt;7. gRPC 的重试策略有哪些？
&lt;/h3&gt;&lt;p&gt;gRPC 的重试机制主要有这些策略：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;固定间隔重试&lt;/strong&gt;：每次失败后，等待固定的时间再重试&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;指数退避重试&lt;/strong&gt;：每次失败后，等待时间指数级增长，比如第一次等 100ms，第二次 200ms，第三次 400ms，避免重试风暴&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;错误过滤&lt;/strong&gt;：不是所有错误都重试，只对可恢复的错误重试，比如&lt;code&gt;Unavailable&lt;/code&gt;、&lt;code&gt;DeadlineExceeded&lt;/code&gt;，而参数错误、AlreadyExists 这些不可恢复的错误就不重试&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;最大重试次数&lt;/strong&gt;：限制最大重试次数，避免无限重试&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;8-什么是-grpc-的健康检查&#34;&gt;8. 什么是 gRPC 的健康检查？
&lt;/h3&gt;&lt;p&gt;健康检查是 gRPC 提供的标准服务，用来检查服务端的健康状态，客户端或者负载均衡器可以通过这个接口来判断服务是否可用。&lt;/p&gt;
&lt;p&gt;健康检查的状态有三种：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;SERVING&lt;/code&gt;：服务正常，可以处理请求&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;NOT_SERVING&lt;/code&gt;：服务不可用，无法处理请求&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;UNKNOWN&lt;/code&gt;：未知状态，无法确定服务的状态&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;健康检查可以用来做服务的自动摘除，当服务不健康的时候，负载均衡器会自动把它从可用列表中移除，避免把请求发过去。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五安全与认证篇进阶必问&#34;&gt;五、安全与认证篇（进阶必问）
&lt;/h2&gt;&lt;h3 id=&#34;1-grpc-支持哪些安全机制&#34;&gt;1. gRPC 支持哪些安全机制？
&lt;/h3&gt;&lt;p&gt;gRPC 提供了完善的安全机制：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TLS/SSL 加密&lt;/strong&gt;：对传输的数据进行加密，防止中间人攻击&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;mTLS（双向 TLS）&lt;/strong&gt;：客户端和服务端互相验证证书，常用于服务间的认证&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Token 认证&lt;/strong&gt;：通过 Metadata 传递 JWT、OAuth2 token，做用户级的认证&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;拦截器认证&lt;/strong&gt;：通过拦截器统一验证 token，实现授权逻辑&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;2-channel-credentials-和-call-credentials-有什么区别&#34;&gt;2. Channel credentials 和 Call credentials 有什么区别？
&lt;/h3&gt;&lt;p&gt;这是 gRPC 中两种不同的认证凭证：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Channel credentials&lt;/strong&gt;：是针对整个连接的凭证，比如 TLS 证书，在建立连接的时候使用，对整个连接的所有 RPC 调用都生效&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Call credentials&lt;/strong&gt;：是针对单个 RPC 调用的凭证，比如 JWT token，每个请求都可以带不同的 token，放在 Metadata 中传输&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;3-如何在-grpc-中实现-jwt-认证&#34;&gt;3. 如何在 gRPC 中实现 JWT 认证？
&lt;/h3&gt;&lt;p&gt;通常通过拦截器来实现：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端拦截器&lt;/strong&gt;：在发送请求之前，把 JWT token 添加到 Metadata 中&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务端拦截器&lt;/strong&gt;：在请求到达业务方法之前，从 Metadata 中取出 token，验证 token 的有效性，如果验证失败，直接返回未认证的错误，不需要进入业务方法&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样就可以统一处理所有接口的认证，不需要每个业务方法都写认证逻辑。&lt;/p&gt;
&lt;h3 id=&#34;4-什么是-mtls它和普通-tls-有什么区别&#34;&gt;4. 什么是 mTLS？它和普通 TLS 有什么区别？
&lt;/h3&gt;&lt;p&gt;普通 TLS 是单向的，只有客户端验证服务端的证书，确认服务端是可信的；而 mTLS（双向 TLS）是客户端和服务端互相验证对方的证书，双方都要确认对方是可信的。&lt;/p&gt;
&lt;p&gt;mTLS 常用于服务间的通信，比如微服务架构中，只有拥有合法证书的服务才能互相调用，防止非法的服务接入，提升系统的安全性。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六服务治理篇生产必问&#34;&gt;六、服务治理篇（生产必问）
&lt;/h2&gt;&lt;h3 id=&#34;1-grpc-如何做服务发现&#34;&gt;1. gRPC 如何做服务发现？
&lt;/h3&gt;&lt;p&gt;gRPC 提供了 Resolver 的扩展接口，我们可以集成服务注册中心：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;服务端启动的时候，把自己的地址注册到 etcd/Consul/Nacos 这些注册中心，带上租约&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;客户端通过自定义的 Resolver，从注册中心获取服务的地址列表&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Resolver 会监听注册中心的变化，当服务端实例上下线的时候，自动更新客户端的地址列表&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;客户端的 Balancer 根据更新后的地址列表，做负载均衡&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样就实现了服务的动态发现，不需要手动配置服务端的地址。&lt;/p&gt;
&lt;h3 id=&#34;2-grpc-的客户端负载均衡和服务端负载均衡有什么区别&#34;&gt;2. gRPC 的客户端负载均衡和服务端负载均衡有什么区别？
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端负载均衡&lt;/strong&gt;：客户端自己维护服务端的地址列表，自己选择要调用的实例，不需要中间的负载均衡器，减少了一跳网络，性能更好，比如 gRPC 的内置 Balancer 就是这种&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务端负载均衡&lt;/strong&gt;：客户端把请求发给统一的负载均衡器，由负载均衡器选择服务端实例，比如 Nginx、Istio，这种方式对客户端透明，但是多了一跳网络&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;3-grpc-中如何实现熔断限流降级&#34;&gt;3. gRPC 中如何实现熔断、限流、降级？
&lt;/h3&gt;&lt;p&gt;这些都是微服务的服务保护机制，在 gRPC 中通常通过拦截器或者框架来实现：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;限流&lt;/strong&gt;：通过拦截器，在请求进入的时候，用令牌桶或者计数器算法，限制单位时间的请求数，超过的直接拒绝&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;熔断&lt;/strong&gt;：当某个服务的错误率达到阈值，熔断器打开，后续的请求直接快速失败，避免把下游压垮，等冷却时间后再探测恢复&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;降级&lt;/strong&gt;：当服务压力大的时候，关闭非核心的功能，或者返回默认的缓存数据，保证核心功能可用&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;比如 go-zero 框架就内置了这些能力，也可以自己通过拦截器实现。&lt;/p&gt;
&lt;h3 id=&#34;4-grpc-的重试为什么要配合幂等性&#34;&gt;4. gRPC 的重试为什么要配合幂等性？
&lt;/h3&gt;&lt;p&gt;因为网络超时的时候，我们不确定请求是否已经到达服务端并执行了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果请求已经执行成功，只是响应丢了，这时候重试就会导致重复执行，比如重复扣款、重复下单&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;所以重试必须配合幂等性，保证同一个请求执行多次和执行一次的效果是一样的&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;常见的幂等实现：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;幂等 Token&lt;/strong&gt;：客户端生成唯一的请求 ID，服务端用这个 ID 做去重，缓存执行结果&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;数据库唯一键&lt;/strong&gt;：比如订单的请求 ID 加唯一索引，重复插入的时候会捕获错误，返回已有结果&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;状态机&lt;/strong&gt;：通过业务状态来控制，比如订单状态已经是已支付，就不会重复处理&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七版本管理与兼容性篇架构必问&#34;&gt;七、版本管理与兼容性篇（架构必问）
&lt;/h2&gt;&lt;h3 id=&#34;1-proto-文件的字段编号为什么不能随意修改&#34;&gt;1. Proto 文件的字段编号为什么不能随意修改？
&lt;/h3&gt;&lt;p&gt;因为 Protobuf 序列化的时候，是用字段编号来标识字段的，不是字段名：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果服务端修改了字段编号，老客户端发送的数据，服务端就会解析到错误的字段上，导致数据错乱&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;比如老版本中&lt;code&gt;id=1&lt;/code&gt;，&lt;code&gt;name=2&lt;/code&gt;，服务端改成&lt;code&gt;name=1&lt;/code&gt;，&lt;code&gt;id=2&lt;/code&gt;，那么老客户端发的 id=1 的数据，服务端就会解析到 name 字段上，完全乱了&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正确的版本升级做法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;只新增字段，不要修改已有字段的编号&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;废弃的字段，用&lt;code&gt;reserved&lt;/code&gt;关键字保留编号，不要复用&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这样就能保证向后兼容，老客户端可以正常访问新服务，新客户端也可以访问老服务&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;2-grpc-服务如何做版本管理&#34;&gt;2. gRPC 服务如何做版本管理？
&lt;/h3&gt;&lt;p&gt;常见的版本管理方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;包版本&lt;/strong&gt;：把不同版本的服务放在不同的 proto 包中，比如&lt;code&gt;package helloworld.v1&lt;/code&gt;和&lt;code&gt;package helloworld.v2&lt;/code&gt;，两个版本的服务可以共存，客户端可以选择调用哪个版本&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;URL 版本&lt;/strong&gt;：把版本放在服务的地址中，比如不同版本的服务监听不同的端口，或者不同的路径&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;字段兼容&lt;/strong&gt;：对于小的非破坏性变更，通过新增字段来实现，不需要升级版本号，利用 Protobuf 的向后兼容能力&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;3-protobuf-如何保证向后兼容&#34;&gt;3. Protobuf 如何保证向后兼容？
&lt;/h3&gt;&lt;p&gt;Protobuf 的设计天然支持向后兼容：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;新增的字段，老客户端会忽略，老服务端也会忽略新客户端发的新字段&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;废弃的字段，只要保留编号，就不会影响老客户端&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可以把字段从可选改成重复，或者反过来，只要类型兼容&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;只要不修改已有字段的编号和类型，就不会破坏兼容性&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;八调试监控与运维篇运维必问&#34;&gt;八、调试、监控与运维篇（运维必问）
&lt;/h2&gt;&lt;h3 id=&#34;1-grpc-的二进制不好调试有什么调试工具&#34;&gt;1. gRPC 的二进制不好调试，有什么调试工具？
&lt;/h3&gt;&lt;p&gt;有很多专用的调试工具：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;grpcurl&lt;/strong&gt;：命令行工具，类似 curl，可以直接调用 gRPC 接口&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;BloomRPC/Postman/Insomnia&lt;/strong&gt;：GUI 工具，可以可视化的调用 gRPC 接口，查看请求和响应&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;gRPC reflection&lt;/strong&gt;：服务端开启反射服务，客户端就可以动态获取服务的接口定义，不需要 proto 文件，调试工具就是靠这个来工作的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;grpc_cli&lt;/strong&gt;：官方的命令行调试工具&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;2-grpc-如何做监控和链路追踪&#34;&gt;2. gRPC 如何做监控和链路追踪？
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Metrics 监控&lt;/strong&gt;：通过拦截器收集请求的 QPS、延迟、错误率、流量等指标，上报到 Prometheus 等监控系统&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;链路追踪&lt;/strong&gt;：通过拦截器，把 OpenTelemetry/Jaeger/Zipkin 的 Trace 信息放到 Metadata 中，跨服务传递，实现整个调用链的追踪&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;日志&lt;/strong&gt;：通过拦截器统一记录请求的日志，包括方法名、客户端地址、延迟、错误信息等&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;3-grpc-的健康检查是如何工作的&#34;&gt;3. gRPC 的健康检查是如何工作的？
&lt;/h3&gt;&lt;p&gt;gRPC 提供了标准的&lt;code&gt;Health&lt;/code&gt;服务，定义了一个&lt;code&gt;Check&lt;/code&gt;方法，客户端可以调用这个方法来检查服务的健康状态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;服务端实现这个方法，返回自己的健康状态&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;负载均衡器或者服务注册中心会定期调用这个接口，来判断服务是否可用&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果健康检查失败，就会把服务从可用列表中移除&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;4-grpc-服务的压测需要关注哪些指标&#34;&gt;4. gRPC 服务的压测需要关注哪些指标？
&lt;/h3&gt;&lt;p&gt;压测的时候需要关注：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;QPS&lt;/strong&gt;：服务能承载的最大每秒请求数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;延迟 P99/P999&lt;/strong&gt;：请求的延迟分布，长尾延迟很重要&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;错误率&lt;/strong&gt;：请求的错误比例&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CPU / 内存使用率&lt;/strong&gt;：服务端的资源使用情况&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;连接数&lt;/strong&gt;：TCP 连接的数量，是否有连接泄露&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GC 情况&lt;/strong&gt;：对于 Go/Java 这些有 GC 的语言，要关注 GC 的停顿时间&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九生产实践与常见问题篇踩坑必问&#34;&gt;九、生产实践与常见问题篇（踩坑必问）
&lt;/h2&gt;&lt;h3 id=&#34;1-grpc-有什么缺点&#34;&gt;1. gRPC 有什么缺点？
&lt;/h3&gt;&lt;p&gt;gRPC 不是银弹，它也有缺点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;调试成本高&lt;/strong&gt;：二进制格式，不能直接用 curl 调试，需要专用工具&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;浏览器不支持&lt;/strong&gt;：浏览器原生不支持 HTTP/2 的双向流，需要用 gRPC-Web 或者网关转换&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;学习成本高&lt;/strong&gt;：需要学习 Protobuf 的语法，还有 gRPC 的相关概念&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Proto 文件管理&lt;/strong&gt;：多服务的时候，需要管理 proto 文件的版本和依赖&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;对老系统不友好&lt;/strong&gt;：一些老旧的网络设备可能不支持 HTTP/2&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;2-什么场景下适合用-grpc什么场景下不适合&#34;&gt;2. 什么场景下适合用 gRPC？什么场景下不适合？
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;适合的场景&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;微服务内部的服务间通信，需要高性能&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;实时通信场景，比如聊天、实时推送、协同编辑&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;多语言的异构系统，需要跨语言调用&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;大数据传输场景，比如文件上传下载、批量数据同步&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;不适合的场景&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;对外的简单 API，需要浏览器直接调用的场景&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对调试要求很高，需要人类可读的接口的场景&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;非常简单的小服务，用 REST 就够了，没必要引入 gRPC 的复杂度&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-grpc-和-thrift-有什么区别&#34;&gt;3. gRPC 和 Thrift 有什么区别？
&lt;/h3&gt;&lt;p&gt;两者都是 RPC 框架，区别在于：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;传输协议&lt;/strong&gt;：gRPC 基于 HTTP/2，可以更好的兼容现有的网络基础设施，而 Thrift 通常用自己的 TCP 协议&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;序列化&lt;/strong&gt;：gRPC 用 Protobuf，Thrift 有自己的序列化格式，性能差不多&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;生态&lt;/strong&gt;：gRPC 的生态更完善，有负载均衡、服务发现、网关、监控这些配套的工具&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;流式支持&lt;/strong&gt;：gRPC 的流式更成熟，基于 HTTP/2 的流，而 Thrift 也有流，但是用的少&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;4-常见的-grpc-错误排查&#34;&gt;4. 常见的 gRPC 错误排查
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;protoc: command not found&lt;/strong&gt;：protoc 没有安装，或者没有加到 PATH&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Plugin protoc-gen-go not found&lt;/strong&gt;：Go 的插件没有安装，或者 GOPATH/bin 没有加到 PATH&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;连接超时&lt;/strong&gt;：检查服务端是否启动，端口是否开放，防火墙是否拦截，Docker 的端口映射是否正确&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模块路径错误&lt;/strong&gt;：导入 pb 包的时候，要替换成自己的 go mod 的模块路径&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;流关闭错误&lt;/strong&gt;：流没有正确关闭，导致资源泄露，要确保用完后关闭流&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;max message size exceeded&lt;/strong&gt;：消息太大，超过了默认的 4MB 限制，需要调整&lt;code&gt;max_receive_message_size&lt;/code&gt;参数&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十高频面试真题汇总速查&#34;&gt;十、高频面试真题汇总（速查）
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Q：gRPC 为什么比 REST 快？&lt;/strong&gt;
A：三个原因：Protobuf 二进制序列化小且快；HTTP/2 多路复用减少连接开销；强类型无反射开销。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Q：gRPC 的四种通信模式是什么？&lt;/strong&gt;
A：一元 RPC、服务端流、客户端流、双向流，分别对应不同的场景。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Q：什么是拦截器？作用是什么？&lt;/strong&gt;
A：中间层组件，用来统一处理认证、日志、监控等逻辑，类似中间件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Q：HTTP/2 的多路复用解决了什么问题？&lt;/strong&gt;
A：解决了 HTTP/1.1 的队头阻塞，单连接可以并发处理多个请求，提升连接利用率。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Q：Deadline 的作用是什么？&lt;/strong&gt;
A：设置请求的截止时间，防止资源泄露，防止服务雪崩，而且会跨服务链路传递。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Q：Proto 的字段编号为什么不能改？&lt;/strong&gt;
A：因为序列化用编号标识字段，改了会导致新旧版本解析错乱，破坏兼容性。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Q：gRPC 的负载均衡有哪些策略？&lt;/strong&gt;
A：轮询、加权轮询、最小请求、P2C 等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Q：gRPC 如何做认证？&lt;/strong&gt;
A：TLS 加密、mTLS 双向认证、Token 认证，通过拦截器统一处理。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Q：Metadata 是什么？&lt;/strong&gt;
A：传递请求的元数据，类似 HTTP Header，用来传 token、trace 信息等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Q：gRPC 的缺点是什么？&lt;/strong&gt;
A：调试难、浏览器不支持、学习成本高、proto 管理成本。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
</description>
        </item>
        <item>
        <title>Go gRPC Tutorial</title>
        <link>https://SymphonyW.github.io/p/go-grpc-tutorial/</link>
        <pubDate>Thu, 26 Mar 2026 17:29:38 +0800</pubDate>
        
        <guid>https://SymphonyW.github.io/p/go-grpc-tutorial/</guid>
        <description>&lt;img src="https://SymphonyW.github.io/p/go-grpc-tutorial/gRPC.png" alt="Featured image of post Go gRPC Tutorial" /&gt;&lt;h2 id=&#34;前言&#34;&gt;前言
&lt;/h2&gt;&lt;p&gt;gRPC 是一个高性能、开源的远程过程调用（RPC）框架，由 Google 开发。它基于 HTTP/2 协议设计，默认使用 Protocol Buffers 作为序列化工具，支持跨语言、跨平台的服务调用。对于 Go 语言开发者来说，gRPC 是构建微服务架构的首选工具之一，它能够极大地简化服务间的通信开发，同时提供极高的性能。&lt;/p&gt;
&lt;p&gt;本文章将从最基础的概念开始，带你一步步完成从环境搭建到实战开发，再到容器化部署的全流程，覆盖 Windows、Linux、Mac 三大主流系统，以及 Docker 容器化部署方案，让你从零开始，快速掌握 Go 语言下的 gRPC 开发。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一核心概念入门&#34;&gt;一、核心概念入门
&lt;/h2&gt;&lt;h3 id=&#34;11-什么是-grpc&#34;&gt;1.1 什么是 gRPC？
&lt;/h3&gt;&lt;p&gt;gRPC 是一个现代化的 RPC 框架，它允许客户端应用直接调用不同机器上的服务端方法，就像调用本地方法一样。它基于 HTTP/2 协议，带来了以下优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;多路复用&lt;/strong&gt;：在同一个 TCP 连接上并行处理多个请求，减少连接开销&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;流处理&lt;/strong&gt;：支持客户端、服务端的双向流处理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;强类型接口&lt;/strong&gt;：基于 Protobuf 定义服务接口，天然支持跨语言&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;高性能&lt;/strong&gt;：二进制序列化，比 JSON 等文本格式更快、更小&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;12-什么是-protocol-buffers&#34;&gt;1.2 什么是 Protocol Buffers？
&lt;/h3&gt;&lt;p&gt;Protocol Buffers（简称 Protobuf）是 Google 推出的一种轻量级、高效的结构化数据序列化格式，它比 XML、JSON 更小、更快、更简单。在 gRPC 中，我们使用 Protobuf 来定义服务的接口和消息结构，然后通过编译器生成不同语言的代码。&lt;/p&gt;
&lt;h3 id=&#34;13-grpc-的四种服务类型&#34;&gt;1.3 gRPC 的四种服务类型
&lt;/h3&gt;&lt;p&gt;gRPC 支持四种不同类型的服务调用模式，以满足不同的业务场景：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;简单 RPC（Unary RPC）&lt;/strong&gt;：最基础的调用模式，客户端发送一个请求，服务端返回一个响应，就像普通的函数调用一样。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端流 RPC（Client-side Streaming RPC）&lt;/strong&gt;：客户端发送多个请求给服务端，服务端处理完后返回一个响应。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务端流 RPC（Server-side Streaming RPC）&lt;/strong&gt;：客户端发送一个请求，服务端返回多个响应流。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;双向流 RPC（Bidirectional Streaming RPC）&lt;/strong&gt;：客户端和服务端都可以通过读写流来发送多个消息，双方可以独立地读写。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;14-grpc-工作架构&#34;&gt;1.4 gRPC 工作架构
&lt;/h3&gt;&lt;p&gt;下图展示了 gRPC 客户端与服务端的基础交互架构：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://SymphonyW.github.io/p/go-grpc-tutorial/2026-03-26-20-51-37.png&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;img src=&#34;https://SymphonyW.github.io/p/go-grpc-tutorial/2026-03-26-20-51-37.png&#34; alt=&#34;&#34; width=&#34;1690&#34; height=&#34;931&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; class=&#34;gallery-image&#34; data-flex-grow=&#34;181&#34; data-flex-basis=&#34;435px&#34;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;客户端侧：业务代码调用本地的 gRPC Stub（客户端桩），Stub 会把调用参数通过 Protobuf 序列化为二进制数据，再通过 HTTP/2 客户端把请求发送出去。&lt;/li&gt;
&lt;li&gt;网络传输：基于 TCP 连接的 HTTP/2 协议，支持多路复用，同一个连接可以并行处理多个请求。&lt;/li&gt;
&lt;li&gt;服务端侧：HTTP/2 服务端收到请求后，先通过 Protobuf 反序列化出请求参数，交给 gRPC 服务端，再转发到你的业务服务实现，处理完成后把结果原路返回给客户端。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二protobuf-基础语法&#34;&gt;二、Protobuf 基础语法
&lt;/h2&gt;&lt;p&gt;在开始开发之前，我们需要先掌握 Protobuf 的基础语法，因为我们所有的服务定义都需要写在 &lt;code&gt;.proto&lt;/code&gt; 文件中。我们这里使用最新的 &lt;code&gt;proto3&lt;/code&gt; 语法，这也是目前 gRPC 推荐的版本。&lt;/p&gt;
&lt;h3 id=&#34;21-基础文件结构&#34;&gt;2.1 基础文件结构
&lt;/h3&gt;&lt;p&gt;一个标准的 &lt;code&gt;.proto&lt;/code&gt; 文件的基础结构如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-protobuf&#34; data-lang=&#34;protobuf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 1. 声明语法版本，必须放在文件第一行（注释除外）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syntax&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;proto3&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 2. 包声明，用于防止命名冲突，相当于命名空间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;helloworld&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 3. Go语言专属选项，指定生成代码的包路径和包名
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 格式为: 生成文件的输出路径; 包名
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;option&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;go_package&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;./;pb&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;22-消息message定义&#34;&gt;2.2 消息（Message）定义
&lt;/h3&gt;&lt;p&gt;消息是 Protobuf 中数据的载体，你可以把它理解为 Go 语言中的结构体，用来定义传输的数据结构。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-protobuf&#34; data-lang=&#34;protobuf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 定义请求消息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;HelloRequest&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 字段格式: 类型 字段名 = 字段编号;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 定义响应消息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;HelloReply&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;关键字段说明&#34;&gt;关键字段说明
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;字段类型&lt;/strong&gt;：支持基础类型（&lt;code&gt;int32&lt;/code&gt;、&lt;code&gt;int64&lt;/code&gt;、&lt;code&gt;float&lt;/code&gt;、&lt;code&gt;bool&lt;/code&gt;、&lt;code&gt;string&lt;/code&gt; 等），也支持嵌套的消息类型。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;字段编号&lt;/strong&gt;：每个字段都必须有一个唯一的数字编号，范围是 1~536870911，其中 1~15 占用 1 个字节，16~2047 占用 2 个字节，所以高频使用的字段尽量用小编号。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;重复字段&lt;/strong&gt;：如果需要定义数组 / 列表，使用 &lt;code&gt;repeated&lt;/code&gt; 关键字：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-protobuf&#34; data-lang=&#34;protobuf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Person&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// repeated 表示这是一个列表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;repeated&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hobbies&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;23-服务service定义&#34;&gt;2.3 服务（Service）定义
&lt;/h3&gt;&lt;p&gt;服务定义了 gRPC 接口的方法，用来声明客户端可以调用的远程方法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-protobuf&#34; data-lang=&#34;protobuf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 定义 Greeter 服务
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;service&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Greeter&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 定义一个简单的 RPC 方法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 格式: rpc 方法名(请求消息) returns (响应消息);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;rpc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SayHello&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HelloRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;returns&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HelloReply&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;三开发环境搭建&#34;&gt;三、开发环境搭建
&lt;/h2&gt;&lt;p&gt;接下来我们分别介绍在 Windows、Linux、Mac 三大系统下的 gRPC Go 开发环境搭建步骤，你可以根据自己的系统选择对应的部分。&lt;/p&gt;
&lt;h3 id=&#34;31-前置条件安装-go-语言&#34;&gt;3.1 前置条件：安装 Go 语言
&lt;/h3&gt;&lt;p&gt;首先你需要安装 Go 语言环境，gRPC 要求 Go 版本 &amp;gt;= 1.19，推荐使用最新的稳定版本。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;下载地址：&lt;a class=&#34;link&#34; href=&#34;https://go.dev/dl/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Go 官方下载页&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;安装完成后，打开终端 / 命令行，执行 &lt;code&gt;go version&lt;/code&gt; 验证是否安装成功：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go version
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输出类似: go version go1.22.0 linux/amd64&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;32-安装-protobuf-编译器protoc&#34;&gt;3.2 安装 Protobuf 编译器（protoc）
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;protoc&lt;/code&gt; 是 Protobuf 的编译器，用来把我们的 &lt;code&gt;.proto&lt;/code&gt; 文件编译成不同语言的代码。&lt;/p&gt;
&lt;h4 id=&#34;linux-系统ubuntudebian&#34;&gt;Linux 系统（Ubuntu/Debian）
&lt;/h4&gt;&lt;p&gt;使用 apt 包管理器直接安装：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install -y protobuf-compiler
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 验证安装&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;protoc --version
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输出: libprotoc 3.xx.x，确保版本 &amp;gt;= 3.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;mac-系统&#34;&gt;Mac 系统
&lt;/h4&gt;&lt;p&gt;使用 Homebrew 安装：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install protobuf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 验证安装&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;protoc --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;windows-系统&#34;&gt;Windows 系统
&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;前往 &lt;a class=&#34;link&#34; href=&#34;https://github.com/protocolbuffers/protobuf/releases&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Protobuf 发布页&lt;/a&gt;，下载对应 Windows 版本的压缩包，比如 &lt;code&gt;protoc-xx.x-x-win64.zip&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;解压压缩包，把解压后的 &lt;code&gt;bin&lt;/code&gt; 目录下的 &lt;code&gt;protoc.exe&lt;/code&gt; 所在的路径添加到系统的 &lt;code&gt;Path&lt;/code&gt; 环境变量中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;右键此电脑 -&amp;gt; 属性 -&amp;gt; 高级系统设置 -&amp;gt; 环境变量&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在系统变量中找到 &lt;code&gt;Path&lt;/code&gt;，点击编辑，添加 &lt;code&gt;protoc.exe&lt;/code&gt; 所在的文件夹路径&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;打开新的命令行，执行 &lt;code&gt;protoc --version&lt;/code&gt; 验证是否安装成功。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;33-安装-go-语言的-protobuf-插件&#34;&gt;3.3 安装 Go 语言的 Protobuf 插件
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;protoc&lt;/code&gt; 本身只支持基础的 C++ 等语言，要生成 Go 语言的代码，我们需要安装两个 Go 专属的插件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;protoc-gen-go&lt;/code&gt;：用来生成基础的 Protobuf 消息结构代码&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;protoc-gen-go-grpc&lt;/code&gt;：用来生成 gRPC 的服务端和客户端代码&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;执行以下命令安装这两个插件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 安装基础的 Protobuf 插件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 安装 gRPC 插件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;安装完成后，需要把 Go 的二进制目录添加到系统的 &lt;code&gt;PATH&lt;/code&gt; 环境变量中，这样系统才能找到这两个插件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看 Go 的 GOPATH 路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go env GOPATH
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输出类似: /home/user/go 或者 C:\Users\YourName\go&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Linux/Mac&lt;/strong&gt;：在终端中执行以下命令，或者把它添加到 &lt;code&gt;~/.bashrc&lt;/code&gt; 或 &lt;code&gt;~/.zshrc&lt;/code&gt; 中永久生效：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PATH&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;go env GOPATH&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/bin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt;：把 &lt;code&gt;%GOPATH%\bin&lt;/code&gt;（通常是 &lt;code&gt;C:\Users\你的用户名\go\bin&lt;/code&gt;）添加到系统的 &lt;code&gt;Path&lt;/code&gt; 环境变量中。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;验证插件是否安装成功：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 执行这两个命令，如果没有提示命令不存在，就说明安装成功了&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;protoc-gen-go --version
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;protoc-gen-go-grpc --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;四实战入门hello-world-示例&#34;&gt;四、实战入门：Hello World 示例
&lt;/h2&gt;&lt;p&gt;现在我们来完成一个最基础的 Hello World 示例，带你完整走一遍 gRPC 开发的全流程。&lt;/p&gt;
&lt;h3 id=&#34;41-创建项目结构&#34;&gt;4.1 创建项目结构
&lt;/h3&gt;&lt;p&gt;首先创建我们的项目目录，结构如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Plain&#34; data-lang=&#34;Plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grpc-hello/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── go.mod
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── go.sum
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── proto/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── helloworld.proto  # 我们的 proto 定义文件
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── server/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── main.go           # 服务端代码
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── client/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    └── main.go           # 客户端代码
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;执行以下命令初始化项目：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建项目文件夹&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir grpc-hello &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; grpc-hello
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 初始化 Go mod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go mod init github.com/yourname/grpc-hello
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;42-编写-protobuf-定义文件&#34;&gt;4.2 编写 Protobuf 定义文件
&lt;/h3&gt;&lt;p&gt;创建 &lt;code&gt;proto/helloworld.proto&lt;/code&gt; 文件，写入以下内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-protobuf&#34; data-lang=&#34;protobuf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;syntax&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;proto3&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;helloworld&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;option&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;go_package&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;../pb;pb&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 定义请求消息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;HelloRequest&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 定义响应消息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;HelloReply&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 定义 Greeter 服务
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;service&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Greeter&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;rpc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SayHello&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HelloRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;returns&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HelloReply&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;43-生成-go-代码&#34;&gt;4.3 生成 Go 代码
&lt;/h3&gt;&lt;p&gt;现在我们使用 &lt;code&gt;protoc&lt;/code&gt; 编译器，把上面的 &lt;code&gt;.proto&lt;/code&gt; 文件编译成 Go 语言的代码。在项目根目录执行以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;protoc --go_out&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;. --go-grpc_out&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;. proto/helloworld.proto
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;执行完成后，你会发现项目根目录下多了一个 &lt;code&gt;pb&lt;/code&gt; 文件夹，里面有两个文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;helloworld_grpc.pb.go&lt;/code&gt;：gRPC 相关的服务端、客户端代码&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;helloworld.pb.go&lt;/code&gt;：Protobuf 消息结构的代码&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;44-编写服务端代码&#34;&gt;4.4 编写服务端代码
&lt;/h3&gt;&lt;p&gt;创建 &lt;code&gt;server/main.go&lt;/code&gt; 文件，这是我们的 gRPC 服务端代码，用来处理客户端的请求：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;net&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;google.golang.org/grpc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nx&#34;&gt;pb&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/yourname/grpc-hello/pb&amp;#34;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 替换成你自己的模块路径
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 定义服务结构体，必须嵌入 pb.UnimplementedGreeterServer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 这是为了兼容未来的接口扩展，保证向前兼容
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nx&#34;&gt;pb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UnimplementedGreeterServer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 实现 SayHello 方法，这是我们在 proto 中定义的服务方法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;SayHello&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;HelloRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;HelloReply&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 打印收到的请求信息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Received request from: %v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;GetName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 返回响应
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;HelloReply&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Hello &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;GetName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()},&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 监听 50051 端口，这是 gRPC 的默认端口
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;lis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;net&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Listen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;:50051&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;failed to listen: %v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 创建 gRPC 服务端实例
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;grpc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewServer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 把我们的服务注册到 gRPC 服务端中
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;pb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;RegisterGreeterServer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;server listening at %v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 启动服务，开始处理请求
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Serve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;failed to serve: %v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;45-编写客户端代码&#34;&gt;4.5 编写客户端代码
&lt;/h3&gt;&lt;p&gt;创建 &lt;code&gt;client/main.go&lt;/code&gt; 文件，这是我们的客户端代码，用来调用服务端的远程方法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;google.golang.org/grpc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;google.golang.org/grpc/credentials/insecure&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nx&#34;&gt;pb&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/yourname/grpc-hello/pb&amp;#34;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 替换成你自己的模块路径
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 连接到 gRPC 服务端，这里我们连接本地的 50051 端口
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;// 使用 insecure  credentials，因为我们的示例没有开启 TLS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;grpc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Dial&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;localhost:50051&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;grpc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WithTransportCredentials&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;insecure&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewCredentials&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;did not connect: %v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 函数结束时关闭连接
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;defer&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 创建 Greeter 客户端
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewGreeterClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 设置超时时间，防止请求阻塞
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cancel&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WithTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Background&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;defer&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;cancel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 调用远程的 SayHello 方法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SayHello&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;HelloRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;could not greet: %v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// 打印响应结果
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Greeting: %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;GetMessage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;46-运行测试&#34;&gt;4.6 运行测试
&lt;/h3&gt;&lt;p&gt;现在我们来启动服务端，然后运行客户端，测试我们的 gRPC 服务。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;首先启动服务端：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go run server/main.go
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;你会看到输出：&lt;code&gt;server listening at [::]:50051&lt;/code&gt;，说明服务端已经启动成功了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;打开一个新的终端，运行客户端：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go run client/main.go
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;客户端会输出：&lt;code&gt;Greeting: Hello World&lt;/code&gt;，说明调用成功了！&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;回到服务端的终端，你会看到服务端输出：&lt;code&gt;Received request from: World&lt;/code&gt;，说明服务端成功收到了客户端的请求。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;恭喜你！你已经成功运行了你的第一个 gRPC 应用！&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五docker-容器化部署&#34;&gt;五、Docker 容器化部署
&lt;/h2&gt;&lt;p&gt;当我们开发完成后，我们可以把我们的 gRPC 服务打包成 Docker 镜像，方便部署和分发。这里我们使用&lt;strong&gt;多阶段构建&lt;/strong&gt;的方式，来最小化镜像的体积。&lt;/p&gt;
&lt;h3 id=&#34;51-编写-dockerfile&#34;&gt;5.1 编写 Dockerfile
&lt;/h3&gt;&lt;p&gt;在项目的根目录创建 &lt;code&gt;Dockerfile&lt;/code&gt; 文件，内容如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 第一阶段：构建阶段，使用完整的 Go 环境来编译代码&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; golang:1.22-alpine AS builder&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 设置工作目录&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WORKDIR&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; /app&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 复制 go.mod 和 go.sum，先下载依赖，利用 Docker 缓存&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt; go.mod go.sum ./&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; go mod download&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 复制所有代码&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt; . .&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 编译代码，CGO_ENABLED=0 表示静态编译，不依赖动态库&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# GOOS=linux 指定目标系统是 Linux&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CGO_ENABLED&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;GOOS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;linux go build -a -installsuffix cgo -o grpc-server ./server/main.go&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 第二阶段：运行阶段，使用极小的 alpine 镜像，只保留编译后的二进制文件&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; alpine:latest&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 安装 ca-certificates，用于 TLS 证书验证&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; apk --no-cache add ca-certificates&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WORKDIR&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; /root/&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 从构建阶段复制编译好的二进制文件&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt; --from&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;builder /app/grpc-server .&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 暴露服务端口&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;EXPOSE&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; 50051&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 启动服务&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CMD&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;./grpc-server&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;52-构建-docker-镜像&#34;&gt;5.2 构建 Docker 镜像
&lt;/h3&gt;&lt;p&gt;在项目根目录执行以下命令，构建 Docker 镜像：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker build -t grpc-hello-server:latest .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这个命令会自动完成多阶段构建，最终生成的镜像体积非常小，通常只有十几 MB，比直接用 Go 基础镜像小了几十倍。&lt;/p&gt;
&lt;h3 id=&#34;53-运行-docker-容器&#34;&gt;5.3 运行 Docker 容器
&lt;/h3&gt;&lt;p&gt;构建完成后，我们可以运行这个镜像，启动我们的 gRPC 服务：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker run -p 50051:50051 grpc-hello-server:latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这个命令会把容器的 50051 端口映射到主机的 50051 端口，这样我们就可以像之前一样，用本地的客户端来连接这个服务了。&lt;/p&gt;
&lt;h3 id=&#34;54-测试容器化服务&#34;&gt;5.4 测试容器化服务
&lt;/h3&gt;&lt;p&gt;现在你可以运行之前的客户端代码，来测试容器中的服务：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go run client/main.go
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;你会看到和之前一样的输出，说明容器化的服务已经正常工作了！&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六常见问题与排错&#34;&gt;六、常见问题与排错
&lt;/h2&gt;&lt;h3 id=&#34;61-protoc-command-not-found&#34;&gt;6.1 protoc: command not found
&lt;/h3&gt;&lt;p&gt;这说明系统找不到 &lt;code&gt;protoc&lt;/code&gt; 命令，你需要检查：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;是否已经正确安装了 protoc&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;是否把 protoc 的路径添加到了系统的 &lt;code&gt;PATH&lt;/code&gt; 环境变量中&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;62-plugin-protoc-gen-go-not-found&#34;&gt;6.2 Plugin protoc-gen-go not found
&lt;/h3&gt;&lt;p&gt;这说明系统找不到 &lt;code&gt;protoc-gen-go&lt;/code&gt; 插件，检查：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;是否已经正确安装了插件&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;是否把 &lt;code&gt;$GOPATH/bin&lt;/code&gt; 添加到了 &lt;code&gt;PATH&lt;/code&gt; 环境变量中&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;63-模块路径错误&#34;&gt;6.3 模块路径错误
&lt;/h3&gt;&lt;p&gt;在导入 &lt;code&gt;pb&lt;/code&gt; 包的时候，一定要替换成你自己的 &lt;code&gt;go mod&lt;/code&gt; 初始化的模块路径，否则会出现导入错误。&lt;/p&gt;
&lt;h3 id=&#34;64-连接超时&#34;&gt;6.4 连接超时
&lt;/h3&gt;&lt;p&gt;如果客户端连接服务端超时，检查：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;服务端是否已经正常启动&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;端口是否正确，防火墙是否开放了对应的端口&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果是 Docker 部署，检查端口映射是否正确&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结
&lt;/h2&gt;&lt;p&gt;通过本指南，你已经完成了从 0 开始的 gRPC 学习，掌握了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;gRPC 和 Protobuf 的核心概念&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;三大系统下的开发环境搭建&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完整的 gRPC 服务开发流程&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Docker 容器化部署方案&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;接下来你可以继续深入学习 gRPC 的高级特性，比如 TLS 加密、认证拦截、流处理、负载均衡等，来构建生产级的微服务应用。&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
