java_将数据存入elasticsearch进行高效搜索

news2025/1/16 16:11:55

使用技术简介:

(1) 使用Nginx实现反向代理,使前端可以调用多个微服务

(2) 使用nacos将多个服务管理关联起来

(3) 将数据存入elasticsearch进行高效搜索

(4) 使用消息队列rabbitmq进行消息的传递 

(5) 使用 openfeign 进行多个服务之间的api调用

参考: 56 尚上优选项目-平台管理端-整合ES+MQ实现商品上下架-功能最终测试_哔哩哔哩_bilibili

1. 使用Nginx实现反向代理

使用Nginx实现反向代理,使前端可以通过一个端口,调用多个微服务(的端口)

前端中的配置的base api端口 9901:

反向代理逻辑图:

图示 /acl /sys 为两个服务的名称中的路径字符串。

在nginx中的配置如下:

2. 使用nacos将多个服务管理关联起来

 通过nacos将多个服务关联起来,这里实现一个产品上架(存入elasticsearch仓库,简称es)下架(从es仓库删除)的功能.service-product提供商品的信息,service-search通过远程调用(FeignClient)调用service-product的接口,获取商品的具体信息,存入或者从es中删除。

service-product 和 service-search 两个服务通过消息队列进行通讯(rabbitmq消息队列, pom名称:spring-cloud-starter-bus-amqp)

nacos部分:pom依赖:

<!--服务注册 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

在各个服务中添加在naco的注册:示例 service-produce模块的application.yml中添加cloud.nacos.discovery.server-addr

 在 main主程序中添加 启用Nacos服务发现注解:@EnableDiscoveryClient

同理在service-search模块中:

启动nacos服务,同时启动自己的服务,看自己的服务是否注册成功:

下载安装合适版本的nacos(版本不对应会出兼容性问题):

到nacos目录,执行:

.\bin\startup.cmd -m standalone

启动nacos后,启动自己的服务,如自己的服务注册成功,会在log中有如下提示:

在浏览器中输入: http://192.168.136.1:8848/nacos/index.html 也可看是否启动成功,若你的目标服务在“服务列表”中,说明服务注册成功,nacos正常运行,服务可以被关联起来,可以通过restful风格进行数据的传递了。

3. 使用消息队列rabbitmq进行消息的传递 

在需要使用的服务service-search的pom.xml中添加依赖:

<!--rabbitmq消息队列-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

安装rabbitmq服务,示例在虚拟机docker中安装 rabbitmq:3.8-management:

#拉取镜像
docker pull rabbitmq:3.8-management
#创建容器启动
docker run -d --restart=always -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:3.8-management

查看安装rabbitmq的虚拟机ip地址: 

docker中安装好后,可以在本机(win11)上测试下安装是否成功:浏览器中输入:http://192.168.68.132:15672/ (注意这里是自己虚拟机的ip地址)

根据自己虚拟机的地址,在需要使用的服务service-search 和 serivice-product 中配置ip地址和端口号等信息:

 

编写代码,使用 RabbitTemplate, 进行消息的发送:

进行消息发送:指定j交换机exchange 字符串(自定义一个),路由器routing, 发送内容 Object message (这里是 Long skuId)

为了让发送的消息能构正确解析,需要定义一个@Configuration, 使用Jackson2JsonMessageConverter,进行消息的类型转换,这里是将Long skuId 转换成Json格式:

package com.atguigu.ssyx.mq.config;

import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MQConfig {
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }
}

注意rabbit端还需要对 RabbitTemplate 做一些初始化操作,参考init():

package com.atguigu.ssyx.mq.config;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class MQProducerAckConfig implements RabbitTemplate.ReturnCallback, RabbitTemplate.ConfirmCallback {

    //  我们发送消息使用的是 private RabbitTemplate rabbitTemplate; 对象
    //  如果不做设置的话 当前的rabbitTemplate 与当前的配置类没有任何关系!
    @Autowired
    private RabbitTemplate rabbitTemplate;

    //  设置 表示修饰一个非静态的void方法,在服务器加载Servlet的时候运行。并且只执行一次!
    // PostConstruct注解的函数:在 Spring 容器创建并初始化 Bean 后自动调用,一个类中只能有一个 @PostConstruct 注解的方法
    @PostConstruct
    public void init(){
        rabbitTemplate.setReturnCallback(this);
        rabbitTemplate.setConfirmCallback(this);
    }

    /**
     * 表示消息是否正确发送到了交换机上
     *
     * @param correlationData 消息的载体
     * @param ack             判断是否发送到交换机上
     * @param cause           原因
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack) {
            System.out.println("消息发送成功!");
        } else {
            System.out.println("消息发送失败!" + cause);
        }
    }

    /**
     * 消息如果没有正确发送到队列中,则会走这个方法!如果消息被正常处理,则这个方法不会走!
     *
     * @param message
     * @param replyCode
     * @param replyText
     * @param exchange
     * @param routingKey
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText,
                                String exchange, String routingKey) {
        System.out.println("消息主体: " + new String(message.getBody()));
        System.out.println("应答码: " + replyCode);
        System.out.println("描述:" + replyText);
        System.out.println("消息使用的交换器 exchange : " + exchange);
        System.out.println("消息使用的路由键 routing : " + routingKey);
    }
}

消息的接收端(消费者) ,通过RabbitListener 定位到到发送者发送的消息队列上:

package com.atguigu.ssyx.receiver;
import com.atguigu.ssyx.mq.constant.MqConst;
import com.rabbitmq.client.Channel;
import com.atguigu.ssyx.search.service.SkuService;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class SkuReceiver {

    @Autowired
    private SkuService skuService;

    /**
     * 商品上架
     * @param skuId
     * @param message
     * @param channel
     * @throws IOException
     */
    @RabbitListener(bindings = @QueueBinding( //绑定接收什么消息 //消费者,接收消息
            value=@Queue(value = MqConst.QUEUE_GOODS_UPPER, durable = "true"), // durable 持久化
            exchange = @Exchange(value = MqConst.EXCHANGE_GOODS_DIRECT),
            key = {MqConst.ROUTING_GOODS_UPPER}
    ))
    public void upperSku(Long skuId, Message message, Channel channel) throws IOException {
        // 发送者:   rabbitService.sendMsg(MqConst.EXCHANGE_GOODS_DIRECT,
        //                      MqConst.ROUTING_GOODS_UPPER,
        //                      skuId);
        //:发送者函数原型:public boolean sendMsg(String exchange, String routingKey, Object message)
        // 这里的Object message使用 public MessageConverter messageConverter() 方法转成了json格式,
        // 确保了生产者和消费者之间的消息序列化与反序列化逻辑一致,
        // 所以 Object message 对应这里接收者的 Long skuId 形参,实现了参数传递的目的。
        try{
            if(skuId != null) {
                skuService.upperSku(skuId);
            }
            /**
             * 第一个参数:表示收到的消息的标号
             * 第二个参数:如果为true表示可以签收多个消息
             */
            //DeliveryTag 交货标签
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            System.out.print("john:IOException occurred while processing message");
            throw e;  // 或者处理该异常
        }
    }

    /**
     * 商品下架
     * @param skuId
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_GOODS_LOWER, durable = "true"),
            exchange = @Exchange(value = MqConst.EXCHANGE_GOODS_DIRECT), //交换器
            key = {MqConst.ROUTING_GOODS_LOWER} //路由器
    ))
    public void lowerSku(Long skuId, Message message, Channel channel) throws IOException {
        try {
            if (skuId != null) {
                skuService.lowerSku(skuId);
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }
        catch (IOException e) {
        System.out.print("john:IOException occurred while processing message");
        throw e;  // 或者处理该异常
    }
    }
}

注意着的对应关系:

public boolean sendMsg(String exchange, String routingKey, Object message

sendMsg中的 Object message 被 public MessageConverter messageConverter() 正确解析成了 Long skuId

upperSku(Long skuId, Message message, Channel channel)

至此,rabbitmq完成了从service-product传递一个skuId到service-serach的动作。

若有消息发送送时,界面中会出翔对应的消息队列的名称:

4.将数据存入elasticsearch进行高效搜索

elasticsearch的配置

下载elasticsearch Past Releases of Elastic Stack Software | Elastic, elasticsearch-7.8.0-windows-x86_64.zip (注意版本不对,会有兼容性问题)

下载对应版本的分词器:elasticsearch-analysis-ik-7.8.0.zip 

下载 kibana来进行客户端操作 kibana-7.8.0-windows-x86_64.zip

解压 elasticsearch-7.8.0-windows-x86_64文件,在

path\elasticsearch-7.8.0-windows-x86_64\elasticsearch-7.8.0\plugins\ 目录下新建文件夹,命名为ik,解压elasticsearch-analysis-ik-7.8.0,将内容所有内容copy到ik目录中:

在 es目录下运行 .\bin\elasticsearch.bat 启动es:

可以看到启动成功了:

浏览器界面看下 http://localhost:9200/ ,出现下面界面,说明启动成功

配置kibana

下载,解压,修改如下3个配置 (不同版本可能不一样,有增加或者删除项):

启动kibana:

 .\bin\kibana.bat

浏览器访问看下:http://127.0.0.1:5601/ 

分词器的测试:

代码配置

首先定义一个存储仓库对应的类  SkuRepository, 继承自 ElasticsearchRepository:

package com.atguigu.ssyx.search.repository;
import com.atguigu.ssyx.model.search.SkuEs;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface SkuRepository extends ElasticsearchRepository<SkuEs, Long> {
}

使用 skuRepository.save(skuEs) 方法,即可存入 es仓库中。

使用 skuRepository.deleteById(skuId); 即可从仓库中删除。 

使用 ProductFeignClient 进行跨服务访问

因为service-serach要使用service-product的信息,但时两个服务,这是需要使用 ProductFeignClient 进行restful风格的api传递参数,进行远程调用。

首先需要pom中引入依赖:

<!-- 服务调用feign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <scope>provided </scope>
</dependency>

在对应的 service-serach的application-dev.yml中进行配置:

在主程序中药加入远程调用注释: @EnableFeignClients 

然后,定义service-product中的api,将参数传入到指定的api接口:

package com.atguigu.ssyx.product.api;

import com.atguigu.ssyx.model.product.Category;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.product.service.CategoryService;
import com.atguigu.ssyx.product.service.SkuInfoService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
// 内部调用,不是前端调用
@RequestMapping("/api/product") //该类是为了内部调用,供service-search使用,所以命名为内部inner
public class ProductInnerController {

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private SkuInfoService skuInfoService;

    //根据categoryId获取商品的category信息
    @ApiOperation(value = "根据categoryId获取商品的category信息")
    @GetMapping("inner/getCategory/{categoryId}")
    public Category getCategoryById(@PathVariable("categoryId") Long categoryId) {
        return categoryService.getById(categoryId);
    }

    //根据skuId获取skuInfo
    @ApiOperation(value = "根据skuId获取skuInfo信息")
    @GetMapping("inner/getSkuInfo/{skuId}")
    public SkuInfo getSkuInfoById(@PathVariable("skuId") Long skuId) {
        return skuInfoService.getById(skuId);
    }

}

定义使用该api的函数,并使用@FeignClient 注明从哪个模块进行api的对接:

package com.atguigu.ssyx.client.product;

import com.atguigu.ssyx.model.product.Category;
import com.atguigu.ssyx.model.product.SkuInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

//与service\service-product\src\main\resources\application.yml中的application。name名称一致
@FeignClient(value = "service-product")
public interface ProductFeignClient {
    //作为\service\service-product中的\product\api\ProductInnerController.java
    //中函数的接口定义文件
    //注意要使用完整的restful风格的路径
    //用于远程调用(service-search远程调用service-product)
    @GetMapping("/api/product/inner/getCategory/{categoryId}")
    public Category getCategoryById(@PathVariable("categoryId") Long categoryId);

    @GetMapping("/api/product/inner/getSkuInfo/{skuId}")
    public SkuInfo getSkuInfoById(@PathVariable("skuId") Long skuId);

}

定义feign对象 productFeignClient,调用api对应的函数接口:

SkuInfo skuInfo = productFeignClient.getSkuInfoById(skuId);
// ...
Category category = productFeignClient.getCategoryById(skuInfo.getCategoryId());
package com.atguigu.ssyx.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.ssyx.client.product.ProductFeignClient;
import com.atguigu.ssyx.enums.SkuType;
import com.atguigu.ssyx.model.product.Category;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.model.search.SkuEs;
import com.atguigu.ssyx.search.repository.SkuRepository;
import com.atguigu.ssyx.search.service.SkuService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import static jdk.nashorn.internal.runtime.regexp.joni.Config.log;

@Slf4j
@Service
public class SkuServiceImpl implements SkuService {

    //通过service-product-client中的feign远程调用service-product中的方法
    @Autowired
    private ProductFeignClient productFeignClient;

    //写入ES,即ElasticsearchRepository的接口类
    @Autowired
    private SkuRepository skuRepository;

    @Override
    public void upperSku(Long skuId) {
        SkuEs skuEs = new SkuEs();

        //为skuEs一个一个属性赋值
        SkuInfo skuInfo = productFeignClient.getSkuInfoById(skuId);
        if (skuInfo == null) {
            return;
        }
        Category category = productFeignClient.getCategoryById(skuInfo.getCategoryId());
        if (category != null) {
            skuEs.setCategoryId(category.getId());
            skuEs.setCategoryName(category.getName());
        }
        skuEs.setId(skuInfo.getId());
        skuEs.setKeyword(skuInfo.getSkuName() + "," + skuEs.getCategoryName()); //keyword不分词查询
        skuEs.setWareId(skuInfo.getWareId());
        skuEs.setIsNewPerson(skuInfo.getIsNewPerson());
        skuEs.setImgUrl(skuInfo.getImgUrl());
        //分词查询的字段数据类型必须是 FieldType.Text
        skuEs.setTitle(skuInfo.getSkuName());
        if (skuInfo.getSkuType() == SkuType.COMMON.getCode()) //普通还是秒杀
        {
            skuEs.setSkuType(0);
            skuEs.setPrice(skuInfo.getPrice().doubleValue());
            skuEs.setStock(skuInfo.getStock()); //仓库数量
            skuEs.setSale(skuInfo.getSale());
            skuEs.setPerLimit(skuInfo.getPerLimit()); //每人限购数量
        } else {
            //TODO 待完善-秒杀商品
        }

        //使用 ElasticsearchRepository 提供的方法保存ES信息
        SkuEs save = skuRepository.save(skuEs);
        System.out.print("upperSku:" + JSON.toJSONString(save));

    }

    @Override
    public void lowerSku(Long skuId) {
        //使用 ElasticsearchRepository 提供的方法删除ES信息
        skuRepository.deleteById(skuId);
    }
}

5. 效果验证

实验中的表的名称是skues:

对应

public interface SkuRepository extends ElasticsearchRepository<SkuEs, Long> {
}

对应代码中的名称: 

当服务中的  public void upperSku(Long skuId) 被调用时,即 skuRepository.save(skuEs);被调用时,对应的数据就会写入 es中,可在 kibana中进行查看:

使用下面命令进行查看:

GET /_cat/indices?v

查看更多数据,(数据结构取决于自己数据库和代码中对数据的定义):

使用如下命令,其中skues为你的index (表名)名称

POST /skues/_search
{
    "query":{
        "match_all":{}
    }
}

结果有了:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

win32汇编环境,对话框程序中组合框的应用举例

;运行效果 ;win32汇编环境,对话框程序中组合框的应用举例 ;比如在对话框中生成组合框&#xff0c;增加子项&#xff0c;删除某项&#xff0c;取得指定项内容等 ;直接抄进RadAsm可编译运行。重点部分加备注。 ;以下是ASM文件 ;>>>>>>>>>>>>…

occ的开发框架

occ的开发框架 1.Introduction This manual explains how to use the Open CASCADE Application Framework (OCAF). It provides basic documentation on using OCAF. 2.Purpose of OCAF OCAF (the Open CASCADE Application Framework) is an easy-to-use platform for ra…

Linux检查磁盘占用情况

1.检查使用情况 df -h发现是/dev/vda1占用很高 2.查看/dev/vda1文件夹 cd /dev/vda1发现不是文件夹 3.继续查看使用情况 df -h *4.原因可能是文件已经删除但是进程还在&#xff0c;没有释放空间 5.查看删除操作的进程 lsof -n | grep deleted6.杀死进程 kill -9 PID

C# (图文教学)在C#的编译工具Visual Studio中使用SQLServer并对数据库中的表进行简单的增删改查--14

目录 一.安装SQLServer 二.在SQLServer中创建一个数据库 1.打开SQL Server Manager Studio(SSMS)连接服务器 2.创建新的数据库 3.创建表 三.Visual Studio 配置 1.创建一个简单的VS项目(本文创建为一个简单的控制台项目) 2.添加数据库连接 四.简单连通代码示例 简单连…

Flutter插件制作、本地/远程依赖及缓存机制深入剖析(原创-附源码)

Flutter插件在开发Flutter项目的过程中扮演着重要的角色&#xff0c;我们从 ​​​​​​https://pub.dev 上下载添加到项目中的第三方库都是以包或者插件的形式引入到代码中的&#xff0c;这些第三方工具极大的提高了开发效率。 深入的了解插件的制作、发布、工作原理和缓存机…

自动化办公|xlwings简介

xlwings 是一个开源的 Python 库&#xff0c;旨在实现 Python 与 Microsoft Excel 的无缝集成。它允许用户使用 Python 脚本自动化 Excel 操作&#xff0c;读取和写入数据&#xff0c;执行宏&#xff0c;甚至调用 VBA 脚本。这使得数据分析、报告生成和其他与 Excel 相关的任务…

Dify应用-工作流

目录 DIFY 工作流参考 DIFY 工作流 2025-1-15 老规矩感谢参考文章的作者,避免走弯路。 2025-1-15 方便容易上手 在dify的一个桌面上,添加多个节点来完成一个任务。 每个工作流必须有一个开始和结束节点。 节点之间用线连接即可。 每个节点可以有输入和输出 输出类型有,字符串,…

《C++11》并发库:简介与应用

在C11之前&#xff0c;C并没有提供原生的并发支持。开发者通常需要依赖于操作系统的API&#xff08;如Windows的CreateThread或POSIX的pthread_create&#xff09;或者第三方库&#xff08;如Boost.Thread&#xff09;来创建和管理线程。这些方式存在以下几个问题&#xff1a; …

建筑综合布线可视化管理

随着数字化转型的加速&#xff0c;越来越多的业务应用依赖网络来实现&#xff0c;综合布线系统作为网络基础设施&#xff0c;加强对综合布线系统的管理维护是业务安全稳定运行的重要保障。传统的表格CAD图纸的综合布线管理模式&#xff0c;易造成综合布线系统线缆混乱、随意变更…

ESXi 切换硬盘直通后无法恢复的解决办法

起因&#xff1a;近日&#xff0c;准备了一块SATA固态硬盘&#xff0c;计划对现有的ESXI虚拟机上新增扩容。因为只增加一块固态&#xff0c;也不打算做raid&#xff0c;就打算把它当作单独的存储来用。在网上搜了一些方法&#xff0c;脑子一热&#xff0c;通过ESXI控制台程序&a…

计算机网络 (43)万维网WWW

前言 万维网&#xff08;World Wide Web&#xff0c;WWW&#xff09;是Internet上集文本、声音、动画、视频等多种媒体信息于一身的信息服务系统。 一、基本概念与组成 定义&#xff1a;万维网是一个分布式、联机式的信息存储空间&#xff0c;通过超文本链接的方式将分散的信息…

汽车免拆诊断案例 | 2007 款法拉利 599 GTB 车发动机故障灯异常点亮

故障现象  一辆2007款法拉利599 GTB车&#xff0c;搭载6.0 L V12自然吸气发动机&#xff08;图1&#xff09;&#xff0c;累计行驶里程约为6万km。该车因发动机故障灯异常点亮进厂检修。 图1 发动机的布置 故障诊断 接车后试车&#xff0c;发动机怠速轻微抖动&#xff0c;…

ChatGPT正在朝着全面个人助手迈出重要一步,推出了一个名为“Tasks”的新功能

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

微软震撼发布:Phi-4语言模型登陆Hugging Face

近日&#xff0c;微软公司在Hugging Face平台上正式发布了其最新的语言模型Phi-4&#xff0c;这一发布标志着人工智能技术的又一重要进步。Phi-4模型以其140亿参数的高效配置&#xff0c;在复杂推理任务中表现出色&#xff0c;特别是在数学领域&#xff0c;更是展现出了卓越的能…

RTC(Real_Time Clock)

RTC概述&#xff1a; RTC&#xff08;实时时钟&#xff0c;Real-Time Clock&#xff09;是一种用于跟踪当前日期和时间的计时设备。RTC可以是独立的芯片&#xff0c;也可以是集成在微控制器或处理器中的一个模块。RTC是现代电子设备中不可或缺的一部分&#xff0c;为各种应用提…

[leetcode]链表基础回顾

一.创建带头节点的链表 #include <iostream> #include <string> #include <algorithm> using namespace std; typedef struct Node { char ch; Node* next; }*LinkList,ListNode; void printLinkList(LinkList& head) { LinkList p head…

rclone,云存储备份和迁移的瑞士军刀,千字常文解析,附下载链接和安装操作步骤...

一、什么是rclone&#xff1f; rclone是一个命令行程序&#xff0c;全称&#xff1a;rsync for cloud storage。是用于将文件和目录同步到云存储提供商的工具。因其支持多种云存储服务的备份&#xff0c;如Google Drive、Amazon S3、Dropbox、Backblaze B2、One Drive、Swift、…

JAVA:利用 RabbitMQ 死信队列实现支付超时场景的技术指南

1、简述 在支付系统中&#xff0c;订单支付的超时自动撤销是一个非常常见的业务场景。通常用户未在规定时间内完成支付&#xff0c;系统会自动取消订单&#xff0c;释放相应的资源。本文将通过利用 RabbitMQ 的 死信队列&#xff08;Dead Letter Queue, DLQ&#xff09;来实现…

favor的本质

英文单词 favor&#xff0c;通常指一个的“喜好或偏爱”&#xff1a; favor n.赞成&#xff1b;喜爱&#xff0c;宠爱&#xff0c;好感&#xff0c;赞同&#xff1b;偏袒&#xff0c;偏爱&#xff1b;善行&#xff0c;恩惠 v.赞同&#xff1b;喜爱&#xff0c;偏爱&#xff1b…

[青基解读一] 2025年国家自然科学基金---指南解读

指南解读 1 需要2个高级专业技术职称推荐&#xff08;2个正教授&#xff09; 2 国自然、国社科只能申请一个 3 资助类别 亚类说明 附注说明 自由探索or目标导向 4 申请代码到二级 申请代码、研究方向、关键词 主要参与者不写学生仅写人数 主要参与者 在线采集、填写简历、生成…