RabbitMQ(一) - 基本结构、SpringBoot整合RabbitMQ、工作队列、发布订阅、直接、主题交换机模式

RabbitMQ结构

  • Publisher : 生产者

  • Queue: 存储消息的容器队列;

  • Consumer:消费者

  • Connection:消费者与消息服务的TCP连接

  • Channel:信道,是TCP里面的虚拟连接。例如:电缆相当于TCP,信道是一条独立光纤束,一条TCP连接上创建多少条信道是没有限制的。TCP一旦打开,就会出AMQP信道。无论是发布消息,接收消息,订阅队列,这些动作都是通过信道完成的。
    Broker: 一台消息服务就是一个Broker;

  • Exchange:交换机、负责接收生产者的消息,转发到队列中、交换机和队列通过路由键绑定、可以理解为每个队列都有自己的名称;
    在这里插入图片描述

SpringBoot整合RabbitMQ

  • Queue
    • 消息存放于队列中, 若是RabbitMQ挂了,则消息会丢失,因此要开启持久化, 将durable设置为true,
    • 若是没有消费者消费该队列,则该队列会自动删除, 因此需要将autoDelete参数设置为false;
    public Queue(String name) {//  队列名称, 是否持久化,是否独占, 是否自动删除this(name, true, false, false);}
  • @RabbitListener
@RabbitListener(bindings=@QueueBinding(value= @Queue(value="${mq.config.queue.info}",autoDelete="true"),exchange=@Exchange(value="${mq.config.exchange}",type=ExchangeTypes.DIRECT),key="${mq.config.queue.info.routing.key}"))

用来标记消费者;exchange表示交换器信息、类型;bindings表示监听器要绑定的队列、以及队列信息;
key:代表交换机和队列通过key绑定的;

  • AmqpTemplate / RabbitTempldate:
    生产者通过依赖此工具类发送消息;

先安装RabbitMQ,创建SpringBoot项目,修改配置

# 应用名称
spring.application.name=boolfilter# 应用服务 WEB 访问端口
server.port=8080spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

入门级别程序

发送hello world程序;
在这里插入图片描述
生产者:

public class Tut1Sender {@Autowiredprivate RabbitTemplate template;@Autowiredprivate Queue queue;@Scheduled(fixedDelay = 1000, initialDelay = 500)public void send() {String message = "Hello World!";this.template.convertAndSend(queue.getName(), message);System.out.println(" [x] Sent '" + message + "'");}
}

消费者:

@RabbitListener(queues = "hello")
public class Tut1Receiver {@RabbitHandlerpublic void receive(String in) {System.out.println(" [x] Received '" + in + "'");}
}

将生产者、消费者注入容器;

@Configuration
@EnableScheduling
public class Tut1Config {@Beanpublic Queue hello() {return new Queue("hello");}@Beanpublic Tut1Receiver receiver() {return new Tut1Receiver();}@Beanpublic Tut1Sender sender() {return new Tut1Sender();}
}

运行结果:

[x] Sent ‘Hello World!’
[x] Received ‘Hello World!’
[x] Sent ‘Hello World!’
[x] Received ‘Hello World!’
[x] Sent ‘Hello World!’

工作队列

主要思想是避免 立即执行资源密集型任务,必须等待它要完成。相反,我们将任务安排在以后完成。我们将任务封装为消息并将其发送到队列。正在运行的工作进程 在后台将弹出任务并最终执行工作
在这里插入图片描述
生产者:

public class Tut2Sender {@Autowiredprivate RabbitTemplate template;@Autowiredprivate Queue queue;AtomicInteger dots = new AtomicInteger(0);AtomicInteger count = new AtomicInteger(0);@Scheduled(fixedDelay = 1000, initialDelay = 500)public void send() {`在这里插入代码片`StringBuilder builder = new StringBuilder("Hello");if (dots.incrementAndGet() == 4) {dots.set(1);}for (int i = 0; i < dots.get(); i++) {builder.append('.');}builder.append(count.incrementAndGet());String message = builder.toString();template.convertAndSend(queue.getName(), message);System.out.println(" [x] Sent '" + message + "'");}}

消费者:

@RabbitListener(queues = "hello")
public class Tut2Receiver {private final int instance;public Tut2Receiver(int i) {this.instance = i;}@RabbitHandlerpublic void receive(String in) throws InterruptedException {StopWatch watch = new StopWatch();watch.start();System.out.println("instance " + this.instance +" [x] Received '" + in + "'");doWork(in);watch.stop();System.out.println("instance " + this.instance +" [x] Done in " + watch.getTotalTimeSeconds() + "s");}private void doWork(String in) throws InterruptedException {for (char ch : in.toCharArray()) {if (ch == '.') {Thread.sleep(1000);}}}
}

队列、生产者、消费者注入容器:

@Configuration
public class Tut2Config {@Beanpublic Queue hello() {return new Queue("hello");}private static class ReceiverConfig {@Beanpublic Tut2Receiver receiver1() {return new Tut2Receiver(1);}@Beanpublic Tut2Receiver receiver2() {return new Tut2Receiver(2);}}@Beanpublic Tut2Sender sender() {return new Tut2Sender();}
}

运行结果:

[x] Sent ‘Hello.1’
instance 1 [x] Received ‘Hello.1’
[x] Sent ‘Hello…2’
instance 2 [x] Received ‘Hello…2’
instance 1 [x] Done in 1.0062309s
[x] Sent ‘Hello…3’
instance 1 [x] Received ‘Hello…3’
instance 2 [x] Done in 2.0085791s
[x] Sent ‘Hello.4’
instance 2 [x] Received ‘Hello.4’

消息确认

  • SpringBoot整合RabbitMQ代码中,若消费者消费出现异常,则会重新进入队列, 一般生产环境中,是要有重试机制的;
  • 若是要关闭重试机制、则设置defaultRequeueRejected=false, 或者抛出AmqpRejectAndDontRequeueException异常,这样框架会帮我们自动提交确认channel.basicAck()
  • 重试机制也会存在问题、若是消费者服务关闭、则消息会不断重新入队、导致RabbitMQ内存最终爆满宕机;
  • 消息的ACK确认机制默认是打开的;如果忘记了ACK,后果很严重,当Consumer退出时,消息会一直重新分发,然后RabbitMq会占用越来越多的内存,由于RabbitMq会长时间运行,出现“内存泄露”是致命的

异常处理方案:

  • 使用try-catch捕捉
  • 使用重试机制、超过一定次数、则丢弃消息或放入死信队列;

spring.rabbitmq.listener.retry.max-attempts=5 //重试超过5次,消息丢弃;

公平调度与循环调度

  • 默认情况下,RabbitMQ 会将每条消息发送给下一个消费者。平均而言,每个消费者将获得相同数量的 消息。这种分发消息的方式称为轮询。 在这种模式下,调度不一定完全按照我们想要的方式工作。 若是存在两台机器,一台性能好、一台性能差, 而RabbitMQ对此一无所知,仍然会调度 消息均匀。发生这种情况是因为 RabbitMQ 只是在消息时调度消息 进入队列。它不看未确认的数量 面向消费者的消息。它只是盲目地发送每 n 条消息 给第 n 个消费者,这就导致了一台机器特别忙碌、一台机器空闲;

  • “公平调度”是Spring AMQP的默认配置。Consumer可以向服务器声明一个prefetchCount, 表示轮到自己时、自己可处理多少消息;这样RabbitMQ转发消息给消费者时、会先看Consumer正在处理的消息数量是否达到了prefetchCount, 若已达到该值,则发给其他的Consumer;

发布/订阅

在这里插入图片描述
特点:一条消息同时会被所有消费者消息;X是交换机(Exchange);交换机和队列进行绑定(Binding)
交换机负责接收生产者发送的消息,再转发消息到队列中;实现了生产者与队列的解耦;

RabbitMQ 中消息传递模型的核心思想是生产者 从不将任何消息直接发送到队列

示例1 : 广播匿名队列

发送者:

public class Tut3Sender {@Autowiredprivate RabbitTemplate template;@Autowiredprivate FanoutExchange fanout;AtomicInteger dots = new AtomicInteger(0);AtomicInteger count = new AtomicInteger(0);@Scheduled(fixedDelay = 1000, initialDelay = 500)public void send() {StringBuilder builder = new StringBuilder("Hello");if (dots.getAndIncrement() == 3) {dots.set(1);}for (int i = 0; i < dots.get(); i++) {builder.append('.');}builder.append(count.incrementAndGet());String message = builder.toString();template.convertAndSend(fanout.getName(), "", message);System.out.println(" [x] Sent '" + message + "'");}}

消费者:

public class Tut3Receiver {@RabbitListener(queues = "#{autoDeleteQueue1.name}")public void receive1(String in) throws InterruptedException {receive(in, 1);}@RabbitListener(queues = "#{autoDeleteQueue2.name}")public void receive2(String in) throws InterruptedException {receive(in, 2);}public void receive(String in, int receiver) throws InterruptedException {StopWatch watch = new StopWatch();watch.start();System.out.println("instance " + receiver + " [x] Received '" + in + "'");doWork(in);watch.stop();System.out.println("instance " + receiver + " [x] Done in "+ watch.getTotalTimeSeconds() + "s");}private void doWork(String in) throws InterruptedException {for (char ch : in.toCharArray()) {if (ch == '.') {Thread.sleep(1000);}}}}

交换机、匿名队列、绑定,生产者、消费者注入容器;

public class Tut3Config {@Beanpublic FanoutExchange fanout() {return new FanoutExchange("tut.fanout");}private static class ReceiverConfig {@Beanpublic Queue autoDeleteQueue1() {return new AnonymousQueue();}@Beanpublic Queue autoDeleteQueue2() {return new AnonymousQueue();}@Beanpublic Binding binding1(FanoutExchange fanout,Queue autoDeleteQueue1) {return BindingBuilder.bind(autoDeleteQueue1).to(fanout);}@Beanpublic Binding binding2(FanoutExchange fanout,Queue autoDeleteQueue2) {return BindingBuilder.bind(autoDeleteQueue2).to(fanout);}@Beanpublic Tut3Receiver receiver() {return new Tut3Receiver();}}@Beanpublic Tut3Sender sender() {return new Tut3Sender();}
}

运行结果:

instance 1 [x] Received 'Hello.1'
instance 2 [x] Received 'Hello.1'
instance 2 [x] Done in 1.0057994s
instance 1 [x] Done in 1.0058073s
....

模拟Spring容器发布ContextRefreshedEvent事件

通常情况下,业务开发中,经常会监听该事件做扩展,例如初始化数据, 打印日志等等;
生产者:

public class AppContextSender {@AutowiredRabbitTemplate rabbitTemplate;@Scheduled(fixedDelay = 1000, initialDelay = 500)public void publishContextRefreshEvent() {rabbitTemplate.convertAndSend("contextRefreshedExchange", "", "publish refreshed event");}
}

消费者:

@RabbitListener(queues = {"initQueue"})
public class InitContextRefreshedConsumer {@RabbitHandlerpublic void consum(String in) {System.out.println("init :"+in);}
}@RabbitListener(queues = "logQueue")
public class LogContextRefreshedConsumer {@RabbitHandlerpublic void consum(String in) {System.out.println("log : "+in);}
}

交换机、队列、绑定、生产者、消费者注入容器:

@Configuration
public class ContextRefreshedConfig {@Beanpublic FanoutExchange contextRefreshedExchange(){return new FanoutExchange("contextRefreshedExchange");}@Beanpublic AppContextSender appContextSender() {return new AppContextSender();}public static class ConsumerConfig {@Beanpublic Queue initQueue() {return new Queue("initQueue");}@Beanpublic Queue logQueue() {return new Queue("logQueue");}@Beanpublic Binding initBinding(Queue initQueue, FanoutExchange contextRefreshedExchange) {return BindingBuilder.bind(initQueue).to(contextRefreshedExchange);}@Beanpublic Binding logBinding(Queue logQueue, FanoutExchange contextRefreshedExchange) {return BindingBuilder.bind(logQueue).to(contextRefreshedExchange);}@Beanpublic InitContextRefreshedConsumer initContextRefreshedConsumer() {return new InitContextRefreshedConsumer();}@Beanpublic LogContextRefreshedConsumer logContextRefreshedConsumer() {return new LogContextRefreshedConsumer();}}}

log : publish refreshed event
init :publish refreshed event
log : publish refreshed event
init :publish refreshed event

Direct直接模式

  • 交换器绑定多个队列,每个绑定关系有自己的路由键;
  • 之前业务开发中、有一个交换机、绑定了两个队列,一个队列用来发送邮件,一个队列用来发送短信, 像广播模式下,如果只想发邮件,则没法t做到,使用direct模式和工作模式则可以做到, 最后使用了direct

在这里插入图片描述
生产者:

public class BaseServiceSender {@Autowiredprivate RabbitTemplate template;@Autowiredprivate DirectExchange messageExchange;AtomicInteger index = new AtomicInteger(0);AtomicInteger count = new AtomicInteger(0);private final String[] keys = {"sms", "mail"};@Scheduled(fixedDelay = 1000, initialDelay = 500)public void send() {//短信String sms = "{userName: xxx; phone:xxx}";template.convertAndSend(messageExchange.getName(), "sms", sms);//邮件String mail = "{userName: xxx; mail:xxx}";template.convertAndSend(messageExchange.getName(), "mail", mail);}
}

消费者:

@RabbitListener(queues = "mailQueue")
public class MailConsumer {@RabbitHandlerpublic void consum(String in) {System.out.println("send mail : " + in);}
}@RabbitListener(queues = "smsQueue")
public class SmsConsumer {@RabbitHandlerpublic void consum(String in) {System.out.println("send sms : " + in);}
}

交换机、队列,绑定、消费者,生产者注入容器:

@Configuration
public class DirectConfig {@Beanpublic DirectExchange messageExchange() {return new DirectExchange("messageExchange");}@Beanpublic BaseServiceSender baseServiceSender() {return new BaseServiceSender();}public static class ConsumerGroup {@Beanpublic MailConsumer mailConsumer() {return new MailConsumer();}@Beanpublic SmsConsumer smsConsumer() {return new SmsConsumer();}@Beanpublic Queue mailQueue() {return new Queue("mailQueue");}@Beanpublic Queue smsQueue() {return new Queue("smsQueue");}@Beanpublic Binding smsBinding(DirectExchange messageExchange, Queue smsQueue){return BindingBuilder.bind(smsQueue).to(messageExchange).with("sms");}@Beanpublic Binding mailBinding(DirectExchange messageExchange, Queue mailQueue){return BindingBuilder.bind(mailQueue).to(messageExchange).with("mail");}}
}

运行结果

send mail : {userName: xxx; mail:xxx}
send sms : {userName: xxx; phone:xxx}
send sms : {userName: xxx; phone:xxx}
send mail : {userName: xxx; mail:xxx}

Topic主题模式

  • 发送到主题交换的消息不能有任意routing_key
    • 它必须是单词列表,由点分隔。这 单词可以是任何东西,一些有效的路由密钥示例: “stock.usd.nyse”, “nyse.vmw”, “quick.orange.rabbit”。可以有 路由密钥中随心所欲地包含多个单词,最多可达 255 个 字节。
  • 绑定密钥也必须采用相同的形式。主题交换背后的逻辑类似于直接交换 - 发送的消息带有 特定的路由键将被传递到所有队列 绑定匹配的绑定键
  • *(星号)可以代替一个词。
  • #(哈希)可以替换零个或多个单词。

在这里插入图片描述
若是消息指定的路由键为"xxx.orange.xxx", 则会匹配到Q1, 若是"lazy.xxx.xx"则是Q2;

生产者:

public class Tut5Sender {@Autowiredprivate RabbitTemplate template;@Autowiredprivate TopicExchange topic;AtomicInteger index = new AtomicInteger(0);AtomicInteger count = new AtomicInteger(0);private final String[] keys = {"quick.orange.rabbit", "lazy.orange.elephant", "quick.orange.fox","lazy.brown.fox", "lazy.pink.rabbit", "quick.brown.fox"};@Scheduled(fixedDelay = 1000, initialDelay = 500)public void send() {StringBuilder builder = new StringBuilder("Hello to ");if (this.index.incrementAndGet() == keys.length) {this.index.set(0);}String key = keys[this.index.get()];builder.append(key).append(' ');builder.append(this.count.incrementAndGet());String message = builder.toString();template.convertAndSend(topic.getName(), key, message);System.out.println(" [x] Sent '" + message + "'");}}

消费者:

public class Tut5Receiver {@RabbitListener(queues = "#{autoDeleteQueue1.name}")public void receive1(String in) throws InterruptedException {receive(in, 1);}@RabbitListener(queues = "#{autoDeleteQueue2.name}")public void receive2(String in) throws InterruptedException {receive(in, 2);}public void receive(String in, int receiver) throwsInterruptedException {StopWatch watch = new StopWatch();watch.start();System.out.println("instance " + receiver + " [x] Received '"+ in + "'");doWork(in);watch.stop();System.out.println("instance " + receiver + " [x] Done in "+ watch.getTotalTimeSeconds() + "s");}private void doWork(String in) throws InterruptedException {for (char ch : in.toCharArray()) {if (ch == '.') {Thread.sleep(1000);}}}
}

交换器,队列,绑定、生产者,消费者注入容器:

@Configuration
public class Tut5Config {@Beanpublic TopicExchange topic() {return new TopicExchange("tut.topic");}private static class ReceiverConfig {@Beanpublic Tut5Receiver receiver() {return new Tut5Receiver();}@Beanpublic Queue autoDeleteQueue1() {return new AnonymousQueue();}@Beanpublic Queue autoDeleteQueue2() {return new AnonymousQueue();}@Beanpublic Binding binding1a(TopicExchange topic,Queue autoDeleteQueue1) {return BindingBuilder.bind(autoDeleteQueue1).to(topic).with("*.orange.*");}@Beanpublic Binding binding1b(TopicExchange topic,Queue autoDeleteQueue1) {return BindingBuilder.bind(autoDeleteQueue1).to(topic).with("*.*.rabbit");}@Beanpublic Binding binding2a(TopicExchange topic,Queue autoDeleteQueue2) {return BindingBuilder.bind(autoDeleteQueue2).to(topic).with("lazy.#");}}@Beanpublic Tut5Sender sender() {return new Tut5Sender();}}

运行结果:

[x] Sent ‘Hello to lazy.orange.elephant 1’
instance 2 [x] Received ‘Hello to lazy.orange.elephant 1’
instance 1 [x] Received ‘Hello to lazy.orange.elephant 1’
[x] Sent ‘Hello to quick.orange.fox 2’
[x] Sent ‘Hello to lazy.brown.fox 3’
instance 1 [x] Done in 2.0110456s

RPC远程过程调用

RabbitMQ也实现了RPC的功能,但是业务开发中,根本没有使用场景,RPC要么使用Dubbo, 要么使用OpenFeign, 使用RabbitMQ做RPC的信息,目前都没有看到;

总结

  • 就目前来说、工作队列、发布订阅两个模式,业务开发中会使用到,其他的消息场景很少见。
  • 底层是基于RabbitMQ-client做的封装出RabbitTempldate使用;除非远古项目,否则不推荐使用RabbitMQ-Client原生API写,太费时间了。我写了一会就放弃了

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

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

相关文章

web开发中的安全和防御入门——csp (content-security-policy内容安全策略)

偶然碰到iframe跨域加载被拒绝的问题&#xff0c;原因是父页面默认不允许加载跨域的子页面&#xff0c;也就是的content-security-policy中没有设置允许跨域加载。 简单地说&#xff0c;content-security-policy能限制页面允许和不允许加载的所有资源&#xff0c;常见的包括&a…

原型链污染

文章目录 1. javascript 原型链2. 原型链变量的搜索3. prototype 原型链污染4. 原型链污染例题4.1 题1&#xff1a;4.2.题2&#xff1a; 1. javascript 原型链 js在ECS6之前没有类的概念&#xff0c;之前的类都是用funtion来声明的。如下 可以看到b在实例化为test对象以后&…

【C语言进阶】指针的高级应用(下)

文章目录 一、指针数组与数组指针1.1 指针数组与数组指针的表达式 二、函数指针2.1 函数指针的书写方式 三、二重指针与一重指针3.1 二重指针的本质3.2 二重指针的用法3.3 二重指针与数组指针 总结 一、指针数组与数组指针 (1)指针数组的实质是一个数组&#xff0c;这个数组中存…

Linux进程(二)

文章目录 进程&#xff08;二&#xff09;Linux的进程状态R &#xff08;running&#xff09;运行态S &#xff08;sleeping&#xff09;阻塞状态D &#xff08;disk sleep&#xff09;深度睡眠T&#xff08;stopped&#xff09;状态X&#xff08;dead&#xff09;状态Z&#x…

SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--搭建Vue 前端工程[一]

文章目录 SSM--搭建Vue 前端工程--项目基础界面实现功能01-搭建Vue 前端工程需求分析/图解代码实现搭建Vue 前端工程下载node.js LTS 并安装: node.js 的npm创建Vue 项目使用idea 打开ssm_vue 项目, 并配置项目启动 Vue3 项目目录结构梳理Vue3 项目结构介绍 配置Vue 服务端口El…

Dockerfile构建mysql

使用dockerfile构建mysql详细教学加案例 Dockerfile 文件 # 使用官方5.6版本&#xff0c;latest为默认版本 FROM mysql:5.6 #复制my.cof至容器内 ADD my.cnf /etc/mysql/my.cof #设置环境变量 密码 ENV MYSQL_ROOT_PASSWORD123456my.cof 文件 [mysqld] character-set-server…

IDEA SpringBoot Maven profiles 配置

IDEA SpringBoot Maven profiles 配置 IDEA版本&#xff1a; IntelliJ IDEA 2022.2.3 注意&#xff1a;切换环境之后务必点击一下刷新&#xff0c;推荐点击耗时更短。 application.yaml spring:profiles:active: env多环境文件名&#xff1a; application-dev.yaml、 applicat…

【MATLAB第63期】基于MATLAB的改进敏感性分析方法IPCC,拥挤距离与皮尔逊系数法结合实现回归与分类预测

【MATLAB第63期】基于MATLAB的改进敏感性分析方法IPCC&#xff0c;拥挤距离与皮尔逊系数法结合实现回归与分类预测 思路 考虑拥挤距离指标与PCC皮尔逊相关系数法相结合&#xff0c;对回归或分类数据进行降维&#xff0c;通过SVM支持向量机交叉验证得到平均指标&#xff0c;来…

基于CentOS 7构建LVS-DR集群

DIPVIPRIPClient192.169.41.139 LVS 192.168.41.134192.169.41.10RS1192.168.41.135RS2192.168.41.138 要求&#xff1a; node4为客户端&#xff0c;node2为LVS&#xff0c;node3和node4为RS。 1.配置DNS解析&#xff08;我这里使用本地解析&#xff09; 192.168.41.134 www.y…

一、8.分页

当物理内存不够时就把不常用的内存暂时存入磁盘&#xff0c;并且描述符的P位置0&#xff0c;把要使用的段放入内存&#xff0c;描述符P位置1 但是这种方式会产生大量内存碎片&#xff0c;影响内存分配效率 设想一个虚拟内存&#xff0c;每隔任务都有他独立的虚拟内存&#xf…

golang pprof 监控系列—— cpu 占用率 统计原理

经过前面的几节对pprof的介绍&#xff0c;对pprof统计的原理算是掌握了七八十了&#xff0c;我们对memory,block,mutex,trace,goroutine,threadcreate这些维度的统计原理都进行了分析&#xff0c;但唯独还没有分析pprof 工具是如何统计cpu使用情况的&#xff0c;今天我们来分析…

[Pytorch]卷积运算conv2d

文章目录 [Pytorch]卷积运算conv2d一.F.Conv2d二.nn.Conv2d三.nn.Conv2d的运算过程 [Pytorch]卷积运算conv2d 一.F.Conv2d torch.nn.functional.Conv2d()的详细参数&#xff1a; conv2d(input: Tensor, weight: Tensor, bias: Optional[Tensor]None, stride: Union[_int, _s…

常见Charles在Windows10抓包乱码问题

废话不多说 直接开整 最近反复安装证书还是乱码 网上各种百度还是不行 首先计算机查看安装好的证书 certmgr.msc 找到并删除掉 重新安装证书 具体解决方法&#xff1a; 第一步&#xff1a;点击 【工具栏–>Proxy–>SSL Proxying Settings…】 第二步&#xff1a;配置…

C++路线(全网20篇高赞文章总结)

为节省时间&#xff0c;可直接跳转到 --> &#x1f33c;干货 目录 &#x1f33c;前言 &#x1f33c;来源 &#x1f416;现状 &#x1f33c;干货 入门阶段 入门项目 学习顺序 &#x1f409;大二打算 &#x1f33c;前言 来源的20篇博客&#xff0c;视频中&#x…

XML约束和解析

文章目录 概述使用场景语法dtd约束Schema约束解析DOM4j&#xff08;重点&#xff09; 概述 可扩展的标记性语言 使用场景 以前: 传输数据的媒介。 例如&#xff1a;微服务架构中&#xff0c;可以用xml文件进行多语言之间的的联系。 现在: 做配置文件 现在作为传输数据的媒介…

【java】使用maven完成一个servlet项目

一、创建项目 创建一个maven项目 maven是一个管理java项目的工具&#xff0c;根据maven的pom.xml可以引入各种依赖&#xff0c;插件。 步骤 打开idea&#xff0c;点击新建项目 点击创建项目&#xff0c;项目创建就完成了 进入时会自动打开pom.xml文件。 pom是项目的配置文件…

css, resize 拖拉宽度

效果如下&#xff1a; 可直接复制预览查看属性值: 关键样式属性&#xff1a; resize: horizontal; overflow-x: auto; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content…

【一次调频】考虑储能电池参与一次调频技术经济模型的容量配置方法(Matlab代码实现)

目录 &#x1f4a5;1 概述 1.1 文献来源 1.2 储能电池参与一次调频的方法 1.3 流程图 &#x1f4da;2 运行结果 2.1 数据 2.2 文献结果 2.3 复现结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码、数据、文章讲解 &#x1f4a5;1 概述 1.1 文献来源 摘要&#xff1a;规…

【学习笔记】Java安全之反序列化

文章目录 反序列化方法的对比PHP的反序列化Java的反序列化Python反序列化 URLDNS链利用链分析触发DNS请求 CommonCollections1利用链利用TransformedMap构造POC利用LazyMap构造POCCommonsCollections6 利用链 最近在学习Phith0n师傅的知识星球的Java安全漫谈系列&#xff0c;随…

Django实现音乐网站 ⑶

使用Python Django框架制作一个音乐网站&#xff0c;在系列文章2的基础上继续开发&#xff0c; 本篇主要是后台单曲、专辑、首页轮播图表模块开发。 目录 后台单曲、专辑表模块开发 表结构设计 单曲表&#xff08;singe&#xff09;结构 专辑表&#xff08;album&#xff0…