Consul:系统学习笔记

目录

  • Consul概述
  • 核心概念
  • 架构设计
  • 环境搭建
  • 服务注册与发现
  • 键值存储
  • 健康检查
  • 服务网格
  • ACL权限控制
  • 集群配置
  • API使用
  • 客户端集成
  • 监控运维
  • 故障排查
  • 实战案例
  • 最佳实践

Consul概述

Consul是HashiCorp开源的一个服务网格解决方案,提供服务发现、配置管理、健康检查等功能的完整控制平面。它支持多数据中心部署,提供强一致性保证。

核心特性

服务发现:自动服务注册和发现

健康检查:内置多种健康检查机制

键值存储:分布式键值存储系统

多数据中心:原生支持多数据中心

服务网格:提供服务间安全通信

Web UI:直观的管理界面

应用场景

微服务架构 → Consul → 服务治理

配置中心 → Consul KV → 配置管理

DNS服务 → Consul DNS → 服务发现

服务网格 → Consul Connect → 安全通信

微服务注册中心:替代Eureka、Zookeeper

配置中心:基于KV存储的配置管理

DNS服务器:提供DNS接口的服务发现

API网关:动态路由和负载均衡

服务网格:提供服务间加密通信

核心概念

Agent(代理)

Consul集群中的每个节点都运行一个Consul Agent,分为Client和Server两种模式:

java 复制代码
# Server模式 - 参与共识算法,存储数据
consul agent -server -bootstrap-expect=3 -data-dir=/opt/consul/data -node=server1

# Client模式 - 转发请求到Server,无状态
consul agent -data-dir=/opt/consul/data -node=client1

Datacenter(数据中心)

java 复制代码
Datacenter-1          Datacenter-2
┌─────────────┐      ┌─────────────┐
│ Server1     │      │ Server4     │
│ Server2     │◄────►│ Server5     │
│ Server3     │      │ Server6     │
│ Client1     │      │ Client4     │
│ Client2     │      │ Client5     │
│ Client3     │      │ Client6     │
└─────────────┘      └─────────────┘

Service(服务)

java 复制代码
{
  "service": {
    "id": "user-service-1",
    "name": "user-service",
    "tags": ["v1.0", "production"],
    "address": "192.168.1.100",
    "port": 8080,
    "meta": {
      "version": "1.0.0",
      "environment": "production"
    },
    "check": {
      "http": "http://192.168.1.100:8080/health",
      "interval": "10s",
      "timeout": "3s"
    }
  }
}

Node(节点)

java 复制代码
{
  "node": {
    "id": "40e4a748-2192-161a-0510-9bf59fe950b5",
    "name": "node1",
    "address": "192.168.1.100",
    "datacenter": "dc1",
    "meta": {
      "consul-network-segment": "",
      "zone": "us-east-1a"
    }
  }
}

架构设计

整体架构

java 复制代码
┌─────────────────────────────────────────────────────────────┐
│                    Consul架构图                              │
├─────────────────────────────────────────────────────────────┤
│           Client Applications                                │
│  ┌─────────┬─────────┬─────────┬─────────┬─────────┐        │
│  │HTTP API │DNS API  │gRPC API │ Web UI  │  CLI    │        │
│  └─────────┴─────────┴─────────┴─────────┴─────────┘        │
├─────────────────────────────────────────────────────────────┤
│                 Consul Agent                                 │
│  ┌─────────────────────────────────────────────────────────┐ │
│  │ Client Agent     │        Server Agent                  │ │
│  │ - Service Reg    │ - Raft Consensus                     │ │
│  │ - Health Check   │ - Data Storage                       │ │
│  │ - DNS/HTTP API   │ - Cross-DC                          │ │
│  └─────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│              Core Components                                 │
│  ┌─────────┬─────────┬─────────┬─────────┬─────────┐        │
│  │Catalog  │ Health  │   KV    │Connect  │  ACL    │        │
│  │         │ Checker │ Store   │ (Mesh)  │         │        │
│  └─────────┴─────────┴─────────┴─────────┴─────────┘        │
├─────────────────────────────────────────────────────────────┤
│                Storage Layer                                 │
│  ┌─────────────────────────────────────────────────────────┐ │
│  │            Raft Consensus Log                           │ │
│  └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

Raft共识算法

Consul使用Raft算法保证数据一致性:

Leader选举过程:

1.Follower → Candidate (选举超时)

2.Candidate → Leader (获得多数投票)

3.Leader → Follower (发现更高term)

日志复制过程:

1.Client → Leader (写请求)

2.Leader → Followers (日志条目)

3.Followers → Leader (确认)

4.Leader → Client (提交确认)

环境搭建

单节点部署

java 复制代码
# 下载Consul
wget https://releases.hashicorp.com/consul/1.16.1/consul_1.16.1_linux_amd64.zip
unzip consul_1.16.1_linux_amd64.zip
sudo mv consul /usr/local/bin/

# 创建目录
sudo mkdir -p /opt/consul/{data,config,logs}
sudo useradd --system --home /etc/consul.d --shell /bin/false consul
sudo chown -R consul:consul /opt/consul

# 配置文件
sudo tee /opt/consul/config/consul.json > /dev/null <<EOF
{
"datacenter": "dc1",
"data_dir": "/opt/consul/data",
"log_level": "INFO",
"server": true,
"bootstrap_expect": 1,
"bind_addr": "0.0.0.0",
"client_addr": "0.0.0.0",
"retry_join": ["127.0.0.1"],
"ui_config": {
    "enabled": true
  },
"connect": {
    "enabled": true
  },
"ports": {
    "grpc": 8502
  },
"acl": {
    "enabled": false,
    "default_policy": "allow"
  }
}
EOF

# 启动服务
consul agent -config-dir=/opt/consul/config

Docker部署

java 复制代码
# docker-compose.yml
version:'3.8'

services:
consul-server:
    image:consul:1.16.1
    container_name:consul-server
    restart:unless-stopped
    volumes:
      -./consul/data:/consul/data
      -./consul/config:/consul/config
    ports:
      -"8500:8500"
      -"8600:8600/udp"
      -"8300:8300"
      -"8301:8301"
      -"8302:8302"
    command:>
      consul agent
      -server
      -bootstrap-expect=1
      -datacenter=dc1
      -data-dir=/consul/data
      -bind=0.0.0.0
      -client=0.0.0.0
      -retry-join=consul-server
      -ui
    environment:
      CONSUL_LOCAL_CONFIG:|
        {
          "connect": { "enabled": true },
          "ports": { "grpc": 8502 },
          "acl": {
            "enabled": false,
            "default_policy": "allow"
          }
        }

consul-agent:
    image:consul:1.16.1
    container_name:consul-agent
    restart:unless-stopped
    volumes:
      -./consul/agent/data:/consul/data
    ports:
      -"8501:8500"
    command:>
      consul agent
      -datacenter=dc1
      -data-dir=/consul/data
      -bind=0.0.0.0
      -client=0.0.0.0
      -retry-join=consul-server
    depends_on:
      -consul-server

集群部署

java 复制代码
# Server节点配置
# server1.json
{
"datacenter": "dc1",
"data_dir": "/opt/consul/data",
"log_level": "INFO",
"server": true,
"bootstrap_expect": 3,
"bind_addr": "192.168.1.10",
"client_addr": "0.0.0.0",
"retry_join": ["192.168.1.10", "192.168.1.11", "192.168.1.12"],
"ui_config": {
    "enabled": true
  },
"connect": {
    "enabled": true
  },
"ports": {
    "grpc": 8502
  },
"acl": {
    "enabled": true,
    "default_policy": "deny",
    "enable_token_persistence": true
  },
"encrypt": "uDBV4e+LbFW3019YKPxIrg=="
}

# 启动集群
# Node 1
consul agent -config-file=/opt/consul/config/server1.json

# Node 2  
consul agent -config-file=/opt/consul/config/server2.json

# Node 3
consul agent -config-file=/opt/consul/config/server3.json

服务注册与发现

服务注册

java 复制代码
# 1. 通过HTTP API注册
curl -X PUT http://localhost:8500/v1/agent/service/register \
  -d '{
    "id": "user-service-1",
    "name": "user-service",
    "tags": ["v1.0", "production"],
    "address": "192.168.1.100",
    "port": 8080,
    "meta": {
      "version": "1.0.0",
      "environment": "production"
    },
    "check": {
      "http": "http://192.168.1.100:8080/health",
      "interval": "10s",
      "timeout": "3s"
    }
  }'

# 2. 通过配置文件注册
# /opt/consul/config/services/user-service.json
{
"service": {
    "id": "user-service-1",
    "name": "user-service", 
    "tags": ["v1.0", "production"],
    "address": "192.168.1.100",
    "port": 8080,
    "meta": {
      "version": "1.0.0",
      "environment": "production"
    },
    "checks": [
      {
        "id": "user-service-health",
        "name": "User Service Health Check",
        "http": "http://192.168.1.100:8080/health",
        "interval": "10s",
        "timeout": "3s"
      },
      {
        "id": "user-service-tcp",
        "name": "User Service TCP Check",
        "tcp": "192.168.1.100:8080",
        "interval": "30s",
        "timeout": "5s"
      }
    ]
  }
}

服务发现

java 复制代码
# 1. HTTP API查询
# 查询所有服务
curl http://localhost:8500/v1/catalog/services

# 查询特定服务
curl http://localhost:8500/v1/catalog/service/user-service

# 查询健康的服务实例
curl http://localhost:8500/v1/health/service/user-service?passing

# 2. DNS查询
# 查询服务
dig @localhost -p 8600 user-service.service.consul

# 查询特定标签的服务
dig @localhost -p 8600 production.user-service.service.consul

# 查询SRV记录
dig @localhost -p 8600 user-service.service.consul SRV

Spring Boot集成

java 复制代码
<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-config</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>
java 复制代码
# application.yml
spring:
application:
    name:user-service
cloud:
    consul:
      host:localhost
      port:8500
      discovery:
        enabled:true
        service-name:${spring.application.name}
        instance-id:${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
        hostname:${spring.cloud.client.ip-address}
        port:${server.port}
        prefer-ip-address:true
        health-check-path:/actuator/health
        health-check-interval:10s
        health-check-timeout:3s
        health-check-critical-timeout:30s
        heartbeat:
          enabled:true
        tags:
          -version=1.0.0
          -environment=dev
        metadata:
          version:1.0.0
          zone:zone-1
      config:
        enabled:true
        format:yaml
        prefix:config
        default-context:application
        profile-separator:','
        data-key:data
        watch:
          enabled:true
          delay:1000

server:
port:8080

management:
endpoints:
    web:
      exposure:
        include:"*"
endpoint:
    health:
      show-details:always
java 复制代码
@SpringBootApplication
@EnableDiscoveryClient
publicclass UserServiceApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}
java 复制代码
@RestController
@RequestMapping("/users")
@Slf4j
publicclass UserController {
    
    @Autowired
    private DiscoveryClient discoveryClient;
    
    @Value("${server.port}")
    privateint port;
    
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = User.builder()
            .id(id)
            .name("张三")
            .email("zhangsan@example.com")
            .port(port)
            .build();
        
        log.info("获取用户信息:{}", user);
        return ResponseEntity.ok(user);
    }
    
    @GetMapping("/services")
    public ResponseEntity<List<String>> getServices() {
        List<String> services = discoveryClient.getServices();
        return ResponseEntity.ok(services);
    }
    
    @GetMapping("/instances/{serviceName}")
    public ResponseEntity<List<ServiceInstance>> getInstances(@PathVariable String serviceName) {
        List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
        return ResponseEntity.ok(instances);
    }
}
java 复制代码
// 服务调用
@Service
@Slf4j
publicclass OrderService {
    
    @Autowired
    private DiscoveryClient discoveryClient;
    
    @Autowired
    private RestTemplate restTemplate;
    
    public User getUserById(Long userId) {
        List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
        
        if (instances.isEmpty()) {
            thrownew ServiceUnavailableException("用户服务不可用");
        }
        
        // 简单的负载均衡
        ServiceInstance instance = instances.get(
            ThreadLocalRandom.current().nextInt(instances.size())
        );
        
        String url = String.format("http://%s:%d/users/%d", 
            instance.getHost(), instance.getPort(), userId);
        
        log.info("调用用户服务:{}", url);
        return restTemplate.getForObject(url, User.class);
    }
}

键值存储

KV存储操作

java 复制代码
# 1. 存储数据
curl -X PUT http://localhost:8500/v1/kv/config/database/url \
  -d 'jdbc:mysql://localhost:3306/mydb'

curl -X PUT http://localhost:8500/v1/kv/config/database/username \
  -d 'dbuser'

curl -X PUT http://localhost:8500/v1/kv/config/redis/host \
  -d 'redis.example.com'

# 2. 获取数据
curl http://localhost:8500/v1/kv/config/database/url

# 获取目录下所有键值
curl http://localhost:8500/v1/kv/config/?recurse

# 3. 删除数据
curl -X DELETE http://localhost:8500/v1/kv/config/database/url

# 删除目录
curl -X DELETE http://localhost:8500/v1/kv/config/?recurse

# 4. 原子操作
# CAS (Compare-And-Swap)
curl -X PUT http://localhost:8500/v1/kv/config/counter?cas=0 -d '1'

Spring Cloud Config集成

java 复制代码
# bootstrap.yml
spring:
application:
    name:user-service
cloud:
    consul:
      config:
        enabled:true
        format:yaml
        prefix:config
        default-context:${spring.application.name}
        profile-separator:','
        data-key:data
        watch:
          enabled:true
          delay:1000

在Consul中存储配置:

java 复制代码
# 存储应用配置
curl -X PUT http://localhost:8500/v1/kv/config/user-service/data \
  -d 'server:
  port: 8080

app:
  name: User Service
  version: 1.0.0

database:
  url: jdbc:mysql://localhost:3306/userdb
  username: user
  password: password

redis:
  host: localhost
  port: 6379

logging:
  level:
    com.example: DEBUG'

# 存储环境特定配置
curl -X PUT http://localhost:8500/v1/kv/config/user-service,dev/data \
  -d 'database:
  url: jdbc:mysql://dev-db:3306/userdb
  username: dev_user
  password: dev_password
  
redis:
  host: dev-redis
  port: 6379'

配置监听和刷新

java 复制代码
@Component
@Slf4j
publicclass ConfigWatcher {
    
    @Autowired
    private ConsulTemplate consulTemplate;
    
    @Value("${app.name:Unknown}")
    private String appName;
    
    privatefinal AtomicLong lastIndex = new AtomicLong(0);
    
    @PostConstruct
    public void startWatching() {
        watchConfig("config/user-service");
    }
    
    @Async
    public void watchConfig(String keyPrefix) {
        while (true) {
            try {
                QueryParams queryParams = QueryParams.builder()
                    .setIndex(lastIndex.get())
                    .setWaitTime(30)
                    .build();
                
                Response<List<GetValue>> response = consulTemplate.getKVValues(keyPrefix, queryParams);
                
                if (response.getConsulIndex() > lastIndex.get()) {
                    lastIndex.set(response.getConsulIndex());
                    
                    log.info("检测到配置变更,重新加载配置");
                    handleConfigChange(response.getValue());
                }
                
            } catch (Exception e) {
                log.error("监听配置变更失败", e);
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
    }
    
    private void handleConfigChange(List<GetValue> values) {
        for (GetValue value : values) {
            String key = value.getKey();
            String decodedValue = value.getDecodedValue();
            
            log.info("配置项变更:key={}, value={}", key, decodedValue);
            
            // 触发配置刷新事件
            applicationEventPublisher.publishEvent(
                new ConfigChangeEvent(key, decodedValue)
            );
        }
    }
}
java 复制代码
@ConfigurationProperties(prefix = "app")
@RefreshScope
@Component
@Data
publicclass AppConfig {
    private String name;
    private String version;
    private Database database;
    private Redis redis;
    
    @Data
    publicstaticclass Database {
        private String url;
        private String username;
        private String password;
    }
    
    @Data
    publicstaticclass Redis {
        private String host;
        privateint port;
    }
}

健康检查

健康检查类型

java 复制代码
{
  "checks": [
    {
      "id": "http-check",
      "name": "HTTP Health Check",
      "http": "http://localhost:8080/health",
      "method": "GET",
      "header": {
        "Authorization": ["Bearer token"]
      },
      "interval": "10s",
      "timeout": "3s"
    },
    {
      "id": "tcp-check", 
      "name": "TCP Health Check",
      "tcp": "localhost:8080",
      "interval": "30s",
      "timeout": "5s"
    },
    {
      "id": "script-check",
      "name": "Script Health Check", 
      "args": ["/usr/local/bin/check_service.sh"],
      "interval": "60s",
      "timeout": "10s"
    },
    {
      "id": "grpc-check",
      "name": "gRPC Health Check",
      "grpc": "localhost:9090",
      "grpc_use_tls": false,
      "interval": "15s",
      "timeout": "5s"
    },
    {
      "id": "docker-check",
      "name": "Docker Health Check",
      "docker_container_id": "container_id",
      "shell": "/bin/bash",
      "args": ["/app/health_check.sh"],
      "interval": "30s",
      "timeout": "10s"
    }
  ]
}

自定义健康检查

java 复制代码
#!/bin/bash
# check_service.sh

# 检查进程是否运行
PID=$(pgrep -f user-service)
if [ -z "$PID" ]; then
    echo"Service not running"
    exit 1
fi

# 检查端口是否监听
if ! nc -z localhost 8080; then
    echo"Port 8080 not listening"
    exit 1
fi

# 检查数据库连接
if ! mysql -h localhost -u user -ppassword -e "SELECT 1" > /dev/null 2>&1; then
    echo"Database connection failed"
    exit 1
fi

# 检查磁盘空间
DISK_USAGE=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 90 ]; then
    echo"Disk usage too high: ${DISK_USAGE}%"
    exit 1
fi

echo"All checks passed"
exit 0

Spring Boot健康检查

java 复制代码
@Component
@Slf4j
publicclass ConsulHealthIndicator implements HealthIndicator {
    
    @Autowired
    private ConsulTemplate consulTemplate;
    
    @Override
    public Health health() {
        try {
            // 检查Consul连接
            Response<Map<String, Object>> response = consulTemplate.getStatus();
            
            if (response != null) {
                return Health.up()
                    .withDetail("consul", "Connected")
                    .withDetail("leader", response.getValue().get("leader"))
                    .withDetail("peers", response.getValue().get("peers"))
                    .build();
            } else {
                return Health.down()
                    .withDetail("consul", "Disconnected")
                    .build();
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("consul", "Error")
                .withDetail("error", e.getMessage())
                .build();
        }
    }
}
java 复制代码
@RestController
@RequestMapping("/health")
publicclass HealthController {
    
    @Autowired
    private Environment environment;
    
    @Autowired
    private DataSource dataSource;
    
    @GetMapping
    public ResponseEntity<Map<String, Object>> health() {
        Map<String, Object> health = new HashMap<>();
        
        try {
            // 检查应用状态
            health.put("status", "UP");
            health.put("timestamp", Instant.now());
            health.put("port", environment.getProperty("server.port"));
            
            // 检查数据库连接
            try (Connection conn = dataSource.getConnection()) {
                health.put("database", "UP");
                health.put("db_url", conn.getMetaData().getURL());
            }
            
            // 检查内存使用
            Runtime runtime = Runtime.getRuntime();
            long totalMemory = runtime.totalMemory();
            long freeMemory = runtime.freeMemory();
            long usedMemory = totalMemory - freeMemory;
            
            Map<String, Object> memory = new HashMap<>();
            memory.put("total", totalMemory);
            memory.put("used", usedMemory);
            memory.put("free", freeMemory);
            memory.put("usage_percent", (double) usedMemory / totalMemory * 100);
            health.put("memory", memory);
            
            return ResponseEntity.ok(health);
            
        } catch (Exception e) {
            health.put("status", "DOWN");
            health.put("error", e.getMessage());
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(health);
        }
    }
    
    @GetMapping("/ready")
    public ResponseEntity<String> readiness() {
        // 检查应用是否就绪
        try {
            // 检查必要的依赖服务
            checkDependencies();
            return ResponseEntity.ok("READY");
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("NOT_READY");
        }
    }
    
    @GetMapping("/live")
    public ResponseEntity<String> liveness() {
        // 检查应用是否存活
        return ResponseEntity.ok("ALIVE");
    }
    
    private void checkDependencies() throws Exception {
        // 检查数据库
        try (Connection conn = dataSource.getConnection()) {
            conn.isValid(3);
        }
        
        // 检查其他依赖服务
        // ...
    }
}

服务网格

Consul Connect

Consul Connect提供服务间的安全通信:

java 复制代码
# 启用Connect
consul agent -config-file=consul.json

# consul.json
{
  "connect": {
    "enabled": true
  },
  "ports": {
    "grpc": 8502
  }
}

服务网格配置

java 复制代码
{
  "service": {
    "name": "user-service",
    "port": 8080,
    "connect": {
      "sidecar_service": {
        "proxy": {
          "upstreams": [
            {
              "destination_name": "database",
              "local_bind_port": 5432
            },
            {
              "destination_name": "cache",
              "local_bind_port": 6379
            }
          ]
        }
      }
    }
  }
}

Envoy代理集成

java 复制代码
# 生成Envoy配置
consul connect envoy -sidecar-for user-service -admin-bind 0.0.0.0:19000

# 启动Envoy代理
consul connect envoy -sidecar-for user-service &

# 或者使用docker
docker run --rm -d \
  --name user-service-sidecar \
  --network consul_default \
  -p 19000:19000 \
  envoyproxy/envoy:v1.23-latest \
  /usr/local/bin/envoy --config-yaml "$(consul connect envoy -sidecar-for user-service)"

服务意图配置

java 复制代码
# 创建服务意图
curl -X PUT http://localhost:8500/v1/connect/intentions \
  -d '{
    "SourceName": "order-service",
    "DestinationName": "user-service", 
    "Action": "allow",
    "Description": "Allow order service to call user service"
  }'

# 或者使用配置文件
# intentions.json
{
"Kind": "service-intentions",
"Name": "user-service",
"Sources": [
    {
      "Name": "order-service",
      "Action": "allow"
    },
    {
      "Name": "payment-service",
      "Action": "allow"
    },
    {
      "Name": "*",
      "Action": "deny"
    }
  ]
}

# 应用配置
consul config write intentions.json

ACL权限控制

ACL系统配置

java 复制代码
{
  "acl": {
    "enabled": true,
    "default_policy": "deny",
    "enable_token_persistence": true,
    "tokens": {
      "master": "bootstrap-token-here",
      "agent": "agent-token-here"
    }
  }
}

Bootstrap ACL系统

java 复制代码
# 1. Bootstrap ACL系统
consul acl bootstrap

# 返回示例:
# AccessorID:       12345678-1234-1234-1234-123456789012
# SecretID:         abcdefgh-1234-1234-1234-123456789012
# Description:      Bootstrap Token (Global Management)
# Local:            false
# Create Time:      2023-01-01 12:00:00 +0000 UTC
# Policies:
#    00000000-0000-0000-0000-000000000001 - global-management

# 2. 设置环境变量
export CONSUL_HTTP_TOKEN=abcdefgh-1234-1234-1234-123456789012

创建策略和令牌

java 复制代码
# 1. 创建策略
consul acl policy create \
  -name "service-policy" \
  -description "Policy for service operations" \
  -rules @service-policy.hcl

# service-policy.hcl
service_prefix "" {
  policy = "write"
}

node_prefix "" {
  policy = "read"
}

key_prefix "config/" {
  policy = "write"
}

session_prefix "" {
  policy = "write"
}

# 2. 创建令牌
consul acl token create \
  -description "Service token" \
  -policy-name "service-policy"

# 3. 为特定服务创建策略
consul acl policy create \
  -name "user-service-policy" \
  -description "Policy for user service" \
  -rules @user-service-policy.hcl

# user-service-policy.hcl
service "user-service" {
  policy = "write"
}

service_prefix "" {
  policy = "read"
}

node_prefix "" {
  policy = "read"
}

key_prefix "config/user-service/" {
  policy = "write"
}

key_prefix "config/shared/" {
  policy = "read"
}

使用令牌

java 复制代码
# 1. 使用HTTP头
curl -H "X-Consul-Token: your-token-here" \
  http://localhost:8500/v1/kv/config/database/url

# 2. 使用查询参数
curl http://localhost:8500/v1/kv/config/database/url?token=your-token-here

# 3. 在客户端配置中使用
# Spring Boot配置
spring:
  cloud:
    consul:
      discovery:
        acl-token: your-service-token-here
      config:
        acl-token: your-config-token-here
java 复制代码
// Java客户端配置
@Configuration
public class ConsulConfig {
    
    @Value("${consul.token}")
    private String consulToken;
    
    @Bean
    public ConsulClient consulClient() {
        return new ConsulClient("localhost", 8500, consulToken);
    }
}

集群配置

多节点集群

java 复制代码
# 服务器节点配置
# server1.json
{
"datacenter": "dc1",
"data_dir": "/opt/consul/data",
"log_level": "INFO",
"node_name": "server1", 
"bind_addr": "192.168.1.10",
"client_addr": "0.0.0.0",
"retry_join": ["192.168.1.10", "192.168.1.11", "192.168.1.12"],
"server": true,
"bootstrap_expect": 3,
"ui_config": {
    "enabled": true
  },
"connect": {
    "enabled": true
  },
"ports": {
    "grpc": 8502
  },
"encrypt": "pUqJrVyVRj5jsiYEkM/tFQYfWyJIv4s3XkvDwy7Cu5s=",
"acl": {
    "enabled": true,
    "default_policy": "deny",
    "enable_token_persistence": true
  },
"performance": {
    "raft_multiplier": 1
  }
}

# 客户端节点配置  
# client1.json
{
"datacenter": "dc1",
"data_dir": "/opt/consul/data",
"log_level": "INFO",
"node_name": "client1",
"bind_addr": "192.168.1.100",
"client_addr": "127.0.0.1",
"retry_join": ["192.168.1.10", "192.168.1.11", "192.168.1.12"],
"server": false,
"encrypt": "pUqJrVyVRj5jsiYEkM/tFQYfWyJIv4s3XkvDwy7Cu5s=",
"acl": {
    "enabled": true,
    "default_policy": "deny",
    "tokens": {
      "agent": "agent-token-here"
    }
  }
}

多数据中心

java 复制代码
# DC1配置
# dc1-server.json
{
"datacenter": "dc1",
"primary_datacenter": "dc1",
"data_dir": "/opt/consul/data",
"node_name": "dc1-server1",
"bind_addr": "192.168.1.10",
"server": true,
"bootstrap_expect": 3,
"retry_join": ["192.168.1.10", "192.168.1.11", "192.168.1.12"],
"acl": {
    "enabled": true,
    "default_policy": "deny",
    "enable_token_persistence": true,
    "enable_token_replication": true
  },
"connect": {
    "enabled": true,
    "enable_mesh_gateway_wan_federation": true
  }
}

# DC2配置
# dc2-server.json
{
"datacenter": "dc2", 
"primary_datacenter": "dc1",
"data_dir": "/opt/consul/data",
"node_name": "dc2-server1",
"bind_addr": "192.168.2.10",
"server": true,
"bootstrap_expect": 3,
"retry_join": ["192.168.2.10", "192.168.2.11", "192.168.2.12"],
"retry_join_wan": ["192.168.1.10", "192.168.1.11", "192.168.1.12"],
"acl": {
    "enabled": true,
    "default_policy": "deny",
    "enable_token_persistence": true,
    "enable_token_replication": true
  },
"connect": {
    "enabled": true,
    "enable_mesh_gateway_wan_federation": true
  }
}

# 连接数据中心
consul join -wan 192.168.1.10

网格网关配置

java 复制代码
{
  "service": {
    "name": "mesh-gateway",
    "kind": "mesh-gateway",
    "port": 8443,
    "meta": {
      "version": "1.0"
    },
    "proxy": {
      "config": {
        "envoy_gateway_bind_addresses": {
          "all-interfaces": "0.0.0.0:8443"
        }
      }
    }
  }
}

API使用

Catalog API

java 复制代码
# 1. 节点相关
# 获取所有节点
curl http://localhost:8500/v1/catalog/nodes

# 获取特定节点信息
curl http://localhost:8500/v1/catalog/node/node1

# 2. 服务相关
# 获取所有服务
curl http://localhost:8500/v1/catalog/services

# 获取特定服务的所有实例
curl http://localhost:8500/v1/catalog/service/user-service

# 根据标签过滤
curl http://localhost:8500/v1/catalog/service/user-service?tag=production

# 3. 数据中心
# 获取所有数据中心
curl http://localhost:8500/v1/catalog/datacenters

Health API

java 复制代码
# 1. 节点健康检查
curl http://localhost:8500/v1/health/node/node1

# 2. 服务健康检查
# 获取所有实例(包括不健康的)
curl http://localhost:8500/v1/health/service/user-service

# 只获取健康的实例
curl http://localhost:8500/v1/health/service/user-service?passing

# 获取特定数据中心的服务
curl http://localhost:8500/v1/health/service/user-service?dc=dc1

# 3. 检查特定健康检查
curl http://localhost:8500/v1/health/checks/user-service

KV API

java 复制代码
# 1. 基本操作
# 存储键值
curl -X PUT http://localhost:8500/v1/kv/config/app/name -d "My Application"

# 获取键值
curl http://localhost:8500/v1/kv/config/app/name

# 获取原始值(不含元数据)
curl http://localhost:8500/v1/kv/config/app/name?raw

# 2. 目录操作
# 递归获取
curl http://localhost:8500/v1/kv/config/?recurse

# 只获取键名
curl http://localhost:8500/v1/kv/config/?keys

# 3. 原子操作
# CAS操作
curl -X PUT http://localhost:8500/v1/kv/config/counter?cas=123 -d "456"

# 获取并包含索引
curl http://localhost:8500/v1/kv/config/counter

# 4. 事务操作
curl -X PUT http://localhost:8500/v1/txn -d '[
  {
    "KV": {
      "Verb": "set",
      "Key": "config/app/name",
      "Value": "TXN Application"
    }
  },
  {
    "KV": {
      "Verb": "get",
      "Key": "config/app/version"
    }
  }
]'

Agent API

java 复制代码
# 1. 节点信息
curl http://localhost:8500/v1/agent/self

# 2. 成员信息
curl http://localhost:8500/v1/agent/members

# 3. 服务管理
# 注册服务
curl -X PUT http://localhost:8500/v1/agent/service/register -d '{
  "id": "user-service-1",
  "name": "user-service",
  "address": "192.168.1.100",
  "port": 8080
}'

# 注销服务
curl -X PUT http://localhost:8500/v1/agent/service/deregister/user-service-1

# 获取本地服务
curl http://localhost:8500/v1/agent/services

# 4. 健康检查管理
# 注册检查
curl -X PUT http://localhost:8500/v1/agent/check/register -d '{
  "id": "service-check",
  "name": "Service Health Check",
  "http": "http://localhost:8080/health",
  "interval": "10s"
}'

# 手动设置检查状态
curl -X PUT http://localhost:8500/v1/agent/check/pass/service-check
curl -X PUT http://localhost:8500/v1/agent/check/warn/service-check
curl -X PUT http://localhost:8500/v1/agent/check/fail/service-check

# 注销检查
curl -X PUT http://localhost:8500/v1/agent/check/deregister/service-check

客户端集成

Java客户端

java 复制代码
<dependency>
    <groupId>com.ecwid.consul</groupId>
    <artifactId>consul-api</artifactId>
    <version>1.4.5</version>
</dependency>
java 复制代码
@Service
@Slf4j
publicclass ConsulService {
    
    privatefinal ConsulClient consulClient;
    
    public ConsulService() {
        this.consulClient = new ConsulClient("localhost", 8500);
    }
    
    // 服务注册
    public void registerService(String serviceId, String serviceName, 
                               String address, int port) {
        NewService newService = new NewService();
        newService.setId(serviceId);
        newService.setName(serviceName);
        newService.setAddress(address);
        newService.setPort(port);
        
        // 添加健康检查
        NewService.Check check = new NewService.Check();
        check.setHttp("http://" + address + ":" + port + "/health");
        check.setInterval("10s");
        check.setTimeout("3s");
        newService.setCheck(check);
        
        // 添加标签
        newService.setTags(Arrays.asList("v1.0", "production"));
        
        consulClient.agentServiceRegister(newService);
        log.info("服务注册成功:{}:{}", serviceName, serviceId);
    }
    
    // 服务发现
    public List<ServiceHealth> discoverService(String serviceName) {
        HealthServicesRequest request = HealthServicesRequest.newBuilder()
            .setPassing(true)
            .build();
        
        Response<List<ServiceHealth>> response = consulClient.getHealthServices(serviceName, request);
        return response.getValue();
    }
    
    // 负载均衡选择
    public ServiceHealth selectService(String serviceName) {
        List<ServiceHealth> services = discoverService(serviceName);
        
        if (services.isEmpty()) {
            thrownew ServiceUnavailableException("No available instances for " + serviceName);
        }
        
        // 简单的随机负载均衡
        return services.get(ThreadLocalRandom.current().nextInt(services.size()));
    }
    
    // KV操作
    public void putKV(String key, String value) {
        consulClient.setKVValue(key, value);
        log.info("存储KV:{}={}", key, value);
    }
    
    public String getKV(String key) {
        Response<GetValue> response = consulClient.getKVValue(key);
        if (response.getValue() != null) {
            return response.getValue().getDecodedValue();
        }
        returnnull;
    }
    
    public Map<String, String> getKVRecursive(String keyPrefix) {
        Response<List<GetValue>> response = consulClient.getKVValues(keyPrefix);
        
        Map<String, String> result = new HashMap<>();
        if (response.getValue() != null) {
            for (GetValue value : response.getValue()) {
                result.put(value.getKey(), value.getDecodedValue());
            }
        }
        return result;
    }
    
    // 监听KV变化
    @Async
    public void watchKV(String key, Consumer<String> callback) {
        long lastIndex = 0;
        
        while (true) {
            try {
                QueryParams queryParams = QueryParams.Builder.builder()
                    .setIndex(lastIndex)
                    .setWaitTime(30)
                    .build();
                
                Response<GetValue> response = consulClient.getKVValue(key, queryParams);
                
                if (response.getConsulIndex() > lastIndex) {
                    lastIndex = response.getConsulIndex();
                    
                    if (response.getValue() != null) {
                        String value = response.getValue().getDecodedValue();
                        callback.accept(value);
                    }
                }
                
            } catch (Exception e) {
                log.error("监听KV变化失败:{}", key, e);
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
    }
}

Go客户端

java 复制代码
package main

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

func main() {
    // 创建客户端
    config := api.DefaultConfig()
    config.Address = "localhost:8500"
    client, err := api.NewClient(config)
    if err != nil {
        log.Fatal(err)
    }
    
    // 服务注册
    registration := &api.AgentServiceRegistration{
        ID:      "user-service-1",
        Name:    "user-service",
        Address: "localhost",
        Port:    8080,
        Tags:    []string{"v1.0", "production"},
        Check: &api.AgentServiceCheck{
            HTTP:     "http://localhost:8080/health",
            Interval: "10s",
            Timeout:  "3s",
        },
    }
    
    err = client.Agent().ServiceRegister(registration)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("服务注册成功")
    
    // 服务发现
    services, _, err := client.Health().Service("user-service", "", true, nil)
    if err != nil {
        log.Fatal(err)
    }
    
    for _, service := range services {
        fmt.Printf("服务实例:%s:%d\n", service.Service.Address, service.Service.Port)
    }
    
    // KV操作
    kv := client.KV()
    
    // 存储
    p := &api.KVPair{Key: "config/app/name", Value: []byte("My Go App")}
    _, err = kv.Put(p, nil)
    if err != nil {
        log.Fatal(err)
    }
    
    // 获取
    pair, _, err := kv.Get("config/app/name", nil)
    if err != nil {
        log.Fatal(err)
    }
    if pair != nil {
        fmt.Printf("配置值:%s\n", string(pair.Value))
    }
}

Python客户端

java 复制代码
import consul
import json
import time
from typing import List, Dict, Optional

class ConsulService:
    def __init__(self, host='localhost', port=8500, token=None):
        self.consul = consul.Consul(host=host, port=port, token=token)
    
    def register_service(self, service_id: str, service_name: str, 
                        address: str, port: int, tags: List[str] = None):
        """注册服务"""
        check = consul.Check.http(f"http://{address}:{port}/health", interval="10s")
        
        self.consul.agent.service.register(
            name=service_name,
            service_id=service_id,
            address=address,
            port=port,
            tags=tags or [],
            check=check
        )
        print(f"服务注册成功:{service_name}:{service_id}")
    
    def discover_services(self, service_name: str) -> List[Dict]:
        """服务发现"""
        index, services = self.consul.health.service(service_name, passing=True)
        return services
    
    def select_service(self, service_name: str) -> Optional[Dict]:
        """负载均衡选择服务"""
        services = self.discover_services(service_name)
        ifnot services:
            returnNone
        
        import random
        return random.choice(services)
    
    def put_kv(self, key: str, value: str):
        """存储KV"""
        self.consul.kv.put(key, value)
        print(f"存储KV:{key}={value}")
    
    def get_kv(self, key: str) -> Optional[str]:
        """获取KV"""
        index, data = self.consul.kv.get(key)
        if data:
            return data['Value'].decode('utf-8')
        returnNone
    
    def get_kv_recursive(self, key_prefix: str) -> Dict[str, str]:
        """递归获取KV"""
        index, data = self.consul.kv.get(key_prefix, recurse=True)
        result = {}
        if data:
            for item in data:
                key = item['Key']
                value = item['Value'].decode('utf-8') if item['Value'] else''
                result[key] = value
        return result
    
    def watch_kv(self, key: str, callback):
        """监听KV变化"""
        index = None
        whileTrue:
            try:
                index, data = self.consul.kv.get(key, index=index)
                if data:
                    value = data['Value'].decode('utf-8')
                    callback(value)
                time.sleep(1)
            except Exception as e:
                print(f"监听KV变化失败:{e}")
                time.sleep(5)

# 使用示例
if __name__ == "__main__":
    consul_service = ConsulService()
    
    # 注册服务
    consul_service.register_service(
        service_id="python-service-1",
        service_name="python-service",
        address="localhost",
        port=8080,
        tags=["python", "v1.0"]
    )
    
    # 服务发现
    services = consul_service.discover_services("user-service")
    for service in services:
        print(f"发现服务:{service['Service']['Address']}:{service['Service']['Port']}")
    
    # KV操作
    consul_service.put_kv("config/python/app_name", "Python Application")
    app_name = consul_service.get_kv("config/python/app_name")
    print(f"应用名称:{app_name}")

监控运维

Prometheus监控

java 复制代码
# prometheus.yml
global:
scrape_interval:15s

scrape_configs:
-job_name:'consul'
    static_configs:
      -targets:['localhost:8500']
    metrics_path:/v1/agent/metrics
    params:
      format:['prometheus']
    scrape_interval:30s

-job_name:'consul-services'
    consul_sd_configs:
      -server:'localhost:8500'
        services:[]
    relabel_configs:
      -source_labels:[__meta_consul_service]
        target_label:service
      -source_labels:[__meta_consul_node]
        target_label:node
      -source_labels:[__meta_consul_tags]
        target_label:tags

Grafana仪表板

java 复制代码
{
  "dashboard": {
    "title": "Consul Monitoring",
    "panels": [
      {
        "title": "Consul Cluster Health",
        "type": "stat",
        "targets": [
          {
            "expr": "consul_up",
            "legendFormat": "Consul Nodes Up"
          }
        ]
      },
      {
        "title": "Service Count",
        "type": "graph",
        "targets": [
          {
            "expr": "consul_catalog_services",
            "legendFormat": "Total Services"
          }
        ]
      },
      {
        "title": "Raft Transactions",
        "type": "graph", 
        "targets": [
          {
            "expr": "rate(consul_raft_apply[5m])",
            "legendFormat": "Raft Apply Rate"
          }
        ]
      },
      {
        "title": "KV Operations",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(consul_kvs_apply[5m])",
            "legendFormat": "KV Apply Rate"
          }
        ]
      }
    ]
  }
}

健康检查监控

java 复制代码
#!/bin/bash
# consul_health_monitor.sh

CONSUL_HOST="localhost:8500"
ALERT_WEBHOOK="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"

check_consul_health() {
    # 检查Consul leader
    LEADER=$(curl -s http://${CONSUL_HOST}/v1/status/leader)
    if [ "$LEADER" == '""' ]; then
        send_alert "Consul集群没有leader"
        return 1
    fi
    
    # 检查节点数量
    NODE_COUNT=$(curl -s http://${CONSUL_HOST}/v1/catalog/nodes | jq length)
    if [ "$NODE_COUNT" -lt 3 ]; then
        send_alert "Consul节点数量不足:$NODE_COUNT"
    fi
    
    # 检查关键服务
    CRITICAL_SERVICES=("user-service""order-service""payment-service")
    for service in"${CRITICAL_SERVICES[@]}"; do
        HEALTHY_COUNT=$(curl -s "http://${CONSUL_HOST}/v1/health/service/${service}?passing" | jq length)
        if [ "$HEALTHY_COUNT" -eq 0 ]; then
            send_alert "关键服务不可用:$service"
        fi
    done
}

send_alert() {
    local message="$1"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    
    curl -X POST -H 'Content-type: application/json' \
        --data "{\"text\":\"⚠️ Consul Alert [$timestamp]: $message\"}" \
        "$ALERT_WEBHOOK"
    
    echo"[$timestamp] ALERT: $message"
}

# 主循环
whiletrue; do
    check_consul_health
    sleep 60
done

日志监控

java 复制代码
# filebeat.yml
filebeat.inputs:
-type:log
enabled:true
paths:
    -/opt/consul/logs/*.log
fields:
    service:consul
    datacenter:dc1
multiline.pattern:'^\d{4}/\d{2}/\d{2}'
multiline.negate:true
multiline.match:after

output.logstash:
hosts:["logstash:5044"]

processors:
-add_host_metadata:
    when.not.contains.tags:forwarded
# logstash.conf
input {
  beats {
    port => 5044
  }
}

filter {
if [fields][service] == "consul" {
    grok {
      match => { 
        "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{LOGLEVEL:level}\] %{GREEDYDATA:message}"
      }
    }
    
    date {
      match => [ "timestamp", "ISO8601" ]
    }
    
    if [level] in ["ERROR", "WARN"] {
      mutate {
        add_tag => [ "alert" ]
      }
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "consul-logs-%{+YYYY.MM.dd}"
  }

if"alert"in [tags] {
    email {
      to => ["admin@example.com"]
      subject => "Consul Alert: %{level}"
      body => "Message: %{message}\nTimestamp: %{timestamp}"
    }
  }
}

故障排查

常见问题诊断

java 复制代码
# 1. 检查Consul状态
consul info
consul members
consul operator raft list-peers

# 2. 检查日志
tail -f /opt/consul/logs/consul.log
journalctl -u consul -f

# 3. 检查网络连接
netstat -tulpn | grep consul
ss -tulpn | grep :8500

# 4. 检查磁盘空间
df -h /opt/consul/data
du -sh /opt/consul/data/*

# 5. 检查性能指标
curl http://localhost:8500/v1/agent/metrics?format=prometheus

集群脑裂处理

java 复制代码
# 1. 识别脑裂
# 检查每个节点的leader
for server in server1 server2 server3; do
    echo"=== $server ==="
    ssh $server"consul info | grep leader"
done

# 2. 停止所有节点
for server in server1 server2 server3; do
    ssh $server"systemctl stop consul"
done

# 3. 清理状态(谨慎操作)
# 在主节点上保留数据,其他节点清理
ssh server2 "rm -rf /opt/consul/data/raft/*"
ssh server3 "rm -rf /opt/consul/data/raft/*"

# 4. 重新启动集群
# 先启动主节点
ssh server1 "systemctl start consul"

# 等待主节点稳定后启动其他节点
sleep 10
ssh server2 "systemctl start consul"
ssh server3 "systemctl start consul"

# 5. 验证集群状态
consul members
consul operator raft list-peers

数据恢复

java 复制代码
# 1. 创建快照
consul snapshot save backup.snap

# 2. 恢复快照
consul snapshot restore backup.snap

# 3. 自动化备份脚本
#!/bin/bash
# backup_consul.sh

BACKUP_DIR="/opt/consul/backups"
DATE=$(date +%Y%m%d_%H%M%S)
SNAPSHOT_FILE="${BACKUP_DIR}/consul_${DATE}.snap"

# 创建备份目录
mkdir -p $BACKUP_DIR

# 创建快照
consul snapshot save $SNAPSHOT_FILE

# 验证快照
consul snapshot inspect $SNAPSHOT_FILE

# 保留最近7天的备份
find $BACKUP_DIR -name "consul_*.snap" -mtime +7 -delete

echo"备份完成:$SNAPSHOT_FILE"

# 添加到crontab
# 0 2 * * * /opt/consul/scripts/backup_consul.sh

性能调优

java 复制代码
{
  "performance": {
    "raft_multiplier": 1,
    "rpc_hold_timeout": "7s"
  },
"limits": {
    "http_max_conns_per_client": 200,
    "https_handshake_timeout": "5s",
    "rpc_handshake_timeout": "5s",
    "rpc_max_burst": 1000,
    "rpc_max_conns_per_client": 100,
    "rpc_rate": 100,
    "kv_max_value_size": 1048576
  },
"ports": {
    "server": 8300,
    "serf_lan": 8301,
    "serf_wan": 8302,
    "http": 8500,
    "https": -1,
    "grpc": 8502,
    "dns": 8600
  }
}

实战案例

微服务电商平台

java 复制代码
# 服务架构设计
services:
# 用户服务
user-service:
    instances:3
    tags:[user,core]
    health_check:http://localhost:8080/health
    
# 订单服务  
order-service:
    instances:2
    tags:[order,business]
    health_check:http://localhost:8081/health
    
# 支付服务
payment-service:
    instances:2
    tags:[payment,critical]
    health_check:http://localhost:8082/health
    
# 通知服务
notification-service:
    instances:1
    tags:[notification,support]
    health_check:http://localhost:8083/health

# 配置管理
configurations:
database:
    user_db_url:mysql://user-db:3306/users
    order_db_url:mysql://order-db:3306/orders
    payment_db_url:mysql://payment-db:3306/payments
    
redis:
    cache_url:redis://redis-cluster:6379
    session_url:redis://redis-session:6379
    
messaging:
    rabbitmq_url:amqp://rabbitmq:5672
    kafka_brokers:kafka1:9092,kafka2:9092,kafka3:9092

服务注册配置

java 复制代码
// 统一服务注册配置
@Configuration
publicclass ConsulServiceRegistration {
    
    @Value("${spring.application.name}")
    private String serviceName;
    
    @Value("${server.port}")
    privateint port;
    
    @Autowired
    private ConsulClient consulClient;
    
    @PostConstruct
    public void registerService() {
        String serviceId = serviceName + "-" + getHostname() + "-" + port;
        
        NewService service = new NewService();
        service.setId(serviceId);
        service.setName(serviceName);
        service.setAddress(getLocalIpAddress());
        service.setPort(port);
        
        // 添加标签
        service.setTags(Arrays.asList(
            "version=" + getVersion(),
            "environment=" + getEnvironment(),
            "zone=" + getZone()
        ));
        
        // 添加元数据
        Map<String, String> meta = new HashMap<>();
        meta.put("version", getVersion());
        meta.put("git-commit", getGitCommit());
        meta.put("build-time", getBuildTime());
        service.setMeta(meta);
        
        // 健康检查
        NewService.Check check = new NewService.Check();
        check.setHttp("http://" + getLocalIpAddress() + ":" + port + "/actuator/health");
        check.setInterval("10s");
        check.setTimeout("3s");
        check.setDeregisterCriticalServiceAfter("30s");
        service.setCheck(check);
        
        // 注册服务
        consulClient.agentServiceRegister(service);
        log.info("服务注册成功:{}", serviceId);
        
        // 添加优雅关闭钩子
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            consulClient.agentServiceDeregister(serviceId);
            log.info("服务注销成功:{}", serviceId);
        }));
    }
    
    private String getHostname() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (Exception e) {
            return"unknown";
        }
    }
    
    private String getLocalIpAddress() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (Exception e) {
            return"127.0.0.1";
        }
    }
    
    private String getVersion() {
        return getClass().getPackage().getImplementationVersion() != null ? 
            getClass().getPackage().getImplementationVersion() : "unknown";
    }
    
    private String getEnvironment() {
        return System.getProperty("spring.profiles.active", "default");
    }
    
    private String getZone() {
        return System.getProperty("deployment.zone", "default");
    }
    
    private String getGitCommit() {
        try {
            InputStream is = getClass().getResourceAsStream("/META-INF/build-info.properties");
            Properties props = new Properties();
            props.load(is);
            return props.getProperty("build.revision", "unknown");
        } catch (Exception e) {
            return"unknown";
        }
    }
    
    private String getBuildTime() {
        try {
            InputStream is = getClass().getResourceAsStream("/META-INF/build-info.properties");
            Properties props = new Properties();
            props.load(is);
            return props.getProperty("build.time", "unknown");
        } catch (Exception e) {
            return"unknown";
        }
    }
}

配置中心实现

java 复制代码
@Component
@Slf4j
publicclass ConsulConfigManager {
    
    @Autowired
    private ConsulClient consulClient;
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    privatefinal Map<String, String> configCache = new ConcurrentHashMap<>();
    privatefinal Map<String, Long> watchIndexes = new ConcurrentHashMap<>();
    
    @PostConstruct
    public void init() {
        // 初始化配置监听
        startConfigWatcher("config/common/");
        startConfigWatcher("config/" + getServiceName() + "/");
    }
    
    @Async
    public void startConfigWatcher(String keyPrefix) {
        while (true) {
            try {
                Long lastIndex = watchIndexes.getOrDefault(keyPrefix, 0L);
                
                QueryParams queryParams = QueryParams.Builder.builder()
                    .setIndex(lastIndex)
                    .setWaitTime(30)
                    .build();
                
                Response<List<GetValue>> response = consulClient.getKVValues(keyPrefix, queryParams);
                
                if (response.getConsulIndex() > lastIndex) {
                    watchIndexes.put(keyPrefix, response.getConsulIndex());
                    
                    if (response.getValue() != null) {
                        updateConfigs(response.getValue());
                    }
                }
                
            } catch (Exception e) {
                log.error("配置监听失败:{}", keyPrefix, e);
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
    }
    
    private void updateConfigs(List<GetValue> values) {
        Map<String, String> changes = new HashMap<>();
        
        for (GetValue value : values) {
            String key = value.getKey();
            String newValue = value.getDecodedValue();
            String oldValue = configCache.put(key, newValue);
            
            if (!Objects.equals(oldValue, newValue)) {
                changes.put(key, newValue);
                log.info("配置更新:{} = {}", key, newValue);
            }
        }
        
        if (!changes.isEmpty()) {
            eventPublisher.publishEvent(new ConfigChangeEvent(this, changes));
        }
    }
    
    public String getConfig(String key) {
        String value = configCache.get(key);
        if (value == null) {
            // 从Consul获取
            Response<GetValue> response = consulClient.getKVValue(key);
            if (response.getValue() != null) {
                value = response.getValue().getDecodedValue();
                configCache.put(key, value);
            }
        }
        return value;
    }
    
    public String getConfig(String key, String defaultValue) {
        String value = getConfig(key);
        return value != null ? value : defaultValue;
    }
    
    public void putConfig(String key, String value) {
        consulClient.setKVValue(key, value);
        configCache.put(key, value);
        log.info("配置设置:{} = {}", key, value);
    }
    
    private String getServiceName() {
        return System.getProperty("spring.application.name", "unknown");
    }
}
java 复制代码
@Component
@Slf4j
publicclass ConfigChangeHandler {
    
    @Autowired
    private DataSource dataSource;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @EventListener
    public void handleConfigChange(ConfigChangeEvent event) {
        Map<String, String> changes = event.getChanges();
        
        for (Map.Entry<String, String> entry : changes.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            
            if (key.startsWith("config/database/")) {
                handleDatabaseConfigChange(key, value);
            } elseif (key.startsWith("config/redis/")) {
                handleRedisConfigChange(key, value);
            } elseif (key.startsWith("config/feature/")) {
                handleFeatureToggleChange(key, value);
            }
        }
    }
    
    private void handleDatabaseConfigChange(String key, String value) {
        log.info("处理数据库配置变更:{} = {}", key, value);
        
        if (dataSource instanceof HikariDataSource) {
            HikariDataSource hikariDs = (HikariDataSource) dataSource;
            
            if (key.endsWith("/max-pool-size")) {
                int maxPoolSize = Integer.parseInt(value);
                hikariDs.setMaximumPoolSize(maxPoolSize);
                log.info("更新数据库连接池大小:{}", maxPoolSize);
            } elseif (key.endsWith("/connection-timeout")) {
                long timeout = Long.parseLong(value);
                hikariDs.setConnectionTimeout(timeout);
                log.info("更新数据库连接超时:{}", timeout);
            }
        }
    }
    
    private void handleRedisConfigChange(String key, String value) {
        log.info("处理Redis配置变更:{} = {}", key, value);
        
        // 这里可以实现Redis连接池的动态更新
        // 由于Spring Redis的限制,通常需要重新创建连接工厂
    }
    
    private void handleFeatureToggleChange(String key, String value) {
        log.info("处理功能开关变更:{} = {}", key, value);
        
        // 更新功能开关缓存
        String featureName = key.substring(key.lastIndexOf("/") + 1);
        boolean enabled = Boolean.parseBoolean(value);
        
        FeatureToggleManager.updateFeature(featureName, enabled);
    }
}

最佳实践

1. 服务命名规范

java 复制代码
# 服务命名规范
naming_convention:
service_name:
    pattern:"^[a-z][a-z0-9-]*[a-z0-9]$"
    examples:
      -user-service
      -order-service
      -payment-gateway
      -notification-service
    
service_id:
    pattern:"{service-name}-{hostname}-{port}"
    examples:
      -user-service-node1-8080
      -order-service-node2-8081
      
tags:
    required:[version,environment]
    optional:[zone,cluster,team]
    examples:
      -version=1.0.0
      -environment=production
      -zone=us-east-1a
      -cluster=main
      -team=backend

2. 健康检查策略

java 复制代码
health_check_strategy:
  http_check:
    path:/actuator/health
    interval:10s
    timeout:3s
    deregister_critical_service_after:30s
    
tcp_check:
    interval:30s
    timeout:5s
    
script_check:
    interval:60s
    timeout:10s
    
grpc_check:
    interval:15s
    timeout:5s

3. 配置管理规范

java 复制代码
config_structure:
  global:
    prefix:"config/global/"
    keys:
      -logging/level
      -monitoring/enabled
      -security/jwt/secret
      
service_specific:
    prefix:"config/{service-name}/"
    keys:
      -database/url
      -database/pool-size
      -redis/host
      -features/enabled
      
environment_specific:
    prefix:"config/{service-name}/{environment}/"
    keys:
      -database/password
      -external-api/endpoints
      
sensitive_data:
    encryption:required
    access_control:strict
    audit:enabled

4. 安全加固

java 复制代码
security_hardening:
  acl:
    enabled:true
    default_policy:deny
    token_ttl:24h
    
encryption:
    gossip:enabled
    rpc:enabled
    
network:
    bind_addr:private_ip
    client_addr:"127.0.0.1"
    
authentication:
    method:jwt
    provider:external_oauth
    
authorization:
    service_policies:granular
    kv_policies:path_based
    
audit:
    enabled:true
    log_level:info
    retention:90d

5. 监控告警

java 复制代码
monitoring:
  metrics:
    -consul_up
    -consul_raft_leader
    -consul_catalog_services
    -consul_catalog_nodes
    -consul_health_service_status
    -consul_kvs_apply_rate
    
alerts:
    -name:ConsulDown
      condition:consul_up==0
      severity:critical
      
    -name:NoLeader
      condition:consul_raft_leader==0
      severity:critical
      
    -name:ServiceDown
      condition:consul_health_service_status{status="critical"}>0
      severity:warning
      
    -name:HighKVLoad
      condition:rate(consul_kvs_apply[5m])>100
      severity:warning

logging:
level:INFO
format:json
destinations:
    -file:/var/log/consul/consul.log
    -syslog:enabled
    -stdout:enabled

6. 高可用部署

java 复制代码
high_availability:
  cluster_size:3# 奇数个节点

server_distribution:
    availability_zones:3
    nodes_per_zone:1
    
client_distribution:
    co_located:true# 与应用服务同节点
    
backup_strategy:
    frequency:daily
    retention:30d
    verification:automatic
    
disaster_recovery:
    rpo:1h# Recovery Point Objective
    rto:15m# Recovery Time Objective
    
network_partitioning:
    handling:quorum_based
    split_brain_prevention:enabled

总结

Consul作为HashiCorp开源的服务网格解决方案,为现代微服务架构提供了完整的服务发现、配置管理和安全通信能力。通过本文的学习,您应该掌握:

核心概念:Agent、Service、Node、Datacenter等基本概念

服务治理:服务注册发现、健康检查、负载均衡

配置管理:基于KV存储的动态配置管理

安全控制:ACL权限控制、服务间安全通信

集群管理:多节点集群、多数据中心部署

运维监控:健康监控、性能指标、故障排查

进阶学习建议

深入架构:学习Raft共识算法和分布式系统原理

服务网格:深入了解Consul Connect和Envoy集成

企业特性:学习Consul Enterprise的高级功能

云原生集成:与Kubernetes、Istio等平台集成

性能调优:大规模集群的性能优化和调优

Consul是构建可靠微服务架构的重要基础设施,掌握它对于现代应用开发和运维至关重要。

相关推荐
LastWhisperw2 小时前
简历填写Agent开发笔记
笔记
会编程的李较瘦3 小时前
【Spark学习】数据清洗
学习·ajax·spark
RisunJan3 小时前
【行测】实词辨析
学习
m0_626535204 小时前
双线性插值学习
学习
YJlio4 小时前
进程和诊断工具学习笔记(8.24):Handle——谁占着不放?句柄泄漏排查、强制解锁与检索技巧
服务器·笔记·学习
charlie1145141914 小时前
面向C++程序员的JavaScript 语法实战学习4
开发语言·前端·javascript·学习·函数
IUGEI5 小时前
【后端开发笔记】JVM底层原理-内存结构篇
java·jvm·笔记·后端
wdfk_prog5 小时前
[Linux]学习笔记系列 -- [kernel]trace
linux·笔记·学习
charlie1145141915 小时前
勇闯前后端Week2:后端基础——HTTP与REST
开发语言·网络·笔记·网络协议·学习·http