Kitex开发02 | 青训营

174 阅读3分钟

2、gateway编写

目录结构

.
├── handlers
│   ├── handle.go
│   └── register.go
├── main.go
└── rpc
    ├── init.go
    └── user.go

handlers对应所有的 http接口

rpc封装了对应需要的所有RPC服务的client端以及相应的请求方法

main.go主要是包含了启动类配置

1、编写RPCclient端

编写RPCclient端,将RPCcall封装为外部可访问函数,借由httphandler调用

  1. Init类

    启动类封装,封装所有的私有RPC启动类为一个公有方法,供主启动类使用

    //init.go
    package rpc
    ​
    func InitRPC() {
        initUserRpc()
    }
    ​
    
  2. userclient

    //user.gp
    package rpc
    ​
    import (
        "context"
        "easynote/kitex_gen/user"
        "easynote/kitex_gen/user/userservice"
        "easynote/pkg/constants"
        "easynote/pkg/errno"
        "easynote/pkg/middleware"
        "github.com/cloudwego/kitex/client"
        "github.com/cloudwego/kitex/pkg/retry"
        etcd "github.com/kitex-contrib/registry-etcd"
        trace "github.com/kitex-contrib/tracer-opentracing"
        "time"
    )
    ​
    var userClient userservice.Client
    ​
    func initUserRpc() {
        //引入服务发现
        r, err := etcd.NewEtcdResolver([]string{constants.EtcdAddress})
        if err != nil {
            panic(err)
        }
        c, err := userservice.NewClient(
            constants.UserServiceName,                          //配置服务名称
            client.WithMiddleware(middleware.CommonMiddleware), //配置中间件
            client.WithMiddleware(middleware.ClientMiddleware), //配置客户端中间件
            client.WithMuxConnection(1),                        //配置最大复用连接数
            client.WithRPCTimeout(3*time.Second),               //配置RPC请求超时时间
            client.WithConnectTimeout(50*time.Second),          //配置连接超时时间
            client.WithFailureRetry(retry.NewFailurePolicy()),  //配置失败重试
            client.WithSuite(trace.NewDefaultClientSuite()),    //配置链路追踪
            client.WithResolver(r),                             //配置服务发现
        )
        if err != nil {
            panic(err)
        }
        userClient = c
    }
    ​
    // 封装PRC请求
    func CreateUser(ctx context.Context, req *user.CreateUserRequest)error{
        resp,err := userClient.CreateUser(ctx, req)
        if err !=nil{
            return err
        }
        if resp.BaseResp.StatusCode != 0{
            return errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage)
        }
        return nil
    }
    ​
    func CheckUser(ctx context.Context, req *user.CheckUserRequest) (int64,error){
        resp, err := userClient.CheckUser(ctx,req)
        if err != nil{
            return 0, err
        }
        if resp.BaseResp.StatusCode != 0{
            return 0 ,errno.NewErrNo(resp.BaseResp.StatusCode,resp.BaseResp.StatusMessage)
        }
        return resp.UserId, nil
    }
    

2、编写httpHandler

demo中将每一个业务操作对应一个httphandler,类似web开发的service端,目前还不涉及复杂的业务逻辑,我们就按照demo的方式编写接口

//register
package handlers
import (
    "context"
    "easynote/kitex_gen/user"
    "github.com/gin-gonic/gin"
    "net/http""easynote/cmd/api/rpc"
    "easynote/pkg/errno"
)
​
// Register register user info 封装为了一个gin handler
func Register(c *gin.Context) {
    var registerVar UserParam
    if err := c.Bind(&registerVar); err != nil {
        Err(c, http.StatusBadRequest, errno.ConvertErr(err), nil)
        return
    }
​
    if len(registerVar.UserName) == 0 || len(registerVar.PassWord) == 0 {
        Err(c, http.StatusBadRequest, errno.ParamErr, nil)
        return
    }
​
    err := rpc.CreateUser(context.Background(), &user.CreateUserRequest{
        UserName: registerVar.UserName,
        Password: registerVar.PassWord,
    })
    if err != nil {
        Err(c, http.StatusInternalServerError, errno.ConvertErr(err), nil)
        return
    }
    Success(c, http.StatusOK, errno.Success, nil)
}

3、编写handle类

demo中将前端可能发来的表单页面和封装返回函数放在了一个文件中

//handle.go
package handlers
​
import (
    "context"
    "easynote/kitex_gen/user"
    "github.com/gin-gonic/gin"
    "net/http""easynote/cmd/api/rpc"
    "easynote/pkg/errno"
)
​
// Register register user info 封装为了一个gin handler
func Register(c *gin.Context) {
    var registerVar UserParam
    if err := c.Bind(&registerVar); err != nil {
        Err(c, http.StatusBadRequest, errno.ConvertErr(err), nil)
        return
    }
​
    if len(registerVar.UserName) == 0 || len(registerVar.PassWord) == 0 {
        Err(c, http.StatusBadRequest, errno.ParamErr, nil)
        return
    }
​
    err := rpc.CreateUser(context.Background(), &user.CreateUserRequest{
        UserName: registerVar.UserName,
        Password: registerVar.PassWord,
    })
    if err != nil {
        Err(c, http.StatusInternalServerError, errno.ConvertErr(err), nil)
        return
    }
    Success(c, http.StatusOK, errno.Success, nil)
}

4、编写主启动类

//main.go
package main
​
import (
    "easynote/cmd/api/handlers"
    "easynote/cmd/api/rpc"
    "easynote/pkg/constants"
    "easynote/pkg/middleware"
    "easynote/pkg/tracer"
    "github.com/gin-gonic/gin"
)
​
func Init() {
    tracer.InitJaeger(constants.ApiServiceName)
    rpc.InitRPC()
}
​
func main() {
    Init()
    r := gin.Default()
    //分配路由。装配顺序: / + v1/ + user/ + list
    ApiGroup := r.Group("/v1/")
    //路由分组
    user := ApiGroup.Group("/user")
    //路由添加,接口多了后可以将路由放入到同一个包中封装
    //调用链条
    //httpreq-> ginEngine(gateway) -> handlers.Register(controller) -> clientRPCcall -> userServerRecv -> deal -> response ->..
    user.POST("/register", handlers.Register)
    user.POST("/login", middleware.JWTAuth())
    //添加中间件(全局中间件)
    ApiGroup.Use(middleware.Cors())
}
    err := r.Run(fmt.Sprintf(":%d", 8080))
    if err != nil {
        panic(err)
    }

3、启动gateway与user服务

  1. user服务

    image-20230727212336598

  2. gatewayAPI

    image-20230727212314074

4、测试接口

image-20230727212401541


到目前位置,基本的微服务实践就完成了,麻雀虽小,五脏俱全,这里采用了Kitex官方demo easy_note作文蓝本,学习使用Kitex的基本用法以及微服务的开发流程,那么接下来就聚焦到微服务的几个重点元素中一一查看实现原理。