Service Registration and Discovery (Consul/Etcd/Nacos)
Service Registration and Discovery Code Example
Service registration and discovery are important components of a microservices architecture. They form the foundation for service governance. In the sponge microservices framework, service registration and discovery are implemented using three mechanisms: Consul, Etcd, and Nacos. Below is an example of how to use service registration and discovery:
Service Registration Code Example
import "github.com/go-dev-frame/sponge/pkg/servicerd/registry"
func registerService(scheme string, host string, port int) (registry.Registry, *registry.ServiceInstance) {
var (
instanceEndpoint = fmt.Sprintf("%s://%s:%d", scheme, host, port)
cfg = config.Get()
iRegistry registry.Registry
instance *registry.ServiceInstance
err error
id = cfg.App.Name + "_" + scheme + "_" + host
logField logger.Field
)
switch cfg.App.RegistryDiscoveryType {
// registering service with consul
case "consul":
iRegistry, instance, err = consul.NewRegistry(
cfg.Consul.Addr,
id,
cfg.App.Name,
[]string{instanceEndpoint},
)
if err != nil {
panic(err)
}
logField = logger.Any("consulAddress", cfg.Consul.Addr)
// registering service with etcd
case "etcd":
iRegistry, instance, err = etcd.NewRegistry(
cfg.Etcd.Addrs,
id,
cfg.App.Name,
[]string{instanceEndpoint},
)
if err != nil {
panic(err)
}
logField = logger.Any("etcdAddress", cfg.Etcd.Addrs)
// registering service with nacos
case "nacos":
iRegistry, instance, err = nacos.NewRegistry(
cfg.NacosRd.IPAddr,
cfg.NacosRd.Port,
cfg.NacosRd.NamespaceID,
id,
cfg.App.Name,
[]string{instanceEndpoint},
)
if err != nil {
panic(err)
}
logField = logger.String("nacosAddress", fmt.Sprintf("%v:%d", cfg.NacosRd.IPAddr, cfg.NacosRd.Port))
}
if instance != nil {
msg := fmt.Sprintf("register service address to %s", cfg.App.RegistryDiscoveryType)
logger.Info(msg, logField, logger.String("id", id), logger.String("name", cfg.App.Name), logger.String("endpoint", instanceEndpoint))
return iRegistry, instance
}
return nil, nil
}
// ------------------------------------------------------------------------------------------
iRegistry, serviceInstance := registerService("http", "127.0.0.1", 8080)
// register service
ctx, _ := context.WithTimeout(context.Background(), 3*time.Second)
if err := iRegistry.Register(ctx, serviceInstance); err != nil {
panic(err)
}
// deregister service
ctx, _ = context.WithTimeout(context.Background(), 3*time.Second)
if err := iRegistry.Deregister(ctx, serviceInstance); err != nil {
return err
}
Service Discovery Code Example
import "github.com/go-dev-frame/sponge/pkg/servicerd/discovery"
var cliOptions = []grpccli.Option{}
var endpoint string
switch grpcClientCfg.RegistryDiscoveryType {
// discovering services using consul
case "consul":
endpoint = "discovery:///" + grpcClientCfg.Name // Connecting to grpc services by service name
cli, err := consulcli.Init(cfg.Consul.Addr, consulcli.WithWaitTime(time.Second*5))
if err != nil {
panic(fmt.Sprintf("consulcli.Init error: %v, addr: %s", err, cfg.Consul.Addr))
}
iDiscovery := consul.New(cli)
cliOptions = append(cliOptions, grpccli.WithDiscovery(iDiscovery))
// discovering services using etcd
case "etcd":
endpoint = "discovery:///" + grpcClientCfg.Name // Connecting to grpc services by service name
cli, err := etcdcli.Init(cfg.Etcd.Addrs, etcdcli.WithDialTimeout(time.Second*5))
if err != nil {
panic(fmt.Sprintf("etcdcli.Init error: %v, addr: %s", err, cfg.Etcd.Addrs))
}
iDiscovery := etcd.New(cli)
cliOptions = append(cliOptions, grpccli.WithDiscovery(iDiscovery))
// discovering services using nacos
case "nacos":
// example: endpoint = "discovery:///serverName.scheme"
endpoint = "discovery:///" + grpcClientCfg.Name + ".grpc"
cli, err := nacoscli.NewNamingClient(
cfg.NacosRd.IPAddr,
cfg.NacosRd.Port,
cfg.NacosRd.NamespaceID)
if err != nil {
panic(fmt.Sprintf("nacoscli.NewNamingClient error: %v, ipAddr: %s, port: %d",
err, cfg.NacosRd.IPAddr, cfg.NacosRd.Port))
}
iDiscovery := nacos.New(cli)
cliOptions = append(cliOptions, grpccli.WithDiscovery(iDiscovery))
}
serverNameExampleConn, err = grpccli.DialInsecure(context.Background(), endpoint, cliOptions...)
if err != nil {
panic(fmt.Sprintf("dial rpc server failed: %v, endpoint: %s", err, endpoint))
}
Example of Using Service Registration and Discovery in gRPC Services
Services created by sponge support Consul
, Etcd
, Nacos
as service registration and discovery centers. Below, using Consul
as an example, we will introduce how to enable the service registration and discovery functions, helping developers quickly complete configuration and implementation. The process is the same for Etcd
or Nacos
.
Enabling Service Registration
The service registration function is used to register services with service registration centers (such as Consul, Etcd, Nacos, etc.), making it easy for other services to discover and call them. The following steps demonstrate how to enable service registration on Consul:
Modify Configuration File
Open the configuration file
configs/xxx.yml
.In the
app
configuration block, set the value ofhost
to the local IP address or domain name. If the service and Consul are not on the same machine, you cannot use 127.0.0.1.In the
app
configuration block, set the value ofregistryDiscoveryType
toconsul
, and remove the comment symbol (#
) before this field.Find the
consul
configuration block at the end of the configuration file, uncomment it, and fill in the Consul address, for example:consul: address: "192.168.3.37:8500"
Modify Service Initialization Code
- Open the file
cmd/xxx/initial/createService.go
and adjust the code according to the following steps: - Comment out the code block under
// case 1, create a grpc/http service without registry
. - Uncomment the code block under
// case 2, create a grpc service and register it with consul or etcd or nacos
. - Uncomment the
// register service with consul or etcd or nacos, select one of them to use
section, and delete the registration code for Etcd and Nacos that you do not need, keeping only the Consul code.
- Open the file
Enabling Service Discovery
The service discovery function is used to dynamically obtain the addresses of target services from the service registration center, enabling load balancing and high availability. The following steps demonstrate how to enable service discovery on Consul:
Modify Configuration File
Open the configuration file
configs/xxx.yml
.In the
grpcClient
configuration block, set the value ofregistryDiscoveryType
toconsul
, and remove the comment symbol (#
) before this field.Find the
consul
configuration block at the end of the configuration file, uncomment it, and fill in the Consul address, for example:consul: address: "192.168.3.37:8500"
Modify Client Code
- Open the file
internal/rpcclient/xxx.go
. If the file does not exist, generate it through the [Public] → [Generate gRPC Service Connection Code] function on the Sponge Generate Code page. - Adjust the code according to the following steps:
- Uncomment the code block under
// using service discovery
. - Uncomment the code block under
// discovery service with consul or etcd or nacos, select one of them to use
, and delete the irrelevant Etcd and Nacos related code, keeping only the Consul code.
- Uncomment the code block under
- Open the file
Example of Using Service Registration and Discovery in Web Services
Web services created by sponge do not include service registration and discovery code by default. If you need to use them, you can configure it as follows:
Add Service Registration Code
Service registration allows registering your service with a service registry (such as Consul, Etcd, Nacos, etc.) so other services can discover and call it. The following steps show how to enable service registration with Consul:
Modify the Configuration File
Open the configuration file
configs/xxx.yml
.Under the
app
configuration block, set the value ofhost
to your local IP address or domain name. If the service and Consul are not running on the same machine, do not use127.0.0.1
.Under the
app
configuration block, set the value ofregistryDiscoveryType
toconsul
and remove the comment symbol (#
) in front of this field.At the end of the configuration file, locate the
consul
block, uncomment it, and fill in the address of Consul, for example:consul: address: "192.168.3.37:8500"
Modify the Service Initialization Code
- Open the file
cmd/xxx/initial/createService.go
and adjust the code as follows: - Copy the
registerService
function code block from the file createService.go. It is recommended to remove any unused Etcd and Nacos registration code, keeping only the Consul-related code. - Copy lines 25 to 30 from the file createService.go and replace the code block under the comment
// create a http service
.
- Open the file
Add Service Discovery Code
Service discovery dynamically retrieves the addresses of target services from the service registry, enabling load balancing and high availability. The following steps show how to enable service discovery with Consul:
Modify the Configuration File
Open the configuration file
configs/xxx.yml
.Under the
grpcClient
configuration block, set the value ofregistryDiscoveryType
toconsul
and remove the comment symbol (#
) in front of this field.At the end of the configuration file, locate the
consul
block, uncomment it, and fill in the address of Consul, for example:consul: address: "192.168.3.37:8500"
Client Service Discovery Code
var rrIndex int
func pickOneRoundRobin(sis []discovery.ServiceInstance) (string, error) {
if len(sis) == 0 {
return "", fmt.Errorf("no available service instances")
}
addr := sis[rrIndex%len(sis)].Endpoints[0]
rrIndex++
return addr, nil
}
func discoveryAndCall() {
// Retrieve service addresses from Consul using the service name
endpoint = "discovery:///" + your_service_name
cli, err := consulcli.Init(cfg.Consul.Addr, consulcli.WithWaitTime(time.Second*10))
if err != nil {
panic(fmt.Sprintf("consulcli.Init error: %v, addr: %s", err, cfg.Consul.Addr))
}
iDiscovery := consul.New(cli)
sis, err := iDiscovery.GetService(context.Background(), your_service_name)
if err != nil {
panic(fmt.Sprintf("GetService error:%v, serverName: %s", err, your_service_name))
}
addr, err := pickOneRoundRobin(sis) // Round-robin strategy
// After obtaining the addr, make the HTTP client request
// ......
}