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...

相关推荐
Java水解3 小时前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
栀秋6667 小时前
重塑 AI 交互边界:基于 LangChain 与 MCP 协议的全栈实践
langchain·llm·mcp
初次攀爬者1 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺1 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端
Derek_Smart1 天前
从一次 OOM 事故说起:打造生产级的 JVM 健康检查组件
java·jvm·spring boot
神秘的猪头1 天前
🔌 给 AI 装上“三头六臂”!实战大模型接入第三方 MCP 全攻略
langchain·llm·mcp
Nyarlathotep01131 天前
SpringBoot Starter的用法以及原理
java·spring boot
神秘的猪头2 天前
🔌 把 MCP 装进大脑!手把手带你构建能“热插拔”工具的 AI Agent
langchain·llm·mcp