Spring Cloud Nacos服务发现案例

wen java案例 1

本文目录导读:

Spring Cloud Nacos服务发现案例

  1. 项目结构
  2. 父POM配置
  3. 服务提供者 (user-service)
  4. 服务消费者 (order-service)
  5. 启动和测试
  6. Nacos控制台配置(可选)
  7. 常见配置示例
  8. 注意事项

我来为你提供一个完整的Spring Cloud Nacos服务发现案例,包含服务提供者和服务消费者。

项目结构

nacos-discovery-demo
├── pom.xml (父POM)
├── user-service (服务提供者)
└── order-service (服务消费者)

父POM配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>nacos-discovery-demo</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.14</version>
        <relativePath/>
    </parent>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2021.0.5</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
    </properties>
    <modules>
        <module>user-service</module>
        <module>order-service</module>
    </modules>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

服务提供者 (user-service)

1 POM配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>nacos-discovery-demo</artifactId>
        <version>1.0.0</version>
    </parent>
    <artifactId>user-service</artifactId>
    <name>user-service</name>
    <description>用户服务提供者</description>
    <dependencies>
        <!-- Spring Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Nacos Discovery -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- Nacos Config (可选) -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!-- Actuator 健康检查 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

2 主启动类

package com.example.userservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
        System.out.println("用户服务启动成功!");
    }
}

3 配置文件 application.yml

server:
  port: 8081
spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        # 命名空间ID(可选,默认public)
        namespace: public
        # 集群名称(可选)
        # cluster-name: DEFAULT
        # 权重(默认1.0)
        weight: 1.0
        # 是否注册服务(默认true)
        register-enabled: true
        # 是否心跳检测
        heart-beat-interval: 5000
        heart-beat-timeout: 15000
        ip-delete-timeout: 30000
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        group: DEFAULT_GROUP
        # 支持多个配置
        shared-configs:
          - data-id: common.yaml
            group: DEFAULT_GROUP
            refresh: true
# 日志配置
logging:
  level:
    com.alibaba.nacos: warn
    com.example: debug
# 管理端点
management:
  endpoints:
    web:
      exposure:
        include: '*'

4 实体类

package com.example.userservice.entity;
public class User {
    private Long id;
    private String username;
    private String email;
    private Integer age;
    public User() {}
    public User(Long id, String username, String email, Integer age) {
        this.id = id;
        this.username = username;
        this.email = email;
        this.age = age;
    }
    // Getter和Setter方法
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", email='" + email + '\'' +
                ", age=" + age +
                '}';
    }
}

5 Controller

package com.example.userservice.controller;
import com.example.userservice.entity.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/api/users")
@RefreshScope
public class UserController {
    private static final Logger log = LoggerFactory.getLogger(UserController.class);
    @Value("${server.port}")
    private String serverPort;
    @Value("${test.config:默认配置}")  // 从Nacos配置中心读取
    private String testConfig;
    // 模拟数据库
    private static final Map<Long, User> userMap = new ConcurrentHashMap<>();
    static {
        userMap.put(1L, new User(1L, "张三", "zhangsan@example.com", 25));
        userMap.put(2L, new User(2L, "李四", "lisi@example.com", 28));
        userMap.put(3L, new User(3L, "王五", "wangwu@example.com", 32));
    }
    @GetMapping
    public List<User> getAllUsers() {
        List<User> users = new ArrayList<>(userMap.values());
        log.info("从端口 {} 获取用户列表,数量:{}", serverPort, users.size());
        return users;
    }
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        User user = userMap.get(id);
        log.info("从端口 {} 获取用户:{}", serverPort, user);
        return user;
    }
    @PostMapping
    public User createUser(@RequestBody User user) {
        user.setId(System.currentTimeMillis());
        userMap.put(user.getId(), user);
        log.info("从端口 {} 创建用户:{}", serverPort, user);
        return user;
    }
    @GetMapping("/info")
    public String getServerInfo() {
        return "当前服务实例端口:" + serverPort + ",配置信息:" + testConfig;
    }
}

5b 负载均衡测试接口

package com.example.userservice.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/health")
public class HealthController {
    @Value("${server.port}")
    private String serverPort;
    @GetMapping
    public String health() {
        return "User Service instance on port " + serverPort + " is healthy!";
    }
}

服务消费者 (order-service)

1 POM配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>nacos-discovery-demo</artifactId>
        <version>1.0.0</version>
    </parent>
    <artifactId>order-service</artifactId>
    <name>order-service</name>
    <description>订单服务消费者</description>
    <dependencies>
        <!-- Spring Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Nacos Discovery -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- OpenFeign 服务调用 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- LoadBalancer 负载均衡 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <!-- Actuator 健康检查 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

2 主启动类

package com.example.orderservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
        System.out.println("订单服务启动成功!");
    }
}

3 配置文件 application.yml

server:
  port: 8082
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: public
        register-enabled: true
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
    loadbalancer:
      nacos:
        enabled: true  # 使用Nacos负载均衡策略
# Ribbon负载均衡配置(可选)
ribbon:
  nacos:
    enabled: true
  ConnectTimeout: 5000
  ReadTimeout: 5000
# Feign配置
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic
logging:
  level:
    com.example.orderservice.client: debug

4 使用OpenFeign的定义

package com.example.orderservice.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "user-service")  // 通过服务名调用
public interface UserClient {
    @GetMapping("/api/users/{id}")
    Object getUserById(@PathVariable Long id);
    @GetMapping("/api/users")
    Object getAllUsers();
    @GetMapping("/health")
    String getHealth();
}

5 使用RestTemplate + @LoadBalanced

package com.example.orderservice.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
    @Bean
    @LoadBalanced  // 开启负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

6 Service层

package com.example.orderservice.service;
import com.example.orderservice.client.UserClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@Service
public class OrderService {
    @Autowired
    private UserClient userClient;
    @Autowired
    private RestTemplate restTemplate;
    /**
     * 通过Feign调用服务
     */
    public Object getUserByFeign(Long userId) {
        return userClient.getUserById(userId);
    }
    /**
     * 通过RestTemplate调用服务
     */
    public Object getUserByRestTemplate(Long userId) {
        String url = "http://user-service/api/users/" + userId;
        return restTemplate.getForObject(url, Object.class);
    }
    /**
     * 获取所有用户(Feign)
     */
    public Object getAllUsersByFeign() {
        return userClient.getAllUsers();
    }
    /**
     * 获取所有用户(RestTemplate)
     */
    public Object getAllUsersByRestTemplate() {
        String url = "http://user-service/api/users";
        return restTemplate.getForObject(url, Object.class);
    }
    /**
     * 获取用户服务健康状态
     */
    public String getUserServiceHealth() {
        return userClient.getHealth();
    }
}

7 Controller

package com.example.orderservice.controller;
import com.example.orderservice.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    private static final Logger log = LoggerFactory.getLogger(OrderController.class);
    @Autowired
    private OrderService orderService;
    @Autowired
    private DiscoveryClient discoveryClient;
    @GetMapping("/users/{userId}")
    public Map<String, Object> getUserInfo(@PathVariable Long userId) {
        Map<String, Object> result = new HashMap<>();
        // 通过Feign调用
        Object feignData = orderService.getUserByFeign(userId);
        // 通过RestTemplate调用
        Object restTemplateData = orderService.getUserByRestTemplate(userId);
        result.put("feignData", feignData);
        result.put("restTemplateData", restTemplateData);
        return result;
    }
    @GetMapping("/users/all")
    public Map<String, Object> getAllUsers() {
        Map<String, Object> result = new HashMap<>();
        result.put("feignAllUsers", orderService.getAllUsersByFeign());
        result.put("restTemplateAllUsers", orderService.getAllUsersByRestTemplate());
        return result;
    }
    @GetMapping("/discovery")
    public List<ServiceInstance> discoverServices() {
        return discoveryClient.getInstances("user-service");
    }
    @GetMapping("/health")
    public String getUserServiceHealth() {
        return orderService.getUserServiceHealth();
    }
}

8 服务发现控制器

package com.example.orderservice.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/services")
public class ServiceDiscoveryController {
    @Autowired
    private DiscoveryClient discoveryClient;
    @GetMapping
    public Map<String, Object> getAllServices() {
        Map<String, Object> result = new HashMap<>();
        List<String> services = discoveryClient.getServices();
        result.put("services", services);
        for (String service : services) {
            List<ServiceInstance> instances = discoveryClient.getInstances(service);
            List<Map<String, Object>> instanceInfo = new ArrayList<>();
            for (ServiceInstance instance : instances) {
                Map<String, Object> info = new HashMap<>();
                info.put("serviceId", instance.getServiceId());
                info.put("host", instance.getHost());
                info.put("port", instance.getPort());
                info.put("uri", instance.getUri());
                info.put("instanceId", instance.getInstanceId());
                info.put("metadata", instance.getMetadata());
                instanceInfo.add(info);
            }
            result.put(service, instanceInfo);
        }
        return result;
    }
    @GetMapping("/{serviceName}")
    public List<ServiceInstance> getServiceInstances(@PathVariable String serviceName) {
        return discoveryClient.getInstances(serviceName);
    }
}

启动和测试

1 环境准备

下载并启动Nacos Server

# Linux/Mac
sh startup.sh -m standalone
# Windows
startup.cmd -m standalone

访问Nacos控制台

  • 地址:http://127.0.0.1:8848/nacos
  • 默认账号密码:nacos/nacos

2 启动服务

# 启动用户服务
cd user-service
mvn spring-boot:run
# 启动订单服务
cd order-service
mvn spring-boot:run

3 测试API

# 1. 测试服务发现
curl http://localhost:8082/services
# 2. 测试通过Feign调用用户服务
curl http://localhost:8082/api/orders/users/1
# 3. 测试通过RestTemplate调用用户服务
curl "http://localhost:8082/api/orders/users/all"
# 4. 获取用户服务健康状态
curl http://localhost:8082/api/orders/health
# 5. 直接调用用户服务
curl http://localhost:8081/api/users/1

4 负载均衡测试

启用多个user-service实例测试负载均衡:

# 启动多个用户服务实例(不同端口)
mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8083
mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8084
# 多次调用观察负载均衡效果
for i in {1..5}; do
  curl http://localhost:8082/api/orders/health
  echo ""
done

Nacos控制台配置(可选)

1 配置中心配置

在Nacos控制台创建数据ID:order-service.yaml

# 订单服务配置
order:
  timeout: 3000
  auto-confirm: true
# 自定义配置
test:
  config: 这是从Nacos配置中心读取的配置

2 配置项说明

# 服务发现配置
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848  # Nacos服务器地址
        namespace: public            # 命名空间
        group: DEFAULT_GROUP        # 分组
        weight: 1.0                 # 权重,范围0-10000
        register-enabled: true      # 是否注册
        ip: 127.0.0.1              # 指定IP(可选)
        port: 8081                  # 指定端口(可选)
        metadata:                   # 元数据
          version: 1.0
          region: cn-hangzhou

常见配置示例

1 集群环境配置

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848,127.0.0.2:8848,127.0.0.3:8848
        namespace: prod-namespace
        cluster-name: HANGZHOU

2 多环境配置

# bootstrap.yml
spring:
  profiles:
    active: dev
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        namespace: ${spring.profiles.active}

注意事项

  1. 版本兼容性:注意Spring Boot、Spring Cloud和Spring Cloud Alibaba的版本对应关系
  2. 服务命名:服务名只能使用小写字母、数字和连字符
  3. 防火墙配置:确保8848端口可以访问
  4. 集群环境:生产环境建议使用集群部署
  5. 安全性:可以配置Nacos的认证和权限控制

这个案例展示了Nacos服务发现的核心功能,包括服务注册、发现、调用和负载均衡,你可以根据实际需求进行扩展和调整。

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