这是我参与「第三届青训营 -后端场」笔记创作活动的第3篇笔记
Kitex可以很方便的帮助我们对服务进行拆分,使得更方便进行团队协作编写代码,本文将通过实现一个极简的注册登录来简单的了解Kitex的使用。
1. 安装Kitex和thriftgo
首先安装go语言环境并配置好代理,参见go语言基础 | 青训营笔记第一部分。然后执行以下命令
go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
go install github.com/cloudwego/thriftgo@latest
添加环境变量,在/etc/profile文件中添加export PATH=~/go/bin:$PATH并执行
source /etc/profile
最后执行下列语句,检查是否安装成功
kitex --version
thriftgo --version
2. 安装etcd
参照etcd Install,先下载etcd,将文件解压到合适的文件夹下,并将该文件目录添加到/etc/profile文件中,然后执行
source /etc/profile
最后执行下列语句,检查是否安装成功
etcd --version
3. 编写IDL文件
IDL(Interface Definition Language,接口定义语言),对于我们将要实现的极简的注册登录而言,IDL相对比较简单,request只需要用户名和密码,response只需要返回一个status_code和status_msg。
namespace go user
struct BaseResponse {
1:i64 status_code
2:string status_msg
}
struct CreateUserResponse {
1:string username
2:string password
}
struct CreateUserResponse {
1:BaseResponse base_resp
}
struct CheckUserRequest {
1:string username
2:string password
}
struct CheckUserResponse {
1:BaseResponse base_resp
}
service UserService {
CreateUserResponse CreateUser(1:CreateUserRequest req)
CheckUserResponse CheckUser(1:CheckUserRequest req)
}
4. 使用Kitex生成服务段代码
使用以下命令,module name为demo,service name为user
kitex -module demo -service user user.thrift
用go mod tidy下载所需要的包,因为目前使用的Kitex版本为v0.3.1,需要添加这两条命令
go mod edit -droprequire=github.com/apache/thrift/lib/go/thrift
go mod edit -replace=github.com/apache/thrift=github.com/apache/thrift@v0.13.0
5. 编写服务端代码
- main.go
- 将服务注册到etcd,etcd默认会开启2379端口
r, err := etcd.NewEtcdRegistry([]string{"127.0.0.1:2379"})
if err != nil {
panic(err)
}
- 指定user服务端口
addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:6660")
if err != nil {
panic(err)
}
- 完善NewServer方法参数
svr := user.NewServer(new(UserServiceImpl),
server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: "user"}), // server name
server.WithServiceAddr(addr), // address
server.WithRegistry(r),
)
- 设置一个全局变量存放用户名和密码,在main函数里初始化
var Users map[string]string
Users := make(map[string]string)
- handler.go
- CreateUser函数
先new一个CreateUserResponse的对象,检查Users的map中是否以及存在req中的username,若有,返回相应的状态码和信息,若没有,将username和password加入map,并返回注册成功的状态码和信息。
resp = new(user.CreateUserResponse)
_, ok := Users[req.Username]
if ok {
resp.BaseResp = &user.BaseResponse{
StatusCode: 1,
StatusMsg: "用户名已被使用",
}
} else {
Users[req.Username] = req.Password
resp.BaseResp = &user.BaseResponse{
StatusCode: 0,
StatusMsg: "注册成功",
}
}
return resp, nil
- CheckUser函数
先new一个CreateUserResponse的对象,检查Users的map中是否以及存在req中的username,若有,检查密码是否与req中的password相同,若相同,则返回登录成功的状态码和信息,否则,返回其他相应的状态码和信息。
resp = new(user.CheckUserResponse)
password, ok := Users[req.Username]
if ok {
if password == req.Password {
resp.BaseResp = &user.BaseResponse{
StatusCode: 0,
StatusMsg: "登录成功",
}
} else {
resp.BaseResp = &user.BaseResponse{
StatusCode: 1,
StatusMsg: "用户名或密码错误",
}
}
} else {
resp.BaseResp = &user.BaseResponse{
StatusCode: 1,
StatusMsg: "用户不存在",
}
}
return resp, nil
6. 编写客户端代码
创建rpc目录,在该目录下user.go文件中编写客户端代码, 从etcd发现服务,etcd默认会开启2379端口
package rpc
import (
"context"
"demo/kitex_gen/user"
"demo/kitex_gen/user/userservice"
"github.com/cloudwego/kitex/client"
etcd "github.com/kitex-contrib/registry-etcd"
)
var userClient userservice.Client
func InitRPC() {
r, err := etcd.NewEtcdResolver([]string{"127.0.0.1:2379"})
if err != nil {
panic(err)
}
c, err := userservice.NewClient("user", client.WithResolver(r))
if err != nil {
panic(err)
}
userClient = c
}
func CreateUser(ctx context.Context, req *user.CreateUserRequest) (resp *user.CreateUserResponse, err error) {
return userClient.CreateUser(ctx, req)
}
func CheckUser(ctx context.Context, req *user.CheckUserRequest) (resp *user.CheckUserResponse, err error) {
return userClient.CheckUser(ctx, req)
}
7. 编写HTTP Server代码
创建http目录,在该目录下server.go文件中编写客户端代码
package main
import (
"context"
"demo/kitex_gen/user"
"demo/rpc"
"github.com/gin-gonic/gin"
)
func main() {
rpc.InitRPC()
r := gin.Default()
r.POST("/register", func(c *gin.Context) {
username := c.Query("username")
password := c.Query("password")
resp, err := rpc.CreateUser(context.Background(), &user.CreateUserRequest{
Username: username,
Password: password,
})
if err != nil {
c.JSON(200, gin.H{
"status_code": 1,
"status_msg": "服务错误",
})
}
c.JSON(200, gin.H{
"status_code": resp.BaseResp.StatusCode,
"status_msg": resp.BaseResp.StatusMsg,
})
})
r.POST("/login", func(c *gin.Context) {
username := c.Query("username")
password := c.Query("password")
resp, err := rpc.CheckUser(context.Background(), &user.CheckUserRequest{
Username: username,
Password: password,
})
if err != nil {
c.JSON(200, gin.H{
"status_code": 1,
"status_msg": "服务错误",
})
}
c.JSON(200, gin.H{
"status_code": resp.BaseResp.StatusCode,
"status_msg": resp.BaseResp.StatusMsg,
})
})
r.Run()
}
8. 运行
- 启动etcd
etcd
- 启动user服务
sh build.sh
sh output/bootstrap.sh
- 启动http服务
go run http/server.go
9. postman测试
- 注册接口
- 登录接口