当前位置: 首页 > article >正文

Go如何写一个通用grpc接口

我来为您详细讲解如何在 Go 中编写通用 gRPC 接口涵盖从基础到高级的设计模式。1. 基础通用接口设计1.1 标准 gRPC 服务定义proto// api.protosyntax proto3;package api;option go_package github.com/example/api;// 通用请求/响应包装器message GenericRequest {string service 1; // 服务名string method 2; // 方法名bytes payload 3; // 序列化的请求数据JSON/Protobufmapstring, string metadata 4; // 额外元数据}message GenericResponse {int32 code 1; // 业务状态码string message 2; // 状态信息bytes data 3; // 响应数据mapstring, string metadata 4;}// 通用服务service GenericAPI {rpc Call(GenericRequest) returns (GenericResponse);rpc StreamCall(stream GenericRequest) returns (stream GenericResponse);}1.2 服务端实现反射注册表模式package mainimport (contextencoding/jsonfmtreflectgoogle.golang.org/grpcgoogle.golang.org/grpc/codesgoogle.golang.org/grpc/statusgoogle.golang.org/protobuf/types/known/anypb)// Handler 通用处理函数类型type Handler func(ctx context.Context, req interface{}) (interface{}, error)// ServiceRegistry 服务注册表type ServiceRegistry struct {handlers map[string]Handler // key: Service.Method}func NewServiceRegistry() *ServiceRegistry {return ServiceRegistry{handlers: make(map[string]Handler),}}// Register 注册服务方法func (r *ServiceRegistry) Register(service, method string, handler Handler) {key : fmt.Sprintf(%s.%s, service, method)r.handlers[key] handler}// Get 获取处理器func (r *ServiceRegistry) Get(service, method string) (Handler, bool) {key : fmt.Sprintf(%s.%s, service, method)h, ok : r.handlers[key]return h, ok}// GenericServer 通用gRPC服务器type GenericServer struct {registry *ServiceRegistryUnimplementedGenericAPIServer}func NewGenericServer(registry *ServiceRegistry) *GenericServer {return GenericServer{registry: registry}}// Call 实现通用调用func (s *GenericServer) Call(ctx context.Context, req *GenericRequest) (*GenericResponse, error) {handler, ok : s.registry.Get(req.Service, req.Method)if !ok {return nil, status.Errorf(codes.NotFound, service %s.%s not found, req.Service, req.Method)}// 反序列化请求var payload interface{}if err : json.Unmarshal(req.Payload, payload); err ! nil {return nil, status.Errorf(codes.InvalidArgument, invalid payload: %v, err)}// 执行处理resp, err : handler(ctx, payload)if err ! nil {return GenericResponse{Code: 500,Message: err.Error(),}, nil}// 序列化响应data, err : json.Marshal(resp)if err ! nil {return nil, status.Errorf(codes.Internal, marshal error: %v, err)}return GenericResponse{Code: 200,Message: success,Data: data,}, nil}2. 高级基于反射的自动路由2.1 结构体服务自动注册package mainimport (contextencoding/jsonfmtreflectstringsunicodegoogle.golang.org/grpc/codesgoogle.golang.org/grpc/status)// AutoRegister 自动扫描结构体方法并注册func (r *ServiceRegistry) AutoRegister(serviceName string, serviceImpl interface{}) error {v : reflect.ValueOf(serviceImpl)t : v.Type()// 遍历所有方法for i : 0; i v.NumMethod(); i {method : t.Method(i)// 只导出公开方法if !isExported(method.Name) {continue}// 分析方法签名: func(ctx context.Context, req T) (resp T, error)if method.Type.NumIn() ! 2 || method.Type.NumOut() ! 2 {continue // 不符合标准handler签名}// 检查第一个参数是否为context.ContextctxType : method.Type.In(0)if ctxType ! reflect.TypeOf((*context.Context)(nil)).Elem() {continue}// 获取请求类型reqType : method.Type.In(1)respType : method.Type.Out(0)// 创建闭包处理函数handler : func(ctx context.Context, req interface{}) (interface{}, error) {// 将map[string]interface{}转换为具体类型reqBytes, _ : json.Marshal(req)reqPtr : reflect.New(reqType)if err : json.Unmarshal(reqBytes, reqPtr.Interface()); err ! nil {return nil, err}// 调用方法results : v.Method(i).Call([]reflect.Value{reflect.ValueOf(ctx),reqPtr.Elem(),})// 处理错误if !results[1].IsNil() {return nil, results[1].Interface().(error)}return results[0].Interface(), nil}r.Register(serviceName, method.Name, handler)}return nil}func isExported(name string) bool {if name {return false}r : []rune(name)[0]return unicode.IsUpper(r)}// 使用示例 // UserRequest 具体请求类型type UserRequest struct {ID int64 json:idName string json:name}// UserResponse 具体响应类型type UserResponse struct {ID int64 json:idName string json:nameEmail string json:email}// UserService 业务服务type UserService struct{}func (s *UserService) GetUser(ctx context.Context, req UserRequest) (*UserResponse, error) {// 业务逻辑return UserResponse{ID: req.ID,Name: req.Name,Email: userexample.com,}, nil}func (s *UserService) CreateUser(ctx context.Context, req UserRequest) (*UserResponse, error) {// 创建逻辑return UserResponse{ID: 100,Name: req.Name,Email: newexample.com,}, nil}// 初始化func main() {registry : NewServiceRegistry()// 自动注册所有方法userSvc : UserService{}if err : registry.AutoRegister(UserService, userSvc); err ! nil {panic(err)}// 启动gRPC服务器...}3. 流式通用接口3.1 双向流处理// StreamCall 实现双向流func (s *GenericServer) StreamCall(stream GenericAPI_StreamCallServer) error {ctx : stream.Context()// 为每个流创建会话状态session : StreamSession{ID: generateSessionID(),Streams: make(map[string]chan *GenericResponse),}// 处理流入消息errChan : make(chan error, 1)go func() {for {req, err : stream.Recv()if err ! nil {errChan - errreturn}// 异步处理每个请求go s.handleStreamRequest(ctx, session, req, stream)}}()// 等待流结束return -errChan}func (s *GenericServer) handleStreamRequest(ctx context.Context,session *StreamSession,req *GenericRequest,stream GenericAPI_StreamCallServer,) {handler, ok : s.registry.Get(req.Service, req.Method)if !ok {stream.Send(GenericResponse{Code: 404,Message: handler not found,})return}// 流式处理支持推送respChan : make(chan *GenericResponse, 10)session.Lock()session.Streams[req.Method] respChansession.Unlock()// 执行处理支持异步推送go func() {payload : make(map[string]interface{})json.Unmarshal(req.Payload, payload)resp, err : handler(ctx, payload)var respData []byteif err nil {respData, _ json.Marshal(resp)}stream.Send(GenericResponse{Code: func() int32 { if err ! nil { return 500 }; return 200 }(),Message: func() string { if err ! nil { return err.Error() }; return success }(),Data: respData,})}()}4. 客户端通用调用封装4.1 动态客户端package clientimport (contextencoding/jsonfmtgoogle.golang.org/grpcgoogle.golang.org/grpc/metadata)// GenericClient 通用gRPC客户端type GenericClient struct {conn *grpc.ClientConnclient GenericAPIClient}func NewGenericClient(target string, opts ...grpc.DialOption) (*GenericClient, error) {conn, err : grpc.NewClient(target, opts...)if err ! nil {return nil, err}return GenericClient{conn: conn,client: NewGenericAPIClient(conn),}, nil}// Call 通用调用方法func (c *GenericClient) Call(ctx context.Context,service, method string,req, resp interface{},opts ...grpc.CallOption,) error {// 序列化请求payload, err : json.Marshal(req)if err ! nil {return fmt.Errorf(marshal request: %w, err)}// 构建通用请求genericReq : GenericRequest{Service: service,Method: method,Payload: payload,}// 执行调用genericResp, err : c.client.Call(ctx, genericReq, opts...)if err ! nil {return err}if genericResp.Code ! 200 {return fmt.Errorf(business error: %s, genericResp.Message)}// 反序列化响应return json.Unmarshal(genericResp.Data, resp)}// CallWithMetadata 带元数据的调用func (c *GenericClient) CallWithMetadata(ctx context.Context,service, method string,req, resp interface{},md map[string]string,opts ...grpc.CallOption,) error {// 注入metadatactx metadata.NewOutgoingContext(ctx, metadata.New(md))return c.Call(ctx, service, method, req, resp, opts...)}// Close 关闭连接func (c *GenericClient) Close() error {return c.conn.Close()}4.2 使用示例func main() {client, err : NewGenericClient(localhost:50051, grpc.WithInsecure())if err ! nil {panic(err)}defer client.Close()// 调用UserService.GetUserreq : UserRequest{ID: 123}var resp UserResponseerr client.Call(context.Background(),UserService,GetUser,req,resp,)if err ! nil {panic(err)}fmt.Printf(User: %v\n, resp)}5. 完整架构图┌─────────────────────────────────────────────────────────────┐│ Client Side │├─────────────────────────────────────────────────────────────┤│ Service Stub (强类型) │ GenericClient (动态) ││ userSvc.GetUser(ctx, req) │ client.Call(ctx,User,Get,req,resp) │└──────────┬──────────────────┬───────────────────────────────┘│ │▼ ▼┌─────────────────────────────────────────────────────────────┐│ gRPC Transport ││ HTTP/2 Protobuf 序列化 │└──────────┬──────────────────┬─────────────────────────────┘│ │▼ ▼┌─────────────────────────────────────────────────────────────┐│ Server Side │├─────────────────────────────────────────────────────────────┤│ ┌─────────────────────────────────────────────────────────┐││ │ GenericServer (统一入口) │││ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │││ │ │ Call │ │ StreamCall │ │ HealthCheck │ │││ │ │ (Unary) │ │ (Bidi Stream)│ │ │ │││ │ └──────┬──────┘ └──────┬──────┘ └─────────────┘ │││ │ │ │ │││ │ ▼ ▼ │││ │ ┌─────────────────────────────────────────────────┐ │││ │ │ ServiceRegistry (路由表) │ │││ │ │ map[UserService.GetUser] → Handler │ │││ │ │ map[OrderService.Create] → Handler │ │││ │ └──────────────────┬────────────────────────────┘ │││ │ │ │││ │ ┌───────────┼───────────┐ │││ │ ▼ ▼ ▼ │││ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │││ │ │UserService│ │OrderService│ │PayService │ │││ │ │ (AutoReg) │ │ (AutoReg) │ │ (Manual) │ │││ │ └──────────┘ └──────────┘ └──────────┘ │││ └─────────────────────────────────────────────────────────┘│└─────────────────────────────────────────────────────────────┘6. 进阶插件化中间件// Middleware 通用中间件类型type Middleware func(ctx context.Context, req interface{}, info CallInfo, handler Handler) (interface{}, error)type CallInfo struct {Service stringMethod string}// Chain 中间件链func Chain(middlewares ...Middleware) Middleware {return func(ctx context.Context, req interface{}, info CallInfo, handler Handler) (interface{}, error) {// 构建调用链chain : handlerfor i : len(middlewares) - 1; i 0; i-- {m : middlewares[i]next : chainchain func(ctx context.Context, req interface{}) (interface{}, error) {return m(ctx, req, info, next)}}return chain(ctx, req)}}// 使用示例日志中间件func LoggingMiddleware(ctx context.Context, req interface{}, info CallInfo, handler Handler) (interface{}, error) {log.Printf([GRPC] %s.%s called, info.Service, info.Method)resp, err : handler(ctx, req)log.Printf([GRPC] %s.%s finished: err%v, info.Service, info.Method, err)return resp, err}这套设计提供了类型安全与动态灵活的平衡- 服务端支持自动注册反射和手动注册- 客户端支持强类型stub和动态调用- 扩展中间件链、流式处理、元数据传递

相关文章:

Go如何写一个通用grpc接口

我来为您详细讲解如何在 Go 中编写通用 gRPC 接口,涵盖从基础到高级的设计模式。1. 基础通用接口设计1.1 标准 gRPC 服务定义(proto) // api.proto syntax "proto3";package api;option go_package "github.com/example/api…...

30天从0到1!小白程序员必备的大模型(LLM)实战学习计划,附全套高清资料

人工智能大模型(Large Language Models, LLMs)早已成为科技圈的核心风口技术。从ChatGPT横空出世引爆全网关注,到LLaMA、Qwen(通义千问)、Mistral等开源模型群雄逐鹿,掌握大模型相关技术,不再是…...

2026年AI大变革:电网成稀缺资源,AI伴侣崛起,首个AI恶意软件现身!你准备好了吗?

2月初,AI领域权威机构发布了《2026年人工智能状况报告》。这份长达54页的深度分析,不仅复盘了过去一年AI在技术、产业、地缘等方面的激烈震荡,更对未来12个月给出了27个极具前瞻性的“硬核”预测。 如果说2025年是AI“百模大战”的混战期&…...

掌握 RAG 核心技术:揭秘 AI 如何精准调用私有知识库,避免“答非所问”的窘境!

本文深入探讨了 RAG(检索增强生成)技术的原理与实现,阐述了如何通过 Embedding 技术将私有文档转化为 AI 可检索的向量,并利用向量数据库进行高效相似度匹配。文章详细介绍了 Embedding 的作用、余弦相似度计算方法,以…...

SkillHub作为本地镜像站,在事实上分流了原站的用户流量和生态注意力,这是扶持生态还是釜底抽薪?

SkillHub这个本地镜像站的出现,确实是个挺有意思的现象。它表面上看起来是在帮原站做分发,让国内用户访问更快、更稳定,但仔细想想,背后牵扯的东西其实挺复杂的。 很多人第一反应会觉得,这肯定是在扶持生态啊。毕竟访…...

当马化腾亲自发文推动养虾计划,而创始人却在抱怨服务器成本被推高,这反映了开源世界与资本巨头之间怎样的权力不对等?

马化腾在社交媒体上提到养虾计划,这本身不是什么技术新闻,但背后牵扯出的讨论却很有意思。创始人抱怨服务器成本被推高,这种声音在开源圈子里其实一直都有,只是这次被摆到了台面上。 开源世界和资本巨头之间,从来就不是…...

御风未来“空中出租车”亮相东方枢纽,海外客商“零距离”感受中国低空经济发展

3月12日~15日,中国家电及消费电子博览会(Appliance&electronics World Expo,AWE)在上海举行。作为全球三大家电及消费电子展之一,本届AWE在上海新国际博览中心与上海东方枢纽国际商务合作区同步举办。作…...

为什么有些论文看起来普通,但是,一答辩就“安全通过”?

很多读研博的人都会遇到一个看似矛盾的现象。有些论文,看起来并不惊艳: 创新不算突出,结构也比较常规,甚至有些地方还略显普通。但到了答辩那天,结果却很顺利:基本没被难为,顺利通过。反而有些同…...

LSTM与BP算法结合的Matlab多输入单输出组合预测建模程序

LSTM结合BP做多输入单输出的组合预测建模。 程序内注释详细直接替换数据就可以使用。 程序语言为matlab。 程序直接运行可以出拟合预测图,线性拟合预测图,多个预测评价指标。PS:以下效果图为测试数据的效果图,主要目的是为了显示程序运行可以…...

CPT Markets平台内地合规性存疑,跨境金融衍生品交易风险大需警惕

CPT Markets平台内地合规性存疑,跨境金融衍生品交易风险大需警惕CPT Markets作为一家注册于塞舌尔的外汇交易平台,近年来通过线上渠道积极拓展中国市场,但其运营模式存在明显的合规性缺陷。该平台虽宣称受英国FCA、南非FSCA等多国监管&#x…...

智慧养殖鱼类疾病鱼类病害检测数据集VOC+YOLO格式457张7类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)图片数量(jpg文件个数):457标注数量(xml文件个数):457标注数量(txt文件个数):457标注类别数&…...

《QGIS快速入门与应用基础》220:工具栏:布局元素添加/编辑

作者:翰墨之道,毕业于国际知名大学空间信息与计算机专业,获硕士学位,现任国内时空智能领域资深专家、CSDN知名技术博主。多年来深耕地理信息与时空智能核心技术研发,精通 QGIS、GrassGIS、OSG、OsgEarth、UE、Cesium、OpenLayers、Leaflet、MapBox 等主流工具与框架,兼具…...

2026高职大数据工程技术毕业生就业难度分析

一、行业需求现状企业数字化转型加速推动大数据人才需求增长,尤其在金融、电商、医疗等领域。互联网大厂更倾向招聘具备算法优化和分布式系统经验的毕业生,而中小企业偏好掌握ETL流程和可视化工具的实用型人才。据第三方机构预测,2025年国内大…...

AI巨额融资推动二月风投创新高

根据 Crunchbase 的数据,2026 年 2 月全球风险投资总额达到 1890 亿美元,创下初创公司单月融资的历史新高。然而,高达 83% 的融资额流向了仅三家公司,其中包括 OpenAI,它筹集了 1100 亿美元,这也是有风险投…...

计算机毕业设计springboot社交网络平台“多乐” 基于SpringBoot的在线互动社区平台“乐享圈“ 基于SpringBoot的个性化社交分享系统“友聚“

计算机毕业设计springboot社交网络平台“多乐”eb3c1775 (配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。随着移动互联网的蓬勃发展和智能终端的全面普及,社交网络已深…...

计算机毕业设计springboot基于与Vue的货运系统 基于SpringBoot与Vue的物流运输管理平台 基于SpringBoot与Vue的智慧货运服务系统

计算机毕业设计springboot基于与Vue的货运系统6tmt4n38 (配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。在全球化贸易持续深化与电子商务蓬勃发展的当下,货运物流行业…...

网格隐藏技术在ANSYS仿真分析中的应用研究

网格隐藏 ansys仿真分析在ANSYS仿真分析里折腾过复杂模型的朋友,肯定都有过被满屏网格线晃瞎眼的经历。鼠标滚轮放大缩小两下,零件结构没看清,倒是先被密密麻麻的网格线整得晕头转向。这时候要是会玩"网格隐身术",工作…...

Dify简介

Dify简介 目录 Dify 发展历史Dify 流行原因Dify 核心组件Dify 架构图Dify 工作机制Dify 应用场景 Dify 发展历史 起源背景 Dify 是一款开源的 LLM 应用开发平台,由 LangGenius 团队开发。该项目诞生于 2023 年,正值大语言模型(LLM&#x…...

这次终于选对了!10个降AI率网站测评:本科生降AI率必备指南

在当前高校论文写作中,AI工具的广泛应用带来了效率提升,但也让论文的AIGC率问题变得愈发突出。许多本科生在完成初稿后,常常面临查重率过高、AI痕迹明显的问题,这不仅影响成绩,还可能引发学术不端的质疑。因此&#xf…...

python基于微信小程序的高校图书馆座位管理系统的设计与实现

目录需求分析与功能设计技术选型与开发环境搭建核心功能模块实现测试与优化部署与维护项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作需求分析与功能设计 明确高校图书馆座位管理系统的核心需求&…...

python基于微信小程序的宝宝儿童成长记录系统的设计与实现

目录 需求分析与功能规划技术栈选择数据库设计核心功能实现步骤数据可视化与统计测试与部署注意事项 项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作 需求分析与功能规划 明确系统核心功能&#xff1…...

python基于微信小程序的健身俱乐部信息管理系统的 功能多

目录系统架构设计核心功能模块扩展功能实现技术实现要点运维与安全项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作系统架构设计 采用前后端分离架构,前端基于微信小程序框架开发&#xff…...

python基于Android的学校教师工作量业绩考核计分系统 小程序

目录需求分析与功能设计技术栈选择数据库设计后端API开发前端小程序开发计分算法实现测试与部署安全与权限控制项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作需求分析与功能设计 明确教师工作量业绩…...

7个文件,把OpenClaw从聊天机器人变成你的全职AI员工!Wes Sander开源配置全拆解

最近刷GitHub,看到一个真正让人眼前一亮的仓库:Wes Sander直接把他个人用的OpenClaw完整配置全开源了。不是教程,不是卖课,就是他每天真正在跑的那套文件和模板。 我点进去一口气看完,瞬间明白为什么很多人用OpenClaw还…...

一次纠正,全队同步!我的OpenClaw AI Agent 3层记忆系统,彻底告别“失忆”烦恼

最近我在Mac Mini上跑着6个AI Agent,全天候24/7开工:一个负责研究、一个写内容、一个搞工程、还有newsletter、LinkedIn发帖,以及负责团队协调的。它们全靠cron定时唤醒,每次一睁眼,就像刚出厂的新机器,什么…...

航空航天需求:Vue3如何扩展百度WebUploader支持卫星遥感数据的分片校验上传?

大文件上传方案探索:从WebUploader到自定义分片上传的实践 作为一名前端开发工程师,最近遇到了一个颇具挑战性的需求:需要在Vue项目中实现4GB左右大文件的稳定上传,且要兼容Chrome、Firefox、Edge等主流浏览器,后端使…...

汽车制造经验:JS如何基于百度WebUploader插件实现设计图纸的加密分片断传?

(叼着冰棍敲键盘,显示器蓝光映着稀疏的头发) 各位爷瞧好了啊!咱这老码农被甲方爸爸按在地上摩擦了三个月,终于用原生JS搓出个能兼容IE9的文件夹上传怪兽。先说好哈,100块预算连我键盘缝里的烟灰都买不起&a…...

go gorm极简元数据处理

func Test003_GetDbMeta(t *testing.T) {var dbfacade FindBeanDbmetaFacade()ret : dbfacade.GetDbMeta("plat_menu")golog.Info("dbmeta:", ret) }2026-03-15 13:42:01.939 [INFO] dbmeta: {"code": 200,"msg": "成功",&…...

避坑 3:Docker 致命大坑!容器一删,业务数据全没了?3 套解决方案,直接抄,不翻车

文章目录避坑 3:Docker 致命大坑!容器一删,业务数据全没了?3 套解决方案,直接抄,不翻车方案一:根治方案!命名数据卷持久化(生产 / 测试环境通用,官方首选&…...

社会上有以假乱真的假钞,数学中有以假乱真的假R轴迷惑世人几百年

黄小宁R各元x有对应数2x。R轴即x轴沿本身保序不保距地拉伸变换为元为点2x的2x轴不全等于x轴从而更不x轴,然而自有函数和解析几何概念几百年来数学一直误以为x轴2x轴。将两异直线误为同一线自然就会将两异直线段误为同一线段从而使康脱推出错上加错的更重大错误&…...