入门 KiteX 基础篇

点击上方蓝色字体,选择“设为星标”

回复”云原生“获取基础架构实践

9d0c5d5550fb2d8e86f0f431b624e10d.jpeg

概览

KiteX 是 bytedance 开源的高性能 RPC 框架,实现了高吞吐、高负载、高性能等居多特性,具体请看 KiteX 的实践,文章介绍多传输协议、消息协议时,说到 KiteX 支持的协议类型:Thrift、Protobuf 等,今天我们主要来实践如何利用 KiteX 基于对应的 IDL 生成对应协议的代码。

Thrift 简介

Thrift 本身是一软件框架(远程过程调用框架),用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引 擎,以构建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。同时,作为 IDL(接口定义语言 Interface Definition Language),允许你定义一个简单的定义文件中的数据类型和服务接口,以作为输入文件,编译器生成代码用来方便地生成 RPC 客户端和服务器通信的无缝跨编程语言。

Protobuf 简介

Protobuf 全称是 Google Protocol Buffer,是一种高效轻便的结构化数据存储方式,用于数据的通信协议、数据存储等。相对比 XML 来说,其特点:

  • 语言无关,平台无关

  • 高效

  • 扩展性、兼容性更强

基于 IDL 的 KiteX 实践

在 RPC 框架中,我们知道,服务端与客户端通信的前提是远程通信,但这种通信又存在一种关联,那就是通过一套相关的协议(消息、通信、传输等)来规范,但客户端又不用关心底层的技术实现,只要定义好了这种通信方式即可。

在 KiteX 中,也提供了一种生成代码的命令行工具:kitex,目前支持 thrift、protobuf 等 IDL,并且支持生成一个服务端项目的骨架。

安装命令行工具

环境
  • 如果您之前未搭建 Golang 开发环境, 可以参考 Golang 安装

  • 推荐使用最新版本的 Golang,我们保证最新三个正式版本的兼容性(现在 >= v1.16)。

  • 确保打开 go mod 支持 (Golang >= 1.15 时,默认开启)

  • kitex 暂时没有针对 Windows 做支持,如果本地开发环境是 Windows 建议使用 WSL2

安装
  • 确保 GOPATH 环境变量已经被正确地定义(例如 export GOPATH=~/go)并且将 添加到环境变量之中(例如GOPATH/bin:$PATH);请勿将 GOPATH 设置为当前用户没有读写权限的目录

  • 安装 kitex:go install github.com/cloudwego/kitex/tool/cmd/kitex@latest

  • 安装 thriftgo:go install github.com/cloudwego/thriftgo@latest

安装成功后,执行 kitex --version 可以看到如下信息:

$ kitex --version
v0.4.2

如果在安装阶段发生问题,可能主要是由于对 Golang 的不当使用造成的,需要逐一排查。

编写一个 IDL

我们先新建一个项目:hz-kitex-examples,新建完之后,我们在该项目下新建一个目录:idl,然后我们编写一个 thrift IDL:

99b6e2eca38a9f8759cb3e6fb2cd8dbf.png

接下来,我们编写这个 IDL:

namespace go hello

struct ReqBody {
    1: string name
    2: i32 type
    3: string email
}

struct Request {
 1: string data
 2: string message
 3: ReqBody reqBody
}

struct Msg {
 1: i64 status
 2: i64 code
 3: string msg
}

struct Response {
 1: Msg msg
 2: string data
}

service HelloService {
    Response echo(1: Request req)
    Response testHello4Get(1: Request req)
    Response testHello4Post(1: Request req)
}

这里我们定义了一个命名空间:hello,这个是代表生成的代码中有一个目录:hello,然后我们编写一个请求对象:ReqBody,接着定义一个泛对象,包括了那个请求对象,这块没要求,自己定义好就行,同时我们定义了响应对象Response,此外,我们还定义了一个类,类中存在三个函数方法。

生成代码

在定义完 IDL 后,我们来看如何生成代码呢?直接执行如下命令:

kitex -module "hz-kitex-examples" -thrift frugal_tag -service helloserver idl/hello.thrift

这里有几个参数 tag:

-module module_name

  • 该参数用于指定生成代码所属的 Go 模块,会影响生成代码里的 import path。

  • 如果当前目录是在 下的一个目录,那么可以不指定该参数;会使用GOPATH/src 开始的相对路径作为 import path 前缀。例如,在 $GOPATH/src/example.com/hello/world 下执行 kitex,那么 kitex_gen/example_package/example_package.go 在其他代码代码里的 import path 会是 example.com/hello/world/kitex_gen/example_package。

  • 如果当前目录不在 $GOPATH/src 下,那么必须指定该参数。

  • 如果指定了 -module 参数,那么 kitex 会从当前目录开始往上层搜索 go.mod 文件

    • 如果不存在 go.mod 文件,那么 kitex 会调用 go mod init 生成 go.mod;

    • 如果存在 go.mod 文件,那么 kitex 会检查 -module 的参数和 go.mod 里的模块名字是否一致,如果不一致则会报错退出;

    • 最后,go.mod 的位置及其模块名字会决定生成代码里的 import path。

-service service_name

  • 使用该选项时,kitex 会生成构建一个服务的脚手架代码,参数 service_name 给出启动时服务自身的名字,通常其值取决于使用 Kitex 框架时搭配的服务注册和服务发现功能。

对于当前项目,我们执行如下:

kitex -module "hz-kitex-examples" -thrift frugal_tag -service helloserver idl/hello.thrift

由于当前项目不在环境路径下,需要指定 go.mod 所在的目录模块的名称,同时,我们指定一个服务名。

这样在执行后,我们会发现生成的目录结构如下图:

bb55fa0afd46de2f7e2fd9eae05a7e61.png

在生成的目录中根目录是kitex_gen,代表是 kitex 工具生成的,其次其目录下有一个 hello 目录,这是代表 IDL 文件中的 ns,在其下面有一个文件:定义了请求对象与响应对象的序列化、传输信息的读写等操作。

在其下面还存在一个 service 目录,用来生成跟客户端与服务端相关的 service 处理逻辑。其中也定义了 service 中处理的方法信息。

同时,我们可以看到生成了服务端的基础框架:

205a09f82b1081177acc2d01bebf5c22.png

服务端

新建一个server 目录,然后在里面新建一个项目hello,此时把生成服务端的骨架代码拷贝到里面:

17390ea97519c7651099c365b6b3de19.png

拷贝完之后,我们可以丰满服务端的函数的逻辑,以 Echo 函数为例:

func (s *HelloApi) Echo(ctx context.Context, req *api.Request) (resp *api.Response, err error) {
 klog.Info("hello service enter: " + GetIpAddr2())

 resp = &api.Response {
  Msg:  &api.Msg {
   Status: 200,
   Code:   10000,
   Msg:    req.Message,
  },
  Data: req.Message,
 }
 return resp, nil
}

func GetIpAddr2() string {
 conn, err := net.Dial("udp", "8.8.8.8:53")
 if err != nil {
  klog.Error(err)
  return ""
 }
 localAddr := conn.LocalAddr().(*net.UDPAddr)
 // 192.168.1.20:61085
 ip := strings.Split(localAddr.String(), ":")[0]

 return ip
}

然后再定义启动函数:

svr := hello.NewServer(new(api.HelloApi),
    server.WithServiceAddr(&net.TCPAddr{Port: 2008}),
  server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: constants.HelloServiceName}),
    server.WithPayloadCodec(thrift.NewThriftCodecWithConfig(thrift.FastRead | thrift.FastWrite)),
  server.WithErrorHandler(func(err error) error {
   error := errno.ConvertErr(err)
   return error
  }),
  //指定默认 Codec 的包大小限制,默认无限制 option: codec.NewDefaultCodecWithSizeLimit
  server.WithCodec(codec.NewDefaultCodecWithSizeLimit(1024 * 1024 * 10)),//10M
  server.WithLimit(&limit.Option{MaxConnections: 10000, MaxQPS: 5000}),
  //连接多路复用(mux)
  server.WithMuxTransport(),
  server.WithMetaHandler(transmeta.ServerTTHeaderHandler),
  server.WithRegistry(registry.NewNacosRegistry(r1)),
 )

在这里,我们定义了服务端的端口:2008,同时被注册到 Nacos。启动之后:

e1b0b5aba72339aaa759f5cd93a00340.png

可以看到被注册到 Nacos:

a634ec849cefbc8486129e98a97994ef.png

这里注册 Nacos 的代码前面已经讲过了,具体可以看:KiteX 的实践

客户端

关于客户端,也是一样,新建一个 client 目录,里面新建一个项目customer-service,新建启动 main 函数,这里与服务端类似不再赘述了。主要注意一点:这里由于需要提供 Http 协议接口,需要结合 Hertz 来进行:

cf116cf799924b5a16f491e34befe94d.png

至于 Hertz,它是一个高性能的 Http 微服务框架在后面的文章中会进一步讲解,此处不再赘述。

启动函数新建完后,我们需要初始化一个 RPC 连接的客户端:

acc707b934b1d70a5126d06d185d0234.png

此处客户端的初始化,也是基于之前生成的代码:

fbc99ff15856a4e94016932a67252460.png

在这个函数初始化客户端时,需要定义请求的服务名、网络库、负载均衡策略、出错误处理机制等。同时,我们还需要复写需要调用的函数,去调用相关的接口:

94e0d3b34bc1ffd36b5898bd3381f557.png 9e3c4442603e4fc97ea131bf6b9bb496.png

写完 RPC 的部分,一个简单的 RPC 协议调用就能串联起来了,此时,我们来简单写下客户端的接口调用:

func HelloDemo(ctx context.Context, c *app.RequestContext) {
 req := &api.Request{Message: "my request"}
  // TODO
 resp, err := rpc.Echo(context.Background(), req)
 if err != nil {
  log.Fatal(err)
 }
 klog.Info(resp)
  // TODO
 c.JSON(consts.StatusOK, (resp))
}

上面定义的是客户端的接口入口,进入后,会调用 rpc 的部分。同时在调用 RPC 前后,可以有自己的逻辑处理以及响应数据的处理,在 TODO 部分。

启动客户端进行服务注册:

9fbc0d4ef9df54c3c1007a1ac06a5879.png

测试

在启动完服务端、客户端后,我们访问客户端的 http 接口:

http://192.168.6.51:3000/v1/hello/test
99d5902252a156fdfaf04cc94472c8cf.png

我们再多几次进行访问:

cad577e6e9e87c78eb30f75aa1b25088.png

发现其访问的性能以及速度还是不错的,这得益于 KiteX 框架中使用了自研的 Netpoll 网络库以及实现了高效的吞吐编解码性能提升。

更多源码可以关注号后回复私聊后台获取~

号内回复“云原生”,获取云原生基础架构实践电子书

afdceed15371b25b165b602a53e12ed5.jpeg

云原生社区合肥站

云原生社区合肥站正式启动啦,欢迎Base合肥、关注云原生、长期从事云原生的同志们踊跃加入,云原生社区合肥站会因为你们的加入而变得更加美好~

详情参见Issue:https://github.com/cloudnativeto/community/issues/107

欢迎关注个站:damon008.github.io

往期回顾

微服务自动化部署CI/CD

如何利用k8s拉取私有仓库镜像

个站建设基础教程

ArrayList、LinkedList 你真的了解吗?

大佬整理的mysql规范,分享给大家

如果张东升是个程序员

微服务架构设计之解耦合

浅谈负载均衡

Oauth2的认证实战-HA篇

Oauth2的授权码模式《上》

浅谈开发与研发之差异

浅谈 Java 集合 | 底层源码解析

基于 Sentinel 作熔断 | 文末赠资料

基础设施服务k8s快速部署之HA篇

今天被问微服务,这几点,让面试官刮目相看

Spring cloud 之多种方式限流(实战)

Spring cloud 之熔断机制(实战)

面试被问finally 和 return,到底谁先执行?

Springcloud Oauth2 HA篇

Spring Cloud Kubernetes之实战一配置管理

Spring Cloud Kubernetes之实战二服务注册与发现

Spring Cloud Kubernetes之实战三网关Gateway

220a243dfd92a01ddcf6b3d95d5646b8.gif

ce80faccf845672a2ac9d9f39d1aed5c.gif

点击 "damon008.github.io" 获取更好的阅读体验!

❤️给个「在看」,是对我最大的支持❤️
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值