Spring Boot Microservices Registration and Discovery
Why Service Registration and Discovery
In microservices architecture, service instances change dynamically:
- Scaling: Instance count constantly changes
- Failover: Failed instances need automatic removal
- Dynamic Routing: Clients need to know available service addresses
- Load Balancing: Distribute requests among multiple instances
Major Registration Centers Comparison
| Feature | Eureka | Nacos | Consul | Zookeeper |
|---|---|---|---|---|
| Developer | Netflix | Alibaba | HashiCorp | Apache |
| Consistency | AP | AP/CP | CP | CP |
| Health Check | Client heartbeat | TCP/HTTP/MySQL | TCP/HTTP/gRPC | Ephemeral nodes |
| Multi-DC | Yes | Yes | Yes | No |
| Spring Cloud | Native | Supported | Supported | Supported |
| Config Center | No | Yes | Yes | No |
Approach 1: Eureka
1. Eureka Server Configuration
Add Dependencies
xml<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
Main Class
java@SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
Configuration
yamlserver: port: 8761 spring: application: name: eureka-server eureka: instance: hostname: localhost client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ server: enable-self-preservation: false eviction-interval-timer-in-ms: 5000
2. Eureka Client Configuration
Add Dependencies
xml<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
Configuration
yamlserver: port: 8081 spring: application: name: user-service eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true instance-id: ${spring.application.name}:${server.port} lease-renewal-interval-in-seconds: 5 lease-expiration-duration-in-seconds: 10
Approach 2: Nacos (Recommended)
1. Nacos Server Deployment
bash# Download and start Nacos curl -O https://github.com/alibaba/nacos/releases/download/2.2.3/nacos-server-2.2.3.tar.gz tar -xzf nacos-server-2.2.3.tar.gz cd nacos/bin # Start in standalone mode sh startup.sh -m standalone # Console: http://localhost:8848/nacos # Default credentials: nacos/nacos
2. Nacos Client Configuration
Add Dependencies
xml<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
Configuration
yamlserver: port: 8081 spring: application: name: user-service cloud: nacos: discovery: server-addr: localhost:8848 namespace: dev group: DEFAULT_GROUP metadata: version: v1 region: beijing weight: 1 ephemeral: true
3. Nacos Service Discovery
java@RestController @RequestMapping("/discovery") @RequiredArgsConstructor public class DiscoveryController { private final DiscoveryClient discoveryClient; private final LoadBalancerClient loadBalancerClient; @GetMapping("/services") public List<String> getServices() { return discoveryClient.getServices(); } @GetMapping("/instances/{serviceName}") public List<ServiceInstance> getInstances(@PathVariable String serviceName) { return discoveryClient.getInstances(serviceName); } @GetMapping("/choose/{serviceName}") public ServiceInstance choose(@PathVariable String serviceName) { return loadBalancerClient.choose(serviceName); } }
Approach 3: Consul
1. Consul Server Deployment
bash# Download Consul from https://www.consul.io/downloads # Start in dev mode consul agent -dev # Production mode (cluster) consul agent -server -bootstrap-expect=3 -data-dir=/var/consul \ -bind=192.168.1.1 -client=0.0.0.0 -ui -retry-join="provider=aws ..."
2. Consul Client Configuration
Add Dependencies
xml<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency>
Configuration
yamlserver: port: 8081 spring: application: name: user-service cloud: consul: host: localhost port: 8500 discovery: register: true service-name: ${spring.application.name} health-check-path: /actuator/health health-check-interval: 10s instance-id: ${spring.application.name}:${random.value} tags: version=1.0,profile=dev
Inter-Service Communication
1. Using RestTemplate + @LoadBalanced
java@Configuration public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } @Service @RequiredArgsConstructor public class UserService { private final RestTemplate restTemplate; public Order getOrderByUserId(Long userId) { // Use service name instead of specific IP String url = "http://order-service/orders/user/" + userId; return restTemplate.getForObject(url, Order.class); } }
2. Using OpenFeign (Recommended)
Add Dependencies
xml<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
Enable Feign
java@SpringBootApplication @EnableFeignClients public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } }
Define Feign Client
java@FeignClient( name = "order-service", fallback = OrderClientFallback.class ) public interface OrderClient { @GetMapping("/orders/{id}") Order getOrderById(@PathVariable("id") Long id); @GetMapping("/orders/user/{userId}") List<Order> getOrdersByUserId(@PathVariable("userId") Long userId); @PostMapping("/orders") Order createOrder(@RequestBody Order order); } @Component @Slf4j public class OrderClientFallback implements OrderClient { @Override public Order getOrderById(Long id) { log.warn("Order service is down, returning fallback for order: {}", id); Order order = new Order(); order.setId(id); order.setStatus("UNKNOWN"); return order; } @Override public List<Order> getOrdersByUserId(Long userId) { return Collections.emptyList(); } @Override public Order createOrder(Order order) { throw new RuntimeException("Order service is unavailable"); } }
Load Balancing
1. Spring Cloud LoadBalancer
java@Configuration public class LoadBalancerConfig { @Bean public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer( Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); return new ReactorServiceInstanceLoadBalancer() { private Random random = new Random(); @Override public Mono<Response<ServiceInstance>> choose(Request request) { ServiceInstanceListSupplier supplier = loadBalancerClientFactory .getLazyProvider(name, ServiceInstanceListSupplier.class) .getIfAvailable(); return supplier.get().next().map(instances -> { if (instances.isEmpty()) { return new EmptyResponse(); } int index = random.nextInt(instances.size()); return new DefaultResponse(instances.get(index)); }); } }; } }
Service Gateway
xml<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
yamlserver: port: 8080 spring: application: name: api-gateway cloud: gateway: discovery: locator: enabled: true lower-case-service-id: true routes: - id: user-service uri: lb://user-service predicates: - Path=/api/users/** filters: - StripPrefix=1 - id: order-service uri: lb://order-service predicates: - Path=/api/orders/** filters: - StripPrefix=1
Config Center Integration (Nacos Config)
xml<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
yamlspring: application: name: user-service profiles: active: dev cloud: nacos: config: server-addr: localhost:8848 file-extension: yaml namespace: dev group: DEFAULT_GROUP
java@RestController @RefreshScope public class ConfigController { @Value("${user.name:default}") private String userName; @GetMapping("/config") public Map<String, Object> getConfig() { Map<String, Object> config = new HashMap<>(); config.put("userName", userName); return config; } }
Summary
| Registry | Recommended Scenario | Pros | Cons |
|---|---|---|---|
| Eureka | Traditional Spring Cloud | Mature and stable | Maintenance stopped |
| Nacos | New projects | Comprehensive features, active community | Learning curve |
| Consul | Multi-language environments | Cloud-native, feature-rich | Resource intensive |
| Zookeeper | Existing ZK clusters | Mature and reliable | Limited features |
Recommendations:
- New projects: Nacos (comprehensive features, active community)
- Cloud-native: Consul
- Legacy projects: Eureka