👉 简介
完整的🌰:https://github.com/D-Watson/go-thrift-sample/tree/main
🐱Thrift是用来做什么的?
Thrift 是一个远程调用(RPC)框架~类似于grpc,方便跨语言的服务之间的相互通信~
🐱Thrift的优点?
- 跨语言支持:
可以支持java, c++, python, go, php .etc. - 高性能:
- 高效的序列化反序列化机制,使用二进制格式表示数据,解析时所需的字节数更少,传输速度更快。
- 支持多种通讯协议:使用序列化协议TCompactProtocal,该协议使用变长编码(Variable-Length Encoding)减少数据传输的大小。
- 字段标识符:Thrift数据结构使用字段序号来标识每个字段,而不是使用字段名,这种方法减少了传输中的开销。
- 支持批处理:支持批量请求和流式传输,可以在一次网络中处理多个请求,减少了网络往返次数。
- 非阻塞I/O:在异步模式下,Thrift可以在等待I/O操作时继续处理其他请求,这样可以充分利用CPU和网络资源。
- 多种协议:
Thrift 提供多种协议,包括二进制协议、JSON 协议等,以适应不同的需求。
1. 数据类型
1.1 基本数据类型
1. 整数类型:
- i32: 32位带符号整数
- i64: 64位带符号整数
- i16:16位带符号整数
- byte: 8位带符号整数
2. 浮点类型
- double: 64位双精度浮点数
- float: 32位单精度浮点数
3. 布尔类型
- bool: 布尔值
4. 字符串类型
- string: utf-8编码的字符串
5. 字节类型
- binary: 原始字节流
1.2 复杂数据类型
1. 结构体
struct User{
1: i32 id,
2: string name,
3: bool isActive
}
2. 枚举
enum UserRole{
ADMIN = 1,
USER = 2,
GUEST = 3
}
3. 容器类型
- 列表
list<string> names = 1; - 集合
无序集合,不包含重复元素set<i32> ids = 1; - 字典
map<string, i32> userAge = 1;
2. 定义IDL文件
IDL(Interface Definition Language)文件里面定义了结构体,以及接口和远程调用方法:
// 文件名: user.thrift
namespace go user
// 定义用户角色的枚举类型
enum UserRole {
ADMIN = 1,
USER = 2,
GUEST = 3
}
// 定义用户结构体
struct User {
1: i32 id, // 用户 ID
2: string name, // 用户名
3: bool isActive, // 用户是否活跃
4: UserRole role, // 用户角色
5: list<string> tags // 用户标签
}
// 定义用户服务
service UserService {
User getUser(1: i32 userId), // 获取用户信息
void updateUser(1: User user), // 更新用户信息
list<User> getAllUsers(), // 获取所有用户
}
执行命令:
thrift -r --gen go user.thrift
会在当前目录下生成一个gen-go文件夹,包含了生成的代码。
3. 编写server端代码
接下来我们模拟一个想要暴露api的服务它叫小a🐈,它提供了server端,运行在服务器上,供其他服务调用~
thrift编译器会为你定义的服务生成一个接口,定义如下:
type UserService interface {
// Parameters:
// - UserId
//
GetUser(ctx context.Context, userId int32) (_r *User, _err error)
// Parameters:
// - User
//
UpdateUser(ctx context.Context, user *User) (_err error)
GetAllUsers(ctx context.Context) (_r []*User, _err error)
}
我们需要在小a🐈服务里实现上面的接口:
type UserServiceHandler struct {
}
func (h *UserServiceHandler) GetUser(ctx context.Context, userId int32) (*user.User, error) {
fmt.Println("userId=", userId)
return &user.User{
ID: userId,
Name: "xiaoming",
}, nil
}
func (h *UserServiceHandler) UpdateUser(ctx context.Context, user *user.User) error {
fmt.Println("update user...")
return nil
}
func (h *UserServiceHandler) GetAllUsers(ctx context.Context) ([]*user.User, error) {
var userList []*user.User
userList = append(userList, &user.User{
ID: 0,
Name: "xiaoming",
})
return userList, nil
}
//定义一个server入口方法
func InitUserService() {
//server运行在本地的9091端口
transport, err := thrift.NewTServerSocket("localhost:9091")
if err != nil {
fmt.Println("Error starting server:", err)
return
}
handler := &UserServiceHandler{}
processer := user.NewUserServiceProcessor(handler)
server := thrift.NewTSimpleServer4(processer, transport, thrift.NewTTransportFactory(),
thrift.NewTBinaryProtocolFactoryDefault())
fmt.Println("Starting the server...", transport)
if err = server.Serve(); err != nil {
fmt.Println("Error starting server:", err)
}
}
然后我们在小a🐈服务的main.go文件中启动server:
package main
import (
_ "my-web-app/routers"
"my-web-app/services"
)
func main() {
//beego.Run()
services.InitUserService()
}
4. 编写client端代码
现在我们有一个服务小b🐶,它作为client端想要调用小a🐈,client端也同样需要user.thrift文件,并在本地生成代码,
然后我们创建和返回一个 Thrift客户端,以便于与UserService服务通信。
func GetClient() *user.UserServiceClient {
//填写server端地址
addr := ":9091"
//声明一个 thrift.TTransport 类型的变量,用于管理与 Thrift 服务的连接
var transport thrift.TTransport
var err error
//使用 thrift.NewTSocket 创建一个新的套接字(socket)传输,连接到指定的地址 addr,
//该方法返回一个 TTransport 实例和可能的错误。
transport, err = thrift.NewTSocket(addr)
if err != nil {
fmt.Println("Error opening socket:", err)
}
//声明一个 thrift.TProtocolFactory 类型的变量,并利用 thrift.NewTBinaryProtocolFactory
//创建一个新的二进制协议工厂,用于序列化和反序列化数据
var protocolFactory thrift.TProtocolFactory
protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
//声明一个 thrift.TTransportFactory 类型的变量,并创建一个新的传输工厂
//这里使用的是默认的无缓冲传输工厂。
var transportFactory thrift.TTransportFactory
transportFactory = thrift.NewTTransportFactory()
//使用传输工厂的 GetTransport 方法来获取一个新的传输对象,这里传入之前创建的 transport
transport, err = transportFactory.GetTransport(transport)
if err != nil {
fmt.Println("error running client:", err)
}
//尝试打开传输连接, 若打开失败,则打印错误信息
if err := transport.Open(); err != nil {
fmt.Println("error running client:", err)
}
//iprot输入协议
iprot := protocolFactory.GetProtocol(transport)
//oprot输出协议
oprot := protocolFactory.GetProtocol(transport)
//使用输入和输出协议创建一个新的 UserServiceClient 实例
//thrift.NewTStandardClient 用于创建一个标准的 Thrift 客户端
client := user.NewUserServiceClient(thrift.NewTStandardClient(iprot, oprot))
return client
}
接下来我们创建一个测试方法
func TestGetUser(t *testing.T) {
client := GetClient()
//执行远程调用方法GetUser
rep, err := client.GetUser(context.Background(), 1)
fmt.Println(rep, err)
if err != nil {
fmt.Println("thrift err:", err)
}
}
执行~
5. 总结
thrift是一个比较好的微服务之间通信的框架~不仅能跨语言,还具有高可扩展性,能够支持大规模的服务调用和负载均衡~
