Spring Cloud Microservices Tutorial: Implementing Nacos Service Registration & Discovery
This tutorial walks through setting up Nacos for service registration and discovery in a Spring Cloud microservice project, covering environment preparation, configuring user-service and order-service, verifying registration, demonstrating load balancing, and troubleshooting common issues.
Goal of This Session
The aim is to add the first core component of a microservice architecture—Nacos service registration and discovery—to the project skeleton built in the previous episode.
Why Service Registration & Discovery?
In a monolithic app a call is a simple local method invocation, e.g. userService.getUser(id). In a microservice architecture the caller must know the address of the target service, handle multiple instances, and detect service failures. Nacos solves these problems by providing:
Service registration : each service reports its address to Nacos on startup.
Service discovery : callers query Nacos to obtain the target address.
Health check : Nacos periodically verifies that a service instance is alive.
Load balancing : when multiple instances exist, Nacos distributes requests automatically.
Environment Preparation
Start Nacos with Docker Compose:
# Use Docker Compose to start Nacos
docker-compose up -dThe docker-compose.yml defines a single Nacos container in standalone mode and exposes ports 8848 (HTTP) and 9848 (gRPC, required by Nacos 2.x):
version: '3.8'
services:
nacos:
image: nacos/nacos-server:v2.3.2
container_name: nacos-teaching
environment:
MODE: standalone
ports:
- "8848:8848"
- "9848:9848" # gRPC port (required for newer versions)Access the console at http://localhost:8848/nacos with username/password nacos/nacos.
user-service (User Service)
Core Configuration
server:
port: 8081
spring:
application:
name: user-service # service name (important!)
cloud:
nacos:
discovery:
server-addr: localhost:8848Bootstrap Class
@SpringBootApplication
@EnableDiscoveryClient // Enable registration & discovery
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}REST Endpoint
@RestController
@RequestMapping("/api/user")
public class UserController {
@Value("${server.port}")
private String port;
@GetMapping("/{id}")
public Map<String, Object> getUser(@PathVariable Long id) {
return Map.of(
"id", id,
"name", "用户-" + id,
"from", "user-service:" + port
);
}
}order-service (Order Service)
Core Configuration
server:
port: 8082
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: localhost:8848Bootstrap Class (with Load‑Balanced WebClient)
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
@Bean
@LoadBalanced // Enable load balancing, allow service‑name calls
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}Order Controller (calls user‑service by name)
@RestController
@RequestMapping("/api/order")
public class OrderController {
@Autowired
private WebClient.Builder webClientBuilder;
@Value("${server.port}")
private String port;
@GetMapping("/{id}")
public Mono<Map<String, Object>> getOrder(@PathVariable Long id) {
// Simulated order data
Map<String, Object> order = Map.of(
"orderId", id,
"productName", "Spring Cloud 教学课程",
"userId", 1L,
"from", "order-service:" + port
);
// Call user-service using its service name
return webClientBuilder.build()
.get()
.uri("http://user-service/api/user/1")
.retrieve()
.bodyToMono(Map.class)
.map(user -> {
Map<String, Object> result = new HashMap<>(order);
result.put("userInfo", user);
return result;
});
}
}Verification Steps
1. Start Services
# Start Nacos
docker-compose up -d
# Start user-service
cd user-service
mvn spring-boot:run
# Start order-service in a new terminal
cd order-service
mvn spring-boot:run2. Verify Registration
Open the Nacos console ( http://localhost:8848/nacos) and look under Service Management → Service List . You should see both user-service and order-service with a healthy instance count of 1.
3. Test Call
curl http://localhost:8082/api/order/1Expected JSON response:
{
"orderId":1,
"productName":"Spring Cloud 教学课程",
"userId":1,
"from":"order-service:8082",
"userInfo":{
"id":1,
"name":"用户-1",
"from":"user-service:8081"
}
}Load‑Balancing Demonstration
Run Two Instances of user‑service
# Instance 1 (default 8081)
java -jar user-service/target/user-service.jar
# Instance 2 (different port)
java -jar user-service/target/user-service.jar --server.port=8085Observe Load‑Balancing
Repeatedly call order-service and watch the userInfo.from field alternate between user-service:8081 and user-service:8085:
for i in {1..10}; do
curl -s http://localhost:8082/api/order/1 | grep -o '"from":"[^"]*"'
done
# Output will show alternating service instancesCommon Issues
Issue 1 – Service Not Registered
Check the value of spring.cloud.nacos.discovery.server-addr in the configuration.
Ensure the Nacos container is running: docker ps | grep nacos.
Verify that port 9848 (gRPC) is exposed for Nacos 2.x.
Issue 2 – UnknownHostException When Calling
Cause: the @LoadBalanced annotation was missing on the WebClient.Builder (or RestTemplate) bean.
Fix: add @LoadBalanced to the bean definition.
Issue 3 – Nacos 2.x Requires gRPC Port
Cause: the required gRPC port 9848 was not mapped.
Fix: expose the port in docker-compose.yml (e.g., "9848:9848").
Code Retrieval
The complete code for this episode is packaged in the repository referenced in the first episode. The subdirectory 02 contains the source for this session.
Next Episode Preview
Spring Cloud Microservices Tutorial – Episode 3: OpenFeign Service Calls, covering RestTemplate/WebClient vs OpenFeign, Feign interface definition, timeout & retry configuration, and a unified interceptor for token propagation.
I'm 老 J, see you next time.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Coder Trainee
Experienced in Java and Python, we share and learn together. For submissions or collaborations, DM us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
