消息中间件系列六,rabbit与spring集成实战

本文涉及的产品
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 前言:这是中间件一个系列的文章之一,有需要的朋友可以看看这个系列的其他文章:消息中间件系列一、消息中间件的基本了解消息中间件系列二、Windows下的activeMQ和rabbitMQ的安装消息中间件系列三、JMS和activeMQ的简单使用消息中间件系列四、认识AMQP和RabbiyMq的简单使用消息中间件系列五、rabbit消息的确认机制目前还在持续更新中,敬请期待。

前言:这是中间件一个系列的文章之一,有需要的朋友可以看看这个系列的其他文章:
消息中间件系列一、消息中间件的基本了解
消息中间件系列二、Windows下的activeMQ和rabbitMQ的安装
消息中间件系列三、JMS和activeMQ的简单使用
消息中间件系列四、认识AMQP和RabbiyMq的简单使用
消息中间件系列五、rabbit消息的确认机制
消息中间件系列六,rabbit与spring集成实战

本项目是rabbit和spring整合的实战学习项目,模拟电商下单和库存管理的过程,看过前面几篇博客的同学,相信这篇博客对你不会再难了。一些和本章学习不太相关的内容不会做过多说明,需要的朋友可以下载源码自己查看运行:rabbit与spring集成实战源码

生产者订单系统

一、pom文件引入相关包

rabbit和spring整合以下两个包是必须的

    <dependency>
      <groupId>com.rabbitmq</groupId>
      <artifactId>amqp-client</artifactId>
      <version>5.0.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.amqp</groupId>
      <artifactId>spring-rabbit</artifactId>
      <version>2.0.0.RELEASE</version>
    </dependency>

其他包根据项目需要引进;引入的其他包有兴趣查看的可以下载源码查看。

二、配置文件

web.xml和spring-mvc.xml不是博客重点,不再贴出,有兴趣的下载源码查看;下面说明applicationContext.xml的内容:

1、配置文件中增加命名空间:

xmlns:rabbit="http://www.springframework.org/schema/rabbit"
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-2.0.xsd

1、连接工厂配置

    <!-- rabbitMQ配置 -->
    <bean id="rabbitConnectionFactory"
          class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
        <constructor-arg value="127.0.0.1"/>
        <property name="username" value="guest"/>
        <property name="password" value="guest"/>
        <property name="channelCacheSize" value="8"/>
        <property name="port" value="5672"></property>
        <!-- 发布确认必须配置在CachingConnectionFactory上 -->
        <property name="publisherConfirms" value="true"/>
    </bean>

2、<rabbit:admin>

配置<rabbit:admin>之后才能根据配置文件去生产队列交换器等信息。

<rabbit:admin connection-factory="rabbitConnectionFactory"/>

4、声明队列

durable:是否持久化

<rabbit:queue name="depot_queue" durable="true"/>

5、声明交换器

name:交换器名称,durable:是否持久化

    <rabbit:direct-exchange name="depot-amount-exchange"
          xmlns="http://www.springframework.org/schema/rabbit" durable="true">
        <rabbit:bindings>
            <rabbit:binding queue="depot_queue" key="amount.depot" ></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

6、队列和交换器进行绑定

queue:队列名称,key:绑定的路由键,需要在交换器中绑定。

        <rabbit:bindings>
            <rabbit:binding queue="depot_queue" key="amount.depot" ></rabbit:binding>
        </rabbit:bindings>

7、生产者端要声明RabbitmqTemplate

    <!-- 创建rabbitTemplate 消息模板类 -->
    <bean id="rabbitTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate">
        <constructor-arg ref="rabbitConnectionFactory"></constructor-arg>
        <!--消息确认回调 -->
        <property name="confirmCallback" ref="confirmCallback"/>
        <property name="returnCallback" ref="sendReturnCallback"/>
    </bean>

完整的applicationContext.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 查找最新的schemaLocation 访问 http://www.springframework.org/schema/ -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:task="http://www.springframework.org/schema/task"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd
    http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-2.0.xsd">

     <!-- 配置扫描路径 -->
     <context:component-scan base-package="com.dongnaoedu">
         <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
     </context:component-scan>

    <!-- rabbitMQ配置 -->
    <bean id="rabbitConnectionFactory"
          class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
        <constructor-arg value="127.0.0.1"/>
        <property name="username" value="guest"/>
        <property name="password" value="guest"/>
        <property name="channelCacheSize" value="8"/>
        <property name="port" value="5672"></property>
        <!-- 发布确认必须配置在CachingConnectionFactory上 -->
        <property name="publisherConfirms" value="true"/>
    </bean>
    <rabbit:admin connection-factory="rabbitConnectionFactory"/>

    <rabbit:queue name="depot_queue" durable="true"/>

    <rabbit:direct-exchange name="depot-amount-exchange"
          xmlns="http://www.springframework.org/schema/rabbit" durable="true">
        <rabbit:bindings>
            <rabbit:binding queue="depot_queue" key="amount.depot" ></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <!-- 创建rabbitTemplate 消息模板类 -->
    <bean id="rabbitTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate">
        <constructor-arg ref="rabbitConnectionFactory"></constructor-arg>
        <!--消息确认回调 -->
        <property name="confirmCallback" ref="confirmCallback"/>
    </bean>
</beans>

三、其他代码

1、controller

package com.dongnaoedu.controller;

import com.dongnaoedu.service.ProcessOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class OrderController {

    private Logger logger = LoggerFactory.getLogger(OrderController.class);
    private static final String SUCCESS = "suc";
    private static final String FAILUER = "failure";

    @Autowired
    private ProcessOrder processOrder;

    @RequestMapping("/order")
    public String userReg(){
        return "index";
    }

    @RequestMapping("/confirmOrder")
    @ResponseBody
    public String confirmOrder(@RequestParam("goodsId")String goodsId,
                           @RequestParam("amount")int amount){
        try {
            processOrder.processOrder(goodsId,amount);
            return SUCCESS;
        } catch (Exception e) {
            logger.error("订单确认异常!",e);
            return FAILUER;
        }
    }
}

2、ProcessOrder

package com.dongnaoedu.service;

import com.dongnaoedu.rpc.DepotService;
import com.dongnaoedu.rpc.RpcProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import sun.security.x509.IPAddressName;

import java.net.InetSocketAddress;

@Service
public class ProcessOrder {
    private Logger logger = LoggerFactory.getLogger(ProcessOrder.class);

    @Autowired
    @Qualifier("mq")
    private IProDepot proDepot;

    public void processOrder(String goodsId,int amount){
        try {
            Thread.sleep(80);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        logger.info("--------------------["+goodsId+"]订单入库完成,准备变动库存!");
        proDepot.processDepot(goodsId,amount);

    }
}

3、MqMode

生产者可通过rabbitTemplate调用send方法发送消息,参数分别为exchange交换器,routingKey路由键,Message对象。

package com.dongnaoedu.service;

import com.dongnaoedu.vo.GoodTransferVo;
import com.google.gson.Gson;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
@Qualifier("mq")
public class MqMode  implements IProDepot {

    private final static String DEPOT_RK = "amount.depot";
    private final static String DEPOT_EXCHANGE = "depot-amount-exchange";

    @Autowired
    RabbitTemplate rabbitTemplate;

    private static Gson gson = new Gson();

    public void processDepot(String goodsId, int amount) {
        GoodTransferVo goodTransferVo = new GoodTransferVo();
        goodTransferVo.setGoodsId(goodsId);
        goodTransferVo.setChangeAmount(amount);
        goodTransferVo.setInOrOut(false);
        String goods = gson.toJson(goodTransferVo);
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);//设置消息属性以便进行消息持久化,投递模式设置为2,
        rabbitTemplate.send(DEPOT_EXCHANGE, DEPOT_RK,new Message(goods.getBytes(), messageProperties));
    }
}

4、ConfirmCallback

消息的确认回调,必须实现RabbitTemplate.ConfirmCallback接口

package com.dongnaoedu.service.callback;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.stereotype.Service;

@Service
public class ConfirmCallback implements RabbitTemplate.ConfirmCallback {
    private Logger logger = LoggerFactory.getLogger(ConfirmCallback.class);

    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack) {
            logger.info("消息确认发送给mq成功");
        } else {
            //处理失败的消息
            logger.info("消息发送给mq失败,考虑重发:"+cause);
        }
    }
}

消费者库存系统

一、配置文件

其他配置文件可下载源码查看。
applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 查找最新的schemaLocation 访问 http://www.springframework.org/schema/ -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:task="http://www.springframework.org/schema/task"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd
    http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-2.0.xsd">

     <!-- 配置扫描路径 -->
     <context:component-scan base-package="com.dongnaoedu">
         <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
     </context:component-scan>

    <!-- rabbitMQ配置 -->
    <bean id="rabbitConnectionFactory"
          class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
        <constructor-arg value="127.0.0.1"/>
        <property name="username" value="guest"/>
        <property name="password" value="guest"/>
        <property name="channelCacheSize" value="8"/>
        <property name="port" value="5672"></property>
    </bean>
    <rabbit:admin connection-factory="rabbitConnectionFactory"/>

    <rabbit:queue name="depot_queue" durable="true"/>

    <rabbit:direct-exchange name="depot-amount-exchange"
          xmlns="http://www.springframework.org/schema/rabbit" durable="true">
        <rabbit:bindings>
            <rabbit:binding queue="depot_queue" key="amount.depot" ></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <!-- 对消息要手动确认 -->
    <rabbit:listener-container connection-factory="rabbitConnectionFactory"
                               acknowledge="manual">
        <rabbit:listener queues="depot_queue" ref="processDepot"
                         method="onMessage" />
    </rabbit:listener-container>
</beans>  

二、其他源码

1、ProcessDepot

package com.dongnaoedu.mq;

import com.dongnaoedu.service.DepotManager;
import com.dongnaoedu.vo.GoodTransferVo;
import com.google.gson.Gson;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;

@Service
public class ProcessDepot  implements ChannelAwareMessageListener {

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

    @Autowired
    private DepotManager depotManager;

    private static Gson gson = new Gson();

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        try {
            String msg = new String(message.getBody());
            logger.info(">>>>>>>>>>>>>>接收到消息:"+msg);
            GoodTransferVo goodTransferVo = gson.fromJson(msg,GoodTransferVo.class);
            try {
                depotManager.operDepot(goodTransferVo);
                channel.basicAck(message.getMessageProperties().getDeliveryTag(),
                        false);
                logger.info(">>>>>>>>>>>>>>库存处理完成,应答Mq服务");
            } catch (Exception e) {
                logger.error(e.getMessage());
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);//deliveryTag投递的标记符,multiple是否进行批量回复,requeue是否重新入队分发消息
                logger.info(">>>>>>>>>>>>>>库存处理失败,拒绝消息,要求Mq重新派发");
                throw e;
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }
}

2、DepotManager

package com.dongnaoedu.service;

import com.dongnaoedu.vo.GoodTransferVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DepotManager {

    @Autowired
    private Depot depot;

    public void operDepot(GoodTransferVo goodTransferVo){
        if(goodTransferVo.isInOrOut()){
            depot.inDepot(goodTransferVo.getGoodsId(),goodTransferVo.getChangeAmount());
        }else{
            depot.outDepot(goodTransferVo.getGoodsId(),goodTransferVo.getChangeAmount());
        }
    }

}

3、Depot

package com.dongnaoedu.service;

import com.dongnaoedu.rpc.DepotService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;

@Service
public class Depot {

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

    private ConcurrentHashMap<String,Integer> goodsData =
            new ConcurrentHashMap<String, Integer>();

    @PostConstruct
    public void initDepot(){
        goodsData.put("001",1000);
        goodsData.put("002",500);
        goodsData.put("003",600);
        goodsData.put("004",700);
    }


    /*使用jdk1.8以下的用这个方法
    public synchronized void inDepot(String goodsId,int addAmout){
        int amount = goodsData.get(goodsId)+addAmout;
        goodsData.put(goodsId,amount);
    }
    */
    //增加库存
    public void inDepot(String goodsId,int addAmout){
        logger.info("+++++++++++++++++增加商品:"+goodsId+"库存,数量为:"+addAmout);
        int newValue = goodsData.compute(goodsId, new BiFunction<String, Integer, Integer>() {
            public Integer apply(String s, Integer integer) {
                return integer == null ? addAmout : integer + addAmout;
            }
        });
        logger.info("+++++++++++++++++商品:"+goodsId+"库存,数量变为:"+newValue);
    }

     /*使用jdk1.8以下的用这个方法
    public synchronized void outDepot(String goodsId,int reduceAmout){
        int amount = goodsData.get(goodsId)-reduceAmout;
        goodsData.put(goodsId,amount);
    }
    */
    //减少库存
    public void outDepot(String goodsId,int reduceAmout){
        logger.info("-------------------减少商品:"+goodsId+"库存,数量为:"+reduceAmout);
        int newValue = goodsData.compute(goodsId, new BiFunction<String, Integer, Integer>() {
            public Integer apply(String s, Integer integer) {
                return integer == null ? 0 : integer - reduceAmout;
            }
        });
        logger.info("-------------------商品:"+goodsId+"库存,数量变为:"+newValue);
    }

    public int getGoodsAmount(String goodsId){
        return goodsData.get(goodsId);
    }
}

补充:源码的rpc调用部分补在这里解释,有兴趣的朋友可以自行研究。源码链接:rabbit与spring集成实战源码
目前rabbit的内容就先写到这里,rabbit集群和rpc的内容以后有时间在补充,接下来我会更新缓存系列,敬请期待。

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
4月前
|
Java Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
|
1月前
|
监控 Java Nacos
使用Spring Boot集成Nacos
通过上述步骤,Spring Boot应用可以成功集成Nacos,利用Nacos的服务发现和配置管理功能来提升微服务架构的灵活性和可维护性。通过这种集成,开发者可以更高效地管理和部署微服务。
196 17
|
1月前
|
缓存 安全 Java
Spring Boot 3 集成 Spring Security + JWT
本文详细介绍了如何使用Spring Boot 3和Spring Security集成JWT,实现前后端分离的安全认证概述了从入门到引入数据库,再到使用JWT的完整流程。列举了项目中用到的关键依赖,如MyBatis-Plus、Hutool等。简要提及了系统配置表、部门表、字典表等表结构。使用Hutool-jwt工具类进行JWT校验。配置忽略路径、禁用CSRF、添加JWT校验过滤器等。实现登录接口,返回token等信息。
343 12
|
1月前
|
人工智能 安全 Dubbo
Spring AI 智能体通过 MCP 集成本地文件数据
MCP 作为一款开放协议,直接规范了应用程序如何向 LLM 提供上下文。MCP 就像是面向 AI 应用程序的 USB-C 端口,正如 USB-C 提供了一种将设备连接到各种外围设备和配件的标准化方式一样,MCP 提供了一个将 AI 模型连接到不同数据源和工具的标准化方法。
|
1月前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
81 8
|
26天前
|
监控 安全 中间件
Next.js 实战 (十):中间件的魅力,打造更快更安全的应用
这篇文章介绍了什么是Next.js中的中间件以及其应用场景。中间件可以用于处理每个传入请求,比如实现日志记录、身份验证、重定向、CORS配置等功能。文章还提供了一个身份验证中间件的示例代码,以及如何使用限流中间件来限制同一IP地址的请求次数。中间件相当于一个构建模块,能够简化HTTP请求的预处理和后处理,提高代码的可维护性,有助于创建快速、安全和用户友好的Web体验。
|
2月前
|
XML Java API
Spring Boot集成MinIO
本文介绍了如何在Spring Boot项目中集成MinIO,一个高性能的分布式对象存储服务。主要步骤包括:引入MinIO依赖、配置MinIO属性、创建MinIO配置类和服务类、使用服务类实现文件上传和下载功能,以及运行应用进行测试。通过这些步骤,可以轻松地在项目中使用MinIO的对象存储功能。
147 5
|
3月前
|
消息中间件 Java Kafka
什么是Apache Kafka?如何将其与Spring Boot集成?
什么是Apache Kafka?如何将其与Spring Boot集成?
108 5
|
3月前
|
消息中间件 Java Kafka
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
84 1
|
4月前
|
自然语言处理 Java API
Spring Boot 接入大模型实战:通义千问赋能智能应用快速构建
【10月更文挑战第23天】在人工智能(AI)技术飞速发展的今天,大模型如通义千问(阿里云推出的生成式对话引擎)等已成为推动智能应用创新的重要力量。然而,对于许多开发者而言,如何高效、便捷地接入这些大模型并构建出功能丰富的智能应用仍是一个挑战。
674 6