前言
最近准备把之前发在博客的文章慢慢转到知乎上,这是第一篇。
在参与 Hertz 框架的开发迭代过程中,对 Hertz 的主库也越来越熟悉。接下来的几篇文章我将分别解析 Hertz 的服务注册、服务发现和负载均衡拓展,最后会使用适配于 Hertz 的 etcd 拓展进行实战,欢迎大家关注。
Hertz
Hertz 是一个超大规模的企业级微服务 HTTP 框架,具有高易用性、易扩展、低时延等特点。
Hertz 默认使用自研的高性能网络库 Netpoll,在一些特殊场景中,相较于 go net,Hertz 在 QPS、时延上均具有一定优势。
在内部实践中,某些典型服务,如框架占比较高的服务、网关等服务,迁移 Hertz 后相比 Gin 框架,资源使用显著减少,CPU 使用率随流量大小降低 30%—60% 。
关于 Hertz 更多的信息可移步至 cloudwego/hertz
服务注册拓展
pkg/app/server/registry拓展接口与注册信息
服务注册接口定义与实现
服务注册的接口定义和大部分服务注册的实现类似,含有两个方法,一个为服务注册,另一个为服务取消注册。
// Registry is extension interface of service registry.
type Registry interface {
Register(info *Info) error
Deregister(info *Info) error
}
register.go// NoopRegistry
type noopRegistry struct{}
func (e noopRegistry) Register(*Info) error {
return nil
}
func (e noopRegistry) Deregister(*Info) error {
return nil
}
register.go// NoopRegistry is an empty implement of Registry
var NoopRegistry Registry = &noopRegistry{}
同时也是 Hertz 配置中服务注册的默认值。
func NewOptions(opts []Option) *Options {
options := &Options{
// ...
Registry: registry.NoopRegistry,
}
options.Apply(opts)
return options
}
注册信息
registry.goWithRegistryRegister// Info is used for registry.
// The fields are just suggested, which is used depends on design.
type Info struct {
// ServiceName will be set in hertz by default
ServiceName string
// Addr will be set in hertz by default
Addr net.Addr
// Weight will be set in hertz by default
Weight int
// extend other infos with Tags.
Tags map[string]string
}
服务注册的时机
SpininitOnRunHooksinitOnRunHooksfunc (h *Hertz) initOnRunHooks(errChan chan error) {
// add register func to runHooks
opt := h.GetOptions()
h.OnRun = append(h.OnRun, func(ctx context.Context) error {
go func() {
// delay register 1s
time.Sleep(1 * time.Second)
if err := opt.Registry.Register(opt.RegistryInfo); err != nil {
hlog.SystemLogger().Errorf("Register error=%v", err)
// pass err to errChan
errChan <- err
}
}()
return nil
})
}
initOnRunHookstime.SleepRegister服务取消注册的时机
ShutdownexecuteOnShutdownHooksDeregisterfunc (engine *Engine) Shutdown(ctx context.Context) (err error) {
if atomic.LoadUint32(&engine.status) != statusRunning {
return errStatusNotRunning
}
if !atomic.CompareAndSwapUint32(&engine.status, statusRunning, statusShutdown) {
return
}
ch := make(chan struct{})
// trigger hooks if any
go engine.executeOnShutdownHooks(ctx, ch)
defer func() {
// ensure that the hook is executed until wait timeout or finish
select {
case <-ctx.Done():
hlog.SystemLogger().Infof("Execute OnShutdownHooks timeout: error=%v", ctx.Err())
return
case <-ch:
hlog.SystemLogger().Info("Execute OnShutdownHooks finish")
return
}
}()
if opt := engine.options; opt != nil && opt.Registry != nil {
if err = opt.Registry.Deregister(opt.RegistryInfo); err != nil {
hlog.SystemLogger().Errorf("Deregister error=%v", err)
return err
}
}
// call transport shutdown
if err := engine.transport.Shutdown(ctx); err != ctx.Err() {
return err
}
return
}
总结
在这篇文章中我们了解到了 Hertz 可支持高度自定义服务注册拓展的实现,并解析了 Hertz 是如何将其集成在了框架的核心部分。
最后,如果文章对你有帮助,就点赞分享一下吧,谢谢!