Java Consul案例如何对接

wen java案例 32

本文目录导读:

Java Consul案例如何对接

  1. 环境准备
  2. 原生Java实现
  3. Spring Cloud集成
  4. 高级特性
  5. 最佳实践建议

我来为您详细介绍Java对接Consul的实现案例,包括服务注册、发现和配置管理。

环境准备

1 启动Consul

# 使用Docker启动Consul
docker run -d -p 8500:8500 -p 8600:8600/udp --name=consul consul agent -server -bootstrap -ui -client=0.0.0.0

2 Maven依赖

<dependencies>
    <!-- Consul客户端 -->
    <dependency>
        <groupId>com.ecwid.consul</groupId>
        <artifactId>consul-api</artifactId>
        <version>1.4.5</version>
    </dependency>
    <!-- Spring Cloud Consul(如果使用Spring Cloud) -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        <version>3.1.0</version>
    </dependency>
</dependencies>

原生Java实现

1 Consul客户端配置

import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.agent.model.NewService;
import com.ecwid.consul.v1.health.model.HealthService;
import java.util.List;
public class ConsulExample {
    private static final String CONSUL_HOST = "localhost";
    private static final int CONSUL_PORT = 8500;
    private static ConsulClient consulClient;
    static {
        // 创建Consul客户端连接
        consulClient = new ConsulClient(CONSUL_HOST, CONSUL_PORT);
    }
    // 获取客户端实例
    public static ConsulClient getConsulClient() {
        return consulClient;
    }
}

2 服务注册

public class ServiceRegistration {
    /**
     * 注册服务到Consul
     */
    public static void registerService(String serviceName, 
                                      String serviceId, 
                                      String host, 
                                      int port) {
        NewService newService = new NewService();
        newService.setId(serviceId);
        newService.setName(serviceName);
        newService.setAddress(host);
        newService.setPort(port);
        // 添加健康检查
        NewService.Check check = new NewService.Check();
        check.setHttp("http://" + host + ":" + port + "/health");
        check.setInterval("10s");
        check.setTimeout("3s");
        newService.setCheck(check);
        // 添加标签
        newService.setTags(Arrays.asList("v1.0", "production"));
        // 执行注册
        ConsulExample.getConsulClient().agentServiceRegister(newService);
        System.out.println("服务注册成功: " + serviceName);
    }
    /**
     * 取消服务注册
     */
    public static void deregisterService(String serviceId) {
        ConsulExample.getConsulClient().agentServiceDeregister(serviceId);
        System.out.println("服务注销成功: " + serviceId);
    }
}

3 服务发现

public class ServiceDiscovery {
    /**
     * 发现健康服务实例
     */
    public static List<HealthService> discoverService(String serviceName) {
        // 获取健康状态的服务实例
        List<HealthService> services = ConsulExample.getConsulClient()
            .getHealthServices(serviceName, true, null)
            .getValue();
        for (HealthService service : services) {
            System.out.println("发现服务: " + service.getService().getAddress() 
                             + ":" + service.getService().getPort());
        }
        return services;
    }
    /**
     * 获取服务地址
     */
    public static String getServiceUrl(String serviceName) {
        List<HealthService> services = discoverService(serviceName);
        if (!services.isEmpty()) {
            HealthService service = services.get(0);
            return "http://" + service.getService().getAddress() 
                   + ":" + service.getService().getPort();
        }
        return null;
    }
    /**
     * 负载均衡获取服务(简单轮询)
     */
    public static HealthService getServiceWithLoadBalance(String serviceName) {
        List<HealthService> services = discoverService(serviceName);
        if (services.isEmpty()) {
            return null;
        }
        // 简单轮询
        int index = (int) (System.currentTimeMillis() % services.size());
        return services.get(index);
    }
}

4 KV存储操作

public class ConsulKVOperation {
    /**
     * 存储配置
     */
    public static void putConfig(String key, String value) {
        ConsulExample.getConsulClient().setKVValue(key, value);
        System.out.println("配置存储成功: " + key + " = " + value);
    }
    /**
     * 获取配置
     */
    public static String getConfig(String key) {
        String value = ConsulExample.getConsulClient()
            .getKVValue(key)
            .getValue()
            .getDecodedValue();
        System.out.println("获取配置: " + key + " = " + value);
        return value;
    }
    /**
     * 删除配置
     */
    public static void deleteConfig(String key) {
        ConsulExample.getConsulClient().deleteKVValue(key);
        System.out.println("配置删除成功: " + key);
    }
}

5 健康检查端点

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/health")
public class HealthCheckResource {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response healthCheck() {
        // 检查服务状态
        boolean isHealthy = checkServiceHealth();
        if (isHealthy) {
            return Response.ok()
                .entity("{\"status\": \"UP\"}")
                .build();
        } else {
            return Response.status(Response.Status.SERVICE_UNAVAILABLE)
                .entity("{\"status\": \"DOWN\"}")
                .build();
        }
    }
    private boolean checkServiceHealth() {
        // 实现具体健康检查逻辑
        return true;
    }
}

6 完整使用示例

public class ConsulApplication {
    public static void main(String[] args) {
        try {
            // 1. 注册服务
            String serviceName = "my-service";
            String serviceId = "my-service-1";
            String host = "192.168.1.100";
            int port = 8080;
            ServiceRegistration.registerService(serviceName, serviceId, host, port);
            // 2. 存储配置
            ConsulKVOperation.putConfig("app/database/url", 
                                       "jdbc:mysql://localhost:3306/mydb");
            ConsulKVOperation.putConfig("app/database/username", "root");
            ConsulKVOperation.putConfig("app/database/password", "123456");
            // 3. 启动服务(模拟)
            startService();
            // 4. 发现其他服务
            ServiceDiscovery.discoverService("other-service");
            // 5. 获取配置
            String dbUrl = ConsulKVOperation.getConfig("app/database/url");
            System.out.println("数据库URL: " + dbUrl);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 程序关闭时注销服务
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                ServiceRegistration.deregisterService("my-service-1");
            }));
        }
    }
    private static void startService() {
        // 启动HTTP服务器,提供健康检查端点
        // 这里使用简单的循环模拟
        while (true) {
            try {
                Thread.sleep(10000);
                System.out.println("服务运行中...");
            } catch (InterruptedException e) {
                break;
            }
        }
    }
}

Spring Cloud集成

1 配置application.yml

spring:
  application:
    name: my-service
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        enabled: true
        register: true
        instance-id: ${spring.application.name}:${spring.cloud.client.hostname}
        health-check-interval: 10s
        health-check-url: http://${spring.cloud.client.hostname}:${server.port}/actuator/health
        prefer-ip-address: true
        ip-address: ${spring.cloud.client.ip-address}
      config:
        enabled: true
        format: yaml
        prefix: config
        default-context: application
        watch:
          enabled: true
          delay: 10000

2 Spring Boot启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulSpringApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsulSpringApplication.class, args);
    }
}

3 使用RestTemplate调用服务

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class ServiceInvoker {
    @Autowired
    private LoadBalancerClient loadBalancerClient;
    @Autowired
    private RestTemplate restTemplate;
    public String callOtherService() {
        // 使用负载均衡发现服务
        ServiceInstance instance = loadBalancerClient.choose("other-service");
        if (instance != null) {
            String url = instance.getUri() + "/api/endpoint";
            return restTemplate.getForObject(url, String.class);
        }
        return null;
    }
}

4 配置监听

import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class ConfigChangeListener {
    @EventListener
    public void onApplicationEvent(RefreshRemoteApplicationEvent event) {
        System.out.println("配置发生变化,刷新应用...");
        // 处理配置变更
    }
}

高级特性

1 服务健康检查

import com.ecwid.consul.v1.agent.model.Check;
public class CustomHealthCheck {
    public static void registerCustomCheck() {
        Check check = new Check();
        check.setId("custom-check");
        check.setName("Custom Health Check");
        check.setInterval("15s");
        check.setTtl("30s");
        check.setNotes("Custom health check for critical operations");
        ConsulExample.getConsulClient().agentCheckRegister(check);
    }
}

2 Watch监听

import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.COQueryParams;
import com.ecwid.consul.v1.QueryParams;
public class ConfigWatcher {
    private long lastIndex = 0;
    public void watchConfig(String key) {
        new Thread(() -> {
            while (true) {
                try {
                    // 使用index参数实现长轮询
                    QueryParams queryParams = new QueryParams(30, lastIndex);
                    var response = ConsulExample.getConsulClient()
                        .getKVValue(key, queryParams);
                    if (response.getValue() != null) {
                        lastIndex = response.getConsulIndex();
                        String newValue = response.getValue().getDecodedValue();
                        System.out.println("配置变更: " + key + " = " + newValue);
                        // 触发配置更新
                        updateConfig(key, newValue);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException ex) {
                        break;
                    }
                }
            }
        }).start();
    }
    private void updateConfig(String key, String value) {
        // 更新应用配置
        System.out.println("更新配置: " + key);
    }
}

最佳实践建议

  1. 服务命名规范:使用有意义的命名空间,如 service/group/name
  2. 健康检查:实现详细的健康检查,包括依赖服务状态
  3. 超时配置:设置合理的超时时间,避免服务发现阻塞
  4. 缓存策略:对服务列表进行缓存,减少Consul查询压力
  5. 错误处理:当Consul不可用时,使用本地缓存保证服务可用性
  6. 监控告警:监控Consul集群状态,及时发现异常

这个完整示例涵盖了Java对接Consul的各个方面,可以根据实际需求选择合适的方式集成。

抱歉,评论功能暂时关闭!