开源模型应用落地-业务优化篇(八)

一、前言

    在之前的学习中,我相信您已经学会了一些优化技巧,比如分布式锁、线程池优化、请求排队、服务实例扩容和消息解耦等等。现在,我要给您介绍最后一篇业务优化的内容了。这个优化方法是通过定时统计问题的请求频率,然后将一些经常被请求的问题缓存起来,以提高系统的响应速度。


二、术语

2.1、 任务调度框架(Task Scheduling Framework)

    是一种用于管理和执行任务的软件工具或平台。它提供了一种结构和机制,使用户能够以自动化的方式安排、调度和执行任务,以满足特定的需求和要求。

2.2、分布式任务调度框架(Distributed Task Scheduling Framework)

    是一种用于管理和调度分布式环境中任务的软件工具或平台。它专注于在分布式系统中协调和执行任务,以提高整体性能、可伸缩性和容错性。

    分布式任务调度框架通常用于处理大规模任务和作业,并利用集群、云计算或容器化环境中的多个计算节点来并行执行任务。它们提供了一种分布式任务调度器,可以协调和分配任务到可用的计算节点,并监控任务的执行状态和进度。

2.3、XXL-JOB

    是一个开源的分布式任务调度平台,用于解决大规模任务调度和分布式定时任务管理的需求。它提供了一个可视化的任务调度中心,可以集中管理和调度各种类型的任务,包括定时任务、流程任务和API任务等。

2.4、Milvus

    是一个开源的向量数据库引擎,专门用于存储和处理大规模高维向量数据。它提供了高效的向量索引和相似性搜索功能,使用户能够快速地进行向量数据的存储、查询和分析。

    Milvus的设计目标是为了满足现代应用中对大规模向量数据的需求,例如人脸识别、图像搜索、推荐系统等。它采用了向量空间模型和多种索引算法,包括倒排索引、近似最近邻(Approximate Nearest Neighbor,ANN)等,以支持高效的相似性搜索。

    Milvus提供了易于使用的编程接口和丰富的功能,使用户可以方便地插入、查询和分析向量数据。它支持多种数据类型的向量,包括浮点型、整型等,也支持多种距离度量方法,如欧氏距离、余弦相似度等。

    Milvus还提供了分布式部署和横向扩展的能力,可以在多台机器上构建高可用性和高性能的向量数据库集群。它支持数据的分片和负载均衡,可以处理大规模数据集和高并发查询。
 


三、前置条件

3.1、已经根据前面的“开源模型应用落地”的学习搭建起完整AI流程

    1) 如何部署AI服务

    2) 如何使用向量数据库

    3) 如何使用RocketMQ

    ......

    本篇将通过定时任务周期性的统计问题请求的频次,并从向量数据库中,将热点问题同步至Redis,实现缓存前置,提升访问性能。


四、技术实现

4.1、新增定时任务处理类

import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Map;

@Slf4j
@Component
public class HotTopicStatistics {
    private static final Long DEFAULT_HOTSPOT_THRESHOLD = 10L;

    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private ContentCacheUtils contentCacheUtils;

    @Scheduled(cron = "*/30 * * * * ?")
    public void statistics() {
        RMap<String, String> rmap = redisUtils.hincget("CONTENT_COUNTER");

        if (MapUtil.isNotEmpty(rmap)) {

            for (Map.Entry<String, String> entry : rmap.entrySet()) {
                String keyword = entry.getKey();
                Long count = Long.parseLong(entry.getValue());
                // 计数器统计数值 > 热度阈值
                if(count.compareTo(DEFAULT_HOTSPOT_THRESHOLD) > 0){
                    // 从向量数据库中拉取数据
                    log.info("从向量数据库中拉取数据");

                    String cacheContent = contentCacheUtils.cacheFromMilvus(keyword);

                    if(StringUtils.isNotEmpty(cacheContent) && StringUtils.isNotBlank(cacheContent)){
                        log.info("将热点内容缓存至redis中,过期时间设置为3600秒,内容为:{}",cacheContent);

                        // 将热点内容缓存至redis中,过期时间设置为3600秒
                        redisUtils.buckSet(keyword,cacheContent,60*60);
                    }

                }
            }

        }
    }

}

4.2、新增内容缓存公共类

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Console;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class ContentCacheUtils {
    private static final int DIM = 256;

    @Autowired
    private AIChatUtils aiChatUtils;
    @Autowired
    private MilvusUtils milvusUtils;


    public String cacheFromMilvus(String keyWord){
        if (StringUtils.isEmpty(keyWord) || StringUtils.isBlank(keyWord)){
            return null;
        }

        float[] vector = aiChatUtils.getVector("", keyWord);
        double[] double_arr = milvusUtils.pretreatment(vector, DIM);
        Float[] float_arr = Convert.toFloatArray(double_arr);
        List<Float> vectorList = CollUtil.list(false, float_arr);

        List search_vectors = new ArrayList(1);
//        打印日志
        Console.log(search_vectors);
        search_vectors.add(vectorList);
        Map<String, String> resultMap = milvusUtils.search_data_vector("tb_content", "keyword",
                search_vectors, null, 1, CollUtil.list(false, "content"));


        String status = resultMap.get("status");
        String cacheContent = null;
        if (StringUtils.equals(status, "0")) {
            cacheContent = resultMap.get("content");

        }

        return cacheContent;
    }

}

4.3、修改Redis公共类

    增加以下方法

public  RMap<String, String> hincget(String key){
	RMap<String, String> rmap = null;

	if (StringUtils.isNotEmpty(key) && StringUtils.isNoneBlank(key) ) {
		rmap = redissonClient.getMap(key);
	}
	return rmap;
}

public void buckSet(String key, String value,long second) {
	if (StringUtils.isNotEmpty(key) && StringUtils.isNoneBlank(key) && StringUtils.isNotEmpty(value) && StringUtils.isNoneBlank(value)) {
		RBucket<String> bucket =  redissonClient.getBucket(key);
		bucket.set(value,second, TimeUnit.SECONDS);
	}
}

4.4、修改业务处理类

    使用上面内容缓存公共类替换早前未封装的代码


五、测试

5.1、启动Redis

    启动windows版本的redis服务,redis-server.exe redis.windows.conf

5.2、将CONTENT_COUNTER的值设置为11

    下面使用Redis Desktop Manager工具编辑CONTENT_COUNTER的值

5.3、启动Milvus Server,并初始化数据

5.4、启动SpringBoot项目

    (一)运行Application

    (二)Redis当前只有一个Key,热点内容未缓存

    (三)定时任务触发

    (四)Redis缓存热点内容


六、附带说明

6.1、Spring Boot开启定时任务

    启用类增加@EnableScheduling注解

    需要将具体任务类加入到Spring管理,例如:增加@Component注解

    

6.2、实际项目中,应用使用分布式任务调度平台去替换本示例中SpringBoot内置的任务调度功能

6.3、Milvus Server启动超时

  直接编辑milvus下面的__init__.py文件,将timeout设置大一些

6.4、本章BusinessHandler完整代码

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Console;
import com.alibaba.fastjson.JSON;
import io.netty.channel.ChannelHandler;
import lombok.extern.slf4j.Slf4j;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;


/**
 * @Description: 处理消息的handler
 */
@Slf4j
@ChannelHandler.Sharable
@Component
public class BusinessHandler extends AbstractBusinessLogicHandler<TextWebSocketFrame> {
    public static final String LINE_UP_QUEUE_NAME = "AI-REQ-QUEUE";
    private static final String LINE_UP_LOCK_NAME = "AI-REQ-LOCK";

    private static final int MAX_QUEUE_SIZE = 100;

//    @Autowired
//    private TaskUtils taskExecuteUtils;
//    @Autowired
//    private AIChatUtils aiChatUtils;
//    @Autowired
//    private MilvusUtils milvusUtils;

    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private NettyConfig nettyConfig;
    @Autowired
    private RocketMQProducer rocketMQProducer;
    @Autowired
    private ContentCacheUtils contentCacheUtils;


    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        String channelId = ctx.channel().id().asShortText();
        log.info("add client,channelId:{}", channelId);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        String channelId = ctx.channel().id().asShortText();
        log.info("remove client,channelId:{}", channelId);
    }


    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame)
            throws Exception {
        // 获取客户端传输过来的消息
        String content = textWebSocketFrame.text();
        // 兼容在线测试
        if (StringUtils.equals(content, "PING")) {
            buildResponse(channelHandlerContext, ApiRespMessage.builder().code(String.valueOf(StatusCode.SUCCESS.getCode()))
                    .respTime(String.valueOf(System.currentTimeMillis()))
                    .msgType(String.valueOf(MsgType.HEARTBEAT.getCode()))
                    .contents("心跳测试,很高兴收到你的心跳包")
                    .build());

            return;
        }
        log.info("接收到客户端发送的信息: {}", content);

        Long userIdForReq;
        String msgType = "";
        String contents = "";

        try {
            ApiReqMessage apiReqMessage = JSON.parseObject(content, ApiReqMessage.class);
            msgType = apiReqMessage.getMsgType();
            contents = apiReqMessage.getContents();


            userIdForReq = apiReqMessage.getUserId();
            // 用户身份标识校验
            if (null == userIdForReq || (long) userIdForReq <= 10000) {
                ApiRespMessage apiRespMessage = ApiRespMessage.builder().code(String.valueOf(StatusCode.SYSTEM_ERROR.getCode()))
                        .respTime(String.valueOf(System.currentTimeMillis()))
                        .contents("用户身份标识有误!")
                        .msgType(String.valueOf(MsgType.SYSTEM.getCode()))
                        .build();
                buildResponseAndClose(channelHandlerContext, apiRespMessage);
                return;
            }


            if (StringUtils.equals(msgType, String.valueOf(MsgType.CHAT.getCode()))) {
                // 对用户输入的内容进行自定义违规词检测
                // 对用户输入的内容进行第三方在线违规词检测
                // 对用户输入的内容进行组装成Prompt
                // 对Prompt根据业务进行增强(完善prompt的内容)
                // 对history进行裁剪或总结(检测history是否操作模型支持的上下文长度,例如qwen-7b支持的上下文长度为8192)
                // ...

//                通过线程池来处理
//                String messageId = apiReqMessage.getMessageId();
//                List<ChatContext> history = apiReqMessage.getHistory();
//                AITaskReqMessage aiTaskReqMessage = AITaskReqMessage.builder().messageId(messageId).userId(userIdForReq).contents(contents).history(history).build();
//                taskExecuteUtils.execute(aiTaskReqMessage);


                // 违规词检测
                log.info("contents: {}",contents);
                if (WordDetection.contains_illegal_word(contents)) {
                    log.warn("the content sent contains illegal words");
                    ApiRespMessage apiRespMessage = ApiRespMessage.builder().code(String.valueOf(StatusCode.ILLEGAL_WORDS_FAILURE_731.getCode()))
                            .respTime(String.valueOf(System.currentTimeMillis()))
                            .contents("内容不合规!")
                            .msgType(String.valueOf(MsgType.SYSTEM.getCode()))
                            .build();
                    buildResponseAndClose(channelHandlerContext, apiRespMessage);
                    return;
                }

                String[] filterWords = new String[]{"一", "语文", "老师"};
                List<String> keyWordsList = KeyWordsUtils.extractKeywords(contents, Arrays.asList(filterWords));
                String keyWord = CollUtil.join(keyWordsList, "");
                log.info("keyWord: {}", keyWord);
                String cacheContent = redisUtils.buckGet(keyWord);
                // 返回redis中的缓存数据
                if (StringUtils.isNotEmpty(cacheContent) && StringUtils.isNoneBlank(cacheContent)) {
                    buildResponse(channelHandlerContext, ApiRespMessage.builder().code(String.valueOf(StatusCode.SUCCESS.getCode()))
                            .respTime(String.valueOf(System.currentTimeMillis()))
                            .msgType(String.valueOf(MsgType.CHAT.getCode()))
                            .contents(cacheContent)
                            .build());
                    return;
                } else {
                    // 从milvus中检索数据
                    cacheContent = contentCacheUtils.cacheFromMilvus(keyWord);

                    if (StringUtils.isNotEmpty(cacheContent) && StringUtils.isNoneBlank(cacheContent)) {
                        buildResponse(channelHandlerContext, ApiRespMessage.builder().code(String.valueOf(StatusCode.SUCCESS.getCode()))
                                .respTime(String.valueOf(System.currentTimeMillis()))
                                .msgType(String.valueOf(MsgType.CHAT.getCode()))
                                .contents(cacheContent)
                                .build());
                        return;
                    }

                    //投递消息
                    String msg = "{\"msg\":\""+keyWord+"\"}";
                    rocketMQProducer.send("ai-topic",msg);
                }
//                通过队列来缓冲
                boolean flag = true;

                RLock lock = redissonClient.getLock(LINE_UP_LOCK_NAME);
                String queueName = LINE_UP_QUEUE_NAME + "-" + nettyConfig.getNode();

                //尝试获取锁,最多等待3秒,锁的自动释放时间为10秒
                if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
                    try {
                        if (redisUtils.queueSize(queueName) < MAX_QUEUE_SIZE) {
                            redisUtils.queueAdd(queueName, content);
                            log.info("当前线程为:{}, 添加请求至redis队列", Thread.currentThread().getName());
                        } else {
                            flag = false;
                        }
                    } catch (Throwable e) {
                        log.error("系统处理异常", e);
                    } finally {
                        lock.unlock();
                    }
                } else {
                    flag = false;
                }

                if (!flag) {
                    buildResponse(channelHandlerContext, ApiRespMessage.builder().code(String.valueOf(StatusCode.SUCCESS.getCode()))
                            .respTime(String.valueOf(System.currentTimeMillis()))
                            .msgType(String.valueOf(MsgType.SYSTEM.getCode()))
                            .contents("当前排队人数较多,请稍后再重试!")
                            .build());
                }


            } else if (StringUtils.equals(msgType, String.valueOf(MsgType.INIT.getCode()))) {
                //一、业务黑名单检测(多次违规,永久锁定)

                //二、账户锁定检测(临时锁定)

                //三、多设备登录检测

                //四、剩余对话次数检测

                //检测通过,绑定用户与channel之间关系
                addChannel(channelHandlerContext, userIdForReq);
                String respMessage = "用户标识: " + userIdForReq + " 登录成功";

                buildResponse(channelHandlerContext, ApiRespMessage.builder().code(String.valueOf(StatusCode.SUCCESS.getCode()))
                        .respTime(String.valueOf(System.currentTimeMillis()))
                        .msgType(String.valueOf(MsgType.INIT.getCode()))
                        .contents(respMessage)
                        .build());

            } else if (StringUtils.equals(msgType, String.valueOf(MsgType.HEARTBEAT.getCode()))) {

                buildResponse(channelHandlerContext, ApiRespMessage.builder().code(String.valueOf(StatusCode.SUCCESS.getCode()))
                        .respTime(String.valueOf(System.currentTimeMillis()))
                        .msgType(String.valueOf(MsgType.HEARTBEAT.getCode()))
                        .contents("心跳测试,很高兴收到你的心跳包")
                        .build());
            } else {
                log.info("用户标识: {}, 消息类型有误,不支持类型: {}", userIdForReq, msgType);
            }


        } catch (Exception e) {
            log.warn("【BusinessHandler】接收到请求内容:{},异常信息:{}", content, e.getMessage(), e);
            // 异常返回
            return;
        }

    }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/449198.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

交叉编译x264 zlib ffmpeg以及OpenCV等 以及解决交叉编译OpenCV时ffmpeg始终为NO的问题

文章目录 环境编译流程nasm编译x264编译zlib编译libJPEG编译libPNG编译libtiff编译 FFmpeg编译OpenCV编译问题1解决方案 问题2解决方案 总结 环境 系统&#xff1a;Ubutu 18.04交叉编译链&#xff1a;gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu 我的路径/opt/toolch…

【Flutter】报错Target of URI doesn‘t exist ‘package:flutter/material.dart‘

运行别人项目 包无法导入报错&#xff1a;Target of URI doesn’t exist ‘package:flutter/material.dart’ 解决方法 flutter packages get成功 不会报错

php CI框架异常报错通过钉钉自定义机器人发送

php CI框架异常报错通过钉钉自定义机器人发送 文章目录 php CI框架异常报错通过钉钉自定义机器人发送前言一、封装一个异常监测二、封装好钉钉信息发送总结 前言 我们在项目开发中&#xff0c;经常会遇到自己测试接口没问题&#xff0c;上线之后就会测出各种问题&#xff0c;主…

K8s — PVC|PV Terminating State

在本文中&#xff0c;我们将讨论PV和PVC一直Terminating的状态。 何时会Terminting? 在以下情况下&#xff0c;资源将处于Terminating状态。 在删除Bounded 状态的PVC之前&#xff0c;删除了对应的PV&#xff0c;PV在删除后是Terminting状态。删除PVC时&#xff0c;仍有引用…

python向多个用户发送文字、图片内容邮件和excel附件

话不多说&#xff0c;直接上代码&#xff0c;需要把发件里面的smtp_info替换为自己的信息&#xff0c;其中password是指邮箱在开通POP/SMTP功能后获取的授权码&#xff01; import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import M…

案例分析篇05:数据库设计相关28个考点(9~16)(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12601310.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…

web3D三维引擎(Direct3D、OpenGL、UE、U3D、threejs)基础扫盲

Hi&#xff0c;我是贝格前端工场的老司机&#xff0c;本文介绍文web3D的几个引擎&#xff0c;做个基础扫盲&#xff0c;如果还不能解决问题&#xff0c;可以私信我&#xff0c;搞私人订制呦。 三维引擎是指用于创建和渲染三维图形的软件框架。它们通常提供了图形处理、物理模拟…

案例分析篇02:软件架构设计考点之特定领域软件架构、架构评估、架构视图(2024年软考高级系统架构设计师冲刺知识点总结)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12601310.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…

2024RKDC,新一代AIOT 处理器RK3576发布 !

触觉智能已成功推出RK3576相关开发板核心板&#xff0c;RK3576采用瑞芯微八核芯片&#xff0c;专为 AI0I设计&#xff0c;可用于平板电脑、AI0T应用程序、电子墨水显示器、Arm PC和汽车电子中。集成独立的6TOPS NPU&#xff0c;支持4K视频编解码&#xff0c;性能定位于RK3588和…

smart-doc 社区 Committer 晋升公告

我们非常荣幸地宣布&#xff0c;经过 PMC 委员会的提名和讨论&#xff0c;社区成员李星志&#xff08;GitHub ID: netdied&#xff09;、陈琪&#xff08;GitHub ID: chenqi146&#xff09;和李兵&#xff08;GitHub ID: abing22333&#xff09;正式晋升为同程旅行 smart-doc 开…

redis概述和安装

1 、redis概述和安装 1.1、安装redis 1. 下载redis2. 地址 : https://download.redis.io/releases/ 3. 选择需要的版本1.2 将 redis 安装包拷贝到 /opt/ 目录 1.3. 解压 tar -zvxf redis-6.2.1.tar.gz1.4. 安装gcc yum install gcc1.5. 进入目录 cd redis-6.2.11.6 编译 …

微信小程序云开发教程——墨刀原型工具入门(素材面板)

引言 作为一个小白&#xff0c;小北要怎么在短时间内快速学会微信小程序原型设计&#xff1f; “时间紧&#xff0c;任务重”&#xff0c;这意味着学习时必须把握微信小程序原型设计中的重点、难点&#xff0c;而非面面俱到。 要在短时间内理解、掌握一个工具的使用&#xf…

“2024成都国际电子信息产业展览会”新西部、新重构、新机遇

随着西部大开发和科技兴国的战略深入实施&#xff0c;四川的经济发展已经取得了令人瞩目的成就。作为西部的核心&#xff0c;四川在能源化工、装备制造、航天科技等产业方面均处于国内领先地位&#xff0c;同时也是全国重要的基础电子装备基地。这一发展背景为2024年成都国际电…

ETL的数据挖掘方式

ETL的基本概念 数据抽取&#xff08;Extraction&#xff09;&#xff1a;从不同源头系统中获取所需数据的步骤。比如从mysql中拿取数据就是一种简单的抽取动作&#xff0c;从API接口拿取数据也是。 数据转换&#xff08;Transformation&#xff09;&#xff1a;清洗、整合和转…

常用云产品连接

阿里云常用云产品 云服务器 阿里云&#xff1a;云服务器ECS_云主机_服务器托管_计算-阿里云 对象存储 阿里云&#xff1a;对象存储 OSS_云存储服务_企业数据管理_存储-阿里云 短信服务 阿里云&#xff1a;短信服务_企业短信营销推广_验证码通知-阿里云 CDN服务 阿里云&…

redis源码分析

是什么 是基于内存(而不是磁盘)的kv(而不是关系型mysql那种)数据库&#xff0c;通过空间换时间 源码分析 跳表skiplist 假设你有个有序链表&#xff0c;你想看某个特定的值是否出现在这个链表中&#xff0c;那你是不是只能遍历一次链表才能知道&#xff0c;时间复杂度为O(n…

如何搭建财务数据运营体系:基于财务五力模型的分析

在当今复杂多变的商业环境中,财务数据作为企业决策的重要参考依据,其运营体系的搭建显得尤为关键。一个健全、高效的财务数据运营体系不仅能够为企业提供准确的财务数据支持,还能帮助企业在激烈的市场竞争中保持领先地位。基于财务五力模型的分析,我们可以从收益力、安定力…

windows server 2019 服务器配置的方法步骤

一、启用远程功能二、测试三、解决多用户登录的问题 一、启用远程功能 右键点击【此电脑】–【属性】&#xff0c;进入“【控制面板\系统和安全\系统】”&#xff0c;点击-【远程设置】(计算机找不到就使用【winE】快捷键) 2、在“远程桌面”下方&#xff0c;点击【允许远程连…

NOIP2018-S-DAY1-3-赛道修建(洛谷P5021)的题解

目录 题目 原题描述&#xff1a; 题目描述 输入格式 输出格式 输入输出样例 主要思路&#xff1a; check&#xff1a; 真正的code: 原题描述&#xff1a; 题目描述 C 城将要举办一系列的赛车比赛。在比赛前&#xff0c;需要在城内修建 条赛道。 C 城一共有 个路…

gitee分支管理,合并冲突

1、gitee展示分支 git branch 2、展示远程分支 git branch -r 3、新建分支 git branch base 4、切换分支 git checkout base 合并冲突 当代码在服务器上被提交了&#xff0c;再在本地提交会提示报错 点击merge