Foreword
During the development iterations of the Hertz framework, I became more and more familiar with Hertz's main library. In the next few articles, I will analyze Hertz's service registration, service discovery and load balancing expansion respectively, and finally use the etcd expansion adapted to Hertz for actual combat. Welcome everyone to pay attention.
Hertz
Hertz is an ultra-large-scale enterprise-level microservice HTTP framework, featuring high ease of use, easy expansion, and low latency etc.
Hertz uses the self-developed high-performance network library Netpoll by default. In some special scenarios, Hertz has certain advantages in QPS and latency compared to go net.
In internal practice, some typical services, such as services with a high proportion of frameworks, gateways and other services, after migrating Hertz, compared to the Gin framework, the resource usage is significantly reduced, CPU usage is reduced by 30%-60% with the size of the traffic.
For more details, see cloudwego/hertz.
Service registration extension
Hertz supports custom registration modules. Users can extend and integrate other registries by themselves. The extension is defined under pkg/app/server/registry
.
Extended interface and Info
Service registration interface definition and implementation
The interface definition of service registration is similar to the interface definition of implementing service registration and the implementation of most service registrations. It contains two methods, one for service registration and the other for service deregistration.
// Registry is extension interface of service registry.
type Registry interface {
Register(info *Info) error
Deregister(info *Info) error
}
These two methods are implemented in subsequent code in register.go
.
// NoopRegistry
type noopRegistry struct{}
func (e noopRegistry) Register(*Info) error {
return nil
}
func (e noopRegistry) Deregister(*Info) error {
return nil
}
An empty Registry implementation is also provided in register.go
, the default for service registration in the Hertz configuration.
// NoopRegistry is an empty implement of Registry
var NoopRegistry Registry = &noopRegistry{}
It is also the default value for service registration in Hertz configuration.
func NewOptions(opts []Option) *Options {
options := &Options{
// ...
Registry: registry.NoopRegistry,
}
options.Apply(opts)
return options
}
Registration Info
The definition of registration information is provided in registry.go
. When using WithRegistry
for configuration service registration, the registration information will be initialized and passed to the Register
method for subsequent logic execution. These fields are only suggestions, and the exact usage depends on your design.
// 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
}
Timing of service registration
We run initOnRunHooks
every time the Spin
method is called, and initOnRunHooks
contains the timing of our service registration.
func (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
})
}
When calling initOnRunHooks
, we will add the anonymous function registered by the service to runHooks. In this function, we first start a goroutine. Here we will delay one second through time.Sleep
. When asynchronous registration, the service will not necessarily start, so there will be a delay of one second to wait for the service to start. After this call Register
and pass in the Info from the configuration. If there is an error during registration, pass the error to errChan.
Timing of service deregistration
When receiving a signal that the program exits, Hertz will perform a graceful exit and call the Shutdown
method, in which the service will be unregistered. First execute executeOnShutdownHooks
concurrently and wait for them until the wait times out or finishes execution. Then it will check whether there is a registered service, and if so, call Deregister
to deregister the service.
func (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
}
Summary
In this article we learned about Hertz's implementation of highly customizable service registry extensions, and analyzed how Hertz integrates it into the core of the framework.
Finally, if the article is helpful to you, please like and share, thank you!
Top comments (0)