Java案例怎么实现服务发现?

wen java案例 75

本文目录导读:

Java案例怎么实现服务发现?

  1. 方案一:生产级方案(Spring Cloud Alibaba + Nacos)
  2. 方案二:手动实现简易服务发现(基于HTTP + 内存注册表)
  3. 核心概念对比与总结

在Java中实现服务发现,主要有两种主流方式:使用现成的注册中心(如Nacos、Consul、Eureka)自行开发基于DNS或HTTP的简单方案

下面我会从最常用的生产级方案(Spring Cloud + Nacos)手动实现的简易方案两个角度来给出案例。


生产级方案(Spring Cloud Alibaba + Nacos)

这是目前国内最主流的搭配,功能强大,自带控制台。

环境准备

  • 启动Nacos Server: 下载Nacos(https://github.com/alibaba/nacos/releases),解压后运行 bin/startup.cmd -m standalone(单机模式)。 访问 http://localhost:8848/nacos,默认账号密码 nacos/nacos

Maven依赖(服务提供方 + 消费方都需要)

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.13</version>
</parent>
<dependencies>
    <!-- Spring Cloud Alibaba Nacos Discovery 
         注意版本对应关系 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        <version>2021.0.5.0</version>
    </dependency>
    <!-- Web依赖(用于提供/调用接口) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

服务提供方(Provider)

application.yml

server:
  port: 8081
spring:
  application:
    name: user-service  # 服务名,消费方通过这个名字调用
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848  # Nacos地址

启动类

@SpringBootApplication
@EnableDiscoveryClient  // 启用服务发现(新版本可省略,加上了更明确)
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

Controller (提供API)

@RestController
@RequestMapping(“/users”)
public class UserController {
   @GetMapping(“/{id}”)
   public String getUser(@PathVariable String id) {
       return  “User info for id: ”  + id +  “ from port: 8081”;
   }
}

服务消费方(Consumer)

application.yml

server:
  port: 8090
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

使用 RestTemplate + @LoadBalanced (负载均衡调用)

@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
    // 注入一个具有负载均衡能力的RestTemplate
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Controller (调用UserService)

@RestController
@RequestMapping(“/order”)
public class OrderController {
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping(“/user/{userId}”)
    public String getOrderUser(@PathVariable String userId) {
        // 注意:这里用的是服务名 “user-service”,不是具体的IP和端口
        String url = “http://user-service/users/” + userId;
        String userInfo = restTemplate.getForObject(url, String.class);
        return “Order info, user: ” + userInfo;
    }
}

验证效果

  • 启动 Provider(8081),启动 Consumer(8090)。
  • 访问 http://localhost:8090/order/user/123,应返回 Order info, user: User info for id: 123 from port: 8081
  • 打开 Nacos 控制台,可以看到 user-serviceorder-service 都已经注册成功。

手动实现简易服务发现(基于HTTP + 内存注册表)

如果不依赖Spring Cloud,想理解核心原理,可以自己实现一个轻量版。

核心思想

  1. 服务注册:启动时,服务方将自己的 IP:Port + 服务名 发送给注册中心。
  2. 服务发现:消费方从注册中心拉取服务列表,然后根据负载均衡策略选择一个IP调用。

服务注册中心代码(Registry Center)

使用Spring Boot + ConcurrentHashMap

@RestController
@RequestMapping(“/registry”)
@SpringBootApplication
public class RegistryCenterApplication {
    // 内存注册表:  Key=服务名, Value=IP:Port列表
    private static final ConcurrentHashMap<String, List<String>> SERVICE_MAP = new ConcurrentHashMap<>();
    public static void main(String[] args) {
        SpringApplication.run(RegistryCenterApplication.class, args);
    }
    // 注册接口
    @PostMapping(“/register”)
    public String register(@RequestParam String serviceName, @RequestParam String address) {
        SERVICE_MAP.computeIfAbsent(serviceName, k -> new CopyOnWriteArrayList<>()).add(address);
        System.out.println(“Registered: ” + serviceName + “ -> ” + address);
        return “success”;
    }
    // 服务发现接口(获取所有实例)
    @GetMapping(“/discover/{serviceName}”)
    public List<String> discover(@PathVariable String serviceName) {
        return SERVICE_MAP.getOrDefault(serviceName, Collections.emptyList());
    }
    // 心跳检测/剔除(简易版略,生产需加定时任务判断节点是否存活)
}

application.yml (端口 8761)

server:
  port: 8761

服务提供方(UserService)

@SpringBootApplication
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
    // 注册逻辑
    @Bean
    public CommandLineRunner registerToCenter(RestTemplate restTemplate) {
        return args -> {
            String serviceName = “user-service”;
            String address = “localhost:8081”;  // 本机地址
            // 向注册中心注册
            restTemplate.postForEntity(
                “http://localhost:8761/registry/register?serviceName=” + serviceName + “&address=” + address, 
                null, 
                String.class
            );
            System.out.println(“Registered to registry center.”);
        };
    }
    @RestController
    public class UserApi {
        @GetMapping(“/users/{id}”)
        public String getUser(@PathVariable String id) {
            return “User ” + id + “ from manual registry”;
        }
    }
}

服务消费方(OrderService)

@SpringBootApplication
public class OrderServiceApplication {
    @Autowired
    private RestTemplate restTemplate;
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
    @RestController
    public class OrderApi {
        @GetMapping(“/invoke”)
        public String invokeUserService() {
            // 1. 从注册中心发现服务
            List<String> instances = restTemplate.getForObject(
                “http://localhost:8761/registry/discover/user-service”, 
                List.class
            );
            if (instances == null || instances.isEmpty()) {
                return “No available service”;
            }
            // 2. 简单负载均衡:取第一个(生产应使用轮询/随机)
            String target = instances.get(0);
            // 3. 调用服务
            String result = restTemplate.getForObject(“http://” + target + “/users/1”, String.class);
            return “Result from discovered service: ” + result;
        }
    }
}

核心概念对比与总结

特性 方案一(Nacos/生产级) 方案二(手动/教学级)
注册中心稳定性 高,支持集群、持久化、CP+AP 低,单机,内存存储
健康检查 自动心跳检测、剔除不健康节点 需自己实现
负载均衡 内置(Ribbon/LoadBalancer) 需手动实现轮询/随机
配置管理 Nacos自带配置中心,可动态刷新
使用场景 微服务生产环境 理解原理、小项目或学习
  • 在实际企业Java项目中,强烈推荐使用 Spring Cloud Alibaba + NacosSpring Cloud + Consul,这些组件已经解决了高可用、健康检查、负载均衡、配置同步等复杂问题。
  • 如果想快速理解服务发现的本质,可以自己用 HTTP + ConcurrentHashMap 实现一遍流程,你会发现核心就是:一个集中存储服务地址的地方 + 一个用来获取地址的客户端

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