服务注册发现实战:Nacos与Consul集群部署与健康检查机制对比

服务注册发现模式与选型

服务注册发现是微服务架构的基础设施,解决服务实例动态上下线时的寻址问题。当前主流方案分为客户端发现模式和服务端发现模式。客户端发现模式下,服务消费者直接查询注册中心获取实例列表并自行负载均衡,Nacos和Eureka属于此类。服务端发现模式下,请求先经过网关或代理,由代理查询注册中心并转发,Consul配合Envoy属于此类。

Nacos是阿里开源的注册中心与配置中心一体化方案,与Spring Cloud生态深度集成,支持AP和CP两种一致性模式切换。Consul是HashiCorp出品的分布式服务网格工具,使用Raft协议保证强一致性,内置KV存储和DNS服务发现。选型上,Java技术栈优先考虑Nacos,多语言环境和云原生架构优先考虑Consul。

Nacos集群部署与配置

Nacos集群推荐至少3个节点,使用外置MySQL存储保证数据持久化。AP模式下实例通过心跳保活,节点宕机不影响服务发现;CP模式通过Raft选举,保证强一致性但可用性受限于多数派节点存活。

# Nacos集群配置 cluster.conf
192.168.1.101:8848
192.168.1.102:8848
192.168.1.103:8848

# application.properties 关键配置
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://192.168.1.200:3306/nacos?characterEncoding=utf8
db.user.0=nacos
db.password.0=nacos_password_2026

# 开启鉴权
nacos.core.auth.enabled=true
nacos.core.auth.plugin.nacos.token.expire.seconds=18000

# 启动各节点
sh startup.sh -m cluster -s 192.168.1.101
sh startup.sh -m cluster -s 192.168.1.102
sh startup.sh -m cluster -s 192.168.1.103
// Spring Boot集成Nacos服务注册
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(PaymentServiceApplication.class, args);
    }
}

// application.yml
// spring.cloud.nacos.discovery:
//   server-addr: 192.168.1.101:8848,192.168.1.102:8848,192.168.1.103:8848
//   namespace: production
//   cluster-name: BJ-CLUSTER
//   ephemeral: true  # AP模式
//   heart-beat-interval: 5000

// 服务消费者:OpenFeign + Nacos负载均衡
@FeignClient(name = 'payment-service', fallback = PaymentFallback.class)
public interface PaymentClient {
    @PostMapping('/api/payment/process')
    Result processPayment(@RequestBody PaymentRequest request);
}

// 自定义负载均衡策略:同集群优先
@Component
public class ClusterPriorityLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private final String serviceId;
    private final ObjectProvider<ServiceInstanceListSupplier> supplier;

    public ClusterPriorityLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> p, String id) {
        this.supplier = p; this.serviceId = id;
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return supplier.getIfAvailable().get(request).next()
            .map(instances -> {
                String cluster = 'BJ-CLUSTER';
                List<ServiceInstance> filtered = instances.stream()
                    .sorted((a, b) -> {
                        boolean aSame = a.getMetadata().getOrDefault('cluster', '').equals(cluster);
                        boolean bSame = b.getMetadata().getOrDefault('cluster', '').equals(cluster);
                        return Boolean.compare(bSame, aSame);
                    }).collect(Collectors.toList());
                if (filtered.isEmpty()) return new DefaultResponse(null);
                int total = filtered.stream().mapToInt(i ->
                    Integer.parseInt(i.getMetadata().getOrDefault('weight', '100'))).sum();
                int rand = ThreadLocalRandom.current().nextInt(total);
                int cur = 0;
                for (ServiceInstance inst : filtered) {
                    cur += Integer.parseInt(inst.getMetadata().getOrDefault('weight', '100'));
                    if (rand < cur) return new DefaultResponse(inst);
                }
                return new DefaultResponse(filtered.get(0));
            });
    }
}

Consul集群部署与健康检查

Consul使用Gossip协议进行成员管理和故障检测,Raft协议保证一致性。Consul的健康检查机制比Nacos更丰富,支持HTTP、TCP、gRPC、Script、Docker和TTL六种检查方式。

# Consul集群配置 /etc/consul.d/server.hcl
datacenter = 'production'
data_dir = '/opt/consul/data'
server = true
bootstrap_expect = 3
bind_addr = '192.168.1.101'
client_addr = '0.0.0.0'
retry_join = ['192.168.1.102', '192.168.1.103']

acl = { enabled = true, default_policy = 'deny' }

# 启动Server节点
consul agent -config-dir=/etc/consul.d

# 服务注册(HTTP健康检查)
cat > /etc/consul.d/services/payment.json << 'EOF'
{
  "service": {
    "name": "payment-service",
    "id": "payment-192.168.1.101-8080",
    "address": "192.168.1.101",
    "port": 8080,
    "checks": [
      {
        "id": "http-health",
        "http": "http://192.168.1.101:8080/actuator/health",
        "interval": "10s",
        "timeout": "3s",
        "deregister_critical_service_after": "30s"
      },
      {
        "id": "grpc-health",
        "grpc": "192.168.1.101:9090",
        "interval": "10s"
      }
    ]
  }
}
EOF
consul reload

服务发现客户端集成

// Consul服务发现客户端(Go语言)
package discovery

import (
    "context"
    "fmt"
    "time"
    "github.com/hashicorp/consul/api"
)

type ConsulDiscovery struct { client *api.Client }

func NewConsulDiscovery(addr string) (*ConsulDiscovery, error) {
    config := api.DefaultConfig()
    config.Address = addr
    client, err := api.NewClient(config)
    if err != nil { return nil, err }
    return &ConsulDiscovery{client: client}, nil
}

func (d *ConsulDiscovery) Register(name, id, addr string, port int) error {
    reg := &api.AgentServiceRegistration{
        ID: id, Name: name, Address: addr, Port: port,
        Check: &api.AgentServiceCheck{
            HTTP: fmt.Sprintf("http://%s:%d/health", addr, port),
            Interval: "10s", Timeout: "3s",
            DeregisterCriticalServiceAfter: "30s",
        },
    }
    return d.client.Agent().ServiceRegister(reg)
}

func (d *ConsulDiscovery) Discover(name string) ([]*api.AgentService, error) {
    services, _, err := d.client.Health().Service(name, "", true,
        &api.QueryOptions{UseCache: true, MaxAge: 10 * time.Second})
    if err != nil { return nil, err }
    var instances []*api.AgentService
    for _, s := range services {
        allPassing := true
        for _, c := range s.Checks {
            if c.Status != "passing" { allPassing = false; break }
        }
        if allPassing { instances = append(instances, s.Service) }
    }
    return instances, nil
}

func (d *ConsulDiscovery) DiscoverWithRetry(name string, max int) ([]*api.AgentService, error) {
    for i := 0; i < max; i++ {
        instances, err := d.Discover(name)
        if err == nil && len(instances) > 0 { return instances, nil }
        time.Sleep(time.Duration(1<

高可用与容灾方案对比

Nacos和Consul在容灾策略上有明显差异。Nacos AP模式下,即使集群多数节点宕机,剩余节点仍可提供服务发现能力,适合可用性优先的场景。Consul的Raft协议要求多数派存活才能写入,但Gossip协议在少数派存活时仍可进行服务发现和健康检查(只读模式)。

# 容灾对比测试
# Nacos AP模式:杀死2个节点,剩余1节点仍可用
curl -s 'http://192.168.1.101:8848/nacos/v1/ns/instance/list?serviceName=payment-service' | jq '.hosts | length'

# Consul:杀死2个节点,剩余1节点只读可用
curl -s 'http://192.168.1.101:8500/v1/health/service/payment-service?passing' | jq 'length'

跨数据中心部署方面,Consul原生支持多数据中心WAN Gossip,通过WAN Gossip池实现跨DC服务发现。Nacos通过Namespace隔离不同环境,跨地域部署需要配合Nginx或自研代理做跨地域转发。对于全球部署的业务,Consul的多数据中心支持更成熟;对于单地域多可用区部署,Nacos的配置中心一体化能力更具优势。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/fu-wu-zhu-ce-fa-xian-shi-zhan-nacos-yu-consul-ji-qun-bu-shu/

(0)
小编小编
上一篇 2天前
下一篇 2天前

相关推荐