拥抱云原生——Hertz与Kitex的初探与实践 | 青训营

201 阅读6分钟

拥抱云原生——Hertz与Kitex的初探与实践

随着微服务和云原生技术的日益成熟,企业和开发者都在寻找能够提供高性能、高可用性和高扩展性的解决方案。字节跳动,作为全球领先的技术公司,推出了两个开源项目——Hertz和Kitex,为开发者提供了构建云原生应用的强大工具。本文将深入探讨这两个项目的特点、优势和实践应用。

框架初探

Hertz:高性能的HTTP框架

概述

Hertz是一个为Go设计的高性能、高可用性、可扩展的HTTP框架。它结合了fasthttp、gin和echo等开源框架的优点,并融入了字节跳动的独特挑战,使其在多年的生产环境中得到了验证。

核心特点

  • 高可用性:Hertz旨在提供最佳的用户体验,帮助开发者快速、准确地完成工作。
  • 高性能:默认使用Netpoll,一个从零开始构建的高性能网络库,与go.net相比,Hertz在某些场景中表现更佳。
  • 高扩展性:其分层设计使得框架非常易于扩展,同时保持核心功能的稳定性。

image.png

Kitex:RPC框架的新选择

概述

Kitex是一个高性能、高扩展性的Golang RPC框架,专为微服务设计。它不仅集成了高性能的网络库Netpoll,还提供了多种RPC消息和传输协议的支持。

核心特点

  • 高性能:与go net相比,Kitex提供了显著的性能优势。
  • 可扩展性:Kitex提供了许多带有默认实现的接口供用户自定义,满足不同的业务需求。
  • 多协议支持:Kitex支持多种RPC消息协议,如Thrift、Kitex Protobuf和gRPC。
  • 服务治理:Kitex集成了服务治理模块,如服务注册、服务发现、负载均衡等。

image.png

微服务架构

微服务架构是软件工程中的一种变体,属于面向服务的架构风格。它是一种架构模式,将应用程序组织为一系列松散耦合的、细粒度的服务,这些服务通过轻量级协议进行通信。其主要目标之一是使团队能够独立地开发和部署他们的服务。这是通过减少代码库中的多个依赖关系来实现的,从而允许开发者在用户的有限限制下发展他们的服务,并隐藏额外的复杂性。

微服务的核心特点

  1. 服务组织:微服务通常是通过网络进行通信的进程,使用技术无关的协议(如HTTP)来实现特定的目标。
  2. 业务驱动:服务围绕业务能力进行组织。
  3. 多样性:服务可以使用不同的编程语言、数据库、硬件和软件环境来实现,取决于最适合的选择。
  4. 小而美:服务小巧、支持消息传递、由上下文界定、可以独立开发和部署。

微服务不是单一应用中的一个层(例如,Web控制器或后端的前端)。相反,它是一个具有清晰接口的业务功能单元,并且可能通过其内部组件实现分层架构。从战略角度看,微服务架构基本上遵循Unix哲学的“做一件事,做得好”。

微服务的优势

  1. 模块化:这使得应用程序更容易理解、开发、测试,并变得更能抵御架构退化。
  2. 可伸缩性:由于微服务是独立于其他服务实现和部署的,它们可以独立地进行监控和伸缩。
  3. 集成异构和遗留系统:微服务被认为是现代化现有单体软件应用的有效手段。
  4. 分布式开发:它通过使小型自主团队能够独立地开发、部署和扩展其各自的服务来并行化开发。

微服务的挑战

  1. 服务形成信息障碍
  2. 网络延迟和消息处理时间
    • 与单体服务进程内的调用相比,网络上的服务间调用成本更高。
  3. 测试和部署更为复杂
  4. 服务间的责任转移更为困难

总的来说,微服务架构为构建灵活、独立部署的软件系统提供了一种方法。这种方法是面向服务架构的第一个实现,随着云原生应用、无服务器计算和使用轻量级容器部署的应用的普及,它变得越来越受欢迎。

微服务架构中的Hertz与Kitex:构建一个示例

微服务架构的核心在于将大型应用程序分解为小型、独立的服务单元,这些单元可以独立地开发、部署和扩展。字节跳动的Hertz和Kitex为开发者提供了构建微服务应用的强大工具。在本章节中,我们将探讨如何使用Hertz和Kitex构建一个简单的微服务示例。

项目架构

首先,我们来看一下项目的文件结构:

├─api  
│ ├─biz  
│ │ ├─client  
│ │ ├─handler  
│ │ ├─model  
│ │ └─router  
│ └─script  
├─idl  
│ └─rpc  
└─service  
└─pong  
└─script

在这个结构中,api目录负责处理HTTP请求,idl目录定义了RPC的接口,而service目录则包含了微服务的具体实现。

Hertz:API网关

Hertz在这个示例中扮演了API网关的角色,它负责接收和处理来自客户端的HTTP请求。我们使用/ping端点作为演示。

// api/biz/handler/ping.go
func Ping(ctx context.Context, c *app.RequestContext) {
	c.JSON(consts.StatusOK, utils.H{
		"message": "pong",
		"data":    client.PingClient(),
	})
}

在这段代码中,PingClient函数负责向Kitex的PingService发送RPC请求。

Kitex:RPC服务

在微服务架构中,服务之间的通信通常不是通过可读的格式,而是通过远程过程调用(RPC)进行的。RPC调用的数据是二进制格式,需要在发送端和接收端之间进行编码和解码。为了定义这种数据交互的格式,我们使用接口定义语言(IDL)。

// idl/rpc/pong.thrift
namespace go pong

struct PingReq {
    1: required string ping_time,
}
struct PongResp {
    1: required string pong_time,
}
service PongService {
    PongResp Pong(1: required PingReq req),
}

在这个IDL定义中,我们定义了一个PongService,它接受一个字符串作为请求,并返回一个字符串作为响应。

为了快速构建服务,我们可以使用Kitex的代码生成工具。这个工具可以根据IDL定义生成服务的脚手架代码。

// service/pong/main.go
func main() {
	addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8889")
	if err != nil {
		log.Println(err.Error())
		return
	}
	svr := pong.NewServer(new(PongServiceImpl), server.WithServiceAddr(addr))

	err = svr.Run()

	if err != nil {
		log.Println(err.Error())
	}
}

在这段代码中,我们启动了一个监听在8889端口的微服务。这个服务的具体逻辑在PongServiceImpl中实现。

// service/pong/handler.go
func (s *PongServiceImpl) Pong(ctx context.Context, req *pong.PingReq) (resp *pong.PongResp, err error) {
	// TODO: Your code here...
	resp = &pong.PongResp{
		PongTime: req.PingTime + " " + time.Now().String(),
	}
	return resp, nil
}

在这个实现中,服务接收一个请求,然后返回一个响应。

结论

通过上述示例,我们可以看到Hertz和Kitex如何协同工作,为开发者提供了一个简单、高效的方式来构建微服务应用。这种架构模式不仅提供了高性能和可扩展性,还简化了服务之间的通信和数据交互。