谷粒商城 高级篇 (十九) --------- 消息队列

news/2024/5/5 22:21:52/文章来源:https://blog.csdn.net/m0_51111980/article/details/126961830

目录

  • 一、概述
  • 二、应用
  • 三、RabbitMQ 概念
  • 四、安装 RabbitMQ
  • 五、RabbitMQ 运行机制
    • Exchange 类型
  • 六、RabbitMQ 整合
  • 七、RabbitMQ 消息确认机制
    • 1. ConfirmCallback
    • 2. ReturnCallback
    • 3. Ack 消息确认机制


一、概述

大多应用中,可通过消息服务中间件来提升系统异步通信、扩展解耦能力。

消息服务中两个重要概念:

消息代理 (message broker) 和目的地 (destination) 当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地。

消息队列主要有两种形式的目的地:

  • 队列 (queue) :点对点消息通信 (point-to-point)

  • 主题 (topic) :发布 (publish) / 订阅 (subscribe) 消息通信

点对点式:

  • 消息发送者发送消息,消息代理将其放入一个队列中,消息接收者从队列中获取消息内容,消息读取后被移出队列

  • 消息只有唯一的发送者和接受者,但并不是说只能有一个接收者

发布订阅式:

  • 发送者 (发布者) 发送消息到主题,多个接收者 (订阅者) 监听 (订阅) 这个主题,那么就会在消息到达时同时收到消息

JMS (Java Message Service) JAVA消息服务:

  • 基于JVM消息代理的规范。ActiveMQ、HornetMQ 是 JMS 实现

AMQP (Advanced Message Queuing Protocol)

  • 高级消息队列协议,也是一个消息代理的规范,兼容JMS
  • RabbitMQ是AMQP的实现

在这里插入图片描述

Spring 支持

  • spring-jms提供了对JMS的支持
  • spring-rabbit提供了对AMQP的支持
  • 需要ConnectionFactory的实现来连接消息代理• 提供JmsTemplate、RabbitTemplate来发送消息
  • @JmsListener (JMS)、@RabbitListener (AMQP) 注解在方法上监听消息代理发布的消息
  • @EnableJms、@EnableRabbit 开启支持

Spring Boot自动配置

  • JmsAutoConfiguration
  • RabbitAutoConfiguration

市面的MQ产品

  • ActiveMQ、RabbitMQ、RocketMQ、Kafka

二、应用

消息队列主要用作 异步、解耦与流控

异步处理:

在这里插入图片描述

应用解耦:

在这里插入图片描述

流量控制:

在这里插入图片描述

三、RabbitMQ 概念

在这里插入图片描述

RabbitMQ简介

RabbitMQ是一个由erlang开发的AMQP(Advanved Message Queue Protocol)的开源实现。核心概念
Message 消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key (路由键)、priority (相对于其他消息的优先权)、delivery-mode (指出该消息可能需要持久性存储) 等。

Publisher
消息的生产者,也是一个向交换器发布消息的客户端应用程序。

Exchange
交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。Exchange 有 4 种类型:direct(默认),fanout, topic, 和 headers,不同类型的 Exchange 转发消息的策略有所区别

Queue
消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。

Binding
绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。Exchange 和Queue的绑定可以是多对多的关系。

Connection
网络连接,比如一个TCP连接。

Channel
信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁TCP都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。

Consumer
消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。

Virtual Host
虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的RabbitMQ服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 /

Broker
表示消息队列服务器实体

在这里插入图片描述

四、安装 RabbitMQ

在 linux 上输入如下指令:

docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management

如 docker 没有 rabbitmq 镜像会在第一次执行此行指令时自动拉取 rabbitmq 镜像。。。

在这里插入图片描述

端口说明:

  • 4369、25672: Erlang 发现 & 集群端口
  • 5672、5671: AMQP端口
  • 15672: web管理后台端口
  • 61613、61614: STOMP协议端口
  • 1883、8883: MQTT协议端口

https://www.rabbitmq.com/networking.html

在这里插入图片描述
安装完之后,我们可以进入其后台管理页面。。。

地址为: 服务器ip:15672,登录的账号密码均为 guest

在这里插入图片描述
在这里插入图片描述

添加 direct 类型的交换机

在这里插入图片描述

在这里插入图片描述

添加队列

在这里插入图片描述

将交换机与队列进行绑定

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
发送消息

在这里插入图片描述
在这里插入图片描述

查看消息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
更多测试我们就不再详细展示。。。

五、RabbitMQ 运行机制

AMQP 中消息的路由过程和 Java 开发者熟悉的 JMS 存在一些差别,AMQP 中增加了 Exchange 和
Binding 的角色。生产者把消息发布到 Exchange 上,消息最终到达队列并被消费者接收,而 Binding 决定交换器的消息应该发送到那个队列。

在这里插入图片描述

Exchange 类型

Exchange分发消息时根据类型的不同分发策略有区别,目前共四种类型:direct、fanout、topic、headers 。headers 匹配 AMQP 消息的 header 而不是路由键,headers 交换器和 direct 交换器完全一致,但性能差很多,目前几乎用不到了,所以直接看另外三种类型:

消息中的路由键 (routingkey) 如果和 Binding 中的 binding key一致,交换器就将消息发到对应的队列中。路由键与队列名完全匹配,如果一个队列绑定到交换机要求路由键为 “dog”,则只转发 routingkey 标记为 “dog” 的消息,不会转发 “dog.puppy” ,也不会转发 “dog.guard” 等等。它是完全匹配、单播的模式。

在这里插入图片描述

每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout 类型转发消息是最快的。

在这里插入图片描述

topic 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符:符号 # 和符号 *。#匹配0个或多个单词,*匹配一个单词。

在这里插入图片描述

六、RabbitMQ 整合

步骤:

使用RabbitMQ* 1、引入amqp场景;RabbitAutoConfiguration 就会自动生效** 2、给容器中自动配置了*      RabbitTemplateAmqpAdminCachingConnectionFactoryRabbitMessagingTemplate;*      所有的属性都是 spring.rabbitmq*      @ConfigurationProperties(prefix = "spring.rabbitmq")*      public class RabbitProperties** 3、给配置文件中配置 spring.rabbitmq 信息* 4@EnableRabbit: @EnableXxxxx;开启功能* 5、监听消息:使用@RabbitListener;必须有@EnableRabbit*    @RabbitListener:+方法上(监听哪些队列即可)*    @RabbitHandler:标在方法上(重载区分不同的消息)

A、引入 spring-boot-starter-amqp 依赖

在这里插入图片描述

B、application.yml配置

在这里插入图片描述

# RabbitMQ配置
spring.rabbitmq.host=192.168.38.130
spring.rabbitmq.port=5672# 虚拟主机配置
spring.rabbitmq.virtual-host=/# 开启发送端消息抵达Broker确认
spring.rabbitmq.publisher-confirms=true# 开启发送端消息抵达Queue确认
spring.rabbitmq.publisher-returns=true# 只要消息抵达Queue,就会异步发送优先回调returnfirm
spring.rabbitmq.template.mandatory=true# 手动ack消息,不使用默认的消费端确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual

C、添加 RabbitMQ 功能的注解

在这里插入图片描述

D、测试 RabbitMQ

package com.fancy.gulimall.order;import com.fancy.gulimall.order.entity.OrderEntity;
import com.fancy.gulimall.order.entity.OrderReturnReasonEntity;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.util.Date;
import java.util.UUID;@SpringBootTest
@Slf4j
@RunWith(SpringRunner.class)
class GulimallOrderApplicationTests {//1. AmqpAdmin:管理组件@AutowiredAmqpAdmin amqpAdmin;// 2. RabbitTemplate:消息发送处理组件@AutowiredRabbitTemplate  rabbitTemplate;@Testpublic void sendMessageTest() {// 1. 发送消息, 如果发送的消息是个对象, 我们会使用序列化机制, 将对象写出去。对象必须实现 SerializableString msg = "Hello World";// 2. 发送的对象类型的转换消息, 可以转换成一个 jsonfor (int i = 0; i < 10; i ++ ) {if (i % 2 == 0) {OrderReturnReasonEntity reasonEntity = new OrderReturnReasonEntity();reasonEntity.setId(1L);reasonEntity.setCreateTime(new Date());reasonEntity.setName("哈哈-" + i);rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", reasonEntity, new CorrelationData(UUID.randomUUID().toString()));} else {OrderEntity entity = new OrderEntity();entity.setOrderSn(UUID.randomUUID().toString());rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", entity,new CorrelationData(UUID.randomUUID().toString()));}log.info("消息发送完成{}");}}/*** 1、如何创建Exchange[hello-java-exchange]、Queue、Binding* 1)、使用 AmqpAdmin 进行创建* 2、如何收发消息*/@Testpublic void createExchange() {//amqpAdmin//Exchange/*** DirectExchange(String name, boolean durable, boolean autoDelete, Map<String, Object> arguments)*/DirectExchange directExchange = new DirectExchange("hello-java-exchange", true, false);amqpAdmin.declareExchange(directExchange);log.info("Exchange[{}]创建成功", "hello-java-exchange");}@Testpublic void createQueue() {//public Queue(String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)Queue queue = new Queue("hello-java-queue", true, false, false);amqpAdmin.declareQueue(queue);log.info("Queue[{}]创建成功", "hello-java-queue");}@Testpublic void createBinding() {//(String destination【目的地】,// DestinationType destinationType【目的地类型】,// String exchange【交换机】,// String routingKey【路由键】,//Map<String, Object> arguments【自定义参数】)//将exchange指定的交换机和destination目的地进行绑定,使用routingKey作为指定的路由键Binding binding = new Binding("hello-java-queue", Binding.DestinationType.QUEUE, "hello-java-exchange", "hello.java", null);amqpAdmin.declareBinding(binding);log.info("Binding[{}]创建成功", "hello-java-binding");}}

E、测试接收消息

在上面一段代码中我们编写了在测试类中发送消息的代码,接下来我们测试来接收消息。。。,接受消息我们主要在类或方法上加入 @RabbitListener 或· @RabbitHandler 来执行

例如:

/**
* queues:声明需要监听的所有队列
*
* org.springframework.amqp.core.Message
*
* 参数可以写一下类型
* 1、Message message:原生消息详细信息。头+体
* 2、T<发送的消息的类型> OrderReturnReasonEntity content;
* 3、Channel channel:当前传输数据的通道
*
* Queue:可以很多人都来监听。只要收到消息,队列删除消息,而且只能有一个收到此消息
* 场景:
*    1)、订单服务启动多个;同一个消息,只能有一个客户端收到
*    2)、 只有一个消息完全处理完,方法运行结束,我们就可以接收到下一个消息
*/
//    @RabbitListener(queues = {"hello-java-queue"})
@RabbitHandler
public void recieveMessage(Message message,OrderReturnReasonEntity content,Channel channel) throws InterruptedException {//{"id":1,"name":"哈哈","sort":null,"status":null,"createTime":1581144531744}System.out.println("接收到消息..."+content);byte[] body = message.getBody();//消息头属性信息MessageProperties properties = message.getMessageProperties();
//        Thread.sleep(3000);System.out.println("消息处理完成=>"+content.getName());//channel内按顺序自增的。long deliveryTag = message.getMessageProperties().getDeliveryTag();System.out.println("deliveryTag==>"+deliveryTag);//签收货物,非批量模式try {if(deliveryTag%2 == 0){//收货channel.basicAck(deliveryTag,false);System.out.println("签收了货物..."+deliveryTag);}else {//退货 requeue=false 丢弃  requeue=true 发回服务器,服务器重新入队。//long deliveryTag, boolean multiple, boolean requeue//签收了货物...6channel.basicNack(deliveryTag,false,true);//long deliveryTag, boolean requeue
//                channel.basicReject();System.out.println("没有签收了货物..."+deliveryTag);}}catch (Exception e){//网络中断}}@RabbitHandler
public void recieveMessage2(OrderEntity content) throws InterruptedException {//{"id":1,"name":"哈哈","sort":null,"status":null,"createTime":1581144531744}System.out.println("接收到消息..."+content);
}

@RabbitListener(queues = {"hello-java-queue"}) 可以加到类上面去,然后方法上标注 @RabbitHandler 去处理。。。,区别就是 @RabbitHandler 标注在方法上可以重载区分不同的消息

七、RabbitMQ 消息确认机制

RabbitMQ 消息确认机制保证消息的可靠抵达,可以使用以下几种方式。。。

  • 保证消息不丢失,可靠抵达,可以使用事务消息,性能下降250倍,为此引入确认机制
  • publisher confirmCallback 确认模式
  • publisher returnCallback 未投递到 queue 退回模式
  • consumer ack机制

在这里插入图片描述

1. ConfirmCallback

表示服务器收到消息就回调

  • spring.rabbitmq.publisher-confirms=true
    - 设置确认回调ConfirmCallback
    在配置文件中加入:
spring.rabbitmq.publisher-confirms=true

在创建 connectionFactory 的时候设置 PublisherConfirms (true) 选项,开启 ConfirmCallback ,因此我们可以定制 RabbitTemplate ,在 config 包下,创建 MyRabbitConfig 配置类。。。,设置确认回调,需要编写如下方法

//设置确认回调
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {/**** 1、只要消息抵达Broker就ack=true* @param correlationData 当前消息的唯一关联数据(这个是消息的唯一id)* @param ack  消息是否成功收到* @param cause 失败的原因*/@Overridepublic void confirm(CorrelationData correlationData, boolean ack, String cause) {/*** 1、做好消息确认机制(pulisher,consumer【手动ack】)* 2、每一个发送的消息都在数据库做好记录。定期将失败的消息再次发送一遍*///服务器收到了;//修改消息的状态System.out.println("confirm...correlationData["+correlationData+"]==>ack["+ack+"]==>cause["+cause+"]");}
});

说明:

  • CorrelationData:用来表示当前消息唯一性。
  • 消息只要被 broker 接收到就会执行 confirmCallback,如果是cluster 模式,需要所有broker 接收到才会调用 confirmCallback。
  • 被 broker 接收到只能表示 message 已经到达服务器,并不能保证消息一定会被投递到目标 queue 里。所以需要用到接下来的 returnCallback 。

2. ReturnCallback

消息正确抵达队列进行回调,有以下两个步骤:

1、在 application.properties 中进行配置:

spring.rabbitmq.publisher-returns=true
spring.rabbitmq.template.mandatory=true

2、设置确认回调ReturnCallback

//设置消息抵达队列的确认回调
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {/*** 只要消息没有投递给指定的队列,就触发这个失败回调* @param message   投递失败的消息详细信息* @param replyCode 回复的状态码* @param replyText 回复的文本内容* @param exchange  当时这个消息发给哪个交换机* @param routingKey 当时这个消息用哪个路由键*/@Overridepublic void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {//报错误了。修改数据库当前消息的状态->错误。System.out.println("Fail Message["+message+"]==>replyCode["+replyCode+"]==>replyText["+replyText+"]===>exchange["+exchange+"]===>routingKey["+routingKey+"]");}
});
  • confrim 模式只能保证消息到达 broker,不能保证消息准确投递到目标 queue 里。在有些业务场景下,我们需要保证消息一定要投递到目标 queue 里,此时就需要用到 return 退回模式。
  • 这样如果未能投递到目标 queue 里将调用 returnCallback ,可以记录下详细到投递数据,定期的巡检或者自动纠错都需要这些数据。

MyRabbitConifg 完整配置如下:

package com.fancy.gulimall.order.config;import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;import javax.annotation.PostConstruct;@Configuration
public class MyRabbitConfig {//
//    @AutowiredRabbitTemplate rabbitTemplate;//    public MyRabbitConfig(RabbitTemplate rabbitTemplate){
//        this.rabbitTemplate = rabbitTemplate;
//        initRabbitTemplate();
//    }//TODO@Primary@Beanpublic RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);this.rabbitTemplate = rabbitTemplate;rabbitTemplate.setMessageConverter(messageConverter());initRabbitTemplate();return rabbitTemplate;}/*** 使用JSON序列化机制,进行消息转换*/@Beanpublic MessageConverter messageConverter(){return new Jackson2JsonMessageConverter();}/*** 定制RabbitTemplate* 1、服务器收到消息就回调*      1、spring.rabbitmq.publisher-confirms=true*      2、设置确认回调ConfirmCallback* 2、消息正确抵达队列进行回调*      1、 spring.rabbitmq.publisher-returns=true*          spring.rabbitmq.template.mandatory=true*      2、设置确认回调ReturnCallback** 3、消费端确认(保证每个消息被正确消费,此时才可以broker删除这个消息)。*      spring.rabbitmq.listener.simple.acknowledge-mode=manual 手动签收*      1、默认是自动确认的,只要消息接收到,客户端会自动确认,服务端就会移除这个消息*          问题:*              我们收到很多消息,自动回复给服务器ack,只有一个消息处理成功,宕机了。就会发生消息丢失;*              消费者手动确认模式。只要我们没有明确告诉MQ,货物被签收。没有Ack,*                  消息就一直是unacked状态。即使Consumer宕机。消息不会丢失,会重新变为Ready,下一次有新的Consumer连接进来就发给他*      2、如何签收:*          channel.basicAck(deliveryTag,false);签收;业务成功完成就应该签收*          channel.basicNack(deliveryTag,false,true);拒签;业务失败,拒签*/
//    @PostConstruct //MyRabbitConfig对象创建完成以后,执行这个方法public void initRabbitTemplate(){//设置确认回调rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {/**** 1、只要消息抵达Broker就ack=true* @param correlationData 当前消息的唯一关联数据(这个是消息的唯一id)* @param ack  消息是否成功收到* @param cause 失败的原因*/@Overridepublic void confirm(CorrelationData correlationData, boolean ack, String cause) {/*** 1、做好消息确认机制(pulisher,consumer【手动ack】)* 2、每一个发送的消息都在数据库做好记录。定期将失败的消息再次发送一遍*///服务器收到了;//修改消息的状态System.out.println("confirm...correlationData["+correlationData+"]==>ack["+ack+"]==>cause["+cause+"]");}});//设置消息抵达队列的确认回调rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {/*** 只要消息没有投递给指定的队列,就触发这个失败回调* @param message   投递失败的消息详细信息* @param replyCode 回复的状态码* @param replyText 回复的文本内容* @param exchange  当时这个消息发给哪个交换机* @param routingKey 当时这个消息用哪个路由键*/@Overridepublic void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {//报错误了。修改数据库当前消息的状态->错误。System.out.println("Fail Message["+message+"]==>replyCode["+replyCode+"]==>replyText["+replyText+"]===>exchange["+exchange+"]===>routingKey["+routingKey+"]");}});}
}

3. Ack 消息确认机制

自动回复一旦服务宕机就会发生丢失,因此我们可以采取手动确认机制。。。

在配置文件中加入:

spring.rabbitmq.listener.simple.acknowledge-mode=manual

如何签收消息:

channel.basicAck(deliveryTag,false):签收,业务成功完成就应该签收
channel.basicNack(deliveryTag,false,true),拒签;业务失败,拒签
  • 消费者获取到消息,成功处理,可以回复 Ack 给 Broker
  • basic.ack 用于肯定确认,broker 将移除此消息
  • basic.nack 用于否定确认,可以指定 broker 是否丢弃此消息,可以批量
  • basic.reject 用于否定确认,同上,但不能批量
  • 默认自动ack,消息被消费者收到,就会从broker的queue中移除
  • queue无消费者,消息依然会被存储,直到消费者消费
  • 消费者收到消息,默认会自动ack。但是如果无法确定此消息是否被处理完成,或者成功处理。我们可以开启手动ack模式
  • 消息处理成功,ack(),接受下一个消息,此消息broker就会移除
  • 消息处理失败,nack()/reject(),重新发送给其他人进行处理,或者容错处理后ack
  • 消息一直没有调用ack/nack方法,broker认为此消息正在被处理,不会投递给别人,此时客户端断开,消息不会被broker移除,会投递给别人

在这里插入图片描述

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

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

相关文章

python实现图像添加噪声、噪声处理、滤波器代码实现

目录 加载图像添加噪声 图像傅里叶变换和反变换并可视化 图像处理---高通滤波、低通滤波、带通滤波 低通滤波器---Butterworth低通滤波器、理想低通滤波器、高斯低通滤波器 加载图像添加噪声 高斯噪声是指它的概率密度函数服从高斯分布&#xff08;即正态分布&#xff09;…

Linux设置开机自启动Java程序--三种方式

Linux设置开机自启动Java脚本程序 缘起 公司内部的服务器中有个SpringCloud项目需要运行&#xff0c;之前都是通过nohup java-jar .. &的命令来执行的&#xff0c;但是这个cloud项目服务太多&#xff0c;手动启动太麻烦而且容易出错&#xff0c;干脆写个执行java的脚本好…

C++内存管理(每日更新)

文章目录0 概述0.1 四个层面的基本用法1 Primitives1.1 new expression1.2 delete expression1.3 调用构造函数与析构函数1.4 array new & array delete1.4.1 array new0 概述 C应用程序malloc非常重要 可以看出&#xff0c;C内存管理主要是有四个层面 0.1 四个层面的基本…

NFT重构票务系统

什么是NFT&#xff1f; NFT是运行在区块链上的一种不可分割的凭证&#xff08;Non-Fungible Token&#xff09;&#xff0c;或者称为非同质化代币。NFT目前主要用在数字艺术品的铸造、拍卖、流转&#xff0c;因为一个NFT能唯一地确定它的所有者&#xff0c;并可在链上跟踪每一…

自然语言语义分析研究进展_笔记

自然语言语义分析研究进展_笔记 词语语义分析&#xff1a;确定词语意义&#xff0c;衡量两个词之间的语义相似度或相关度; 句子语义分析&#xff1a;研究包含句义分析和句义相似度分析两方面; 文本语义分析&#xff1a;识别文本的意义、主题、类别等语义信息的过程&#xff…

使用@JsonFormat并进一步了解:格式化java.util.Date对象

Java 8 Spring Boot 2.7.3 jackson 2.13.3 -- ben发布于博客园 0、前言 开发过程中遇到问题: 前端调用接口得到的时间对象(java.util.Date)总是存在这样那样的问题。 调查后发现,可以使用 @JsonFormat注解(来自jackson依赖包)解决相关问题。 ben发布于博客园 新建spring …

区块链分叉带来的安全挑战

区块链分叉分为软分叉和硬分叉。本文主要探讨的是硬分叉&#xff0c;一种不支持向后兼容的软件升级方式。硬分叉是共识的分裂或者改变&#xff0c;共识就是区块链系统中各节点达成数据一致性的算法&#xff0c;正常情况下每个节点需要运行相同规则的算法&#xff0c;例如比特币…

计算机毕业设计之java+javaweb的影院管理系统-电影院管理系统

计算机毕业设计之javajavaweb的影院管理系统-电影院管理系统 项目介绍 影院的需求和管理上的不断提升,影院管理的潜力将无限扩大,影院管理系统在业界被广泛关注,本网站及对此进行总体分析,将影院信息管理的发展提供参考。影院管理系统对影院发展有着明显的带动效应,尤其对当地影…

【ManageEngine】OpManager 2022用户体验报告

关于SoftwareViews SoftwareReviews是Info-Tech Research Group的一个部门&#xff0c;是一家世界级的技术研究和咨询公司&#xff0c;拥有超过20年的基于研究的IT建议和技术实施。 SoftwareViews务实的工具和详细的客户洞察力帮助软件购买者在技术决策中取得最大成功。 Sof…

Java并发编程解析 | 基于JDK源码解析Java领域中ReentrantLock锁的设计思想与实现原理 (一)

苍穹之边,浩瀚之挚,眰恦之美; 悟心悟性,善始善终,惟善惟道! —— 朝槿《朝槿兮年说》写在开头在并发编程领域,有两大核心问题:一个是互斥,即同一时刻只允许一个线程访问共享资源;另一个是同步,即线程之间如何通信、协作。主要原因是,对于多线程实现实现并发,一直以…

全能赛道、热门方向、稀缺数据,“嬴彻-清华AIR杯”自动驾驶技术大赛火热开赛中

如果你关注自动驾驶,那有这样一个算法大赛,值得参与。由卡车自动驾驶领导者嬴彻科技与清华大学智能产业研究院(AIR)精心打造的“嬴彻-清华AIR杯”自动驾驶技术挑战赛正火热开赛中。这是国内首个同时覆盖干线物流和城市道路双赛道的大赛。决策规划是自动驾驶当下的热门方向和技术…

go语言的基本数据类型

基本数据类型中的常量已经介绍了 var const iota 。此处要对字符串特别说明&#xff0c;字符串也会被认为是基本数据类型&#xff0c;字符串实际在底层原理上与复合类型的数据非常相似。同事go语言支持八进制&#xff0c;6进制&#xff0c;科学计数法。空指针的值是nil。 整…

百度地图API

一、百度地图API接入 1、搜索百度地图开发平台 2、注册百度账号 3、登陆并申请成为开发者 4、在百度地图开发平台的首页选择控制台&#xff0c;在控制台中创建应用 创建好应用以后就能在控制台我的应用中看到这个应用&#xff0c;其中最重要的是AK&#xff0c;这是百度地图…

从0-1,如何用低代码搭建管理系统

关键字&#xff1a;功能模块、流程中心、OA 前言&#xff1a;对于搭建系统&#xff0c;字眼上都知道是怎么回事&#xff0c;但要怎么搭建到最后怎么呈现一个投入运作的系统&#xff0c;估计就很少人知道了。当然作为专业的程序员肯定知道怎么操作&#xff0c;但是不是必须要专业…

IDEA集成Git

介绍 参考视频教程: https://www.bilibili.com/video/BV1vy4y1s7k6?p27 1 配置 Git 忽略文件 1.1为什么要配置 问题 1:为什么要忽略他们&#xff1f; 答&#xff1a;与项目的实际功能无关&#xff0c;不参与服务器上部署运行。把它们忽略掉能够屏蔽 IDE 工具之 间的差异。…

湖仓一体电商项目(十八):业务实现之编写写入DWD层业务代码

文章目录 业务实现之编写写入DWD层业务代码 一、代码编写 二、​​​​​​​​​​​​​​创建Iceberg-DWD层表 1、在Hive中添加Iceberg表格式需要的包 2、创建Iceberg表 三、代码测试 1、在Kafka中创建对应的topic 2、将代码中消费Kafka数据改成从头开始消费 3、执…

【展馆攻略】展馆室内精准定位导航服务,便捷无忧,所见必达!

近年来&#xff0c;室内定位导航服务被各大商场、园区、景区等场所广泛使用&#xff0c;逐渐融入到人们日常生活中。室内地图从传统的平面信息图发展到智能化3D可视化交互展示&#xff0c;实现了室内综合场景的精准定位导航。 在面对室内或者建筑物遮挡区域&#xff0c;室内导航…

浏览器如何渲染页面?

DOM 浏览器渲染页面的过程就像是盖房子&#xff0c;一般先请求服务器得到HTML文件&#xff0c;HTML文件就相当于网页的框架结构&#xff0c;不过一开始浏览器得到的是显示字节内容的HTML文件&#xff0c;必须要内化为自己看的懂的语言才行&#xff0c;于是就把字节转化为字符&…

vue小案列(hello world)

目录 1 页签图标的报错解决 2 创建vue实例 3 初识Vue分析 1 页签图标的报错解决 1 首先&#xff0c;在我们的html中定义一个容器&#xff0c;然后右键&#xff08;Open with LIve Server&#xff09;打开&#xff0c;需要安装LIve Server插件 打开之后发现控制台报如下错误&a…

2022年服装进销存软件排行榜重磅出炉!

小编调研了一下身边做服装行业的老板&#xff0c;普遍反映如今服装实体店越来越难做了&#xff0c;日常经营过程中&#xff0c;难免会遇到各种问题&#xff1a;商品种类多、款式多、库存多、活动多……这些都让老板们应接不暇&#xff0c;尽管每天早出晚归地管理店铺&#xff0…