Java案例如何实现服务发现?

wen python案例 1

本文目录导读:

Java案例如何实现服务发现?

  1. 基于Spring Cloud + Netflix Eureka(经典方案)
  2. 基于Consul(更现代的方案)
  3. 基于ZooKeeper(原生实现)
  4. 基于Nacos(阿里巴巴方案)
  5. 手动实现服务发现(学习用)
  6. 选择建议

在Java中实现服务发现,主要有几种方式,从简单到复杂,适用不同的场景。

基于Spring Cloud + Netflix Eureka(经典方案)

这是微服务架构中最常见的方案。

服务注册中心(Eureka Server)

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
// application.yml
server:
  port: 8761
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

服务提供者(Eureka Client)

@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}
@RestController
public class UserController {
    @GetMapping("/users/{id}")
    public String getUser(@PathVariable String id) {
        return "User-" + id;
    }
}
// application.yml
spring:
  application:
    name: user-service
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

服务消费者

@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}
// Feign客户端
@FeignClient(name = "user-service")
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    String getUser(@PathVariable String id);
}
@RestController
public class OrderController {
    @Autowired
    private UserServiceClient userServiceClient;
    @GetMapping("/orders/{orderId}/user/{userId}")
    public String getOrderWithUser(@PathVariable String orderId, @PathVariable String userId) {
        String user = userServiceClient.getUser(userId);
        return "Order-" + orderId + " with " + user;
    }
}

基于Consul(更现代的方案)

Consul提供服务发现、健康检查和KV存储。

@SpringBootApplication
@EnableDiscoveryClient
public class ProductServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductServiceApplication.class, args);
    }
}
// application.yml
spring:
  application:
    name: product-service
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        healthCheckPath: /actuator/health
        healthCheckInterval: 10s
        preferIpAddress: true

服务消费者:

@RestController
public class ShoppingCartController {
    @Autowired
    private DiscoveryClient discoveryClient;
    @GetMapping("/cart/{id}")
    public String getCartWithProducts(@PathVariable String id) {
        // 通过DiscoveryClient获取服务实例
        List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
        ServiceInstance instance = instances.get(0);
        String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/products/" + id;
        return "Cart with product from: " + url;
    }
}

基于ZooKeeper(原生实现)

非Spring环境下的原生实现。

public class ZookeeperServiceRegistry {
    private ZooKeeper zooKeeper;
    private static final String BASE_PATH = "/services";
    public ZookeeperServiceRegistry(String connectString) throws Exception {
        zooKeeper = new ZooKeeper(connectString, 3000, event -> {});
        // 确保基础路径存在
        if (zooKeeper.exists(BASE_PATH, false) == null) {
            zooKeeper.create(BASE_PATH, new byte[0], 
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
    }
    // 注册服务
    public void register(String serviceName, String address, int port) throws Exception {
        String servicePath = BASE_PATH + "/" + serviceName;
        if (zooKeeper.exists(servicePath, false) == null) {
            zooKeeper.create(servicePath, new byte[0], 
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
        String instancePath = servicePath + "/instance-";
        String data = address + ":" + port;
        // 创建临时节点,服务下线时自动删除
        zooKeeper.create(instancePath, data.getBytes(), 
            ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
    }
    // 发现服务
    public List<String> discover(String serviceName) throws Exception {
        String servicePath = BASE_PATH + "/" + serviceName;
        List<String> instances = zooKeeper.getChildren(servicePath, false);
        List<String> result = new ArrayList<>();
        for (String instance : instances) {
            byte[] data = zooKeeper.getData(servicePath + "/" + instance, false, null);
            result.add(new String(data));
        }
        return result;
    }
}

基于Nacos(阿里巴巴方案)

Nacos集服务发现、配置管理于一体。

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(PaymentServiceApplication.class, args);
    }
}
// application.yml
spring:
  application:
    name: payment-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: public

使用Nacos实现负载均衡:

@Configuration
public class NacosConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
@RestController
public class PaymentClient {
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping("/call-payment/{id}")
    public String callPayment(@PathVariable String id) {
        // 直接使用服务名,自动负载均衡
        String url = "http://payment-service/payments/" + id;
        return restTemplate.getForObject(url, String.class);
    }
}

手动实现服务发现(学习用)

适用于学习理解服务发现原理:

// 服务注册表
public class InMemoryServiceRegistry {
    private static final Map<String, List<ServiceInstance>> registry = new ConcurrentHashMap<>();
    public static void register(String serviceName, String host, int port) {
        registry.computeIfAbsent(serviceName, k -> new CopyOnWriteArrayList<>())
                .add(new ServiceInstance(host, port));
    }
    public static void deregister(String serviceName, String host, int port) {
        List<ServiceInstance> instances = registry.get(serviceName);
        if (instances != null) {
            instances.remove(new ServiceInstance(host, port));
        }
    }
    public static List<ServiceInstance> getInstances(String serviceName) {
        return registry.getOrDefault(serviceName, Collections.emptyList());
    }
    // 带简单负载均衡的获取
    public static ServiceInstance getInstance(String serviceName) {
        List<ServiceInstance> instances = getInstances(serviceName);
        if (instances.isEmpty()) {
            return null;
        }
        // 轮询策略
        int index = (int) (System.currentTimeMillis() % instances.size());
        return instances.get(index);
    }
}
@Data
@AllArgsConstructor
public class ServiceInstance {
    private String host;
    private int port;
}

选择建议

方案 复杂度 成熟度 适用场景
Eureka Spring Cloud生态,传统微服务
Consul 需要健康检查和KV存储
Nacos 阿里生态,需要配置管理
ZooKeeper 非Spring生态,大数据场景
Kubernetes Service K8s环境

对于新项目,建议优先考虑Nacos(国内团队常用)或Kubernetes Service(如果使用K8s),它们配置简单、功能完善。

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