Kitex
本文最后更新于 2024-02-18,文章内容可能已经过时。
Kitex:
1.介绍:
Kitex 字节跳动内部的 Golang 微服务 RPC 框架,具有高性能、强可扩展的特点,在字节内部已广泛使用。如果对微服务性能有要求,又希望定制扩展融入自己的治理体系,Kitex 会是一个不错的选择。
框架特点:
高性能:使用自研的高性能网络库 Netpoll,性能相较 go net 具有显著优势。
扩展性:提供了较多的扩展接口以及默认扩展实现,使用者也可以根据需要自行定制扩展。
多消息协议:RPC 消息协议默认支持 Thrift、Kitex Protobuf、gRPC。Thrift 支持 Buffered 和 Framed 二进制协议;Kitex Protobuf 是 Kitex 自定义的 Protobuf 消息协议,协议格式类似 Thrift;gRPC 是对 gRPC 消息协议的支持,可以与 gRPC 互通。除此之外,使用者也可以扩展自己的消息协议。
多传输协议:传输协议封装消息协议进行 RPC 互通,传输协议可以额外透传元信息,用于服务治理,Kitex 支持的传输协议有 TTHeader、HTTP2。TTHeader 可以和 Thrift、Kitex Protobuf 结合使用;HTTP2 目前主要是结合 gRPC 协议使用,后续也会支持 Thrift。
多种消息类型:支持 PingPong、Oneway、双向 Streaming。其中 Oneway 目前只对 Thrift 协议支持,双向 Streaming 只对 gRPC 支持
服务治理:支持服务注册/发现、负载均衡、熔断、限流、重试、监控、链路跟踪、日志、诊断等服务治理模块,大部分均已提供默认扩展,使用者可选择集成。
代码生成:Kitex 内置代码生成工具,可支持生成 Thrift、Protobuf 以及脚手架代码。
2.使用:
基于 IDL 的 KiteX 实践 在 RPC 框架中,我们知道,服务端与客户端通信的前提是远程通信,但这种通信又存在一种关联,那就是通过一套相关的协议(消息、通信、传输等)来规范,但客户端又不用关心底层的技术实现,只要定义好了这种通信方式即可。在 KiteX 中,也提供了一种生成代码的命令行工具:kitex,目前支持 thrift、protobuf 等 IDL,并且支持生成一个服务端项目的骨架。
2.1 安装:
//安装
PS F:\work\Go\golangStudy> go install github.com/cloudwego/kitex/tool/cmd/kitex\@latest
//查看安装是否成功
PS F:\work\Go\golangStudy\example> kitex
//出现这个即为成功
No IDL file found.
2.2 编写IDL文件:
给出类似的thrift文件demo.
namespace go api
struct RegisterRequest {
1: string username
2: string password
3: string name
}
struct RegisterResponse {
1: string code
2: string msg
3: i64 userid
}
struct LoginRequest {
1: string username
2: string password
}
struct LoginResponse {
1: string code
2: string msg
3: i64 userid
}
struct ChangeRequest {
1: i64 userid
2: string token
3: i64 type
4: i64 number
}
struct ChangeResponse {
1: string code
2: string msg
}
struct GetInfoRequest {
1: i64 userid
}
struct GetInfoResponse {
1: i64 userid
2: string name
3: string avatar
4: string backgroundImage
5: string signature
}
service User {
RegisterResponse register(1: RegisterRequest req)
LoginResponse login(1: LoginRequest req)
ChangeResponse change(1: ChangeRequest req)
GetInfoResponse get(1:GetInfoRequest req)
}
2.3 代码生成:
PS F:\work\Go\golangStudy> kitex -module example -service example echo.proto
//出现这个即为成功
go: creating new go.mod: module example
上述命令中,-module
表示生成的该项目的 go module 名,-service
表明我们要生成一个服务端项目,后面紧跟的 example
为该服务的名字。最后一个参数则为该服务的 IDL 文件。
2.4 编写代码:
生成文件后的结构如下:
其他的文件都不用管,只需要在handlergo里面编写相关的逻辑就好.
每个方法都已经在handler.go中生成.
下面给出kitex+gorm的demo:
package main
import (
"context"
"crypto/sha256"
"douyin/kitex_gen/api"
"encoding/hex"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"math/big"
)
// UserImpl implements the last service interface defined in the IDL.
type UserImpl struct{}
type User struct {
Id int64 `gorm:"column:id;"`
UserId int64 `gorm:"column:user_id;"`
Username string `gorm:"column:username;"`
Password string `gorm:"column:password;"`
Name string `gorm:"column:name;"`
Avatar string `gorm:"column:avatar;"`
BackgroundImage string `gorm:"column:background_image;"`
Signature string `gorm:"column:signature;"`
}
func (u *User) TableName() string {
return "user"
}
// 全局盐
var salt = "byteDanceAgain"
// 字符串hash,用于加密密码
func hashTo15Bits(input string) string {
// 创建一个SHA-256哈希对象
h := sha256.New()
// 将字符串输入写入哈希对象
_, err := h.Write([]byte(input))
if err != nil {
panic(err)
}
// 获取SHA-256哈希值的字节数组
hashBytes := h.Sum(nil)
// 取哈希值的前两个字节并转换为16进制字符串
hashHex := hex.EncodeToString(hashBytes[:2])
return hashHex
}
// 用户id生成哈希,根据账号和
func hashToI64(input string) int64 {
// 创建一个SHA-256哈希对象
h := sha256.New()
// 将字符串输入写入哈希对象
_, err := h.Write([]byte(input))
if err != nil {
panic(err)
}
// 获取SHA-256哈希值的字节数组
hashBytes := h.Sum(nil)
// 将哈希值的前8个字节转换为int64
var hashInt big.Int
hashInt.SetBytes(hashBytes[:8])
result := hashInt.Int64()
return result
}
// Register implements the UserImpl interface.
func (s *UserImpl) Register(ctx context.Context, req *api.RegisterRequest) (resp *api.RegisterResponse, err error) {
///建立数据库连接,数据库名:数据库密码
dsn := "root:123456@tcp(127.0.0.1:3306)/dbgotest"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
//判断用户名是否唯一
var user User
db.First(&user, "username = ?", req.Username)
if user.Id != 0 {
resp = &api.RegisterResponse{Code: "1", Msg: "用户名已经存在!"}
return
}
//对密码加盐,加密存储进入数据库
password := hashTo15Bits(salt + req.Password)
fmt.Print(password)
//生成用户id
UserId := hashToI64(req.Username + req.Password)
fmt.Print(UserId)
//创建一条数据,传入一个对象
db.Create(&User{Username: req.Username, Password: password, Name: req.Name, UserId: UserId})
resp = &api.RegisterResponse{Code: "0", Msg: "用户注册成功!", Userid: UserId}
return
}
// Login implements the UserImpl interface.
func (s *UserImpl) Login(ctx context.Context, req *api.LoginRequest) (resp *api.LoginResponse, err error) {
///建立数据库连接,数据库名:数据库密码
dsn := "root:123456@tcp(127.0.0.1:3306)/dbgotest"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
var user User
db.First(&user, "username = ?", req.Username)
if user.Id == 0 {
resp = &api.LoginResponse{Code: "1", Msg: "用户不存在!"}
return
}
//验证密码
hashPassword := hashTo15Bits(salt + req.Password)
if user.Password != hashPassword {
resp = &api.LoginResponse{Code: "1", Msg: "用户密码错误!"}
return
}
resp = &api.LoginResponse{Code: "0", Msg: "用户登录成功!", Userid: user.Id}
return
}
// Get implements the UserImpl interface.
func (s *UserImpl) Get(ctx context.Context, req *api.GetInfoRequest) (resp *api.GetInfoResponse, err error) {
///建立数据库连接,数据库名:数据库密码
dsn := "root:123456@tcp(127.0.0.1:3306)/dbgotest"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
//获取用户信息
var user User
db.First(&user, "user_id = ?", req.Userid)
resp = &api.GetInfoResponse{Userid: user.UserId, Name: user.Name, Avatar: user.Avatar, BackgroundImage: user.BackgroundImage, Signature: user.Signature}
return
}
//批量获取用户信息
// GetAll implements the UserImpl interface.
func (s *UserImpl) GetAll(ctx context.Context, req *api.GetAllInfoResponse) (resp *api.GetAllInfoResponse, err error) {
dsn := "root:123456@tcp(127.0.0.1:3306)/dbgotest"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
var User1 []User
list := req.UserList
for i := 0; i < len(list); i++ {
db.First(&User1[i], "user_id = ?", list[i])
}
resp = &api.GetAllInfoResponse{UserList: User1}
return
}