【Go语言】RPC 使用指南(初学者版)

news2025/4/26 16:18:04

RPC(Remote Procedure Call,远程过程调用)是一种计算机通信协议,允许程序调用另一台计算机上的子程序,就像调用本地程序一样。Go 语言内置了 RPC 支持,下面我会详细介绍如何使用。

1

一、基本概念

在 Go 中,RPC 主要通过 net/rpc 包实现,它使用 Gob 编码进行数据传输。Go 还提供了 net/rpc/jsonrpc 包,支持 JSON 编码的 RPC。

二、最简单的 RPC 示例

1. 定义服务

首先需要定义一个服务类型及其方法:

package main

import (
	"errors"
	"log"
	"net"
	"net/rpc"
)

// 定义服务结构体
type Arith struct{}

// 定义服务方法
// 注意:方法必须满足以下条件:
// 1. 方法是导出的(首字母大写)
// 2. 有两个参数,都是导出类型或内建类型
// 3. 第二个参数是指针
// 4. 返回 error 类型
func (t *Arith) Multiply(args *Args, reply *int) error {
	*reply = args.A * args.B
	return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) error {
	if args.B == 0 {
		return errors.New("divide by zero")
	}
	quo.Quo = args.A / args.B
	quo.Rem = args.A % args.B
	return nil
}

// 定义参数结构体
type Args struct {
	A, B int
}

// 定义返回结构体
type Quotient struct {
	Quo, Rem int
}

2. 启动 RPC 服务器

func main() {
	// 创建服务实例
	arith := new(Arith)
	
	// 注册服务
	rpc.Register(arith)
	
	// 注册服务到HTTP处理器(可选)
	// rpc.HandleHTTP()
	
	// 监听TCP连接
	l, err := net.Listen("tcp", ":1234")
	if err != nil {
		log.Fatal("listen error:", err)
	}
	
	// 开始接受连接
	for {
		conn, err := l.Accept()
		if err != nil {
			log.Fatal("accept error:", err)
		}
		
		// 为每个连接创建goroutine处理
		go rpc.ServeConn(conn)
	}
	
	// 如果使用HTTP,可以这样启动:
	// http.ListenAndServe(":1234", nil)
}

3. 创建 RPC 客户端

package main

import (
	"log"
	"net/rpc"
)

// 定义参数结构体
type Args struct {
	A, B int
}

// 定义返回结构体
type Quotient struct {
	Quo, Rem int
}

func main() {
	// 连接RPC服务器
	client, err := rpc.Dial("tcp", "localhost:1234")
	if err != nil {
		log.Fatal("dialing:", err)
	}
	
	// 同步调用
	args := &Args{7, 8}
	var reply int
	err = client.Call("Arith.Multiply", args, &reply)
	if err != nil {
		log.Fatal("arith error:", err)
	}
	log.Printf("Arith: %d*%d=%d", args.A, args.B, reply)
	
	// 异步调用
	quotient := new(Quotient)
	divCall := client.Go("Arith.Divide", args, quotient, nil)
	replyCall := <-divCall.Done // 等待完成
	if replyCall.Error != nil {
		log.Fatal("arith error:", replyCall.Error)
	}
	log.Printf("Arith: %d/%d=%d...%d", args.A, args.B, quotient.Quo, quotient.Rem)
}

三、JSON-RPC 示例

如果你想使用 JSON 编码而不是 Gob 编码:

服务器端

func main() {
	arith := new(Arith)
	rpc.Register(arith)
	
	l, err := net.Listen("tcp", ":1234")
	if err != nil {
		log.Fatal("listen error:", err)
	}
	
	for {
		conn, err := l.Accept()
		if err != nil {
			log.Fatal("accept error:", err)
		}
		
		// 使用JSON编码
		go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
	}
}

客户端

func main() {
	conn, err := net.Dial("tcp", "localhost:1234")
	if err != nil {
		log.Fatal("dial error:", err)
	}
	
	client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
	
	args := &Args{7, 8}
	var reply int
	err = client.Call("Arith.Multiply", args, &reply)
	if err != nil {
		log.Fatal("arith error:", err)
	}
	log.Printf("Arith: %d*%d=%d", args.A, args.B, reply)
}

四、HTTP 上的 RPC

服务器端

func main() {
	arith := new(Arith)
	rpc.Register(arith)
	rpc.HandleHTTP()
	
	err := http.ListenAndServe(":1234", nil)
	if err != nil {
		log.Fatal("listen error:", err)
	}
}

客户端

func main() {
	client, err := rpc.DialHTTP("tcp", "localhost:1234")
	if err != nil {
		log.Fatal("dialing:", err)
	}
	
	args := &Args{7, 8}
	var reply int
	err = client.Call("Arith.Multiply", args, &reply)
	if err != nil {
		log.Fatal("arith error:", err)
	}
	log.Printf("Arith: %d*%d=%d", args.A, args.B, reply)
}

五、更现代的 gRPC

Go 的标准 RPC 包功能有限,Google 开发的 gRPC 是更现代的 RPC 框架:

1. 安装 gRPC

go get -u google.golang.org/grpc
go get -u github.com/golang/protobuf/protoc-gen-go

2. 定义 proto 文件

创建 hello.proto:

syntax = "proto3";

package hello;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

3. 生成代码

protoc --go_out=plugins=grpc:. hello.proto

4. 实现服务端

package main

import (
	"context"
	"log"
	"net"
	
	"google.golang.org/grpc"
	pb "path/to/your/package" // 替换为你的包路径
)

type server struct{}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func main() {
	lis, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &server{})
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

5. 实现客户端

package main

import (
	"context"
	"log"
	"os"
	"time"
	
	"google.golang.org/grpc"
	pb "path/to/your/package" // 替换为你的包路径
)

func main() {
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)
	
	name := "world"
	if len(os.Args) > 1 {
		name = os.Args[1]
	}
	
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("Greeting: %s", r.Message)
}

六、选择建议

  1. 标准库 RPC:简单、轻量,适合内部服务通信
  2. JSON-RPC:需要跨语言通信时使用
  3. gRPC:现代、高性能、支持多种语言,适合生产环境

七、常见问题

  1. 方法不满足要求:确保方法签名符合要求(两个参数,第二个是指针,返回 error)
  2. 连接问题:检查服务器是否启动,端口是否正确

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2343380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python----深度学习(基于深度学习Pytroch簇分类,圆环分类,月牙分类)

一、引言 深度学习的重要性 深度学习是一种通过模拟人脑神经元结构来进行数据学习和模式识别的技术&#xff0c;在分类任务中展现出强大的能力。 分类任务的多样性 分类任务涵盖了各种场景&#xff0c;例如簇分类、圆环分类和月牙分类&#xff0c;每种任务都有不同的…

uniapp-商城-38-shop 购物车 选好了 进行订单确认4 配送方式1

配送方式在订单确认页面最上方&#xff0c;可以进行选中配送还是自提&#xff0c;这里先看看配送。 代码样式&#xff1a; 可以看出来是通过组件来实现的。组件名字是&#xff1a;delivery-layout 1、建立组件文件夹和页面&#xff0c;delivery-layout这里就只有配送 2、具体…

uniappx 打包配置32位64位x86安装包

{"app": {"distribute": {"android": {"abiFilters": ["armeabi-v7a","arm64-v8a","x86","x86_64"]}}} }

【深度强化学习 DRL 快速实践】逆向强化学习算法 (IRL)

Inverse Reinforcement Learning (IRL) 详解 什么是 Inverse Reinforcement Learning&#xff1f; 在传统的强化学习 (Reinforcement Learning, RL) 中&#xff0c;奖励函数是已知的&#xff0c;智能体的任务是学习一个策略来最大化奖励 而在逆向强化学习 (Inverse Reinforc…

《普通逻辑》学习记录——性质命题及其推理

目录 一、性质命题概述 二、性质命题的种类 2.1、性质命题按质的分类 2.2、性质命题按量的分类 2.3、性质命题按质和量结合的分类 2.4、性质命题的基本形式归纳 三、四种命题的真假关系 3.1、性质命题与对象关系 3.2、四种命题的真假判定 3.3、四种命题的对当关系 四、四种命题…

人工智能(AI)对网络管理的影响

近年来&#xff0c;人工智能(AI)尤其是大语言模型(LLMs)的快速发展&#xff0c;正在深刻改变网络管理领域。AI的核心价值在于其能够通过自动化、模式分析和智能决策&#xff0c;显著提升网络运维效率并应对复杂挑战。具体表现为&#xff1a; 快速数据查询与分析​​&#xff1…

embedding_model模型通没有自带有归一化层该怎么处理?

embedding_model 是什么&#xff1a; 嵌入式模型&#xff08;Embedding&#xff09;是一种广泛应用于自然语言处理&#xff08;NLP&#xff09;和计算机视觉&#xff08;CV&#xff09;等领域的机器学习模型&#xff0c;它可以将高维度的数据转化为低维度的嵌入空间&#xff0…

八大排序——冒泡排序/归并排序

八大排序——冒泡排序/归并排序 一、冒泡排序 1.1 冒泡排序 1.2 冒泡排序优化 二、归并排序 1.1 归并排序&#xff08;递归&#xff09; 1.2 递归排序&#xff08;非递归&#xff09; 一、冒泡排序 1.1 冒泡排序 比较相邻的元素。如果第一个比第二个大&#xff0c;就交换…

银发科技:AI健康小屋如何破解老龄化困局

随着全球人口老龄化程度的不断加深&#xff0c;如何保障老年人的健康、提升他们的生活质量&#xff0c;成为了社会各界关注的焦点。 在这场应对老龄化挑战的战役中&#xff0c;智绅科技顺势而生&#xff0c;七彩喜智慧养老系统构筑居家养老安全网。 而AI健康小屋作为一项创新…

命令行指引的尝试

效果 步骤 首先初始化一个空的项目&#xff0c;然后安装一些依赖 npm init -y npm install inquirer execa chalk ora至于这些依赖是干嘛的&#xff0c;如下图所示&#xff1a; 然后再 package.json 中补充一个 bin 然后再根目录下新建一个 index.js , 其中的内容如下 #!/…

【Dify系列教程重置精品版】第1课 相关概念介绍

文章目录 一、Dify是什么二、Dify有什么用三、如何玩转Dify?从螺丝刀到机甲战士的进阶指南官方网站:https://dify.ai github地址:https://github.com/langgenius/dify 一、Dify是什么 Dify(D​​efine + ​​I​​mplement + ​​F​​or ​​Y​​ou)。这是一款开源的大…

leetcode0106. 从中序与后序遍历序列构造二叉树-medium

1 题目&#xff1a;从中序与后序遍历序列构造二叉树 官方标定难度&#xff1a;中 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入…

Spring Boot默认缓存管理

Spring框架支持透明地向应用程序添加缓存&#xff0c;以及对缓存进行管理&#xff0c;其管理缓存的核心是将缓存应用于操作数据的方法&#xff0c;从而减少操作数据的执行次数&#xff0c;同时不会对程序本身造成任何干扰。Spring Boot继承了Spring框架的缓存管理功能&#xff…

XYNU2024信安杯-REVERSE(复现)

前言 记录记录 1.Can_you_find_me? 签到题&#xff0c;秒了 2.ea_re 快速定位 int __cdecl main_0(int argc, const char **argv, const char **envp) {int v4; // [esp0h] [ebp-1A0h]const char **v5; // [esp4h] [ebp-19Ch]const char **v6; // [esp8h] [ebp-198h]char v7;…

MySQL的MVCC【学习笔记】

MVCC 事务的隔离级别分为四种&#xff0c;其中Read Committed和Repeatable Read隔离级别&#xff0c;部分实现就是通过MVCC&#xff08;Multi-Version Concurrency Control&#xff0c;多版本并发控制&#xff09; 版本链 版本链是通过undo日志实现的&#xff0c; 事务每次修改…

达梦数据库压力测试报错超出全局hash join空间,适当增加HJ_BUF_GLOBAL_SIZE解决

1.名词解释&#xff1a;达梦数据库中的HJ_BUF_GLOBAL_SIZE是所有哈希连接操作可用的最大哈希缓冲区大小&#xff0c;单位为兆字节&#xff08;MB&#xff09; 2.达梦压测报错&#xff1a; 3.找到达梦数据库安装文件 4.压力测试脚本 import http.client import multiprocessi…

Oracle--SQL性能优化与提升策略

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、导致性能问题的内在原因 系统性能问题的底层原因主要有三个方面&#xff1a; CPU占用率过高导致资源争用和等待内存使用率过高导致内存不足并需…

六个能够白嫖学习资料的网站

一、咖喱君的资源库 地址&#xff1a;https://flowus.cn/galijun/share/de0f6d2f-df17-4075-86ed-ebead0394a77 这是一个学习资料/学习网站分享平台&#xff0c;包含了英语、法语、德语、韩语、日语、泰语等几十种外国语言的学习资料及平台&#xff0c;这个网站的优势就是外语…

IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤

以下是在 IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤&#xff1a; 步骤 1&#xff1a;创建 Maven Web 项目 新建项目 File -> New -> Project → 选择 Maven → 勾选 Create from archetype → 选择 maven-archetype-webapp。输入 GroupId&#xff08;如 com.examp…

手机打电话时电脑坐席同时收听对方说话并插入IVR预录声音片段

手机打电话时电脑坐席同时收听对方说话并插入IVR预录声音片段 --本地AI电话机器人 前言 书接上一篇&#xff0c;《手机打电话通话时如何向对方播放录制的IVR引导词声音》中介绍了【蓝牙电话SDK示例App】可以实现手机app在电话通话过程中插播预先录制的开场白等语音片段的功能。…