10.RabbitMQ集群

十、集群与高可用

RabbitMQ 的集群分两种模式,一种是默认集群模式,一种是镜像集群模式;

在RabbitMQ集群中所有的节点(一个节点就是一个RabbitMQ的broker服务器) 被归为两类:一类是磁盘节点,一类是内存节点;

磁盘节点会把集群的所有信息(比如交换机、绑定、队列等信息)持久化到磁盘中,而内存节点只会将这些信息保存到内存中,如果该节点宕机或重启,内存节点的数据会全部丢失,而磁盘节点的数据不会丢失;

1、默认集群模式

默认集群模式也叫 普通集群模式、或者 内置集群模式;

10.1.1、默认集群简介

(1)、元数据

队列元数据:队列名称和属性(是否可持久化,是否自动删除)

交换器元数据:交换器名称、类型和属性

绑定元数据:交换器和队列的绑定列表

vhost元数据:vhost内的相关属性,如安全属性等;

当用户访问其中任何一个RabbitMQ节点时,查询到的queue/user/exchange/vhost等信息都是相同的;

(2)、数据同步特点

RabbitMQ默认集群模式只会把交换机、队列、虚拟主机等元数据信息在各个节点同步,而具体队列中的消息内容不会在各个节点中同步,队列的具体信息数据只在队列的拥有者节点保存,其他节点只知道队列的元数据和指向该节点的指针,所以其他节点接收到不属于该节点队列的消息时会将该消息传递给该队列的拥有者节点上;

集群不复制队列内容和状态到所有节点原因

1)节省存储空间;

2)提升性能;

如果消息需要复制到集群中每个节点,网络开销不可避免,持久化消息还需要写磁盘,占用磁盘空间。

在这里插入图片描述

(3)、数据访问过程

如果有一个消息生产者或者消息消费者通过amqp-client的客户端连接到节点1进行消息的发送或接收,那么此时集群中的消息收发只与节点1相关,这个没有任何问题;

如果消息生产者所连接的是节点2或者节点3,此时队列1的完整数据不在该两个节点上,那么在发送消息过程中这两个节点主要起了一个路由转发作用,根据这两个节点上的元数据(也就是指向queue的owner node的指针)转发至节点1上,最终发送的消息还是会存储至节点1的队列1上;

同样,如果消息消费者所连接的节点2或者节点3,那这两个节点也会作为路由节点起到转发作用,将会从节点1的队列1中获取消息进行消费;

10.1.2、安装

1、安装三台RabbitMQ机器

安装过程参考前面

注意:这里先安装一台MQ,然后克隆两台就可以了

2、设置IP地址

启动并设置三台机器的IP

修改配置文件方式

图形界面方式

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3、修改主机名
sudo hostnamectl set-hostname rabbit11
4、修改/etc/hosts 文件

首先需要配置一下hosts文件,因为RabbitMQ集群节点名称是读取hosts文件得到的;

注意三台机机器都需要配置

vim /etc/hosts
192.168.1.11 rabbit11
192.168.1.12 rabbit12
192.168.1.13 rabbit13

在这里插入图片描述

5、重启网络

三台机器均重启网络,使节点名生效

sudo systemctl restart NetworkManager

低版本CentOS使用如下命令

systemctl restart network
6、重新连接xshell

重启后三台机器的xshell均退出,然后再重新连接,这样才能刷新主机的名字

7、关闭防火墙

三台机器均需关闭

systemctl stop firewalld  ##关闭防火墙
systemctl disable firewalld  ##开机不启动防火墙
systemctl status firewalld	##查看防火墙状态

在这里插入图片描述

8、修改.erlang.cookie文件

三台机器 .erlang.cookie文件保持一致

由于是clone出的三台机器,所以肯定是一样的

  • 如果使用解压缩方式安装的RabbitMQ,那么该文件会在 用户名目录下 , 也就是 {用户名}目录下,也就是 用户名目录下,也就是{用户名}/.erlang.cookie;

    在这里插入图片描述

  • 如果使用rpm安装包方式进行安装,那么这个文件会在/var/lib/rabbitmq目录下;

注意 .erlang.cookie的权限为400,目前已经是400

9、启动MQ

分别启动三台机器上的rabbitmq

rabbitmq-server -detached
10、查看集群状态
  • 查看rabbitmq状态

    rabbitmqctl status
    

    在这里插入图片描述

  • 查看集群状态

    rabbitmqctl cluster_status
    

    在这里插入图片描述

11、构建集群
  • 加入节点1

    在rabbitmq12机器上执行命令,让12的rabbitmq加入集群

    注意:一定要先停止节点,将节点重置之后才能加入集群,否则数据同步会出现混乱

    ## 先停止rabbitmq
    rabbitmqctl stop_app
    ## 重置rabbitmq
    rabbitmqctl reset
    ## 节点加入集群:rabbit@rabbit11是主节点的节点名,在集群状态中可以查看到节点名称
    rabbitmqctl join_cluster rabbit@rabbit11 --ram
    ## 启动节点
    rabbitmqctl start_app
    

    –ram 参数表示让rabbitmq12成为一个内存节点,如果不带参数默认为disk磁盘节点;

    在这里插入图片描述

  • 添加节点2

    在rabbit13节点上也执行同样的命令,使rabbit13节点也加入到集群中

    ## 先停止rabbitmq
    rabbitmqctl stop_app
    ## 重置rabbitmq
    rabbitmqctl reset
    ## 节点加入集群:rabbit@rabbit11是主节点的节点名,在集群状态中可以查看到节点名称
    rabbitmqctl join_cluster rabbit@rabbit11 --ram
    ## 启动节点
    rabbitmqctl start_app
    

    当然也可以让rabbit13作为一个磁盘节点

12、添加用户和权限

操作一个节点,添加用户和权限等

#列出用户
rabbitmqctl list_users
# 添加用户
rabbitmqctl add_user admin 123456
#查看权限
rabbitmqctl list_permissions
#设置权限
rabbitmqctl set_permissions admin ".*" ".*" ".*"
#设置角色
rabbitmqctl set_user_tags admin administrator


在这里插入图片描述

13、启动web控制台

启动web控制台插件

注意:三台机器都要启动,因为插件不属于元数据,因此需要分别启动

#进入插件目录
cd /usr/local/rabbitmq_server-4.0.7/plugins/
#启动web端插件
rabbitmq-plugins enable rabbitmq_management 

在这里插入图片描述

14、创建虚拟主机

使用web浏览器添加一个虚拟主机:longdidi

在这里插入图片描述

15、再次查看集群状态

当执行完操作以后在浏览器访问web管控台来看看效果;

随便在哪个节点打开web管控台都能看到集群环境各节点的信息;

也可以使用"rabbitmqctl cluster_status"查看集群状态;

在这里插入图片描述

以上就是RabbitMQ默认集群模式(普通集群模式)的搭建;

16、验证集群
  1. 创建队列

在这里插入图片描述

  1. 创建交换机

    在这里插入图片描述

  2. 绑定交换机与队列

    • 进入交换机

      在这里插入图片描述

    • 绑定交换机与队列

      在这里插入图片描述

  3. 发布消息

    在这里插入图片描述

  4. 查看消息

    在任意节点查看消息

    在这里插入图片描述

  5. 停止主节点rabbit@rabbit11节点

    在这里插入图片描述

  6. 再在其它节点查看消息

    在这里插入图片描述

17、删除节点

在这里插入图片描述

10.1.3、节点原理

RabbitMQ底层是通过Erlang架构来实现的,所以rabbitmqctl会启动Erlang节点,并基于Erlang节点来使用Erlang系统连接RabbitMQ节点,在连接过程中需要正确的Erlang Cookie和节点名称,Erlang节点通过交换Erlang Cookie以获得认证;

2、镜像集群模式

10.2.1、镜像模式简介

镜像模式是基于默认集群模式加上一定的配置得来的;

在默认模式下的RabbitMQ集群,它会把所有节点的交换机、绑定、队列的元数据进行复制确保所有节点都有一份相同的元数据信息

但是队列数据分为两种

  • 一种是队列的元数据信息(比如队列的最大容量,队列的名称等配置信息)
  • 一种是队列里面的消息

镜像模式则是把所有的队列数据完全同步,包括元数据信息和消息数据信息,当然这对性能肯定会有一定影响,当对数据可靠性要求较高时,可以使用镜像模式;

10.2.2、镜像模式配置

实现镜像模式也非常简单,它是在普通集群模式基础之上搭建而成的

3.X版本设置

镜像队列配置语法:

rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]
  • rabbitmqctl set_policy:固定写法

  • -p Vhost: 可选参数,设置虚拟主机的名字(针对指定vhost下的queue进行设置)

  • Name: 设置策略的名称(自己取个名字就可以)

  • Pattern: queue的匹配模式(正则表达式);^表示所有的队列都是镜像队列

  • Definition:镜像定义(json格式),包括三个部分ha-mode、ha-params、ha-sync-mode

    • ha-mode

      指明镜像队列的模式,有效值为 all/exactly/nodes

      all:表示在集群中所有的节点上进行镜像
      exactly:表示在指定个数的节点上进行镜像,节点的个数由ha-params指定
      nodes:表示在指定的节点上进行镜像,节点名称通过ha-params指定
      
    • ha-params

      ha-mode模式需要用到的参数

    • ha-sync-mode

      队列中消息的同步方式,有效值为automatic(自动向master同步数据)和manual(手动向master同步数据)

  • priority:可选参数,指的是policy策略的优先级;

在默认集群模式的基础上执行上面这个命令就可以把一个默认的集群模式变成镜像集群模式

比如想配置所有名字开头为policy_的队列进行镜像,镜像数量为2,那么命令如下(在任意节点执行如下命令):

rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]

rabbitmqctl set_policy -p longdidi my_policy "^policy_"  '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'  
(1)、同步所有数据

所有节点、所有虚拟主机、所有队列 都进行镜像

如果要在所有节点所有队列上进行镜像则在任意节点执行如下命令

语法:rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]

rabbitmqctl set_policy my-all "^"  '{"ha-mode":"all"}'   
(2)、同步指定数据

针对某个虚拟主机进行镜像

语法:rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]

rabbitmqctl set_policy -p longdidi my-all "^" '{"ha-mode": "exactly", "ha-params": 2, "ha-sync-mode": "automatic"}'

在默认集群模式的基础上执行上面这个命令就可以把一个默认的集群模式变成镜像集群模式

4.X版本设置

在这里插入图片描述

3、SpringBoot集成集群

重点连接配置

spring:
  rabbitmq:
    # 连接单台rabbitmq 服务器的地址
    # host: 192.168.1.101
    # 连接单台rabbitmq 服务器的端口
    # port: 5672
    username: admin
    password: 123456
    virtual-host: longdidi
    publisher-confirm-type: correlated # 开启生产者的确认模式,设置关联模式
    publisher-returns: true #开启return模式
    # 开启消费者手动确认
    listener:
      simple:
        acknowledge-mode: manual
    addresses: 192.168.1.11:5672,192.168.1.12:5672,192.1.13:5672

测试模块:rabbitmq-10-cluster-01

引入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>

配置MQ

server:
  port: 8080

spring:
  application:
    name: cluster-learn01

  rabbitmq:
    # 连接单台rabbitmq 服务器的地址
    # host: 192.168.1.101
    # 连接单台rabbitmq 服务器的端口
    # port: 5672
    username: admin
    password: 123456
    virtual-host: longdidi
    publisher-confirm-type: correlated # 开启生产者的确认模式,设置关联模式
    publisher-returns: true #开启return模式
    # 开启消费者手动确认
    listener:
      simple:
        acknowledge-mode: manual
    addresses: 192.168.1.11:5672,192.168.1.12:5672,192.1.13:5672

  data:
    redis:
      host: 192.168.1.4
      port: 6379
      #password: 123456
      database: 0 # 0号数据库

定义MQ队列

package com.longdidi.config;

import com.longdidi.constants.RabbitMQConstant;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig {

    /**
     * 正常交换机
     * 使用durable()方法设置持久化
     *
     * @return
     */
    @Bean
    public DirectExchange normalExchange() {
        return ExchangeBuilder.directExchange(RabbitMQConstant.EXCHANGE_NAME).durable(true).build();
    }

    /**
     * 正常队列
     * durable()方法就是持久化
     *
     * @return
     */
    @Bean
    public Queue normalQueue() {
        return QueueBuilder.durable(RabbitMQConstant.QUEUE_NAME1)
                .build();
    }

    /**
     * 正常交换机和正常队列绑定
     *
     * @param normalExchange
     * @param normalQueue
     * @return
     */
    @Bean
    public Binding bindingNormal(DirectExchange normalExchange, Queue normalQueue) {
        return BindingBuilder.bind(normalQueue).to(normalExchange).with(RabbitMQConstant.ROUTING_NAME1);
    }

}

生产者

package com.longdidi.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.longdidi.constants.RabbitMQConstant;
import com.longdidi.vo.Orders;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;

@Service
@Slf4j
public class SendMessageService {
    @Resource
    private RabbitTemplate rabbitTemplate;

    //这个对象可以进行序列化和反序列化(json格式)
    @Resource
    private ObjectMapper objectMapper;

    /**
     * 构造方法执行后自动执行
     */
    @PostConstruct
    public void init() {
        //开启生产者的确定模式
        rabbitTemplate.setConfirmCallback(
                (correlationData, ack, cause) -> {
                    if (!ack) {
                        log.error("消息没有到达交换机,原因为:{}", cause);
                        //TODO 重发消息或者记录错误日志
                    }
                }
        );

        rabbitTemplate.setReturnsCallback(
                returnedMessage -> {
                    log.error("消息没有从交换机正确的投递(路由)到队列,原因为:{}", returnedMessage.getReplyText());
                    //TODO 记录错误日志,给程序员发短信或者或者邮件
                }
        );
    }

    public void sendMsg() throws JsonProcessingException {
        {
            //创建订单
            Orders orders1 = Orders.builder()
                    .orderId("order_12345")
                    .orderName("买的手机")
                    .orderMoney(new BigDecimal(2356))
                    .orderTime(new Date())
                    .build();
            //转成json
            String strOrders1 = objectMapper.writeValueAsString(orders1);
            MessageProperties messageProperties = new MessageProperties();
            //设置单条消息的持久化,默认就是持久化
            messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            Message message = MessageBuilder.withBody(strOrders1.getBytes())
                    .andProperties(messageProperties).build();
            rabbitTemplate.convertAndSend(RabbitMQConstant.EXCHANGE_NAME, RabbitMQConstant.ROUTING_NAME1, message);
        }
        {
            Orders orders2 = Orders.builder()
                    .orderId("order_12345")
                    .orderName("买的手机")
                    .orderMoney(new BigDecimal(2356))
                    .orderTime(new Date())
                    .build();
            String strOrders2 = objectMapper.writeValueAsString(orders2);
            MessageProperties messageProperties = new MessageProperties();
            //设置单条消息的持久化,默认就是持久化
            messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            Message message = MessageBuilder.withBody(strOrders2.getBytes())
                    .andProperties(messageProperties).build();
            rabbitTemplate.convertAndSend(RabbitMQConstant.EXCHANGE_NAME, RabbitMQConstant.ROUTING_NAME1, message);
        }

        log.info("消息发送完毕,发送时间为:{}", new Date());
    }
}

消费者

package com.longdidi.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.longdidi.constants.RabbitMQConstant;
import com.longdidi.vo.Orders;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;

import com.rabbitmq.client.Channel;

@Component
@Slf4j
public class ReceiveMessageService {
    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @RabbitListener(queues = {RabbitMQConstant.QUEUE_NAME1})
    public void receiveMsg(Message message, Channel channel) throws IOException {
        //获取消息的唯一标识
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        //使用objectmapper把字节数组反序列化成对象
        Orders orders = objectMapper.readValue(message.getBody(), Orders.class);

        try {
            log.info("接收到的消息为:{}", orders.toString());
            //如果不存在就在redis中存储
            Boolean setResult = stringRedisTemplate.opsForValue().setIfAbsent("idempotent:" + orders.getOrderId(), orders.getOrderId());
            if (setResult) {
                // TODO 向数据库插入订单等
                log.info("向数据库插入订单");
            }
            //手动确认
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("消息处理出现问题");
            try {
                channel.basicNack(deliveryTag, false, true);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
            throw new RuntimeException(e);
        }
    }
}

定义常量

package com.longdidi.constants;

public class RabbitMQConstant {
    // 正常交换机
    public static final String EXCHANGE_NAME = "exchange.idempotent.normal.1";

    // 队列
    public static final String QUEUE_NAME1 = "queue.idempotent.normal.1";

    // 路由key
    public static final String ROUTING_NAME1 = "key.idempotent.normal.1";
}

定义实体类

package com.longdidi.vo;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Orders implements Serializable {
    private String orderId;
    private String orderName;

    private BigDecimal orderMoney;

    private Date orderTime; //下单时间
}

发送消息

package com.longdidi;

import com.longdidi.service.SendMessageService;
import jakarta.annotation.Resource;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Rabbitmq10Cluster01Application implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(Rabbitmq10Cluster01Application.class, args);
    }

    @Resource
    private SendMessageService sendMessageService;

    /**
     * 程序一启动就会运行该方法
     *
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        sendMessageService.sendMsg();
    }
}

测试

在这里插入图片描述

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

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

相关文章

ajax之生成一个ajax的demo示例

目录 一. node.js和express ​二. 使用express创建后端服务 三. 创建前端 一. node.js和express ajax是前端在不刷新的情况下访问后端的技术&#xff0c;所以首先需要配置一个后端服务&#xff0c;可以使用node.js和express。 首先生成一个空项目&#xff0c;新建main目录…

unity学习64,第3个小游戏:一个2D跑酷游戏

目录 学习参考 素材资源导入 1 创建项目 1.1 创建1个2D项目 1.2 导入素材 2 背景图bg 2.0 bg素材 2.1 创建背景 2.2 修改素材&#xff0c;且修改摄像机等 2.2.1 修改导入的原始prefab素材 2.2.2 对应调整摄像机 2.2.3 弄好背景 2.3 背景相关脚本实现 2.3.1 错误…

PyTorch系列教程:编写高效模型训练流程

当使用PyTorch开发机器学习模型时&#xff0c;建立一个有效的训练循环是至关重要的。这个过程包括组织和执行对数据、参数和计算资源的操作序列。让我们深入了解关键组件&#xff0c;并演示如何构建一个精细的训练循环流程&#xff0c;有效地处理数据处理&#xff0c;向前和向后…

PX4中的DroneCAN的实现库Libuavcan及基础功能示例

简介 Libuavcan是一个用C编写的可移植的跨平台库&#xff0c;对C标准库的依赖小。它可以由几乎任何符合标准的C编译器编译&#xff0c;并且可以在几乎任何体系结构/OS上使用。 在 DroneCAN 中&#xff0c;Libuavcan 有一个 DSDL 编译器&#xff0c;将 DSDL 文件转换为 hpp 头…

计算机网络(1) 网络通信基础,协议介绍,通信框架

网络结构模式 C/S-----客户端和服务器 B/S -----浏览器服务器 MAC地址 每一个网卡都拥有独一无二的48位串行号&#xff0c;也即MAC地址&#xff0c;也叫做物理地址、硬件地址或者是局域网地址 MAC地址表示为12个16进制数 如00-16-EA-AE-3C-40 &#xff08;每一个数可以用四个…

PCA(主成分分析)核心原理

一、PCA&#xff08;主成分分析&#xff09;核心原理 即主成分分析技术&#xff0c;又称主分量分析技术&#xff0c;旨在利用降维的思想&#xff0c;把多指标转化为少数几个综合指标。在统计学中&#xff0c;主成分分析PCA是一种简化数据集的技术。它是一个线性变换。这个变换…

SpringBoot-模拟SSE对话交互

SpringBoot-模拟SSE对话交互 后端使用SSE进行会话&#xff0c;前端使用Html模拟大模型的问答交互->【前端】【后端】 1-学习目的 本项目代码仓库&#xff1a;https://gitee.com/enzoism/springboot_sse 1-核心知识点 1&#xff09;什么是SSE协议->客户端发起一次请求&am…

2025DNS二级域名分发PHP网站源码

安装教程 1.程序必须使用PHP8.1 2.将扩展ixed.8.1.lin放入/www/server/php/81/lib/php/extensions/no-debug-non-zts-20210902 3.打开宝塔→软件商店→PHP8.1→配置文件 4.放入&#xff1a;extensionixed.8.1.lin 5.重启PHP8.1 6.新建站点&#xff08;mysql5.6-5.7andPHP8.1&a…

Matlab实现车牌识别

车牌识别技术作为现代智能交通系统、安防监控以及诸多车辆管理应用场景中的关键环节&#xff0c;正发挥着日益重要的作用&#xff0c;它能够自动、快速且精准地从车辆图像或视频流中提取车牌信息&#xff0c;实现车辆身份的智能化识别。 技术原理 车牌识别主要依托于图像处理、…

C语言——链表

大神文献&#xff1a;https://blog.csdn.net/weixin_73588765/article/details/128356985 目录 一、链表概念 1. 什么是链表&#xff1f; 1.1 链表的构成 2. 链表和数组的区别 数组的特点&#xff1a; 链表的特点&#xff1a; 二者对比&#xff1a; 二…

国产化板卡设计原理图:2330-基于FMC接口的JFM7K325T PCIeX4 3U PXIe接口卡

基于FMC接口的JFM7K325T PCIeX4 3U PXIe接口卡 一、板卡概述 本板卡基于 FPGAJFM7K325T 芯片&#xff0c;pin_to_pin兼容FPGAXC7K410T-2FFG900 &#xff0c;支持PCIeX8、64bit DDR3容量2GByte&#xff0c;HPC的FMC连接器&#xff0c;板卡支持PXIE标准协议&#xff0c;其中XJ3…

【网络】HTTP协议、HTTPS协议

HTTP与HTTPS HTTP协议概述 HTTP(超文本传输协议):工作在OSI顶层应用层,用于客户端(浏览器)与服务器之间的通信,B/S模式 无状态:每次请求独立,服务器不保存客户端状态(通过Cookie/Session扩展状态管理)。基于TCP:默认端口80(HTTP)、443(HTTPS),保证可靠传输。请…

设计AI芯片架构的入门 研究生入行数字芯片设计、验证的项目 opentitan

前言 这几年芯片设计行业在国内像坐过山车。时而高亢&#xff0c;时而低潮。最近又因为AI的热潮开始high起来。到底芯片行业的规律是如何&#xff1f; 我谈谈自己观点&#xff1a;芯片设计是“劳动密集型”行业。 “EDA和工具高度标准化和代工厂的工艺标准化之后&#xff0c;芯…

K8S学习之基础十七:k8s的蓝绿部署

蓝绿部署概述 ​ 蓝绿部署中&#xff0c;一共有两套系统&#xff0c;一套是正在提供服务的系统&#xff0c;一套是准备发布的系统。两套系统都是功能完善、正在运行的系统&#xff0c;只是版本和对外服务情况不同。 ​ 开发新版本&#xff0c;要用新版本替换线上的旧版本&…

STM32之I2C硬件外设

注意&#xff1a;硬件I2C的引脚是固定的 SDA和SCL都是复用到外部引脚。 SDA发送时数据寄存器的数据在数据移位寄存器空闲的状态下进入数据移位寄存器&#xff0c;此时会置状态寄存器的TXE为1&#xff0c;表示发送寄存器为空&#xff0c;然后往数据控制寄存器中一位一位的移送数…

Linux基础--用户管理

目录 查看用户 使用命令: id 创建用户 使用命令: useradd ​编辑 为用户设置密码 使用命令: passwd ​编辑 删除用户 使用命令: userdel 创建用户组 使用命令: groupadd 删除用户组 使用命令: groupdel 用户设置 使用命令: usermod 将用户从组中去除 使用…

VSTO(C#)Excel开发3:Range对象 处理列宽和行高

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

本地YARN集群部署

请先完成HDFS的前置部署&#xff0c;部署方式可查看:本地部署HDFS集群https://blog.csdn.net/m0_73641796/article/details/145998092?spm1001.2014.3001.5502 部署说明 组件配置文件启动进程备注Hadoop HDFS需修改 需启动: NameNode作为主节点 DataNode作为从节点 Secondary…

每日一练之移除链表元素

题目&#xff1a; 画图解析&#xff1a; 方法&#xff1a;双指针 解答代码&#xff08;注&#xff1a;解答代码带解析&#xff09;&#xff1a; //题目给的结构体 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* }…

黑金风格人像静物户外旅拍Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 针对人像、静物以及户外旅拍照片&#xff0c;运用 Lightroom 软件进行风格化调色工作。旨在通过软件中的多种工具&#xff0c;如基本参数调整、HSL&#xff08;色相、饱和度、明亮度&#xff09;调整、曲线工具等改变照片原本的色彩、明度、对比度等属性&#xff0c;将…