Java服务发现案例如何开发

wen java案例 28

本文目录导读:

Java服务发现案例如何开发

  1. 项目结构
  2. 基础依赖配置
  3. 自定义注册中心实现
  4. 服务提供者实现
  5. 服务消费者实现
  6. 使用Nacos实现(生产推荐)
  7. 配置文件
  8. 测试和运行
  9. 高级特性
  10. 最佳实践

我来详细介绍如何开发Java服务发现案例,包含服务注册、发现、健康检查等核心功能。

项目结构

service-discovery-demo/
├── service-registry-center    # 注册中心
├── service-provider          # 服务提供者
├── service-consumer          # 服务消费者
└── common                    # 公共模块

基础依赖配置

1 Maven父POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>service-discovery-demo</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <spring.boot.version>2.7.14</spring.boot.version>
        <spring.cloud.version>2021.0.8</spring.cloud.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

自定义注册中心实现

1 注册中心服务端

// ServiceRegistryCenterApplication.java
package com.example.registry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@SpringBootApplication
@RestController
public class ServiceRegistryCenterApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceRegistryCenterApplication.class, args);
    }
}
// RegistryServerController.java
@RestController
@RequestMapping("/registry")
public class RegistryServerController {
    // 服务注册表:serviceName -> List<ServiceInstance>
    private final Map<String, Map<String, ServiceInstance>> registry = new ConcurrentHashMap<>();
    // 服务注册
    @PostMapping("/register")
    public Result register(@RequestBody ServiceInstance instance) {
        Map<String, ServiceInstance> instances = registry
            .computeIfAbsent(instance.getServiceName(), k -> new ConcurrentHashMap<>());
        String instanceId = instance.getHost() + ":" + instance.getPort();
        instance.setInstanceId(instanceId);
        instance.setRegisterTime(System.currentTimeMillis());
        instance.setLastHeartbeat(System.currentTimeMillis());
        instances.put(instanceId, instance);
        return Result.success("注册成功", instance);
    }
    // 服务发现
    @GetMapping("/discover/{serviceName}")
    public Result discover(@PathVariable String serviceName) {
        Map<String, ServiceInstance> instances = registry.get(serviceName);
        if (instances == null || instances.isEmpty()) {
            return Result.error("服务不存在");
        }
        // 过滤掉过期的实例(30秒无心跳)
        long now = System.currentTimeMillis();
        List<ServiceInstance> activeInstances = instances.values().stream()
            .filter(instance -> now - instance.getLastHeartbeat() < 30000)
            .collect(Collectors.toList());
        if (activeInstances.isEmpty()) {
            return Result.error("没有可用的服务实例");
        }
        // 简单的负载均衡:轮询
        ServiceInstance selected = activeInstances.get(
            (int)(System.currentTimeMillis() % activeInstances.size())
        );
        return Result.success("发现成功", selected);
    }
    // 心跳检测
    @PutMapping("/heartbeat/{instanceId}")
    public Result heartbeat(@PathVariable String instanceId) {
        for (Map<String, ServiceInstance> instances : registry.values()) {
            ServiceInstance instance = instances.get(instanceId);
            if (instance != null) {
                instance.setLastHeartbeat(System.currentTimeMillis());
                return Result.success("心跳更新成功");
            }
        }
        return Result.error("实例不存在");
    }
    // 服务下线
    @DeleteMapping("/unregister/{instanceId}")
    public Result unregister(@PathVariable String instanceId) {
        for (Map<String, ServiceInstance> instances : registry.values()) {
            if (instances.remove(instanceId) != null) {
                return Result.success("下线成功");
            }
        }
        return Result.error("实例不存在");
    }
    // 获取所有服务
    @GetMapping("/services")
    public Result getAllServices() {
        Map<String, List<ServiceInstance>> result = new HashMap<>();
        registry.forEach((serviceName, instances) -> {
            List<ServiceInstance> activeList = instances.values().stream()
                .filter(i -> System.currentTimeMillis() - i.getLastHeartbeat() < 30000)
                .collect(Collectors.toList());
            if (!activeList.isEmpty()) {
                result.put(serviceName, activeList);
            }
        });
        return Result.success("查询成功", result);
    }
}

2 服务实例和结果对象

// ServiceInstance.java
package com.example.registry.model;
public class ServiceInstance {
    private String instanceId;
    private String serviceName;
    private String host;
    private int port;
    private String ip;
    private Map<String, String> metadata;
    private long registerTime;
    private long lastHeartbeat;
    // getters and setters...
}
// Result.java
package com.example.registry.model;
public class Result<T> {
    private int code;
    private String message;
    private T data;
    public static <T> Result<T> success(String message, T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMessage(message);
        result.setData(data);
        return result;
    }
    public static <T> Result<T> error(String message) {
        Result<T> result = new Result<>();
        result.setCode(500);
        result.setMessage(message);
        return result;
    }
    // getters and setters...
}

服务提供者实现

1 服务提供者客户端

// ServiceProviderApplication.java
package com.example.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class ServiceProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceProviderApplication.class, args);
    }
}
// RegistryClient.java
@Component
public class RegistryClient {
    @Value("${registry.server.url:http://localhost:8080}")
    private String registryUrl;
    @Value("${server.port}")
    private int serverPort;
    @Value("${spring.application.name}")
    private String serviceName;
    private String instanceId;
    private final RestTemplate restTemplate;
    public RegistryClient() {
        this.restTemplate = new RestTemplate();
    }
    @PostConstruct
    public void register() {
        ServiceInstance instance = new ServiceInstance();
        instance.setServiceName(serviceName);
        instance.setHost(getLocalIp());
        instance.setPort(serverPort);
        try {
            Result result = restTemplate.postForObject(
                registryUrl + "/registry/register", 
                instance, 
                Result.class
            );
            if (result.getCode() == 200) {
                ServiceInstance registered = (ServiceInstance) result.getData();
                this.instanceId = registered.getInstanceId();
                log.info("服务注册成功,instanceId: {}", instanceId);
            }
        } catch (Exception e) {
            log.error("服务注册失败", e);
        }
    }
    @PreDestroy
    public void unregister() {
        if (instanceId != null) {
            try {
                restTemplate.delete(registryUrl + "/registry/unregister/" + instanceId);
                log.info("服务下线成功");
            } catch (Exception e) {
                log.error("服务下线失败", e);
            }
        }
    }
    // 定时心跳
    @Scheduled(fixedRate = 10000)
    public void sendHeartbeat() {
        if (instanceId != null) {
            try {
                restTemplate.put(registryUrl + "/registry/heartbeat/" + instanceId, null);
            } catch (Exception e) {
                log.warn("心跳发送失败", e);
            }
        }
    }
    private String getLocalIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (Exception e) {
            return "127.0.0.1";
        }
    }
}
// UserServiceController.java
@RestController
@RequestMapping("/api/user")
public class UserServiceController {
    @GetMapping("/info")
    public Result getUserInfo(@RequestParam String userId) {
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("userId", userId);
        userInfo.put("name", "张三");
        userInfo.put("email", "zhangsan@example.com");
        userInfo.put("servicePort", serverPort); // 添加端口信息便于验证
        return Result.success("查询成功", userInfo);
    }
}

服务消费者实现

1 服务消费者客户端

// ServiceConsumerApplication.java
package com.example.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
public class ServiceConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceConsumerApplication.class, args);
    }
}
// ServiceDiscoveryClient.java
@Component
public class ServiceDiscoveryClient {
    @Value("${registry.server.url:http://localhost:8080}")
    private String registryUrl;
    private final RestTemplate restTemplate;
    public ServiceDiscoveryClient() {
        this.restTemplate = new RestTemplate();
    }
    public ServiceInstance discoverService(String serviceName) {
        try {
            ResponseEntity<Result> response = restTemplate.getForEntity(
                registryUrl + "/registry/discover/" + serviceName,
                Result.class
            );
            Result result = response.getBody();
            if (result != null && result.getCode() == 200) {
                Map<String, Object> data = (Map<String, Object>) result.getData();
                ServiceInstance instance = new ServiceInstance();
                instance.setHost((String) data.get("host"));
                instance.setPort((Integer) data.get("port"));
                return instance;
            }
        } catch (Exception e) {
            log.error("服务发现失败", e);
        }
        return null;
    }
    public String getServiceUrl(String serviceName, String path) {
        ServiceInstance instance = discoverService(serviceName);
        if (instance != null) {
            return String.format("http://%s:%d%s", 
                instance.getHost(), instance.getPort(), path);
        }
        return null;
    }
}
// UserConsumerController.java
@RestController
@RequestMapping("/api/consumer")
public class UserConsumerController {
    @Autowired
    private ServiceDiscoveryClient discoveryClient;
    @GetMapping("/user/{userId}")
    public Result getUserInfo(@PathVariable String userId) {
        // 服务发现并调用
        ServiceInstance instance = discoveryClient.discoverService("user-service");
        if (instance == null) {
            return Result.error("服务不可用");
        }
        String url = String.format("http://%s:%d/api/user/info?userId=%s",
            instance.getHost(), instance.getPort(), userId);
        try {
            RestTemplate restTemplate = new RestTemplate();
            Result result = restTemplate.getForObject(url, Result.class);
            return result;
        } catch (Exception e) {
            return Result.error("服务调用失败: " + e.getMessage());
        }
    }
    // 显示所有可用服务
    @GetMapping("/services")
    public Result getAllServices() {
        String registryInfo = discoveryClient.getServiceUrl(
            "registry-center", "/registry/services");
        // 直接调用注册中心的API
        RestTemplate restTemplate = new RestTemplate();
        try {
            return restTemplate.getForObject(
                "http://localhost:8080/registry/services", Result.class);
        } catch (Exception e) {
            return Result.error("获取服务列表失败");
        }
    }
}

使用Nacos实现(生产推荐)

1 Nacos服务提供者

<!-- Nacos依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
# application.yml
spring:
  application:
    name: nacos-provider
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: public
        group: DEFAULT_GROUP
server:
  port: 8081
@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(NacosProviderApplication.class, args);
    }
}
@RestController
public class ProviderController {
    @Value("${server.port}")
    private int port;
    @GetMapping("/hello")
    public String hello(@RequestParam String name) {
        return String.format("Hello %s, from port %d", name, port);
    }
}

2 Nacos服务消费者

# application.yml
spring:
  application:
    name: nacos-consumer
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
server:
  port: 8082
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApplication {
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(NacosConsumerApplication.class, args);
    }
}
@RestController
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private DiscoveryClient discoveryClient;
    @GetMapping("/consumer")
    public String consume() {
        // 使用负载均衡调用服务
        String result = restTemplate.getForObject(
            "http://nacos-provider/hello?name=World", 
            String.class
        );
        return "Consumer received: " + result;
    }
    @GetMapping("/services")
    public List<String> getServices() {
        // 获取所有服务
        return discoveryClient.getServices();
    }
    @GetMapping("/instances/{serviceName}")
    public List<ServiceInstance> getInstances(
            @PathVariable String serviceName) {
        // 获取服务的所有实例
        return discoveryClient.getInstances(serviceName);
    }
}

配置文件

1 自定义注册中心配置

# application-registry.properties
server.port=8080
spring.application.name=registry-center

2 服务提供者配置

# application-provider.properties
server.port=8081
spring.application.name=user-service
registry.server.url=http://localhost:8080

3 服务消费者配置

# application-consumer.properties
server.port=8082
spring.application.name=consumer-service
registry.server.url=http://localhost:8080

测试和运行

1 启动顺序

# 1. 启动注册中心
mvn spring-boot:run -pl service-registry-center
# 2. 启动服务提供者(可以启动多个实例)
mvn spring-boot:run -pl service-provider -Dserver.port=8081
mvn spring-boot:run -pl service-provider -Dserver.port=8083
# 3. 启动服务消费者
mvn spring-boot:run -pl service-consumer -Dserver.port=8082

2 测试API

# 1. 查看注册的服务
curl http://localhost:8080/registry/services
# 2. 服务发现
curl http://localhost:8080/registry/discover/user-service
# 3. 消费者调用
curl http://localhost:8082/api/consumer/user/123
# 4. 查看消费者发现的服务
curl http://localhost:8082/api/consumer/services

高级特性

1 健康检查增强

@Component
public class HealthCheckEndpoint extends AbstractHealthIndicator {
    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        // 检查数据库连接
        // 检查外部依赖
        // 检查资源使用情况
        if (isHealthy()) {
            builder.up().withDetail("status", "Service is healthy");
        } else {
            builder.down().withDetail("error", "Service is unhealthy");
        }
    }
    private boolean isHealthy() {
        // 实现健康检查逻辑
        return true;
    }
}

2 负载均衡策略

@Component
public class CustomLoadBalancer implements LoadBalancer {
    private final AtomicInteger position = new AtomicInteger(0);
    @Override
    public ServiceInstance choose(List<ServiceInstance> instances) {
        if (instances.isEmpty()) {
            return null;
        }
        // 加权随机
        int totalWeight = instances.stream()
            .mapToInt(this::getWeight)
            .sum();
        int random = new Random().nextInt(totalWeight);
        int currentWeight = 0;
        for (ServiceInstance instance : instances) {
            currentWeight += getWeight(instance);
            if (random < currentWeight) {
                return instance;
            }
        }
        // 默认轮询
        int pos = Math.abs(position.incrementAndGet());
        return instances.get(pos % instances.size());
    }
    private int getWeight(ServiceInstance instance) {
        // 根据实例的配置或性能指标获取权重
        return instance.getMetadata().getOrDefault("weight", "1");
    }
}

最佳实践

  1. 服务命名规范:使用统一的命名格式,如 {业务域}-{服务名}

  2. 健康检查:实现详细的自定义健康检查逻辑

  3. 熔断降级:结合Hystrix或Sentinel实现熔断

  4. 配置中心:使用Apollo或Nacos Config管理配置

  5. 监控告警:集成Prometheus和Grafana监控

  6. 安全防护:注册中心添加认证和授权机制

  7. 多环境支持:通过Namespace区分开发、测试、生产

这个案例涵盖了服务发现的核心功能,可以根据实际需求进行扩展和优化。

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