服务注册发现模式与选型
服务注册发现是微服务架构的基础设施,解决服务实例动态上下线时的寻址问题。当前主流方案分为客户端发现模式和服务端发现模式。客户端发现模式下,服务消费者直接查询注册中心获取实例列表并自行负载均衡,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/