avatar

mdo

Hello

  • 首页
  • 知识库
  • 归档
  • 标签
  • 关于
主页 Golang 中,结合使用 RPC(远程过程调用)与 WebSocket
文章

Golang 中,结合使用 RPC(远程过程调用)与 WebSocket

发表于 2026-06-19 更新于 2026-06- 19
作者 mdo
16~21 分钟 阅读

在 Golang 中,结合使用 RPC(远程过程调用)与 WebSocket 是一种极佳的实时全双工通信方案。它不仅克服了传统 gRPC 对浏览器支持不佳的痛点,还能利用 WebSocket 的长连接特性消除频繁建立 HTTP 连接的开销。 [1, 2, 3]

常见的实现方案主要基于 JSON-RPC 2.0 协议,并配合经典的 gorilla/websocket(或现代的 nhooyr.io/websocket)库。 [1, 2, 3, 4, 5]


🛠️ 核心架构与原理

  1. 握手阶段:客户端发起标准 HTTP 请求,通过 Upgrade 头将连接升级为 WebSocket。

  2. 传输阶段:双端复用此 TCP 长连接,将带有 id、method 和 params 的 JSON-RPC 载荷封装在 WebSocket 文本/二进制帧中发送。 [1, 2, 3, 4, 5]


💻 完整代码实现

以下是一个基于 JSON-RPC 标准的高效生产级示例:

1. 服务端实现 (server.go) [1, 2]

go

package main

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"log"
	"math/big"
	"net/http"

	"github.com/gorilla/websocket"
)

// 定义 RPC 请求与响应结构体
type RPCRequest struct {
	JSONRPC string          `json:"jsonrpc"`
	Method  string          `json:"method"`
	Params  json.RawMessage `json:"params"`
	ID      interface{}     `json:"id"`
}

type RPCResponse struct {
	JSONRPC string      `json:"jsonrpc"`
	Result  interface{} `json:"result,omitempty"`
	Error   interface{} `json:"error,omitempty"`
	ID      interface{} `json:"id"`
}

// 定义服务业务参数
type MathArgs struct {
	A, B int
}

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool { return true }, // 生产环境请限制跨域安全
}

func handleWebSocket(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("升级连接失败:", err)
		return
	}
	defer conn.Close()

	for {
		// 读取客户端发送的 WebSocket 消息
		_, message, err := conn.ReadMessage()
		if err != nil {
			log.Println("读取消息失败:", err)
			break
		}

		var req RPCRequest
		if err := json.Unmarshal(message, &req); err != nil {
			sendError(conn, nil, "解析 JSON 失败")
			continue
		}

		// 简单的路由分发处理器 (RPC Dispatcher)
		var result interface{}
		switch req.Method {
		case "Math.Multiply":
			var args MathArgs
			json.Unmarshal(req.Params, &args)
			result = args.A * args.B

		case "Crypto.GenerateID":
			nBig, _ := rand.Int(rand.Reader, big.NewInt(100000))
			result = nBig.Int64()

		default:
			sendError(conn, req.ID, "未找到该方法")
			continue
		}

		// 返回成功响应
		resp := RPCResponse{JSONRPC: "2.0", Result: result, ID: req.ID}
		respBytes, _ := json.Marshal(resp)
		conn.WriteMessage(websocket.TextMessage, respBytes)
	}
}

func sendError(conn *websocket.Conn, id interface{}, msg string) {
	resp := RPCResponse{
		JSONRPC: "2.0",
		Error:   map[string]string{"message": msg},
		ID:      id,
	}
	respBytes, _ := json.Marshal(resp)
	conn.WriteMessage(websocket.TextMessage, respBytes)
}

func main() {
	http.HandleFunc("/rpc", handleWebSocket)
	log.Println("RPC WebSocket 服务器已在 :8080/rpc 启动...")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

请谨慎使用此类代码。

2. 客户端实现 (client.go)

go

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/url"

	"github.com/gorilla/websocket"
)

func main() {
	u := url.URL{Scheme: "ws", Host: "localhost:8080", Path: "/rpc"}
	log.Printf("正在连接到 %s", u.String())

	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Fatal("连接失败:", err)
	}
	defer c.Close()

	// 构造 RPC 调用参数
	params, _ := json.Marshal(map[string]int{"A": 6, "B": 7})
	req := map[string]interface{}{
		"jsonrpc": "2.0",
		"method":  "Math.Multiply",
		"params":  json.RawMessage(params),
		"id":      1,
	}

	reqBytes, _ := json.Marshal(req)
	err = c.WriteMessage(websocket.TextMessage, reqBytes)
	if err != nil {
		log.Fatal("发送失败:", err)
	}

	// 接收服务器响应
	_, message, err := c.ReadMessage()
	if err != nil {
		log.Fatal("读取失败:", err)
	}

	fmt.Printf("收到服务器 RPC 响应: %s\n", message)
}

请谨慎使用此类代码。


⚖️ 工业界生产级方案替代

如果你不想在底层自己手写框架并处理复杂的连接复用(Multiplexing),可以使用以下成熟生态: [1]

  • 区块链生态的选择 (Web3 / Ethereum):直接引用 go-ethereum/rpc。以太坊官方高度优化了基于 WebSocket 的 JSON-RPC 服务器与客户端代码,支持发布/订阅 (Pub/Sub) 模式。

  • 企业级高并发双向 RPC:推荐 Chainlink 开源的 smartcontractkit/wsrpc。该库在 WebSockets 之上构建了一个带有 mTLS 强安全认证的全双工 RPC 传输层,性能和安全性极高。

  • 轻量级自定义服务生成:如果更倾向于使用契约(Protobuf),可以尝试 toba/wsrpc,它可以通过 Protobuf 文件直接生成基于 WebSocket 运行的 Go 服务代码。 [1, 2, 3, 4, 5]

技术
许可协议:  CC BY 4.0
分享

相关文章

10月 2, 2026

iOS 侧载(Sideloading)工具

AltStore 和 Sideloadly 都是目前最流行的 iOS 侧载(Sideloading)工具。 简单来说,它们的作用就是让你绕过苹果的官方 App Store,直接把第三方的 IPA 安装包(也就是 iOS 的应用安装文件)安装到你的 iPhone 或 iPad 上。 通过这两个工具,你

9月 28, 2026

注册 Chat Participant

将当前服务接入 VS Code 的 AI 聊天窗口是完全可行的。VS Code 在近期的版本中正式推出了 Chat Extensions API(github.copilot.chat),允许第三方扩展将自定义的 LLM 服务或工具注册到 Copilot Chat 面板中。 结合当前 code_ge

9月 23, 2026

php intelephense 设置 if 空格

在 VS Code 中,如果你使用的是 PHP Intelephense 插件,控制 if 关键字后面是否加空格的设置是由其内置的格式化引擎(基于 PSR-12 标准)决定的。 你可以通过修改 VS Code 的 settings.json 来配置这个行为: 1. 开启 if 后面的空格(默认/推荐

下一篇

自己编写一个自定义的 MCP 服务器

上一篇

开发一个博客文章发布 App

最近更新

  • 比尔盖茨的智慧:想多赚钱,就每天循环做这3件事
  • iOS 侧载(Sideloading)工具
  • 程序员越想创业,越不要急着动手
  • 注册 Chat Participant
  • 中秋给在外游子的一封信

热门标签

API CodeGeex Coding Cursor DeepSeek Docker Gitkraken Harness Laravel Management

目录

©2026 mdo. 保留部分权利。