Nacos与gRPC
前言
关于这部分,前段时间我在看文档以及视频教程的时候,怎么都想不明白,到底为什么要用gRPC是什么,他在项目中应该充当什么样的角色?Nacos又是如何和他结合的?
于是我就决定去看看一些小项目是如何实现的这个功能,现在将我最近学到的分享给大家。
正文
在正文开始之前,我们要先知道Nacos和gRPC在本篇内容中,会涉及到的作用:
gRPC
gRPC 允许服务之间无缝通信,像调用本地函数一样调用远程服务的功能。
Nacos
服务在启动时将自身注册到 Nacos,包含 IP、端口和服务名称,其他服务可以通过服务名称查询可用实例。
正片
在这里,我们以一个实现了用户注册/登陆的接口为例子,进行讲解。
micro # 项目根目录 └── app # 应用逻辑目录 ├── api # 定义API接口 │ └── user.go # 用户相关的API实现 ├── client # 客户端功能实现 │ ├── nacos # Nacos配置相关 │ │ └── nacos.go # Nacos客户端实现 │ └── user # 用户客户端功能 │ └── proto # 用户相关的proto定义 │ └── userclient.go # 用户客户端的实现 ├── cmd # 应用入口 │ └── main.go # 启动程序的主文件 ├── Middleware # 中间件处理 │ └── Token.go # 处理Token的中间件 ├── model # 数据模型定义 │ └── model.go # 数据结构和模型实现 ├── router # 路由功能实现 │ └── router.go # 路由定义和管理 └── utils # 工具函数 └── jwt.go # JWT处理相关函数 └── response # 响应相关定义 ├── Errors.go # 错误处理 ├── Rsp.go # 通用响应结构 └── rspModel.go # 响应模型定义 └── server # 服务器端逻辑 └── user # 用户功能实现 ├── conf # 配置文件 │ ├── conf.go # 配置读取逻辑 │ └── db.yaml # 数据库配置文件 ├── dao # 数据访问对象 │ ├── init.go # 初始化数据库 │ └── user.go # 用户相关的数据库操作 └── proto # proto文件定义 ├── user.pb.go # 编译后的用户proto文件 ├── user.proto # 用户定义的proto文件 └── user_grpc.pb.go # gRPC编译后的用户proto文件 └── main.go # 服务器的主入口/同时实现了相应的业务逻辑函数
结构如上,首先我们需要知道,在app层,我们实现了基本的路由逻辑,随后client相当于我们的客户端,我们通过client的grpc客户端和server进行通信,并且拿到server上面实现的业务逻辑函数,从而调用相关的函数,从而实现完整用户的注册/登陆功能。
了解完这个demo的基本结构以后,下面我来细说。
proto部分
首先我来解释解释这上面都是什么意思,LoginRequest 以及RegisterRequest都相当于一个请求的结构体,上面带有请求的信息,我们可以看到信息就是用户名和密码,随后就是我们的回复信息Response,上面带有请求的结果以及信息,接下来只需要在控制台输入相应的指令就好了
随后就会产生相应的go文件,这里产生的文件我们并不需要理会它,这些文件实现了通信的内部逻辑,而且我们由于我们只需要按照规则简单的定义.proto文件就可以了,所以遵循这个规则的我们就可以轻易的实现通信,并且不需要关心内部的通信逻辑。
gRPC部分
随后我们需要做的是什么呢?在接下来我们需要将这个文件复制到server端一份,以保证两端可以相互通信,然后我们需要将我们定义在.proto文件中的函数方法进行重写,比如:
这部分实现了相应的服务端的逻辑,就相当于我们在单层架构中的service层的作用,在该方法中,我们调用了dao层的函数,以便于实现完整的注册逻辑。
既然重写了相应的方法函数,这并不是完事大吉了,我们还需要在真正运行代码的时候让两端进行通信,此时就需要我们创建相应的grpc客户端和服务端。
考虑到此处我们的重点是gRPC通信和nacos服务注册,我们就采取了不安全的通信方式。
此处我们设置了监听的端口,UserServer是实现了相对应的接口的结构体,使用 pb.RegisterUserServer
函数将 UserServer
实例注册到 gRPC 服务器。这使得通过 gRPC 客户端调用该服务时,可以调用您在 UserServer
中实现的方法。
此处还不着急使用nacos注册服务,我们先来看看客户端如何通信。
此处的addr是我们对应的server的地址+端口号,也是采取了不安全的通信,然后这个UserClient可以这样理解,他作为一个对象,在通信之后,能够作为UserServer对象来调用服务端实现的方法,就是如此,所以我们要在app层创建一个UserClient实例来完成我们的调用。
nacos部分
nacos是什么?他是一个服务注册中心,服务在启动时将自身注册到 Nacos,包含 IP、端口和服务名称,其他服务可以通过服务名称查询可用实例,于是我们在服务端将当前启动服务的ip端口以及服务名称等注册到了nacos,在客户端,我们就可以通过服务的名称等信息查找相应服务对应的信息,这样我们就不需要以硬编码的形式将服务对应的IP端口写在客户端,而且当我们在服务端改变IP和端口时,也是将最新的IP端口注册到nacos中,这样就不需要担心修改客户端的问题了,因为客户端的IP和端口是以变量的形式保存的,保证了灵活性,而当有多个相同服务注册时,客户端可以从多个可用实例中选择,提高系统的整体性能与可靠性。
下面让我们来看看是如何实现的。
首先是在服务端:
在启动服务端时,我们通过函数调用,将服务的地址等信息注册到nacos的相应位置,关于nacos服务的增删查改可以看看我之前发的博客。
这样就算注册完成了,启动完服务端,我们返回来看客户端:
此处我们只需要将搜索信息放入param中,然后根据我们的信息查找这个服务是否注册到了nacos中,如果没注册,就说明服务端还没启动,就停止调用,如果发现了这个服务,我们便可以获取这个服务的地址,然后根据地址,连接gRPC服务,这样就算完成了!
最后,启动服务端,将服务注册进nacos,同时开启grpc服务端,监听端口,然后启动客户端,发送post命令,看到令人满意的结果,就算结束了。
如果想要详细的代码,可以看我的github-Some-WORKs仓库
结语
此处便是我关于nacos和gRPC如何结合起来实现服务注册调用的想法,如果有问题,欢迎提出来!
Other
关于这篇文章的代码,其实有处理的不好的地方,就是在gRPC通信时,错误码的传递,在gRPC里面,如果识别到error不是nil的话,那么返回的msg就会是nil(空指针),就无法访问原本传递的response,另外值得注意的是,如果返回的自定义错误类型,就是gRPC未内置的错误,就会在前面加上UnKnow的字段,便无法进行error.Is()的判断,所以传递错误信息的最好方式是在response里面传递错误码或者传递错误信息,另外还有一些处理的方式,详细可以看看这篇文章,讲得很不错。
最后更新于