本文目录导读:

我来为您详细介绍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);
}
}
最佳实践建议
- 服务命名规范:使用有意义的命名空间,如
service/group/name - 健康检查:实现详细的健康检查,包括依赖服务状态
- 超时配置:设置合理的超时时间,避免服务发现阻塞
- 缓存策略:对服务列表进行缓存,减少Consul查询压力
- 错误处理:当Consul不可用时,使用本地缓存保证服务可用性
- 监控告警:监控Consul集群状态,及时发现异常
这个完整示例涵盖了Java对接Consul的各个方面,可以根据实际需求选择合适的方式集成。