第一章:Go-TS互操作标准提案(GoTS-0.3)概述与演进背景
GoTS-0.3 是面向 Go 与 TypeScript 双语言生态的轻量级互操作协议规范,旨在解决跨语言类型系统不一致、API 契约同步滞后及运行时桥接开销高等长期痛点。该提案并非强制运行时框架,而是一套可验证的契约约定层,覆盖类型映射规则、序列化语义、错误传播模型及模块边界声明机制。
设计动因
现代全栈项目普遍采用 Go 实现高并发后端服务,同时以 TypeScript 构建响应式前端或 CLI 工具。传统做法依赖手工维护接口文档或生成式代码(如 Swagger + openapi-generator),易导致类型漂移与调试断点断裂。GoTS-0.3 的提出直指这一协作瓶颈——它要求开发者在 Go 接口定义中嵌入 // @gots:export 注释,并通过 gots-cli 工具链自动提取类型契约,生成严格对齐的 TypeScript 声明文件。
核心演进特征
- 零运行时依赖:不引入任何中间代理或 RPC 层,仅约束 JSON 序列化行为(如
time.Time→ ISO 8601 字符串、error→{ code: string; message: string }结构) - 渐进式采纳:支持按包/函数粒度启用,未标注的 Go 函数默认不参与契约导出
- 类型收敛保障:内置类型校验器,拒绝导出含
unsafe.Pointer、chan或未导出字段的结构体
快速验证示例
# 安装 GoTS 工具链(需 Go 1.21+ 和 Node.js 18+)
go install github.com/gots-org/cli@v0.3.1
npm install -D @gots/core@0.3.1
# 在 Go 源码中添加契约注释
# type User struct {
# ID int `json:"id"`
# Name string `json:"name"`
# }
# // @gots:export GetUser(id int) (*User, error)
执行 gots-cli generate --src ./server --out ./client/src/types 后,将自动生成 User.ts 与 get-user.ts,其中包含类型安全的调用签名与运行时校验辅助函数。该流程已集成至主流 CI 流水线模板,确保每次 git push 后前端类型定义与后端逻辑保持原子级一致性。
第二章:IDL定义规范与双向类型映射实践
2.1 Go与TypeScript基础类型语义对齐原理与边界案例分析
Go 的静态强类型与 TypeScript 的结构化类型系统在基础类型层面存在隐式对齐,但语义鸿沟常在运行时暴露。
数据同步机制
当 Go int64 映射为 TS number 时,超出 2^53 - 1 的整数精度丢失:
// TS 端接收 Go int64(9007199254740992n + 1n) → 实际变为 9007199254740992
const unsafeNum = 9007199254740992 + 1; // 9007199254740992(错误!)
→ 此处 +1 被 IEEE-754 双精度浮点舍入抹除,需改用 bigint 或字符串序列化。
类型映射边界表
| Go 类型 | TypeScript 类型 | 安全条件 | 风险场景 |
|---|---|---|---|
bool |
boolean |
✅ 全覆盖 | — |
string |
string |
✅ | \0 截断(C-interop) |
[]byte |
Uint8Array |
⚠️ 需显式转换 | 直接赋值触发类型错误 |
空值语义分歧
Go 的 nil slice 与 TS 的 undefined/null 不等价:
- Go
var s []int→ JSON 序列化为null - TS 解析后需
s ?? []防TypeError
// Go: nil slice marshals to null
var data []string
jsonBytes, _ := json.Marshal(data) // → []byte("null")
→ json.Marshal 将 nil slice 输出为 null,TS 侧须统一用 Array.isArray(x) || x == null 判定。
2.2 结构体/接口在IDL中的声明语法与生成器契约约定
IDL(Interface Definition Language)是跨语言服务契约的核心载体,结构体与接口的声明需严格遵循生成器可解析的语法规则。
结构体声明示例
struct User {
1: required i32 id; // 字段序号、修饰符、类型、名称
2: optional string name; // optional 表示可为空,生成代码中映射为指针或Option
3: binary avatar; // binary 类型统一映射为字节数组/Vec<u8>/[]byte
}
字段序号(1/2/3)不可跳变或重复,确保二进制序列化兼容性;required/optional 直接影响目标语言的空安全语义(如 Rust 的 Option<T>、Go 的指针字段)。
接口契约约束
- 方法参数必须为结构体或基础类型,禁止嵌套接口
- 返回值仅支持单结构体、基本类型或
void - 所有方法默认为同步调用,异步需显式标注(如
async关键字)
| 生成器 | struct 生成规则 | interface 方法签名 |
|---|---|---|
| Rust | #[derive(Serialize, Deserialize)] |
fn get_user(&self, req: GetUserReq) -> Result<User, Error> |
| Go | type User struct { Id int32 \thrift:”1,required”`|GetUser(ctx context.Context, req GetUserReq) (User, error)` |
生成器契约流程
graph TD
A[IDL解析] --> B[语法校验:序号连续、无循环引用]
B --> C[语义检查:字段修饰符与类型匹配]
C --> D[代码生成:按目标语言空安全/内存模型适配]
2.3 泛型与联合类型(Union / Discriminated Union)的跨语言建模策略
在跨语言系统(如 TypeScript ↔ Rust ↔ Python)中,泛型需映射为各语言原生支持的参数化类型,而联合类型则需区分“裸联合”(如 TS string | number)与“可鉴别联合”(如 Rust 的 enum 或 TS 的 tagged union)。
数据同步机制
不同语言对联合类型的序列化语义存在差异:
| 语言 | 裸联合支持 | 可鉴别联合语法 | 运行时类型保留 |
|---|---|---|---|
| TypeScript | ✅ | type Shape = Circle \| Rect |
❌(擦除) |
| Rust | ❌ | enum Shape { Circle, Rect } |
✅ |
| Python | ⚠️(typing.Union) | @dataclass + __match_args__ |
✅(需手动) |
// TS: 可鉴别联合(推荐跨语言)
type Event =
| { kind: "click"; x: number; y: number }
| { kind: "keypress"; key: string };
此模式显式携带
kind标签,使 Rust 可直接映射为enum Event { Click { x: f64, y: f64 }, Keypress { key: String } },Python 则用@dataclass+kind字段实现模式匹配。
类型桥接策略
- 泛型接口需通过契约约定类型参数名(如
TItem→TItem),避免语言特有关键字冲突; - 所有联合必须带唯一 discriminator 字段(如
kind,type,_tag),禁止无标签联合跨语言传输。
// Rust: 对应上述 TS 联合
#[derive(Deserialize, Serialize)]
#[serde(tag = "kind")]
pub enum Event {
Click { x: f64, y: f64 },
Keypress { key: String },
}
#[serde(tag = "kind")]指示 Serde 使用kind字段作为判别键,确保与 TS 序列化格式完全一致;f64与 TSnumber对齐,String映射string。
graph TD A[TS Union] –>|JSON序列化| B[“{ kind: ‘click’, x: 10, y: 20 }”] B –>|Serde反序列化| C[Rust Enum Variant] C –>|Serde序列化| B
2.4 枚举与常量同步机制:从IDL到Go struct tag与TS enum的自动推导
数据同步机制
在跨语言服务通信中,IDL(如Protobuf或Thrift)定义的枚举需精准映射至各端类型系统。现代代码生成器通过 AST 解析 IDL 中 enum Status { PENDING = 0; SUCCESS = 1; },并注入语义化 struct tag 与 TypeScript 枚举。
自动生成策略
- Go 端:为字段添加
json:"status" enum:"PENDING=0,SUCCESS=1"tag,供序列化/校验库消费 - TS 端:生成
export enum Status { PENDING = 0, SUCCESS = 1 },保留原始命名与值
type Order struct {
Status int `json:"status" enum:"PENDING=0,SUCCESS=1,FAILED=2"`
}
此 tag 不仅声明 JSON 键名,更显式绑定枚举语义——
enum值被go-jsonenum等工具解析,用于运行时校验与 Swagger 枚举注解生成。
| IDL 定义 | Go struct tag | TS enum 输出 |
|---|---|---|
PENDING = 0 |
PENDING=0 |
PENDING = 0 |
SUCCESS = 1 |
SUCCESS=1 |
SUCCESS = 1 |
graph TD
A[IDL enum] --> B[AST 解析]
B --> C[生成 Go struct tag]
B --> D[生成 TS enum]
C --> E[运行时校验/文档生成]
D --> F[TypeScript 类型安全]
2.5 IDL版本兼容性设计:字段可选性、废弃标记与零值迁移路径
IDL演进中,兼容性需兼顾向后兼容与平滑升级。核心策略包含三重机制:
字段可选性声明
通过optional关键字标识非必需字段,避免旧客户端因缺失字段解析失败:
message User {
int32 id = 1;
optional string nickname = 2; // 新增字段设为optional
}
optional使生成代码中该字段默认为null或undefined,序列化时不强制写入,反序列化时缺失即跳过,消除required字段升级引发的硬中断。
废弃标记与迁移提示
使用deprecated = true标注淘汰字段,并配合注释说明替代方案:
| 字段名 | 状态 | 替代字段 | 生效版本 |
|---|---|---|---|
email_old |
deprecated | contact.email |
v2.3+ |
零值迁移路径
新增字段初始值设为语言零值(如/""/false),配合服务端默认填充逻辑,实现无感升级。
graph TD
A[客户端v1.0] -->|发送无nickname字段| B[服务端v2.3]
B --> C{字段存在?}
C -->|否| D[填入默认空字符串]
C -->|是| E[保留原值]
第三章:统一错误码体系与上下文传播机制
3.1 错误码分层编码模型:业务域、模块、操作粒度三级编码实践
错误码不再是扁平的数字堆砌,而是承载语义的结构化标识。采用 DDD 思维划分三层:业务域(2位)、模块(2位)、操作(2位),形成统一6位十六进制编码(如 010203 → 支付域-订单模块-创建失败)。
编码结构设计
- 业务域:
01=支付,02=用户,03=营销 - 模块:
02=订单,04=账户,05=优惠券 - 操作:
01=查询,03=创建,07=校验
| 域 | 模块 | 操作 | 全码 | 含义 |
|---|---|---|---|---|
| 02 | 04 | 07 | 020407 |
用户域-账户模块-余额校验失败 |
示例实现(Java)
public enum ErrorCode {
USER_ACCOUNT_BALANCE_CHECK_FAILED("020407", "余额不足,无法完成扣款");
private final String code;
private final String message;
ErrorCode(String code, String message) {
this.code = code; // 严格6位,校验位数与格式
this.message = message;
}
}
code 字段强制6位字符串,便于日志解析与监控系统自动提取域/模块/操作维度;message 仅作提示,不参与路由决策。
错误传播路径
graph TD
A[客户端请求] --> B[API网关]
B --> C[业务服务]
C --> D[领域服务]
D --> E[统一错误构造器]
E --> F[返回 020407 + 上下文traceId]
该模型支持按域聚合告警、模块级熔断、操作粒度审计——真正让错误成为可运营的数据资产。
3.2 Go error wrapping与TS Error subclassing的双向错误构造与解包协议
核心设计目标
实现跨语言错误上下文的保真传递:Go 中 fmt.Errorf("failed: %w", err) 的 wrapped error 链,需在 TypeScript 中还原为带 cause 属性的自定义 Error 子类,反之亦然。
双向序列化协议
使用统一 JSON Schema 描述错误结构:
| 字段 | Go 类型 | TS 类型 | 说明 |
|---|---|---|---|
message |
string |
string |
基础错误消息 |
code |
string |
string |
业务错误码(如 "AUTH_TIMEOUT") |
cause |
*json.RawMessage |
Error \| null |
嵌套错误序列化体 |
// TS 端 Error 子类构造器(自动解包 cause)
class ApiError extends Error {
code: string;
cause?: ApiError;
constructor({ message, code, cause }: { message: string; code: string; cause?: unknown }) {
super(message);
this.code = code;
this.cause = cause instanceof ApiError ? cause : undefined;
}
}
此构造器接收原始 JSON 解析结果;若
cause字段存在且为合法ApiError实例,则递归建立错误链,确保err.cause?.cause可逐层访问。
// Go 端包装逻辑(支持嵌套 wrap)
func WrapAPIError(err error, code string) error {
return fmt.Errorf("%s (code=%s): %w", err.Error(), code, err)
}
fmt.Errorf的%w动词保留原始 error 接口,配合errors.Unwrap()和errors.Is()实现运行时链式解包;code作为元数据注入,不破坏标准 error 行为。
错误流图示
graph TD
A[Go error] -->|JSON 序列化| B[{"message":..., "code":..., "cause":...}]
B -->|反序列化| C[TS ApiError]
C -->|toJSON + cause 递归| B
3.3 上下文携带错误元数据:traceID、requestID、重试计数在调用链中的透传实现
在分布式调用链中,错误定位依赖于上下文元数据的全程透传。核心字段包括全局唯一 traceID(标识整条链路)、单次请求 requestID(区分重试实例)及 retryCount(记录当前重试次数)。
透传机制设计
- 使用
ThreadLocal+InheritableThreadLocal管理跨线程上下文 - 通过 HTTP Header(如
X-Trace-ID、X-Request-ID、X-Retry-Count)实现服务间传递 - 框架层自动注入与提取(如 Spring Sleuth + Micrometer)
示例:OpenFeign 拦截器注入
public class TraceContextInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
TraceContext ctx = TraceContextHolder.get(); // 获取当前上下文
template.header("X-Trace-ID", ctx.traceId()); // 全局链路ID
template.header("X-Request-ID", ctx.requestId()); // 当前请求ID
template.header("X-Retry-Count", String.valueOf(ctx.retryCount())); // 重试序号
}
}
逻辑说明:TraceContextHolder.get() 从 ThreadLocal 中安全读取上下文;retryCount 在每次重试前由客户端逻辑递增,确保下游可识别幂等性边界。
元数据传播状态表
| 字段 | 类型 | 是否必需 | 生成时机 | 用途 |
|---|---|---|---|---|
X-Trace-ID |
UUID | ✅ | 首入口生成 | 全链路追踪根ID |
X-Request-ID |
UUID | ✅ | 每次请求生成(含重试) | 区分同一 trace 下不同重试实例 |
X-Retry-Count |
int | ⚠️ | 客户端维护 | 辅助判断是否为重试流量 |
graph TD
A[Client Entry] -->|生成 traceID/requestID| B[Service A]
B -->|透传 headers| C[Service B]
C -->|+1 retryCount| D[Service C]
D -->|上报至 Tracing Backend| E[Jaeger/Zipkin]
第四章:超时熔断协议与弹性通信保障
4.1 双向超时协商机制:Go context.Deadline与TS AbortSignal的语义对齐与转换
在跨语言 RPC 场景中,Go 服务端通过 context.Deadline() 暴露截止时间,而 TypeScript 客户端需将其映射为符合 Web 标准的 AbortSignal。
语义对齐核心原则
- Go 的
Deadline()返回time.Time,表示绝对截止时刻; AbortSignal.timeout()接受毫秒数,表示相对延迟;- 转换必须基于客户端本地时钟与服务端 NTP 同步误差容忍(通常 ≤50ms)。
时间基准转换逻辑
// 将 Go context.Deadline() 转为 AbortSignal(需已知服务端时钟偏移 offsetMs)
function deadlineToAbortSignal(deadlineUnixMs: number, offsetMs: number): AbortSignal {
const now = Date.now();
const clientDeadline = deadlineUnixMs - offsetMs; // 对齐服务端时间轴
const timeoutMs = Math.max(0, clientDeadline - now);
return AbortSignal.timeout(timeoutMs);
}
逻辑分析:
deadlineUnixMs来自 Go 服务端ctx.Deadline().UnixMilli();offsetMs是客户端通过/health/clock接口获取的服务端-本地时钟差值;timeoutMs为非负整数,确保AbortSignal.timeout()行为确定。
关键参数对照表
| 参数 | Go 端来源 | TS 端用途 | 约束条件 |
|---|---|---|---|
deadlineUnixMs |
ctx.Deadline().UnixMilli() |
绝对截止时间基准 | 必须序列化为 JSON number |
offsetMs |
/health/clock 响应体字段 |
服务端-客户端时钟校准 | 动态缓存,有效期 ≤1s |
graph TD
A[Go ctx.Deadline()] --> B[UnixMilli → JSON number]
B --> C[HTTP Header 或 Body 透传]
C --> D[TS 解析 + offset 校准]
D --> E[AbortSignal.timeout relativeMs]
4.2 熔断状态机实现:基于滑动窗口失败率的Go CircuitBreaker与TS fallback策略协同
状态机核心逻辑
熔断器在 Closed → Open → Half-Open 三态间流转,触发条件为滑动窗口内失败率 ≥ 阈值(如 50%),且窗口大小固定为 100 请求。
滑动窗口计数器(Go 实现)
type SlidingWindow struct {
bucketSize time.Duration // 1s
buckets []bucket // 循环数组,长度=窗口秒数
mu sync.RWMutex
}
type bucket struct {
success, failure uint64
}
使用时间分片桶避免锁竞争;
bucketSize决定精度,buckets数量控制内存开销(如 60s 窗口需 60 个桶)。
TS Fallback 协同机制
当熔断器进入 Open 状态时,TypeScript 客户端自动降级至本地缓存或兜底数据源,通过 fetchWithFallback() 封装调用链。
| 状态 | Go 服务行为 | TS 客户端响应 |
|---|---|---|
Closed |
正常转发请求 | 直接消费 API 响应 |
Open |
立即返回 ErrCircuitOpen |
触发 fallback() |
Half-Open |
允许单个试探请求 | 启用 retryOnce() |
graph TD
A[请求到达] --> B{熔断器状态?}
B -->|Closed| C[执行业务逻辑]
B -->|Open| D[返回 ErrCircuitOpen]
B -->|Half-Open| E[放行1次试探]
C --> F[成功?]
F -->|是| G[更新滑动窗口 success++]
F -->|否| H[更新滑动窗口 failure++]
4.3 重试退避策略标准化:指数退避+抖动在Go retry.Retryer与TS retry-axios插件中的统一对齐
为什么需要抖动(Jitter)
纯指数退避易引发“重试风暴”——大量客户端在同一时刻重试,压垮下游服务。抖动通过随机化延迟,实现负载削峰。
标准化参数映射
| 参数 | Go retry.Retryer |
TypeScript retry-axios |
语义 |
|---|---|---|---|
BaseDelay |
retry.NewExponentialBackoff() |
backOff: (retryCount) => ... |
初始等待时间(ms) |
MaxDelay |
MaxJitter |
maxRetryDelay |
最大单次延迟上限 |
JitterRatio |
Jitter bool + ratio |
jitter: 'full' \| 'none' |
抖动强度(0–1) |
Go 端实现示例
cfg := retry.Config{
Attempts: 5,
Backoff: retry.ExponentialBackoff(100*time.Millisecond, 2.0), // base=100ms, factor=2
Jitter: retry.FullJitter, // [0, delay] 均匀随机
}
逻辑分析:ExponentialBackoff(100ms, 2.0) 生成序列:100ms → 200ms → 400ms → 800ms → 1600ms;FullJitter 对每项乘以 rand.Float64(),使实际延迟落在 [0, computed] 区间,避免同步重试。
TS 端对齐配置
axios.create({
retry: {
attempts: 5,
backOff: (retryCount) => Math.min(100 * 2 ** retryCount, 1600), // 指数截断
jitter: 'full', // 启用 full jitter
}
});
该配置确保 JS 与 Go 在相同 retryCount 下生成等价退避基线,并通过 Math.random() 实现相同抖动语义。
4.4 连接级与请求级熔断分离:gRPC-Web over HTTP/2与WebSocket双通道场景下的协议适配
在双通道混合架构中,连接级熔断需保护底层 TCP/WebSocket 长连接稳定性,而请求级熔断须独立管控 gRPC-Web 的单次 RPC 调用成功率。
协议适配核心挑战
- HTTP/2 通道天然支持多路复用,但不暴露连接状态给前端 JS;
- WebSocket 无内置流控,需手动注入心跳与帧级错误码映射;
- 熔断器必须解耦 transport 生命周期与逻辑调用生命周期。
双熔断器协同模型
// 前端双熔断器实例化(伪代码)
const connCircuit = new CircuitBreaker({
failureThreshold: 3, // 连接建立/心跳失败阈值
timeout: 30_000,
stateStorage: 'localStorage' // 持久化连接健康状态
});
const reqCircuit = new CircuitBreaker({
failureThreshold: 5, // RPC StatusCode != OK 次数
timeout: 10_000,
stateStorage: 'memory' // 请求级状态无需持久化
});
connCircuit 监控 WebSocket.onclose 与 fetch().then().catch() 异常;reqCircuit 则解析 gRPC-Web 的 status 和 details 字段,实现细粒度请求失败归因。
熔断策略对比表
| 维度 | 连接级熔断 | 请求级熔断 |
|---|---|---|
| 触发条件 | TCP 断连、WebSocket Close | gRPC status ≠ OK |
| 恢复机制 | 定时探测 + 指数退避 | 半开态下逐个请求试探 |
| 状态粒度 | 全局通道唯一 | 按 service/method 分片 |
graph TD
A[HTTP/2 或 WebSocket 连接] --> B{连接健康?}
B -->|否| C[触发 connCircuit 熔断]
B -->|是| D[发起 gRPC-Web 请求]
D --> E{响应 status == OK?}
E -->|否| F[触发 reqCircuit 熔断]
E -->|是| G[返回数据]
第五章:GoTS-0.3草案落地路线图与社区共建倡议
核心里程碑拆解
GoTS-0.3草案已进入工程化验证阶段。2024年Q3完成首个可运行参考实现(go-ts-runtime v0.3.0-alpha),支持TypeScript 5.4语法子集及Go原生类型映射;Q4启动三类典型场景压测:微服务API契约校验(基于gin+swag)、前端组件类型桥接(React 18 + go-wasm)、CLI工具链集成(cobra + ts-node)。所有测试用例均开源至github.com/gots-org/test-cases,含17个真实业务片段,如电商订单状态机类型约束、IoT设备配置Schema双向同步等。
社区协作机制
采用双轨制贡献模型:
- 规范组:由TC39观察员、Go核心团队成员及TypeScript资深维护者组成,每两周召开RFC评审会议(日程公开于calendar.gots.dev);
- 实现组:开放GitHub Issue标签体系,
area/parser、area/stdlib、area/tooling三类问题优先级自动分级,新PR需通过CI流水线中4类强制检查:AST一致性校验、Go模块兼容性扫描、TS声明文件生成验证、跨平台编译测试(Linux/macOS/Windows)。
关键技术验证表
| 验证项 | 当前状态 | 负责人 | 下一步动作 |
|---|---|---|---|
| 泛型类型参数推导 | ✅ 已通过127个边界用例 | @liu-tao | 合并至v0.3.0-beta分支 |
declare global 声明合并 |
⚠️ Windows路径解析失败 | @kai-zhang | 提交PR #412修复 |
| WebAssembly目标输出 | ❌ 缺失内存管理绑定 | 社区悬赏任务 | 悬赏金额$2000(见gots.dev/bounties) |
开发者体验优化
提供零配置快速启动模板:
# 一键生成符合GoTS-0.3规范的项目骨架
curl -sL https://gots.dev/install.sh | sh -s -- --template=full-stack
# 自动生成:tsconfig.json(含go-ts插件)、go.mod(含gots-runtime)、Dockerfile(多阶段构建)
生态集成案例
字节跳动内部已上线GoTS-0.3试点项目:将12万行TS前端逻辑通过gots convert命令迁移为Go类型定义,自动生成api/v1/types.go与client/types.ts双向同步代码,类型错误捕获率提升63%,CI构建耗时降低22%(数据来自内部DevOps看板2024-Q3报告)。
资源支持计划
每月发布《GoTS实战周报》,包含:
- 新增10个真实故障复盘(如“npm包版本锁导致类型冲突”)
- 社区贡献者排行榜(按PR合并数、文档完善度、Issue响应速度三维加权)
- 免费在线沙盒环境(预装VS Code + GoTS插件 + 实时类型诊断面板)
参与方式入口
- Discord频道
#gots-0.3-dev提供实时协作白板与屏幕共享调试室 - 中文文档翻译任务已拆解为37个Markdown片段,提交至translate.gots.dev
- 教育合作计划:向高校计算机系开放GoTS教学套件(含Jupyter Notebook实验课件、期末项目题库、自动评分脚本)
flowchart LR
A[开发者提交Issue] --> B{是否含复现步骤?}
B -->|是| C[自动触发CI环境复现]
B -->|否| D[标记为needs-repro]
C --> E[生成AST差异快照]
E --> F[匹配历史相似问题]
F -->|匹配成功| G[推送关联PR链接]
F -->|无匹配| H[分配至对应area标签组]
社区每周同步更新GoTS-0.3兼容性矩阵,覆盖132个主流npm包与87个Go模块的版本适配状态,数据源直连GitHub API与proxy.golang.org镜像。
