本文目录导读:

- 基于Spring Cloud + Netflix Eureka(经典方案)
- 基于Consul(更现代的方案)
- 基于ZooKeeper(原生实现)
- 基于Nacos(阿里巴巴方案)
- 手动实现服务发现(学习用)
- 选择建议
在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),它们配置简单、功能完善。