spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战

一、项目实现目标

项目目标是完成传统SASS服务向MCP服务的转型,并实现MCP服务的负载均衡调用,具体目标如下:

  • 服务转型:将现有的传统SASS服务快速、高效地封装并转换为MCP(服务,复用原有业务逻辑,降低转型成本。

  • 负载均衡:实现MCP服务的多实例部署与负载均衡调用,通过Nacos服务注册与发现机制,分发请求流量,避免单实例压力过大,提升服务可用性。

文末有源码链接

二、项目运行环境

项目基于Spring生态搭建,依赖以下核心组件,各组件版本及作用明确如下,确保组件间兼容性与功能正常发挥:

  • NACOS 3.1.1:作为服务注册中心与配置中心,支持MCP服务的注册、发现与配置管理,为MCP服务的负载均衡提供基础支撑,确保服务实例可被正常识别与调用。

  • Spring AI Alibaba:提供ReactAgent组件,该组件是对Spring AI ChatClient的更高层次封装,集成了Agent开发常用的工具调用、拦截器、内存存储等能力,简化Agent端的开发流程。

  • 千问大模型(qwen-flash):与Spring AI Alibaba深度集成,无需额外复杂配置即可快速调用,为Agent端提供自然语言交互与逻辑处理能力,支撑控制台的对话式服务调用。

三、项目整体架构

项目采用模块化架构设计,分为两个核心项目模块,整体架构如下:

  • sass-to-mcp-agent:作为服务调用端,核心功能是通过命令行控制台与用户进行交互,通过Nacos发现已注册的MCP服务,并根据用户输入的指令,自动调用对应的MCP服务工具,返回处理结果。

  • sass-to-mcp-mcp:作为服务提供端,包含原有SASS服务与封装后的MCP服务,部署时需启动两个实例并注册到Nacos,主要用于模拟MCP服务的多实例部署,配合完成负载均衡测试,验证负载分发效果。

两个模块通过Nacos实现服务注册与发现,Agent端通过MCP客户端调用MCP服务端的工具方法,形成完整的"调用-响应"链路,同时借助Nacos实现负载均衡调度。

四、sass-to-mcp-agent 项目详情

该项目为服务调用端,负责与用户交互、发现MCP服务并发起调用,核心包含依赖配置、配置文件、核心代码三大模块,具体实现如下:

4.1 项目依赖

引入核心依赖,涵盖日志、MCP客户端、Agent框架、千问大模型支持等,确保项目正常运行,依赖配置如下(pom.xml):

xml 复制代码
<dependencies>
	<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
	</dependency>
	<dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
  </dependency>
	<!-- Spring AI Alibaba Agent Framework:提供Agent核心能力 -->
	<dependency>
		<groupId>com.alibaba.cloud.ai</groupId>
		<artifactId>spring-ai-alibaba-agent-framework</artifactId>
	</dependency>
	<!-- DashScope ChatModel 支持(千问大模型集成),若更换模型需替换对应starter -->
	<dependency>
		<groupId>com.alibaba.cloud.ai</groupId>
		<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
	</dependency>
	<dependency>
		<groupId>com.alibaba.cloud.ai</groupId>
		<artifactId>spring-ai-alibaba-starter-mcp-distributed</artifactId>
	</dependency>
</dependencies>

4.2 配置文件

通过application.yaml配置文件,设置日志、应用名称、MCP客户端、Nacos连接、千问大模型等参数,确保各组件协同工作,配置如下:

yaml 复制代码
logging:
  config: classpath:logback-spring.xml # 日志配置文件路径

spring:
  application:
    name: sass-to-mcp-agent # 应用名称,用于Nacos识别
  main:
    web-application-type: none # 非Web应用,仅启动命令行交互
  ai:
    dashscope:
      api-key: 你的阿里云百炼key # 千问大模型调用密钥
      base-url: https://dashscope.aliyuncs.com # 千问大模型请求地址
      chat:
        options:
          model: qwen-flash # 选用的千问模型版本
    alibaba:
      mcp:
        nacos:
          client:
            enabled: true # 启用Nacos作为MCP客户端注册中心
            sse:
              connections:
                server1:
                  service-name: sass-to-mcp-mcp-server # Nacos中MCP服务的名称(需与服务端一致)
                  version: 1.0.0 # MCP服务版本(需与服务端一致)
            configs:
              server1:
                 namespace: public # Nacos命名空间(默认public)
                 server-addr: 127.0.0.1:8848 # Nacos服务地址
                 username: nacos # Nacos登录用户名
                 password: nacos # Nacos登录密码
    mcp:
      client:
        enabled: true # 启用MCP客户端
        name: sass-to-mcp-agent # MCP客户端名称
        version: 0.0.1 # MCP客户端版本
        initialized: true # 启动时初始化MCP客户端
        request-timeout: 600s # 请求超时时间
        type: sync # 调用类型(同步)
        toolcallback:
          enabled: true # 启用工具回调
        root-change-notification: true # 启用根节点变更通知

4.3 核心代码

4.3.1 配置类

配置ReactAgent实例,集成工具回调、千问大模型、日志拦截器等,用于处理用户请求并调用MCP服务工具,代码如下:

java 复制代码
@Configuration(proxyBeanMethods = false)
public class Config {

    @Bean
    public ReactAgent buildReactAgent(@Qualifier("distributedSyncToolCallback") ToolCallbackProvider tools, ChatModel chatModel) {
        // 从nacos获取所有可用的工具回调,打印日志便于调试
        ToolCallback[] toolCallbacks = tools.getToolCallbacks();
        System.out.println(">>> Available tools: ");
        for (int i = 0; i < toolCallbacks.length; i++) {
            System.out.println("[" + i + "] " + toolCallbacks[i].getToolDefinition().name());
        }
        // 构建ReactAgent,集成工具、日志拦截器、模型、内存存储
        ReactAgent agent = ReactAgent.builder()
                .tools(tools.getToolCallbacks())
                .interceptors(new LoggingInterceptor())
                .model(chatModel)
                .name("test_agent")
                .saver(new MemorySaver())
                .build();
        return agent;
    }
}

4.3.2 命令行交互类

实现命令行交互功能,接收用户输入的指令,通过ReactAgent调用MCP服务,返回处理结果,支持"exit"命令退出,代码如下:

java 复制代码
@Controller
public class CommandLineRunnerController implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(CommandLineRunnerController.class);

    // 注入ReactAgent实例(由配置类创建)
    ReactAgent agent;

    public CommandLineRunnerController(ReactAgent agent) {
        this.agent = agent;
    }

    @Override
    public void run(String... args) throws Exception {
        // 生成唯一对话ID,用于标识当前对话上下文
        RunnableConfig runnableConfig = RunnableConfig.builder()
                .threadId(UUID.randomUUID().toString())
                .addMetadata("user_id", "1")
                .build();
        // 启动控制台扫描器,接收用户输入
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("\n>>> QUESTION: ");
            String userInput = scanner.nextLine();
            // 输入exit,退出程序
            if (userInput.equalsIgnoreCase("exit")) {
                break;
            }
            // 空输入时,默认发送"你好"进行测试
            if (userInput.isEmpty()) {
                userInput = "你好";
            }
            String res = "";
            try {
                // 调用Agent处理用户输入,获取响应结果
                AssistantMessage response = agent.call(userInput, runnableConfig);
                res = response.getText();
            } catch (GraphRunnerException e) {
                // 捕获异常,打印日志并返回异常提示
                logger.error("", e);
                res = "系统异常";
            }
            // 输出Agent响应结果
            System.out.println("\n>>> ASSISTANT: " + res);
        }
        // 关闭扫描器,释放资源
        scanner.close();
    }

}

五、sass-to-mcp-mcp 项目详情

该项目为服务提供端,负责将传统SASS服务封装为MCP服务,并注册到Nacos,支持多实例部署,核心包含依赖配置、配置文件、核心代码三大模块,具体实现如下:

5.1 项目依赖

引入核心依赖,涵盖Web服务、日志、MCP服务注册、MCP服务端等,确保SASS服务能正常封装为MCP服务并注册到Nacos,依赖配置如下(pom.xml):

xml 复制代码
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-logging</artifactId>
	</dependency>
	<!-- MCP服务注册依赖,用于将MCP服务注册到Nacos -->
	<dependency>
		<groupId>com.alibaba.cloud.ai</groupId>
		<artifactId>spring-ai-alibaba-starter-mcp-registry</artifactId>
	</dependency>
	<!-- MCP服务端依赖,支持WebMVC模式 -->
	<dependency>
		<groupId>org.springframework.ai</groupId>
		<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
	</dependency>
</dependencies>

5.2 配置文件

通过application.yaml配置文件,设置端口、应用名称、MCP服务、Nacos注册等参数,支持多实例部署(需手动修改端口),配置如下:

yaml 复制代码
logging:
  config: classpath:logback-spring.xml # 日志配置文件路径
server:
  port: 8090 # 服务端口(多实例部署时需修改,如8091、8092)
  
spring:
  application:
    name: sass-to-mcp-mcp # 应用名称
  ai:
    mcp:
      server:
        name: sass-to-mcp-mcp-server # MCP服务名称(需与Agent端配置一致)
        version: 1.0.0 # MCP服务版本(需与Agent端配置一致)
        type: SYNC # 调用类型(同步)
        instructions: "这个mcp提供订单、订单商品相关的工具与资源" # MCP服务描述
    alibaba:
      mcp:
        nacos:
          server-addr: localhost:8848 # Nacos服务地址(与Agent端一致)
          namespace: public # Nacos命名空间(默认public)
          username: nacos # Nacos登录用户名
          password: nacos # Nacos登录密码
          register:
            enabled: true # 启用Nacos服务注册(必须开启,否则Agent端无法发现)

5.3 核心代码

5.3.1 MCP服务类

通过自定义注解@McpTools标记该类为MCP服务类,将SASS服务的方法封装为MCP工具方法,供Agent端调用,代码如下:

java 复制代码
@McpTools
public class McpService {
    // 注入原有SASS服务的Controller,复用原有业务逻辑
    @Autowired
    SassController SassController;

    /**
     * 工具方法:获取订单详情
     * @param param 订单详情查询参数(需与SASS服务参数一致)
     * @return 订单详情VO
     */
    @Tool(description = "获取订单详情")
    public OrderDetailVO getOrderDetail(OrderDetailQueryParam param) {
        // 直接调用SASS服务的方法,实现快速封装
        return SassController.getOrderDetail(param);
    }

    /**
     * 工具方法:获取分页订单列表
     * @param param 订单列表查询参数(含分页信息)
     * @return 分页订单列表结果
     */
    @Tool(description = "获取订单列表(分页)")
    public PageResult<OrderVO> getOrderList(OrderListQueryParam param) {
        // 直接调用SASS服务的方法,实现快速封装
        return SassController.getOrderList(param);
    }

}

说明:

  • @McpTools:自定义注解,用于标记MCP服务类,Spring会通过扫描机制,识别该类并注册为MCP服务。

  • @Tool:标记方法为MCP工具方法,description属性用于描述方法功能,供Agent端识别并调用。

  • 依赖注入SassController,直接复用原有SASS服务的业务逻辑,无需重复开发,实现传统SASS服务向MCP服务的快速转型。

5.3.2 MCP服务扫描注册类

实现BeanFactoryPostProcessor接口,用于扫描所有带有@McpTools注解的Bean,自动注册为MCP工具回调提供者,确保Agent端能发现并调用MCP服务方法,代码如下:

java 复制代码
@Component
public class McpToolRegister implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 判断Bean工厂是否为BeanDefinitionRegistry,用于注册Bean定义
        if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

            // 获取所有带有@McpTools注解的Bean名称
            String[] beanNames = beanFactory.getBeanNamesForAnnotation(McpTools.class);

            // 遍历所有@McpTools标记的Bean,注册为MethodToolCallbackProvider
            for (String beanName : beanNames) {
                BeanDefinition bd = registry.getBeanDefinition(beanName);

                try {
                    // 获取Bean的Class对象
                    Class<?> beanClass = Class.forName(bd.getBeanClassName());

                    // 注册MethodToolCallbackProvider,用于提供MCP工具回调
                    registerMethodToolCallbackProvider(registry, beanName, beanClass);

                } catch (ClassNotFoundException e) {
                    // 捕获类加载异常,打印堆栈信息
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 注册MethodToolCallbackProvider Bean,用于将MCP服务方法封装为工具回调
     * @param registry Bean定义注册中心
     * @param originalBeanName 原有@McpTools标记的Bean名称
     * @param originalBeanClass 原有@McpTools标记的Bean的Class
     */
    private void registerMethodToolCallbackProvider(BeanDefinitionRegistry registry, String originalBeanName, Class<?> originalBeanClass) {
        // 生成MethodToolCallbackProvider的Bean名称,避免与原有Bean冲突
        String providerBeanName = "methodToolCallbackProvider_" + originalBeanName;

        // 如果该Bean已注册,则跳过,避免重复注册
        if (registry.containsBeanDefinition(providerBeanName)) {
            return;
        }

        // 创建GenericBeanDefinition,设置BeanClass为ClassMethodToolCallbackProvider
        GenericBeanDefinition definition = new GenericBeanDefinition();
        definition.setBeanClass(ClassMethodToolCallbackProvider.class);

        // 通过构造函数参数,传递MCP服务类的Class对象
        definition.getConstructorArgumentValues().addGenericArgumentValue(Arrays.asList(originalBeanClass));
        // 注册Bean定义,完成MCP工具回调提供者的注册
        registry.registerBeanDefinition(providerBeanName, definition);
    }
}

六、项目运行流程

项目运行需遵循固定流程,确保服务注册、发现与调用正常,具体步骤如下:

  1. 启动Nacos服务 :首先启动本地Nacos服务(版本3.1.1),确保Nacos控制台可正常访问(默认地址:http://127.0.0.1:8848/nacos,用户名/密码:nacos/nacos)。

  2. 启动sass-to-mcp-mcp项目(多实例)

    • 启动第一个实例:使用默认配置,端口为8090,启动命令无需修改端口。

    • 启动第二个实例:手动指定端口(避免端口冲突),启动命令示例:java -jar sass-to-mcp-mcp.jar --server.port=8091(可根据实际情况修改端口号)。

    • 启动完成后,可在Nacos控制台"服务管理-服务列表"中,看到sass-to-mcp-mcp-server服务已注册,且包含2个实例。

  3. 启动sass-to-mcp-agent项目:启动Agent项目,启动成功后,控制台会打印可用的MCP工具列表,同时提示">>> QUESTION: ",等待用户输入。

  4. 测试服务调用与负载均衡:在Agent控制台输入相关问题(如"获取订单详情""获取订单列表"),Agent会自动调用MCP服务,返回处理结果;多次输入相同指令,可通过Nacos控制台或服务日志,观察请求是否在两个MCP实例间均衡分发,验证负载均衡效果。

七、运行截图

nacos mcp服务截图

nacos mcp 方法元数据配置截图

发起请求

服务1收到请求

服务2收到请求

八、总结

结合实战过程中的经验,注意事项如下:

  • 若新增/修改了MCP服务的方法定义(如方法参数、返回值、注解信息等),需清除Nacos中的MCP服务,否则将无法生效,甚至可能导致服务调用异常。

  • Agent端(MCP调用方)必须在所有MCP服务实例启动并成功注册到Nacos后,再启动Agent项目;若提前启动Agent,可能会因无法从Nacos发现MCP服务而报错

项目源码:download.csdn.net/download/Th...

相关推荐
程序员晓琪1 天前
约定大于配置:基于 Java 包名自动生成 API 版本路由的最佳实践
java·spring boot·后端
Flittly1 天前
【AgentScope Java新手村系列】(11)中断与恢复
java·spring boot·spring
ServBay1 天前
Laravel Herd MCP 的替代,多语言与跨平台的 AI 本地开发选择
后端·ai编程·mcp
码哥字节1 天前
我把整个代码库喂给 Claude Code,工具超 50 个就静默丢失,这个坑太阴了
mcp·claude code·ai编程工具
dunky1 天前
Spring 的三级缓存与循环依赖
后端·spring
用户3521802454752 天前
🎆从 Prompt 到 Skill:让 Spring AI Agent 学会"装新技能"
人工智能·spring boot·ai编程
ServBay5 天前
打通 AI 编程本地运维边界,利用 MCP 协议简化环境与服务管理
后端·ai编程·mcp
用户3521802454755 天前
当 Prompt 学会"热更新":Spring Boot × Nacos3 AI 实战
java·spring boot·ai编程
昵称为空C5 天前
手撸一个动态 SQL 执行引擎:不重启服务,在线增删改查任意数据库
spring boot·后端