205、使用消息队列实现 RPC(远程过程调用)模型的 服务器端 和 客户端

news2024/11/24 20:52:24

目录

  • ★ RPC模型(远程过程调用通信模型)
  • ▲ 完整过程:
  • 代码演示
    • 总体流程解释:
    • ConstantUtil 常量工具类
    • ConnectionUtil RabbitMQ连接工具类
    • Server 服务端
    • Client 客户端
    • 测试结果
      • 服务端
      • 客户端
  • 完整代码
    • ConstantUtil 常量工具类
    • ConnectionUtil RabbitMQ连接工具类
    • Server 服务端
    • Client 客户端
    • pom.xml

★ RPC模型(远程过程调用通信模型)

PRC 模型相当于一对一,跟调用方法一样,能拿到方法返回的结果,这就是典型的RPC模型(不仅要传参数,还需要拿到返回值)

reply:回答、答复
correlation :关联、相互关联

▲ 通过使用两个独享队列,可以让RabbitMQ实现RPC(远程过程调用)通信模型,

其通信过程其实也很简单:客户端向服务器消费的独享队列发送一条消息,服务器收到该消息后,对该消息进行处理,然后将处理结果发送给客户端消费的独享队列。

服务器端消费的独享队列负责保存调用参数,客户端消费的独享队列负责保存调用的返回值。

▲ 使用独享队列可以避免其他连接来读取队列的消息、只有当前连接才能读取该队列的消息,这样才能保证服务器能读到客户端发送的每条消息,客户端也能读到服务器返回的每条消息。

▲ 为了让服务器知道客户端所消费的独享队列,客户端发送消息时,应该将自己监听的队列名以 reply_to属性 发送给服务器

▲为了能准确识别服务器应答消息(返回值)与客户端请求消息(调用参数)之间的对应关系,
还需要为每条消息都增加一个 correlation_id 属性,两条具有相同 correlation_id 属性值的消息可认为是配对的两条消息。

【备注】:客户端送出的消息要包含2个属性:

  • reply_to:该属性指定了服务器要将返回的消息送回到哪个队列。

  • correlation_id:该属性指定了服务器返回的消息也要添加相同的correlation_id属性。

在这里插入图片描述

▲ 完整过程:

(1)服务器启动时,它会创建一个名为“rpc_queue”的独享队列(名称可以随意),
     并使用服务器端的消费者监听该独享队列的消息。
    (所有的RPC 调用,一定都是先从服务器端的启动开始的。)

(2)客户端启动时,它会创建一个匿名(默认)(由RabbitMQ命名)的 独享队列,
     并使用客户端的消费者监听该独享队列的消息。
     (这个独享队列的名字也是 reply_to 属性的属性值)

(3)客户端发送带有两个属性的消息:
     一个是代表应答队列名的 reply_to属性(该属性值就是第2步客户端所创建的独享队列名),
     另一个是代表消息标识的 correlation_id 属性。

(4)将消息发送到服务器监听的rpc_queue队列中。

(5)服务器从rpc_queue队列中读取消息,服务器调用处理程序对该消息进行计算,
     将计算结果以消息发送给 reply_to属性 指定的队列,并为消息添加相同的 correlation_id属性。

(6)客户端从 reply_to 对应的队列中读取消息,当消息出现时,它会检查消息的 correlation_id属性。
     如果此属性的值与请求消息的 correlation_id 属性值匹配,将它返回给应用。

      ————上面过程,其实就是对P2P模型的应用,因此无需使用自己的Exchange,
          而是使用系统自动创建的默认Exchange即可。

代码演示

需求:客户端发送个消息到服务端,服务端处理完再返回结果给客户端。
如图:需要有两个消息队列,一个是服务端
在这里插入图片描述

总体流程解释:

仅作为自己梳理代码流程的记录,大佬请直接忽略

Server 类是服务端 , Client 是客户端。

rpc_queue 是自己在服务端声明创建的消息队列,服务端监听着这个消息队列

amq.gen-3Nl6GNjR5BzPJ4N-By4p1g 是客户端声明创建的一个默认生成的消息队列。
(就是调用 Channel 的 queueDeclare() 方法声明队列时,不指定具体的消息队列的参数,全凭默认生成),客户端监听着这个默认的消息队列。

replyTo 的值是 amq.gen-3xxx 这个默认消息队列,作用是指定了服务器要将返回的消息送回到这个默认队列

correlationId 只是一个单纯的消息标识,可以给个1、2、3、4…作为消息标识

上面这些就是涉及到的一些点,下面就是流程:

首先,客户端会发送几个消息,Exchange时默认的,路由key 是 rpc_queue , 每个消息都携带者 replyTo 和 correlationId 这两个属性值;
(解释:如果消息发布者指定默认的Exchange,那么Exchange就会根据消息发布者发来的消息中携带的路由key(假如路由key叫 aaa) ,去找是否有同样名字叫aaa的消息队列,有的话就把消息分发给消息队列,没有的话该消息就会被丢弃);

这些消息会被默认的Exchange分发给 rpc_queue 这个消息队列。

服务端在声明 rpc_queue 这个消息队列的时候,把这个消息队列设置为独享类型(exclusive:true),那么 rpc_queue 这个消息队列里面的消息就只能被这个服务端消费,不能被其他消费者获取到消息。

因为服务端监听这个 rpc_queue 这个消息队列,所以服务端拿到这个消息队列的消息之后,就会把消息中的 replyTo 和 correlationId 先拿出来,然后同时对消息进行业务逻辑处理。

业务逻辑处理完消息后,服务端需要把这些处理后的消息返回给客户端。
重点就是,每个消息在客户端发来之后,都有一个 correlationId 标识,所以服务端在返回回去时,需要把处理好的消息的原本的correlationId 标识对应的设置回去。

(比如:客户端发来消息 A , A 携带的 correlationId 为 1 ,那么服务端在处理完 A 消息后,需要把 correlationId = 1 再设置回 这个消息 A (就是拿出来,处理完消息,再放回去),这样客户端在接收服务端返回来的处理过后的A消息时,才能根据 correlationId = 1 这个标识,得到想要的被处理过的A消息数据。
因为客户端发的消息可能有成千上万条,需要有这个 correlationId 作为消息的标识,才能准确拿到被处理过的想要的那条A消息)

服务端返回处理过的消息给客户端,也是一个发送消息的过程,所以发送消息指定的消息队列就是这个 replyTo(就是amq.gen-3xxx 这个默认消息队列),这个replyTo 也是从客户端发送来的消息中获取获取一个属性,作用是指定了服务端要将返回的消息送回到这个默认队列。

服务端把处理后的消息返回到 amq.gen-3xxx 这个默认消息队列,因为 客户端就是在监听amq.gen-3xxx 这个默认消息队列,所以客户端就能得到自己一开始发送给服务端,然后服务端处理完成后返回来的消息。

然后客户端就能根据 correlationId 这个标识,准确找到每个被处理修改过后的消息,而不至于找混。再根据需求去对处理过的消息进行业务操作。

(以上仅作为自己梳理代码流程的记录,大佬请直接忽略)
更简单点来说,就是

客户端声明并监听着默认队列 amq.gen-3xxx,然后发送消息到客户端,消息携带有correlationId 和 replyto 两个属性,路由key是rpc_queue,exchange是默认的。

服务端声明并监听 rpc_queue 消息队列,从该队列得到消息(messageA)后,从每个消息中获取该消息对应的 correlationId 和 replyto 两个属性的属性值,然后处理消息,对于处理完的消息(resultMessageA),需要把correlationId 和 replyto 两个属性的属性值重新设置回给resultMessageA,在通过Exchange分发回给 replyto 属性值中指定的消息队列(amq.gen-3xxx)。

然后客户端再从 amq.gen-3xxx 默认的消息队列中获取服务端处理并返回回来的消息,进行对应的消费。

ConstantUtil 常量工具类

在这里插入图片描述

ConnectionUtil RabbitMQ连接工具类

在这里插入图片描述

Server 服务端

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

Client 客户端

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

测试结果

启动测试的时候,一定要先启动服务端,再启动客户端

服务端

在这里插入图片描述

客户端

在这里插入图片描述

QUEUE
在这里插入图片描述

完整代码

ConstantUtil 常量工具类

package cn.ljh.rabbitmq.util;

//常量
public class ConstantUtil
{
    //消息队列实现 RPC(远程过程调用)模型 之 服务器端----------------
    //消息队列
    public final static String RPC_QUEUE = "rpc_queue";


    // ------------topic类型的Exchange,需要的相关常量----------
    public final static String QUEUET01 = "qt_01";
    public final static String QUEUET02 = "qt_02";

    // topic 通配符类型的 Exchange
    public static final String EXCHANGE_NAME_TOPIC = "myex03.topic";

    // Exchange 绑定 Queue 队列的路由key  ,通配符类型      *:匹配一个单词。#:匹配零个或多个单词。
    public static final String[] ROUTING_TOPIC_PATTERNS = {"*.crazyit.*", "*.org", "edu.#"};

    // 生产者发送消息给Excahnge携带的路由key
    public static final String[] ROUTING_TOPIC_KEYS = { "www.crazyit.org", "www.crazyit.cn",
            "edu.crazyit.org", "crazyit.org", "fkjava.org", "edu.fkjava.org", "edu.fkjava", "edu.org"};

    //-------------------------------------------------------

    // 消息队列的名称
    public final static String QUEUE01 = "queue_01";
    public final static String QUEUE02 = "queue_02";
    // Exchange的名称
    public static final String EXCHANGE_NAME = "myex02.direct";
    // 三个路由key定义成一个数组的名称
    public static final String[] ROUTING_KEYS = {"info", "error", "warning"};

}

ConnectionUtil RabbitMQ连接工具类

package cn.ljh.rabbitmq.util;

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

//连接工具
public class ConnectionUtil
{
    //获取连接的方法
    public static Connection getConnection() throws IOException, TimeoutException
    {
        //创建连接工厂----这个ConnectionFactory源码可以看出有构造器,所以直接new一个出来
        ConnectionFactory connectionFactory =  new ConnectionFactory();
        //设置连接信息
        connectionFactory.setHost("localhost");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("ljh");
        connectionFactory.setPassword("123456");
        connectionFactory.setVirtualHost("/"); //连接虚拟主机
        //从连接工厂获取连接
        Connection connection = connectionFactory.newConnection();
        //返回连接
        return connection;
    }
}

Server 服务端

package cn.ljh.rabbitmq.producer;

import cn.ljh.rabbitmq.util.ConnectionUtil;
import cn.ljh.rabbitmq.util.ConstantUtil;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Random;
import java.util.concurrent.TimeoutException;

//消息队列实现 RPC(远程过程调用)模型 之 服务器端
public class Server
{
    public static void main(String[] args) throws IOException, TimeoutException
    {
        //1、创建连接
        Connection conn = ConnectionUtil.getConnection();

        //2、通过Connection获取Channel。
        Channel channel = conn.createChannel();

        //3、使用系统自动创建的默认Exchange,无需声明Exchange

        //消息队列设置为独占(exclusive:true)--------------------------------------------------------------------------------------声明消息队列
        channel.queueDeclare(ConstantUtil.RPC_QUEUE,
                true,   /* 是否持久化 */
                true,  /* 是否只允许只有这个消息队列的消息消费者才可以消费这个消息队列的消息 */
                false, /* 是否自动删除 */
                null); /* 指定这个消息队列的额外参数属性 */

        //不需要关闭资源,因为它也要监听自己消费消息的队列
        //4、调用Channel 的 basicConsume()方法开始消费消息----------------------------------------------------------------------------1、服务端监听并消费消息
        channel.basicConsume(
                ConstantUtil.RPC_QUEUE, /* 消费这个名字的消费队列里面的消息 */
                true,
                new DefaultConsumer(channel)
                {
                    //处理消息:当这个 ConstantUtil.RPC_QUEUE 消息队列收到消息的时候,这个方法就会被触发。重写这个方法:
                    @Override
                    public void handleDelivery(String consumerTag,
                                               Envelope envelope /*消息所在的信封,存放消息的exchange、路由key这些*/,
                                               AMQP.BasicProperties properties /* 消息的那些属性 */,
                                               byte[] body /*body:消息的消息体*/) throws IOException
                    {
                        //把消息体中的消息拿出来,此处读取到的消息,就相当于调用参数-------------------------------------------------------2、服务端获取消息队列的消息
                        String param = new String(body, StandardCharsets.UTF_8);
                        //之前只需要用到消息,现在需要额外读取消息里面携带的两个属性:reply_to 和 correlation_id
                        //消息的属性都存放在 AMQP.BasicProperties 这个属性里面,从这个属性获取 reply_to 和 correlation_id
                        String replyTo = properties.getReplyTo();
                        System.err.println("replyTo:  " + replyTo);

                        String correlationId = properties.getCorrelationId();
                        System.err.println("correlationId:  " + correlationId);

                        //调用服务器的处理消息的方法,最终得到处理后的结果。该方法可以是任意的业务处理,该方法的返回值result是要被送回客户端的。------3、服务端处理消费消息
                        String result = format(param);

                        //printf:格式化输出函数   %s:输出字符串  %n:换行
                        System.err.printf("服务端 收到来自Exchange为【%s】、路由key为【%s】的消息,消息内容为%s%n",
                                envelope.getExchange(), envelope.getRoutingKey(), param);

                        //发送消息的方法,需要把返回值result发送回客户端-------------------------------------------------------4、服务端处理消费完的消息返回客户端的操作
                        channel.basicPublish(
                                "", /* 使用默认的Exchange */
                                replyTo,/* 此处的routing key 应该填 reply_to 属性;  reply_to: 该属性指定了服务器要将返回的消息送回到哪个队列 */

                                //把从客户端的 AMQP.BasicProperties 属性获取到的correlationId,再作为参数传回去,用于客户端和服务器的匹配。
                                new AMQP.BasicProperties()
                                        .builder()
                                        .correlationId(correlationId) /* 也需要返回额外的 correlation_id,要与从客户端消息中读取的 correlation_id 完全一样 */
                                        .deliveryMode(2) /* 设置这个消息是持久化类型的 */
                                        .build(), /*这个.build()的作用就是构建得到这个 BasicProperties 对象,这个对象就包含了 correlationId 属性
                                                   因为服务器端返回的消息一定要有这个correlationId。 */
                                result.getBytes(StandardCharsets.UTF_8)
                        );
                    }
                });
    }

    //模拟服务器端消费消息要做的处理业务逻辑操作
    public static String format(String name)
    {
        //此处模拟让服务器处理这里的业务有快有慢的情况,看correlation_id 能不能还是把数据对应上
        int rand = (new Random().nextInt(40) + 20) * 30;
        try
        {
            Thread.sleep(rand);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        return "《" + name + "》";
    }


}

Client 客户端

package cn.ljh.rabbitmq.consumer;

import cn.ljh.rabbitmq.util.ConnectionUtil;
import cn.ljh.rabbitmq.util.ConstantUtil;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;

//消息队列实现 RPC(远程过程调用)模型 之 客户端
public class Client
{
    // paramMap 保存了 correlationid 与 参数(消息)之间的对应关系
    public static Map<String, String> paramMap = new ConcurrentHashMap<>();

    //客户端发送的消息(参数)
    public static String[] params = new String[]{"火影忍者", "七龙珠", "哆啦A梦", "蜡笔小新"};

    public static void main(String[] args) throws IOException, TimeoutException
    {
        //1、创建连接工厂,设置连接信息,然后再通过连接工厂获取连接
        Connection conn = ConnectionUtil.getConnection();

        //2、通过Connection获取Channel 消息通道
        Channel channel = conn.createChannel();

        //3、调用 Channel 的 queueDeclare() 方法声明队列,声明一个有 RabbitMQ 自动创建的、自动命名的、持久化的、独享的、会自动删除的【默认队列】
        AMQP.Queue.DeclareOk declareOk = channel.queueDeclare();
        System.out.println("declareOk  "+declareOk);

        //.getQueue() 用于得到默认队列的返回值,也就是默认队列的名字,之前声明是我们自己设置队列名,这里用默认的队列,就用.getQueue() 得到队列名。
        String queueName = declareOk.getQueue();
        System.out.println("queueName: "+queueName);

        //4、调用Channel 的 basicConsume()方法开始处理消费消息-----------------------------------------------------------------2、客户端监听服务端处理完消息后返回来的消息
        channel.basicConsume(
                queueName /*消费这个消费队列里面的消息*/,
                true /*消息的确认模式:是否自动确认该消息已经被消费完成并返回确认消息给消息队列*/,
                new DefaultConsumer(channel)
                {
                    //处理消息:当这个消息队列收到消息的时候,这个方法就会被触发。重写这个方法:
                    @Override
                    public void handleDelivery(String consumerTag,
                                               Envelope envelope /*消息所在的信封,存放消息的exchange、路由key这些*/,
                                               AMQP.BasicProperties properties /*消息的那些属性*/,
                                               byte[] body /*body:消息的消息体*/) throws IOException
                    {
                        //把消息体中的消息拿出来
                        String resultMessage = new String(body, StandardCharsets.UTF_8);

                        //此处,需要指定每个返回值对应的是哪个参数,靠的就是correlation_id
                        String correlationId = properties.getCorrelationId();

                        //根据服务器端返回的消息中的correlation_id 获取对应的参数
                        String param = paramMap.get(correlationId);

                        System.err.println("客户端发出去的消息内容:"+param +"  , 服务端处理后返回来的消息内容:"+resultMessage);

                        //printf:格式化输出函数   %s:输出字符串  %n:换行
                        System.out.printf("客户端 收到来自Exchange为【%s】、路由key为【%s】的消息,消息内容为%s%n",
                                envelope.getExchange(), envelope.getRoutingKey(), resultMessage);

                        //得到服务器的返回值之后,整个调用过程就完成了,此时就应该从 Map 中删除这组 key-value对了( correlationId 与 参数的对应关系 )。
                        paramMap.remove(correlationId);
                    }
                }
        );

        //客户端发送消息---------------------------------------------------------------------------代码运行后先执行这段--------------------1、客户端发送消息
        for (int i = 0 ; i < params.length ; i++)
        {
            paramMap.put( i + "" , params[i] );

            channel.basicPublish("", /* 使用默认的Exchange */
                    ConstantUtil.RPC_QUEUE, /* 客户端发送消息携带的路由key是服务端监听的消息队列的名字,且使用了默认的Exchange,这就意味着消息会被发送给服务器监听的那个消息队列 */
                    new AMQP.BasicProperties().builder()
                            .correlationId(i + "")  /* 设置 correlation_id 属性;  correlation_id:该属性指定了服务器返回的消息也要添加相同的correlation_id属性*/
                            .replyTo(queueName)     /* reply_to: 该属性指定了服务器要将返回的消息送回到哪个队列 , 设置 reply_to 属性 */
                            .deliveryMode(2)  /* 持久化消息 */
                            .build(),          /* 构建这个BasicProperties对象,这个对象主要存这个correlationId属性 */
                    params[i].getBytes(StandardCharsets.UTF_8)
            );
        }
    }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.ljh</groupId>
    <artifactId>rabbitmq_rpc</artifactId>
    <version>1.0.0</version>
    <name>rabbitmq_rpc</name>

    <!--  属性  -->
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>11</java.version>
    </properties>

    <!--  依赖  -->
    <dependencies>
        <!-- RabbitMQ 的依赖库 -->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.13.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>compile</scope>
        </dependency>

    </dependencies>



</project>

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

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

相关文章

CocosCreator 面试题(十一)Cocos Creator 屏幕适配

Cocos Creator 提供了多种屏幕适配的方式&#xff0c;以确保游戏在不同设备上能够正确显示和布局。 以下是 Cocos Creator 中常用的屏幕适配方式及其说明。 1、 Cocos Creator 项目设置中统一配置设计分辨率和屏幕适配 在同一个项目里的多个 Canvas 的设计分辨率仍然采用同一…

2023年中国改性ABS树脂产能、产量及市场规模分析[图]

ABS树脂是由丙烯腈&#xff08;Acrylonitrile&#xff09;、丁二烯&#xff08;Butadiene&#xff09;和苯乙烯&#xff08;Styrene&#xff09;三种单体共聚而成的热塑性聚合物&#xff0c;是介于通用塑料和工程塑料之间的一种高分子材料&#xff0c;是五大合成树脂之一。随着…

USB转串口芯片GP232RL 完全兼容替代FT232RL SSOP28

GP232RL是一款高度集成的USB到UART桥接控制器&#xff0c;提供了一种简单的解决方案&#xff0c;可以使用最少的元器件和PCB空 间&#xff0c;将RS232接口转换为USB接口 。GP232RL包括一个USB 2.0全速功能控制器、USB收发器、振荡器、EEPROM和带有完整的调制解调器控制信号的异…

优雅而高效的JavaScript——扩展运算符

&#x1f617;博主&#xff1a;小猫娃来啦 &#x1f617;文章核心&#xff1a;优雅而高效的JavaScript——扩展运算符 文章目录 什么是扩展运算符扩展运算符的定义扩展运算符的作用 扩展运算符在数组中的应用数组的展开数组的合并数组的复制数组的解构赋值 扩展运算符在对象中的…

IDEA创建项目失败提示 Failed to create directory 或 “项目初始化失败”

基本只有一个原因&#xff0c;IDEA对该文件夹操作没有权限 比如你把项目建在了C盘的User文件夹下&#xff0c;User是系统盘&#xff0c;不要乱在里面搞东西 其他教程也许有可能教你文件夹开放权限的方法 但我个人建议&#xff0c;换个普通的文件夹创建项目即可 或者新建个文件…

【计算机毕业设计】python学生成绩补考通知管理系统

经过分析和研究&#xff0c;基于Web的学生成绩管理系统主要包括学生信息管理模块&#xff0c;学生成绩管理模块&#xff0c;学生班级管理模块&#xff0c;学生课程管理模块和系统管理模块。其中信息管理包括信息的浏览和处理&#xff0c;成绩管理包括成绩查询和处理&#xff0c…

【C语言】结构体+位段+枚举+联合(2)

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解结构体和位段以及枚举&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 这是这个系列的第二篇&#xff0c;上一篇详细介绍了结构体的基本知识&#xff0c;详情…

基于SSM的高校疫情管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

HTML 核心技术点基础详细解析以及综合小案例

核心技术点 网页组成 排版标签 多媒体标签及属性 综合案例一 - 个人简介 综合案例二 - Vue 简介 02-标签语法 HTML 超文本标记语言——HyperText Markup Language。 超文本&#xff1a;链接 标记&#xff1a;标签&#xff0c;带尖括号的文本 标签结构 标签要成…

python基于协同过滤算法商品商城购物推荐系统vue

随着移动互联网的普及&#xff0c;电子商务的发展也引来了新一轮的发展&#xff0c;越来越手动消费者的喜爱&#xff0c;网络经济的发展对国家经济的发展也带来了很大的利好&#xff0c;带动了很多实体经济的转型&#xff0c;用户可以通过网络可以买到自己称心如意的商品&#…

Windows提权方法论

Windows提权方法论 1.溢出漏洞提权2.计划任务提权3.SAM文件提权4.启动项提权5.不带引号的服务路径提权 1.溢出漏洞提权 溢出提权攻击的基本原理是&#xff0c;通过向目标系统发送过长的输入数据&#xff0c;超出了程序所分配的缓冲区大小&#xff0c;导致溢出。攻击者可以利用…

如何理解BFC、开启BFC、BFC解决哪些问题

1.BFC 概念 BFC 英文名为 Block Formatting Context (块级格式化上下文) 具体可查看 MDN 2.BFC的作用 元素开启BFC后&#xff0c;子元素不会发生margin塌陷问题元素开启BFC后&#xff0c;子元素浮动&#xff0c;元素不发生高度塌陷元素开启BFC后&#xff0c;该元素不被其他元…

2023年中国分子筛稀土催化材料竞争格局及行业市场规模分析[图]

稀土催化材料能够起到提高催化剂热稳定性、催化剂活性、催化剂储氧能力&#xff0c;以及减少贵金属活性组分用量等作用&#xff0c;广泛应用于石油化工、汽车尾气净化、工业废气和人居环境净化、燃料电池等领域。 2015-2023年中国稀土催化材料规模及预测 资料来源&#xff1a;…

基于SSM的失物招领信息交互平台

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

如何用vscode远程连接Linux服务器

文章目录 一、下载所需插件 二、远程连接 三、更改远程服务器名称 一、下载所需插件 打开商店 下载Remote - SSH 下载Remote - SSH扩展包 二、远程连接 点击远程资源管理器 点击SSH旁边的加号 输入&#xff1a;ssh 用户名服务器地址 介绍 第一个是保存到当前用户第二个是保…

2023年中国印花布产量及发展前景分析:数码印花将成为行业趋势[图]

印花布是用坯布印花纸高温印染加工而成&#xff0c;唐宋时期已很盛行&#xff0c;明清时期达到鼎盛。曾深受人们的喜爱&#xff0c;被作为陪嫁被褥、衣服的必备品。印花布上的图案称作花型&#xff0c;瓦栏、花型创意分享平台。 印花布种类 资料来源&#xff1a;共研产业咨询&…

【算法分析与设计】分支限界法(下)

目录 一、最大团问题1.1 问题描述1.2 上界函数1.3 算法思想 二、旅行售货员问题2.1 问题描述2.2 算法描述 三、电路板排列问题3.1 算法描述 四、批处理作业调度问题4.1 问题的描述4.2 限界函数4.3 算法描述 五、小结六、随机化算法 一、最大团问题 1.1 问题描述 给定无向图G(V…

2023年中国智能矿山发展历程及趋势分析:智能矿山健康有序发展[图]

智能矿山系统对矿山生产提质增效的效果已经开始显现&#xff1a;对不合规、有风险的行动进行及时预警&#xff0c;减少安全事故发生概率&#xff0c;避免因停产整顿产生的巨额亏损&#xff1b;精细化管理整个生产流程&#xff0c;避免过往传统粗放的流程导致的浪费&#xff0c;…

2023年中国精准PCI行业发展规模及发展趋势分析:精准PCI日益普及[图]

精准PCI的主要诊断技术包括作为血管内生理学诊断技术的血流储备分数&#xff08;FFR&#xff09;&#xff0c;以及作为成像技术的血管内超声 &#xff08;IVUS&#xff09;及光学相干断层扫描&#xff08;OCT&#xff09;。随着功能性和解剖评估的发展&#xff0c;在精密PCI领…

IDEA启动C:\Users\badboy\.jdks\corretto-17.0.7\bin\java.exe -Xmx700m报错

出现的现象 这里没有记录当时的截图&#xff0c;主要报错如下&#xff1a; C:\Users\badboy.jdks\corretto-17.0.7\bin\java.exe -Xmx700m … Error occurred during initialization of VM Failed setting boot class path. 排查方式 遇到这种问题我首先就是百度&#xff0c;…