<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>yafeiaa Blogs</title>
    <link>https://yafeiaa.github.io/</link>
    <description>Recent content on yafeiaa Blogs</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 14 Jan 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://yafeiaa.github.io/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Agones SDK-Server 与 Allocator</title>
      <link>https://yafeiaa.github.io/posts/agones%E4%BD%93%E9%AA%8C/</link>
      <pubDate>Wed, 14 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://yafeiaa.github.io/posts/agones%E4%BD%93%E9%AA%8C/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;Agones 通过 SDK-Server 让 GameServer 自己管理生命周期状态。每个 GameServer 启动时会连接本地的 SDK Server，通过 gRPC API 同步状态。&lt;/p&gt;&#xA;&lt;p&gt;在实际部署时，sdk-server 会作为一个 sidecar 容器，和 gameserver 容器共享 network namespace，所以 gmaeserver 可以直接以 localhost 访问 sdk-server 的 api。&lt;/p&gt;&#xA;&lt;p&gt;sdk-server 作为 agones 和 gameserver 之间的状态同步器，负责将 gameserver 的状态同步给 agones，以及将 agones 的分配请求同步给 gameserver。&lt;/p&gt;&#xA;&lt;h2 id=&#34;allocator-service&#34;&gt;Allocator Service&lt;/h2&gt;&#xA;&lt;p&gt;Allocator 是 Agones 提供的 gRPC 服务，用于分配 GameServer。它监听集群中所有 Ready 状态的 GameServer，当收到分配请求时，选择一个合适的 GameServer 并将其状态改为 Allocated。&lt;/p&gt;&#xA;&lt;p&gt;Allocator 提供了两种分配方式：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;gRPC Service&lt;/strong&gt; - 直接调用 gRPC API（适合高性能、安全性要求高的场景，通过mtls认证）&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Allocation API&lt;/strong&gt; - 通过 Kubernetes CRD &lt;code&gt;GameServerAllocation&lt;/code&gt;（更简单，通过k8s api调用）&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;核心-api&#34;&gt;核心 API&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Ready()&lt;/strong&gt; - 标记就绪，可以被分配&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Health()&lt;/strong&gt; - 发送心跳，保持健康状态&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Shutdown()&lt;/strong&gt; - 通知关闭&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;SetAnnotation()&lt;/strong&gt; - 设置自定义信息（如 自定义的对外访问地址）&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;WatchGameServer()&lt;/strong&gt; - 监听状态变化&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;状态流转&#34;&gt;状态流转&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Port -&amp;gt; Ready -&amp;gt; Allocated -&amp;gt; Shutdown&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Port&lt;/strong&gt; - 刚启动，等待 Ready&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Ready&lt;/strong&gt; - 调用 &lt;code&gt;Ready()&lt;/code&gt; 后进入，等待分配&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Allocated&lt;/strong&gt; - 被 match-maker 分配后进入，开始游戏逻辑&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Shutdown&lt;/strong&gt; - 调用 &lt;code&gt;Shutdown()&lt;/code&gt; 或收到关闭信号后进入&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;一个dedicate-server和-match-maker通过-sdk-server-和-allocator-交互的例子&#34;&gt;一个dedicate-server和 match-maker通过 sdk-server 和 allocator 交互的例子&lt;/h2&gt;&#xA;&lt;h3 id=&#34;dedicate-server&#34;&gt;dedicate-server&lt;/h3&gt;&#xA;&lt;p&gt;启动流程：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Minimatch 轻量级匹配服务使用体验</title>
      <link>https://yafeiaa.github.io/posts/minimatch%E5%AD%A6%E4%B9%A0/</link>
      <pubDate>Wed, 14 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://yafeiaa.github.io/posts/minimatch%E5%AD%A6%E4%B9%A0/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;最近在一个项目中，了解到了minimatch这个轻量级匹配服务，并借助minimatch实现了简单的match-maker服务，进行了学习。&lt;/p&gt;&#xA;&lt;p&gt;在游戏服务器匹配场景中，Open Match 是一个成熟的解决方案，但它的架构相对复杂，需要部署多个 Kubernetes 组件。在本地开发、或者不希望额外管理open-match的复杂性，只想实现 match 核心逻辑，minimatch 是一个很好的选择。&lt;/p&gt;&#xA;&lt;h2 id=&#34;核心概念&#34;&gt;核心概念&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-frontend-服务&#34;&gt;1. Frontend 服务&lt;/h3&gt;&#xA;&lt;p&gt;Frontend 负责接收玩家的匹配请求，创建和管理 Ticket。每个 Ticket 包含玩家的搜索条件，如地区、等级、角色等。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 创建 Ticket&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ticket&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Ticket&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;SearchFields&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SearchFields&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Tags&lt;/span&gt;: []&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;region:%s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;user&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Region&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;role:%s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;user&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Role&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;DoubleArgs&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;]&lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;level&amp;#34;&lt;/span&gt;: float64(&lt;span style=&#34;color:#a6e22e&#34;&gt;user&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Level&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-backend-服务&#34;&gt;2. Backend 服务&lt;/h3&gt;&#xA;&lt;p&gt;Backend 是匹配的核心，它定期（每个 tick）从 Redis 获取活跃的 Tickets，执行匹配逻辑，并将匹配结果分配给 GameServer。&lt;/p&gt;&#xA;&lt;p&gt;Backend 包含三个关键组件：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;MatchProfile&lt;/strong&gt;：定义匹配规则，包括多个 Pool（匹配池），每个 Pool 可以设置过滤条件（如地区、等级范围）&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;MatchFunction&lt;/strong&gt;：实现具体的匹配算法，根据 Pool 中的 Tickets 生成 Match&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Assigner&lt;/strong&gt;：将匹配结果分配给 GameServer&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;3-匹配流程&#34;&gt;3. 匹配流程&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;玩家请求 → Frontend 创建 Ticket → Redis 存储&#xA;                                    ↓&#xA;Backend 定时轮询（通过提前设置的tick） → 获取 Tickets → MatchFunction 匹配 → Assigner 分配 GameServer → 更新 Ticket 状态&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;实践与-agones-集成实现一个简单的match-maker&#34;&gt;实践：与 Agones 集成实现一个简单的match-maker&lt;/h2&gt;&#xA;&lt;h3 id=&#34;架构设计&#34;&gt;架构设计&lt;/h3&gt;&#xA;&lt;p&gt;我们将 minimatch 的 Frontend 和 Backend 集成到同一个服务中，通过 HTTP API 对外提供服务：&lt;/p&gt;</description>
    </item>
    <item>
      <title>protoc-gen-cloudevents-go：从 Protobuf 自动生成类型安全的事件驱动代码</title>
      <link>https://yafeiaa.github.io/posts/protoc-gen-cloudevents-go-introduction/</link>
      <pubDate>Tue, 25 Nov 2025 00:00:00 +0000</pubDate>
      <guid>https://yafeiaa.github.io/posts/protoc-gen-cloudevents-go-introduction/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;在微服务架构和事件驱动系统中，我们经常需要编写大量的事件发布和订阅代码。这些代码往往充斥着：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;❌ &lt;strong&gt;字符串魔法值&lt;/strong&gt;：事件类型容易拼写错误&lt;/li&gt;&#xA;&lt;li&gt;❌ &lt;strong&gt;类型不安全&lt;/strong&gt;：使用 &lt;code&gt;map[string]interface{}&lt;/code&gt; 传递数据&lt;/li&gt;&#xA;&lt;li&gt;❌ &lt;strong&gt;重复代码&lt;/strong&gt;：每个事件都要手写发布/订阅函数&lt;/li&gt;&#xA;&lt;li&gt;❌ &lt;strong&gt;难以维护&lt;/strong&gt;：事件定义分散在各处，缺乏统一管理&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;为了解决这些问题，我开发了 &lt;strong&gt;protoc-gen-cloudevents-go&lt;/strong&gt; —— 一个基于 &lt;a href=&#34;https://cloudevents.io/&#34;&gt;CloudEvents&lt;/a&gt; 标准的 Protobuf 代码生成器。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href=&#34;https://github.com/yafeiaa/protoc-gen-cloudevents-go&#34;&gt;https://github.com/yafeiaa/protoc-gen-cloudevents-go&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;核心理念&#34;&gt;核心理念&lt;/h2&gt;&#xA;&lt;h3 id=&#34;单一数据源single-source-of-truth&#34;&gt;单一数据源（Single Source of Truth）&lt;/h3&gt;&#xA;&lt;p&gt;使用 Protobuf 定义事件结构，自动生成类型安全的发布/订阅代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-protobuf&#34; data-lang=&#34;protobuf&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;syntax &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;proto3&amp;#34;&lt;/span&gt;;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; myapp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;events;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cloudevents/event_meta.proto&amp;#34;&lt;/span&gt;;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 用户注册事件&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;message&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;UserRegisteredPayload&lt;/span&gt; {&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;option&lt;/span&gt; (cloudevents.event_meta) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;    event_type&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;myapp.user.registered&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;    description&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;用户注册成功&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;  };&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; user_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; email &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;int64&lt;/span&gt; registered_at &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;}&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一条命令生成所有代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;protoc &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -I . &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --go_out&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;. &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --cloudevents_out&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;. &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  events.proto&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;类型安全--零样板代码&#34;&gt;类型安全 + 零样板代码&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;传统方式&lt;/strong&gt; vs &lt;strong&gt;使用 protoc-gen-cloudevents&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Go 服务性能优化实战：从 pprof 分析到落地方案</title>
      <link>https://yafeiaa.github.io/posts/go-performance-optimization-from-pprof-to-practice/</link>
      <pubDate>Fri, 31 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://yafeiaa.github.io/posts/go-performance-optimization-from-pprof-to-practice/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;在生产环境中，Go 服务的性能问题往往不是单一原因造成的。本文基于真实的性能优化经验，通过 pprof 工具分析一个高并发 gRPC 服务的性能瓶颈，并给出具体的优化方案和代码实现。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;优化成果&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;CPU 使用率降低 30-50%&lt;/li&gt;&#xA;&lt;li&gt;内存对象数降低 65-95%&lt;/li&gt;&#xA;&lt;li&gt;P99 延迟降低 35-55%&lt;/li&gt;&#xA;&lt;li&gt;QPS 提升 50-80%&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;一性能分析的正确姿势&#34;&gt;一、性能分析的正确姿势&lt;/h2&gt;&#xA;&lt;h3 id=&#34;11-pprof-数据的类型&#34;&gt;1.1 pprof 数据的类型&lt;/h3&gt;&#xA;&lt;p&gt;Go 的 pprof 提供了多个维度的 profile 数据：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;┌─────────────────────────────────────────────────────┐&#xA;│                  pprof 数据类型                       │&#xA;├─────────────────────────────────────────────────────┤&#xA;│ CPU Profile      │ 程序运行时 CPU 占用情况            │&#xA;│ Heap (inuse)     │ 当前正在使用的内存                │&#xA;│ Heap (alloc)     │ 历史累计的内存分配                │&#xA;│ Goroutine        │ 当前 goroutine 的数量和状态       │&#xA;│ Block            │ 同步原语（锁）的阻塞情况           │&#xA;│ Mutex            │ 互斥锁的竞争情况                  │&#xA;└─────────────────────────────────────────────────────┘&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;关键洞察&lt;/strong&gt;：不同的 profile 数据能揭示不同的问题&lt;/p&gt;</description>
    </item>
    <item>
      <title>读书| 2025年想看的书</title>
      <link>https://yafeiaa.github.io/posts/2025%E5%B9%B4%E6%83%B3%E7%9C%8B%E7%9A%84%E4%B9%A6/</link>
      <pubDate>Tue, 24 Dec 2024 12:03:00 +0800</pubDate>
      <guid>https://yafeiaa.github.io/posts/2025%E5%B9%B4%E6%83%B3%E7%9C%8B%E7%9A%84%E4%B9%A6/</guid>
      <description>&lt;h2 id=&#34;2025年想看的书&#34;&gt;2025年想看的书&lt;/h2&gt;&#xA;&lt;p&gt;2025年，我25岁，我希望自己能读完以下这些书：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;《我们生活在巨大的差距里》- 余华&lt;/li&gt;&#xA;&lt;li&gt;《小城与不确定性的墙》 - 村上春树&lt;/li&gt;&#xA;&lt;li&gt;《乡土中国》 - 费孝通&lt;/li&gt;&#xA;&lt;li&gt;《团圆记》 - 杨云苏&lt;/li&gt;&#xA;&lt;li&gt;《弃长安》 - 张明扬&lt;/li&gt;&#xA;&lt;li&gt;《我与地坛》 - 史铁生&lt;/li&gt;&#xA;&lt;li&gt;《草民》 - 蔡崇达&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;希望能够以娱乐的心态，读完这些书，给自己的生活增添一些乐趣，而不是给自己增加任务。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Golang| golang项目中对json处理的优化</title>
      <link>https://yafeiaa.github.io/posts/golang%E9%A1%B9%E7%9B%AE%E4%B8%AD%E5%AF%B9json%E5%A4%84%E7%90%86%E7%9A%84%E4%BC%98%E5%8C%96/</link>
      <pubDate>Mon, 16 Dec 2024 16:36:15 +0800</pubDate>
      <guid>https://yafeiaa.github.io/posts/golang%E9%A1%B9%E7%9B%AE%E4%B8%AD%E5%AF%B9json%E5%A4%84%E7%90%86%E7%9A%84%E4%BC%98%E5%8C%96/</guid>
      <description>&lt;p&gt;本文主要介绍我在golang项目中遇到的一个json.Marshal导致的内存占用大的问题，以及解决思路和方案。&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题描述&#34;&gt;问题描述&lt;/h2&gt;&#xA;&lt;p&gt;在我的一个golang项目中，其中有一个http服务，它提供的api接口需要返回一些数Mb级别的json数据，在很长的一段时间内都能正常使用，但是随着业务的增长，发现这个服务使用的内存越来越大，从最开始的2gb规模增长到了10gb甚至更高，而且随着时间的推移，内存占用还在不断增长，最终导致服务OOM、进程被kill。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://yafeiaa.github.io/image/golang-memory.png&#34; alt=&#34;内存使用量&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题分析&#34;&gt;问题分析&lt;/h2&gt;&#xA;&lt;p&gt;为了解决这个问题，我首先通过监控面板对比了该服务器的几个指标：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;CPU使用量&lt;/li&gt;&#xA;&lt;li&gt;内存使用量&lt;/li&gt;&#xA;&lt;li&gt;goroutines&lt;/li&gt;&#xA;&lt;li&gt;向操作系统请求的内存量&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在某个内存增长的点，这些指标的值变化如下图：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://yafeiaa.github.io/image/golang-memory-metrics.png&#34; alt=&#34;内存使用量&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;可以在图上看到，内存使用量增长的时候，cpu使用量在增长、向操作系统请求的内存量也在增长。但是goroutines没有明显变化。&lt;/p&gt;&#xA;&lt;p&gt;这里可以排除是goroutines泄漏导致的问题，因为goroutines数量一直都比较平稳。&lt;/p&gt;&#xA;&lt;p&gt;排除了goroutines泄漏的问题之后，这里我们猜测是正常的服务器行为导致的内存使用量增多。&lt;/p&gt;&#xA;&lt;p&gt;然后，我通过golang的pprof endpoint中暴露出的数据，对内存使用量增长的原因进行了分析。在golang的pprof中暴露了几个关于内存的数据：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;inuse_space — 已分配但尚未释放的内存空间&lt;/li&gt;&#xA;&lt;li&gt;inuse_objects——已分配但尚未释放的对象数量&lt;/li&gt;&#xA;&lt;li&gt;alloc_space — 分配的内存总量(已释放的也会统计)&lt;/li&gt;&#xA;&lt;li&gt;alloc_objects — 分配的对象总数(无论是否释放)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这里我们重点对inuse_space进行分析：&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Inuse_space&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://yafeiaa.github.io/image/golang-memory-inusespace.png&#34; alt=&#34;inuse_space&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;在图中，可以明显看到，约89%的内存都来自于json.Marshal函数，json.Marshal在这个服务中充当的作用就是对一些大对象进行序列化，返回给客户端。&lt;/p&gt;&#xA;&lt;p&gt;所以，这里可以确认，内存使用量增长的原因就是json.Marshal函数导致的。&lt;/p&gt;&#xA;&lt;h2 id=&#34;解决方案&#34;&gt;解决方案&lt;/h2&gt;&#xA;&lt;p&gt;既然问题已经确认，那么接下来就是想办法解决这个问题。&lt;/p&gt;&#xA;&lt;p&gt;通过对json.Marshal函数的源码进行分析，发现它使用的是反射机制，对结构体进行遍历，然后对每个字段进行序列化，最终将序列化后的结果拼接成一个字符串返回。&lt;/p&gt;&#xA;&lt;p&gt;众所周知，golang是静态编译型语言，为了在运行时提高灵活性，我们有些时候不得不使用反射机制，但是golang的反射机制性能较差（具体见参考资料）。所以会导致json.Marshal函数在序列化大对象的时候，性能较差。&lt;/p&gt;&#xA;&lt;p&gt;在社区中，有很多对标准库&lt;code&gt;encoding/json&lt;/code&gt;的优化方案，比如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;字节跳动开源的sonic：https://github.com/bytedance/sonic&lt;/li&gt;&#xA;&lt;li&gt;滴滴开源的json-iterator：https://github.com/json-iterator/go&lt;/li&gt;&#xA;&lt;li&gt;jsonparser：https://github.com/buger/jsonparser&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;其中，json-interator完全兼容&lt;code&gt;encoding/json&lt;/code&gt;接口，便于直接替换标准库，而sonic和jsonparser则没有兼容&lt;code&gt;encoding/json&lt;/code&gt;，需要修改代码。&lt;/p&gt;&#xA;&lt;p&gt;所以，基于兼容性考虑，我选择了json-iterator。&lt;/p&gt;&#xA;&lt;p&gt;1、安装json-iterator&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go get github.com/json-iterator/go&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;2、导入json-iterator并替换encoding/json&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// &amp;#34;encoding/json&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/json-iterator/go&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 替换encoding/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;jsoniter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ConfigCompatibleWithStandardLibrary&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;优化效果&#34;&gt;优化效果&lt;/h2&gt;&#xA;&lt;p&gt;在经过调整之后，再次观察在相同qps下，inuse_space的分布：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://yafeiaa.github.io/image/golang-memory-youhua.png&#34; alt=&#34;inuse_space&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;从图上可以看到，json.Marshal函数造成的大量占用已经消失。&lt;/p&gt;&#xA;&lt;p&gt;从grafana的内存使用量上也可以看到内存使用量也降到了原来的1/3左右。&lt;/p&gt;&#xA;&lt;h3 id=&#34;参考资料&#34;&gt;参考资料&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://draveness.me/golang/docs/part2-foundation/ch04-basic/golang-reflect/&#34;&gt;golang反射实现原理&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://juejin.cn/post/7186859098661453884&#34;&gt;golang反射性能的问题&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/json-iterator/go&#34;&gt;json-iterator&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/bytedance/sonic&#34;&gt;sonic&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;</description>
    </item>
    <item>
      <title>gRPC的负载均衡和服务发现</title>
      <link>https://yafeiaa.github.io/posts/grpc%E7%9A%84%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E5%92%8C%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/</link>
      <pubDate>Fri, 27 Sep 2024 14:22:00 +0800</pubDate>
      <guid>https://yafeiaa.github.io/posts/grpc%E7%9A%84%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E5%92%8C%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/</guid>
      <description>&lt;hr&gt;&#xA;&lt;p&gt;在分布式系统中，gRPC凭借其高性能和高效的通信方式，近年来越来越受欢迎，被广泛应用于微服务架构和客户端/服务器（C/S）之间的通信协议中。本文将分享我在使用gRPC过程中关于负载均衡和服务发现的一些实践经验、思考以及遇到的问题，并通过grpc-go的示例代码，深入探讨客户端负载均衡的实现。&lt;/p&gt;&#xA;&lt;h3 id=&#34;为什么选择grpc&#34;&gt;为什么选择gRPC？&lt;/h3&gt;&#xA;&lt;p&gt;gRPC基于HTTP/2协议，支持多路复用、流控、头部压缩等特性，使其在性能和效率上优于传统的HTTP/1.x协议。此外，gRPC支持多种语言，方便跨语言服务间的通信，对于微服务架构的扩展性和灵活性提供了极大的支持。&lt;/p&gt;&#xA;&lt;h3 id=&#34;负载均衡的必要性&#34;&gt;负载均衡的必要性&lt;/h3&gt;&#xA;&lt;p&gt;在微服务架构中，一个服务通常有多个实例以应对高并发和高可用的需求。负载均衡负责将客户端的请求合理分配到各个服务实例上，以实现资源的有效利用和系统的高可用性。gRPC支持多种负载均衡策略，包括客户端负载均衡和服务端负载均衡。&lt;/p&gt;&#xA;&lt;h3 id=&#34;客户端负载均衡在grpc中的实现&#34;&gt;客户端负载均衡在gRPC中的实现&lt;/h3&gt;&#xA;&lt;p&gt;在gRPC中，负载均衡可以在客户端实现，这样客户端能够直接感知到多个服务实例，并根据一定的策略进行请求分发。以下是使用grpc-go实现客户端负载均衡的示例。&lt;/p&gt;&#xA;&lt;h4 id=&#34;示例使用round-robin策略实现客户端负载均衡&#34;&gt;示例：使用Round Robin策略实现客户端负载均衡&lt;/h4&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;安装依赖&#xA;首先，确保已安装grpc-go。可以通过以下命令安装：&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;go get google.golang.org/grpc&#xA;bash&#xA;2. 自定义解析器和负载均衡器&#xA;gRPC需要自定义服务发现和负载均衡策略，这里以Round Robin为例进行实现。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sync/atomic&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;google.golang.org/grpc&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;google.golang.org/grpc/resolver&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;google.golang.org/grpc/balancer&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;google.golang.org/grpc/balancer/base&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 定义服务地址&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;scheme&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;custom&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceName&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;myService&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 解析器实现&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;customResolver&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;target&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Target&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;cc&lt;/span&gt;     &lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ClientConn&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;addrs&lt;/span&gt;  []&lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Address&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;customResolver&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Start&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;addrs&lt;/span&gt; = []&lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Address&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {&lt;span style=&#34;color:#a6e22e&#34;&gt;Addr&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;localhost:50051&amp;#34;&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {&lt;span style=&#34;color:#a6e22e&#34;&gt;Addr&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;localhost:50052&amp;#34;&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {&lt;span style=&#34;color:#a6e22e&#34;&gt;Addr&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;localhost:50053&amp;#34;&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;cc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;UpdateState&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;State&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;Addresses&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;addrs&lt;/span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;customResolver&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;ResolveNow&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;o&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ResolveNowOptions&lt;/span&gt;) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;customResolver&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Close&lt;/span&gt;() {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newCustomResolver&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;target&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Target&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;cc&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ClientConn&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;opts&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;BuildOptions&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Resolver&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;cr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;customResolver&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;target&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;target&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;cc&lt;/span&gt;:     &lt;span style=&#34;color:#a6e22e&#34;&gt;cc&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;cr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Start&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cr&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 注册解析器&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Register&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;resolver&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Builder&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Scheme&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;scheme&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Build&lt;/span&gt;:  &lt;span style=&#34;color:#a6e22e&#34;&gt;newCustomResolver&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// Deprecated: we use a separate build function&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 简单的Round Robin负载均衡器&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;roundRobinBalancer&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;roundRobinBalancer&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;Pick&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;info&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;balancer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PickInfo&lt;/span&gt;) (&lt;span style=&#34;color:#a6e22e&#34;&gt;balancer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PickResult&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;atomic&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AddUint64&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;addr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;info&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CC&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;GetAddresses&lt;/span&gt;()[&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt;uint64(len(&lt;span style=&#34;color:#a6e22e&#34;&gt;info&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CC&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;GetAddresses&lt;/span&gt;()))]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;balancer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PickResult&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;SubConn&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;addr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SubConn&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Done&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;info&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;balancer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DoneInfo&lt;/span&gt;) {}}, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newRoundRobinBuilder&lt;/span&gt;() &lt;span style=&#34;color:#a6e22e&#34;&gt;balancer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Builder&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;base&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewBalancerBuilder&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;round_robin&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;roundRobinBalancer&lt;/span&gt;{}, &lt;span style=&#34;color:#a6e22e&#34;&gt;base&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Config&lt;/span&gt;{})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 注册负载均衡器&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;balancer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Register&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;newRoundRobinBuilder&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 创建连接&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;conn&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;grpc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Dial&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%s:///%s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;scheme&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceName&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;grpc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WithInsecure&lt;/span&gt;(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;grpc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WithBalancerName&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;round_robin&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatalf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;did not connect: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;conn&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Close&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 创建gRPC客户端&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;NewMyServiceClient&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;conn&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 发起请求&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;resp&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MyMethod&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Background&lt;/span&gt;(), &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;MyRequest&lt;/span&gt;{})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Error calling MyMethod: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Response: %v&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;resp&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;time&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Second&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;&#xA;&lt;li&gt;代码解析&#xA;自定义解析器 (customResolver)：实现了resolver.Resolver接口，模拟服务发现过程。在实际应用中，可以从服务注册中心（如etcd、Consul）动态获取服务实例列表。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;注册解析器：使用resolver.Register注册自定义解析器，指定Scheme为custom。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Golang| golang中为什么resp.body要关闭</title>
      <link>https://yafeiaa.github.io/posts/golang%E4%B8%ADresp.body%E8%A6%81%E5%85%B3%E9%97%AD%E7%9A%84%E5%8E%9F%E5%9B%A0/</link>
      <pubDate>Sat, 10 Sep 2022 11:20:00 +0800</pubDate>
      <guid>https://yafeiaa.github.io/posts/golang%E4%B8%ADresp.body%E8%A6%81%E5%85%B3%E9%97%AD%E7%9A%84%E5%8E%9F%E5%9B%A0/</guid>
      <description>&lt;p&gt;本文介绍在golang的标准库net/http中，为什么请求之后，要主动关闭resp.body、以及不关闭resp.body有什么影响。&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题描述&#34;&gt;问题描述&lt;/h2&gt;&#xA;&lt;p&gt;在golang中，我们使用http标准库发起一个http请求，一般会使用如下的方式：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;resp&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;http&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://www.baidu.com&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Body&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Close&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ioutil&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ReadAll&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;resp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Body&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(string(&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上面的代码中，我们使用http.Get发起一个http请求，然后使用ioutil.ReadAll读取resp.Body的内容，最后关闭resp.Body。&lt;/p&gt;&#xA;&lt;p&gt;在学习其他编程语言，如python，我们一般不需要进行类似的操作，这是为什么呢？&lt;/p&gt;&#xA;&lt;h2 id=&#34;问题分析&#34;&gt;问题分析&lt;/h2&gt;&#xA;&lt;h3 id=&#34;阅读源码&#34;&gt;阅读源码&lt;/h3&gt;&#xA;&lt;p&gt;要回答这个问题，我们首先要知道net/http包在做请求的时候，背后都做了哪些事情，所以要阅读net/http包的源码。&lt;/p&gt;&#xA;&lt;p&gt;getConn：获取一个连接，或者重新创建连接&#xA;&lt;img src=&#34;https://yafeiaa.github.io/image/pconn.png&#34; alt=&#34;getConn&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;queueForDial: 把连接创建请求（后文简称w，一个经过初始化的wantConn结构体）放入到dialer的队列中等待被使用&lt;/p&gt;&#xA;&lt;p&gt;一旦满足以下两种条件：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;没有限制MaxConnsPerHost&lt;/li&gt;&#xA;&lt;li&gt;还没有达到MaxConnsPerHost&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://yafeiaa.github.io/image/queueForDial.png&#34; alt=&#34;queueForDial&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;就会通过dialConnFor函数为w创建一个真正的往目标地址的连接，dialConnFor函数创建连接调用了dialConn函数。&lt;/p&gt;&#xA;&lt;p&gt;而dialConn函数是最关键的，在dialConn函数的最后，拉起了两个goroutine：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;readloop&lt;/li&gt;&#xA;&lt;li&gt;writeloop&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;我们这里分析下这两个goroutine的作用以及退出条件。&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;readloop&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;通过调用pc.readResponse函数接收response。&lt;/p&gt;&#xA;&lt;p&gt;我们省略掉一些不重要的代码，只看关键的部分：&#xA;readloop内部有一个for循环，循环的退出条件是：alive == false，而alive赋值为false的时机是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;resp.Close为true&lt;/li&gt;&#xA;&lt;li&gt;rc.req.Close为true&lt;/li&gt;&#xA;&lt;li&gt;resp.StatusCode == 199（https://http.dev/199）&lt;/li&gt;&#xA;&lt;li&gt;bodyWritable为true&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;其他三点我们先忽略，重点关注resp.Close条件。这就是问题的答案，如果不执行resp.Body.Close，那么resp.Close永远为false，所以readloop永远不会退出，就会在程序生命周期中永远保留一个goroutine和文件句柄。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://yafeiaa.github.io/image/readLoop.png&#34; alt=&#34;readLoop&#34;&gt;&lt;/p&gt;&#xA;&lt;ol start=&#34;2&#34;&gt;&#xA;&lt;li&gt;writeloop&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;writeloop的作用是往目标地址写入请求数据，并且进行一些写入的错误处理。&lt;/p&gt;&#xA;&lt;p&gt;它内部是一个阻塞式的for-select控制结构，退出的条件是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;pc.closech能够读到数据&lt;/li&gt;&#xA;&lt;li&gt;写入数据时出错&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://yafeiaa.github.io/image/writeloop.png&#34; alt=&#34;writeloop&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;所以，如果我们在使用http.Get发起请求时，没有执行resp.Body.Close，那么readloop和writeloop就会一直保留，直到程序退出。&lt;/p&gt;&#xA;&lt;h3 id=&#34;问题总结&#34;&gt;问题总结&lt;/h3&gt;&#xA;&lt;p&gt;在net/http包中，如果我们在发起请求时没有执行resp.Body.Close，那么就会导致readloop和writeloop这两个goroutine一直保留，直到程序退出。所以，为了防止goroutine泄露，我们需要在发起请求后，及时执行resp.Body.Close。&lt;/p&gt;&#xA;&lt;h3 id=&#34;参考资料&#34;&gt;参考资料&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/golang/go&#34;&gt;golang源代码&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;</description>
    </item>
    <item>
      <title>Python| Drf常用view解析</title>
      <link>https://yafeiaa.github.io/posts/drf%E5%B8%B8%E8%A7%81%E8%A7%86%E5%9B%BE%E5%89%96%E6%9E%90/</link>
      <pubDate>Wed, 01 Dec 2021 12:11:15 +0800</pubDate>
      <guid>https://yafeiaa.github.io/posts/drf%E5%B8%B8%E8%A7%81%E8%A7%86%E5%9B%BE%E5%89%96%E6%9E%90/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;image/image_eL4tGCOeJs.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-apiview&#34;&gt;1 APIView&lt;/h2&gt;&#xA;&lt;p&gt;drf提供了一个继承自View类的APIView类，它比View多了一些好用的方法。&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;由APIView来对请求对象进行再次封装，将django的HttpRequest变为drf的Request&lt;/li&gt;&#xA;&lt;li&gt;返回Response而不是HttpResponse&lt;/li&gt;&#xA;&lt;li&gt;捕获所有的APIException并进行响应&lt;/li&gt;&#xA;&lt;li&gt;传入的请求通过APIView来进行认证、权限验证等操作&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;11-apiview的aop&#34;&gt;1.1 APIView的AOP&lt;/h3&gt;&#xA;&lt;p&gt;APIView保持了View的特性，dispatch方法在APIView里也存在，它按照不同的请求方法将请求分发至某个函数来处理。&lt;/p&gt;&#xA;&lt;p&gt;继承自APIView的视图可以设置很多AOP(类属性)来进行认证、权限、频率、渲染等控制。&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;.render_classes：设置渲染器&lt;/li&gt;&#xA;&lt;li&gt;.parser_classes：设置解析器&lt;/li&gt;&#xA;&lt;li&gt;.authentication_classes：设置认证器&lt;/li&gt;&#xA;&lt;li&gt;.throttle_classes：设置频率控制器&lt;/li&gt;&#xA;&lt;li&gt;.permission_classes：设置权限控制器&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;12-策略方法&#34;&gt;1.2 策略方法&lt;/h3&gt;&#xA;&lt;p&gt;APIView也提供了一些策略方法：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;.get_renderers(self)&lt;/li&gt;&#xA;&lt;li&gt;.get_parsers(self)&lt;/li&gt;&#xA;&lt;li&gt;.get_authenticators(self)&lt;/li&gt;&#xA;&lt;li&gt;.get_throttles(self)&lt;/li&gt;&#xA;&lt;li&gt;.get_permissions(self)&lt;/li&gt;&#xA;&lt;li&gt;.get_content_negotiator(self)&lt;/li&gt;&#xA;&lt;li&gt;.get_exception_handler(self)&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;这些策略方法会返回解析器、渲染器、频率控制器、权限控制器这些AOP的可调用对象列表：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_renderers&lt;/span&gt;(self):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        Instantiates and returns the list of renderers that this view can use.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [renderer() &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; renderer &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;renderer_classes]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_parsers&lt;/span&gt;(self):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        Instantiates and returns the list of parsers that this view can use.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [parser() &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; parser &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parser_classes]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_authenticators&lt;/span&gt;(self):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        Instantiates and returns the list of authenticators that this view can use.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [auth() &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; auth &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;authentication_classes]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_permissions&lt;/span&gt;(self):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        Instantiates and returns the list of permissions that this view requires.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [permission() &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; permission &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;permission_classes]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_throttles&lt;/span&gt;(self):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        Instantiates and returns the list of throttles that this view uses.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [throttle() &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; throttle &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;throttle_classes]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;13-控制策略实现&#34;&gt;1.3 控制策略实现&lt;/h3&gt;&#xA;&lt;p&gt;APIView如何实现权限、频率控制呢?在APIView内部实现了一些方法，在request分派到具体的处理方法（get、post）之前，它会自动调用一些策略进行验证：&lt;/p&gt;</description>
    </item>
    <item>
      <title>读书｜vue.js快跑</title>
      <link>https://yafeiaa.github.io/posts/vue.js%E5%BF%AB%E8%B7%91%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/</link>
      <pubDate>Mon, 05 Jul 2021 20:14:45 +0800</pubDate>
      <guid>https://yafeiaa.github.io/posts/vue.js%E5%BF%AB%E8%B7%91%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://yafeiaa.github.io/image/image_NY2X1a4XEO.png&#34; alt=&#34;&#34;&gt;&#xA;vue.js，一个渐进式JavaScript框架，借助其优秀的响应式设计，提供了简单的数据状态到视图状态的双向映射。从原生js到jqury，react····，框架设计者们都在探索视图和数据之间的松耦合，以至于可以直接通过改变数据来改变视图。vue是在众多探索者中的一个把view和state之间的关系处理的恰到好处的渐进式框架。&lt;/p&gt;&#xA;&lt;p&gt;为什么说它是渐进式呢？&lt;/p&gt;&#xA;&lt;p&gt;其实vue的核心功能就是前面说到的：提供一个简单的数据状态到视图状态到双向映射，它更像是一个视图引擎，帮助开发者消除数据与视图之间的高耦合。在vue官网中，把这个功能称为——“声明式渲染”。&lt;/p&gt;&#xA;&lt;p&gt;在vue生态中，我们可以通过添加组件系统，客户端路由，大规模状态管理，构建工具来让vue从一个“声明式渲染”引擎变为一个真正意义上的客户端开发框架，所以vue是“渐进式”的js框架（至少我是这么认为的，如果你有不同看法，洗耳恭听😄）。&lt;/p&gt;&#xA;&lt;p&gt;这篇文章笔者准备围绕vue的核心功能——声明式渲染来介绍vue的入门级单文件用法，后面再结合组件系统，vuex，vue-router，webpack/vite等来介绍vue等工程化用法。&lt;/p&gt;&#xA;&lt;h3 id=&#34;感受声明式渲染&#34;&gt;感受声明式渲染&lt;/h3&gt;&#xA;&lt;p&gt;我们前面提到，vue的核心就是它的声明式渲染，帮我们做好数据状态到视图状态的双向映射。&lt;/p&gt;&#xA;&lt;p&gt;在《vue.js快跑》一书中，用模版、指令、数据解释了vue的组成部分，通过这些组成部分可以实现vue的声明式渲染（这里我们暂且不讨论原理）：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;为正常的HTML添加特殊的属性——被称作指令——借助它来告诉vue我们想要实现的效果以及如何处理提供给它的数据。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;div id=&amp;#34;app&amp;#34;&amp;gt;&#xA;  &amp;lt;button v-on:click=&amp;#34;change_show&amp;#34;&amp;gt;点我一下我就让你好看&amp;lt;/button&amp;gt;&#xA;  &amp;lt;span v-show=&amp;#34;show_word&amp;#34;&amp;gt;好看&amp;lt;/span&amp;gt;&#xA;&amp;lt;/div&amp;gt;&#xA;&amp;lt;script&amp;gt;&#xA;  new Vue({&#xA;    el: &amp;#39;#app&amp;#39;,&#xA;    data: {&#xA;      show_word: false &#xA;    },&#xA;    methods: {&#xA;      change_show:function(){&#xA;        this.show_word = !this.show_word;&#xA;      }&#xA;    }&#xA;  })&#xA;&amp;lt;/script&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在上面的代码模型中。点击“点我一下我就让你好看”之后在页面上就会出现“好看”，在这个过程中，vue帮助我们实现了一个完整的交互和状态响应。&lt;/p&gt;&#xA;&lt;p&gt;如果要使用原生js来实现上面的功能，我们需要考虑每一个实现细节：&lt;/p&gt;&#xA;&lt;p&gt;1、捕获点击动作&lt;/p&gt;&#xA;&lt;p&gt;2、更改状态变量值&lt;/p&gt;&#xA;&lt;p&gt;3、根据改变后的状态变量值来渲染页面&lt;/p&gt;&#xA;&lt;p&gt;而在vue中，我们只需要把一个数据和一个改变数据的方法声明在script中，并且通过vue的指令将两者建立联系，就可以实现”声明一个数据，通过改变数据改变状态”，我们不需要再关注其他的东西，因为这些vue已经帮助我们做好了。&lt;/p&gt;&#xA;&lt;h3 id=&#34;v-if-vs-v-show&#34;&gt;v-if vs v-show&lt;/h3&gt;&#xA;&lt;p&gt;在前面的例子中我们已经了解到vue的声明式渲染大概是一个什么样子的，丰富的vue指令让我们可以更好的让数据和视图进行绑定。我们在前面的“点我一下就让你好看”的例子中使用了v-show这个指令，v-show指令是通过判断一个表达式或者变量的值是否为真来决定模版的渲染逻辑，它的作用有点像if语句。但实际上vue中也有专门的v-if用来控制条件渲染。&lt;/p&gt;&#xA;&lt;p&gt;首先看看这两个指令的作用：&lt;/p&gt;&#xA;&lt;p&gt;1、v-if：如果v-if指令的值为假，那么在渲染dom时，当前的元素就不会被插入dom&lt;/p&gt;&#xA;&lt;p&gt;2、v-show：如果v-show的值为假，那么在显示dom的时候，这个元素不会被显示，它是css级别的控制&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;div v-if=&amp;#34;true&amp;#34;&amp;gt;&#xA;  😄&#xA;&amp;lt;/div&amp;gt;&#xA;&amp;lt;div v-if=&amp;#34;false&amp;#34;&amp;gt;&#xA;  👖&#xA;&amp;lt;/div&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上面的代码会被浏览器解析成这样的：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;div&amp;gt;&#xA;  😄&#xA;&amp;lt;/div&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从这里就可以看到，v-if指令的值为false的元素并不会被加入最终的html中。&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;div v-show=&amp;#34;true&amp;#34;&amp;gt;&#xA;  😁&#xA;&amp;lt;/div&amp;gt;&#xA;&amp;lt;div v-show=&amp;#34;false&amp;#34;&amp;gt;&#xA;  👌&#xA;&amp;lt;/div&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;浏览器解析这一段代码之后，它会产生这样的效果：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;div&amp;gt;&#xA;  😁&#xA;&amp;lt;/div&amp;gt;&#xA;&amp;lt;div style=&amp;#34;display:none;&amp;#34;&amp;gt;&#xA;  👌&#xA;&amp;lt;/div&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;v-show指令的值不管是不是真，这个元素最终都会被加入最终的html中。&lt;/p&gt;</description>
    </item>
    <item>
      <title>值得纪念的时刻</title>
      <link>https://yafeiaa.github.io/photos/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://yafeiaa.github.io/photos/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;image/image_eL4tGCOeJs.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
