Springboot + netty + rabbitmq + myBatis

news2024/9/27 11:54:18

目录

  • 0.为什么用消息队列
  • 1.代码文件创建结构
  • 2.pom.xml文件
  • 3.三个配置文件开发和生产环境
  • 4.Rabbitmq 基础配置类 TtlQueueConfig
  • 5.建立netty服务器 + rabbitmq消息生产者
  • 6.建立常规队列的消费者 Consumer
  • 7.建立死信队列的消费者 DeadLetterConsumer
  • 8.建立mapper.xml文件
  • 9.建立mapper文件接口
  • 10.建立接口ProducerController 测试
  • 11.测试接口请求1
  • 12.测试接口请求2
  • 13.网络助手测试NetAssist.exe
  • 14.观察rabbitmq界面管理简单介绍

0.为什么用消息队列

  1. 流量消峰
  2. 应用解耦
  3. 异步确认

1.代码文件创建结构

在这里插入图片描述

2.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.test</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <packaging>war</packaging>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <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-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
        </dependency>

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.86.Final</version> <!-- 根据需要选择版本 -->
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project> 

3.三个配置文件开发和生产环境

在这里插入图片描述
文件一 application.properties

#开发环境
spring.profiles.active=dev
#生产环境
#spring.profiles.active=prod

文件二 application-dev.properties 开发环境

spring.application.name=demo
server.servlet.context-path=/demo1
server.port=1001
#spring.main.allow-circular-references=true
spring.rabbitmq.host=实际ip地址
spring.rabbitmq.port=5672
spring.rabbitmq.username=root
spring.rabbitmq.password=root
spring.rabbitmq.virtual-host=/
##创建单线程监听容器 本项目目前用的是单线程  这里预期值2000 需根据实际情况调整
spring.rabbitmq.listener.simple.prefetch=2000
##创建多线程监听容器
#spring.rabbitmq.listener.direct.prefetch=2000
#spring.rabbitmq.listener.simple.acknowledge-mode=auto
# application.properties 示例
spring.rabbitmq.listener.simple.acknowledge-mode=MANUAL
#开启消息确认机制
spring.rabbitmq.publisher-confirm-type=correlated
spring.rabbitmq.publisher-returns=true


netty.server.port=1002
netty.server.bossThreads=1
netty.server.workerThreads=1


server.max-http-header-size=655360
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
mybatis.type-aliases-package=com.mt.entity
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
logging.level.com.ysd.mapper=info
logging.file.name=demo.log
logging.level.com.mt.mapper=info
#mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis.configuration.map-underscore-to-camel-case=true
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
spring.main.allow-circular-references=true
spring.jackson.default-property-inclusion=non_null

#<!--=====================数据库1 ====================-->
spring.datasource.dynamic.英文数据库名称1.url=jdbc:mysql://ip地址:3306/英文数据库名称?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.dynamic.datasource.英文数据库名称1.username=root
spring.datasource.dynamic.datasource.英文数据库名称1.password=root
spring.datasource.dynamic.datasource.英文数据库名称1.driver-class-name=com.mysql.cj.jdbc.Driver
#<!--=====================数据库2 ====================-->
spring.datasource.dynamic.英文数据库名称2.url=jdbc:mysql://ip地址:3306/英文数据库名称?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.dynamic.datasource.英文数据库名称2.username=root
spring.datasource.dynamic.datasource.英文数据库名称2.password=root
spring.datasource.dynamic.datasource.英文数据库名称2.driver-class-name=com.mysql.cj.jdbc.Driver

4.Rabbitmq 基础配置类 TtlQueueConfig

rabbitmq基础信息配置已经在application-dev.properities中进行配置过一部分

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class TtlQueueConfig {
    public static final String X_EXCHANGE = "X";
    public static final String QUEUE_A = "QA";
    public static final String QUEUE_B = "QB";
    public static final String Y_DEAD_LETTER_EXCHANGE = "Y";
    public static final String DEAD_LETTER_QUEUE = "QD";

    @Bean("xExchange")
    public DirectExchange xExchange() {
        return new DirectExchange(X_EXCHANGE);
    }

    @Bean("yExchange")
    public DirectExchange yExchange() {
        return new DirectExchange(Y_DEAD_LETTER_EXCHANGE);
    }

    @Bean("queueA")
    public Queue queueA() {
        Map<String, Object> args = new HashMap<>(3);
        //声明当前队列绑定的死信交换机
        args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
        //声明当前队列的死信路由 key
        args.put("x-dead-letter-routing-key", "YD");
        //声明队列的 TTL
        args.put("x-message-ttl", 150000);//超出150秒没有被消费  就会进入死信队列
        return QueueBuilder.durable(QUEUE_A).withArguments(args).build();
    }

    @Bean
    public Binding queueaBindingX(@Qualifier("queueA") org.springframework.amqp.core.Queue queueA,
                                  @Qualifier("xExchange") DirectExchange xExchange) {
        return BindingBuilder.bind(queueA).to(xExchange).with("XA");
    }

    //声明队列 B ttl 为 40s 并绑定到对应的死信交换机
    @Bean("queueB")
    public Queue queueB() {
        Map<String, Object> args = new HashMap<>(3);
        //声明当前队列绑定的死信交换机
        args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
        //声明当前队列的死信路由 key
        args.put("x-dead-letter-routing-key", "YD");
        //声明队列的 TTL
        args.put("x-message-ttl", 40000);//超出40秒没有被消费  就会进入死信队列
        return QueueBuilder.durable(QUEUE_B).withArguments(args).build();
    }

    //声明队列 B 绑定 X 交换机
    @Bean
    public Binding queuebBindingX(@Qualifier("queueB") Queue queue1B,
                                  @Qualifier("xExchange") DirectExchange xExchange) {
        return BindingBuilder.bind(queue1B).to(xExchange).with("XB");
    }

    //声明死信队列 QD
    @Bean("queueD")
    public Queue queueD() {
        return new Queue(DEAD_LETTER_QUEUE);
    }

    //声明死信队列 QD 绑定关系
    @Bean
    public Binding deadLetterBindingQAD(@Qualifier("queueD") Queue queueD,
                                        @Qualifier("yExchange") DirectExchange yExchange) {
        return BindingBuilder.bind(queueD).to(yExchange).with("YD");
    }

    @Bean
    public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory = new SimpleRabbitListenerContainerFactory();
        //这个connectionFactory就是我们自己配置的连接工厂直接注入进来
        simpleRabbitListenerContainerFactory.setConnectionFactory(connectionFactory);
        //这边设置消息确认方式由自动确认变为手动确认
        simpleRabbitListenerContainerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        //设置消息预取数量
        // simpleRabbitListenerContainerFactory.setPrefetchCount(1);
        return simpleRabbitListenerContainerFactory;
    }
}

5.建立netty服务器 + rabbitmq消息生产者

创建服务器类 初始化启动服务器NettyServer

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;




@Component
@Slf4j
public class NettyServer implements ApplicationRunner {
    @Autowired
    private RabbitTemplate rabbitTemplate;
	//有可能在项目初始化的时候加载不出来导致项目隐形报错
	//加载application-dev.proerities文件中 对应参数配置项
    @Value("${netty.server.port}")
    private int port;

    public int getPort () {
        return port;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("netty服务启动端口"+getPort());
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // 用于接收进来的连接
        EventLoopGroup workerGroup = new NioEventLoopGroup(); // 用于处理已经被接收的连接
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // 使用Nio的通道类型
                .childHandler(new ChannelInitializer<SocketChannel>() { // 添加一个处理器来处理接收到的数据
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline p = ch.pipeline();
                        p.addLast(new StringDecoder());
                        p.addLast(new StringEncoder());
                        p.addLast(new SimpleChannelInboundHandler<String>() {
                            @Override
                            protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                                //netty监听端口消息发送消息通过rabbitmq生产者发送到消息队列
                                sendMessage(ctx,msg);
                            }

                            @Override
                            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
                                cause.printStackTrace();
                                ctx.close();
                            }
                        });
                    }
                });

        // 绑定端口,开始接收进来的连接
        ChannelFuture f = b.bind(getPort()).sync();
        // 等待服务器socket关闭
        f.channel().closeFuture().sync();
    }
    
 	//netty监听端口消息发送消息通过rabbitmq生产者发送到消息队列中
 	//充当消息的生产者发送
    public  void  sendMessage(ChannelHandlerContext ctx, String msg){
	  //  接收netty监听信息来源作为消息生产者
        rabbitTemplate.convertAndSend("X","XA","来自QA"+msg);
        ctx.writeAndFlush("===>"+msg);
    }
}

6.建立常规队列的消费者 Consumer

import com.rabbitmq.client.Channel;
import com.test.demo.service.MessageProcessService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class Consumer {
    @Resource
    private MessageProcessService messProceService;
    private Map<Integer, Message> messageMap = new HashMap<>();
    private  int i =1;
    private  int j =1;
    @RabbitListener(queues = "QA")
    public void receiveQA(Message message, Channel channel) throws InterruptedException, IOException {
        i++;
        synchronized (this) {
            messageMap.put(i,message);
            if (messageMap.size() >= 1500) {
                    // 模拟数据库插入操作
        System.out.println("模拟插入数据库操作,QA队列处理消息数:" + messageMap.size());
                processMessagesBatch(channel);
                System.out.println("模拟插入成功, 准备进行下一次收集");
            }
        }
    }


//    @RabbitListener(queues = "QB")
//    public void receiveQB(Message message, Channel channel) throws IOException {
//        String msg = new String(message.getBody());
//        log.info("当前时间:{},监听QB队列信息{}", new Date().toString(), msg);
//    }

    public static class SleepUtils {
        public static void sleep(int second) {
            try {
                Thread.sleep(1000 * second);
            } catch (InterruptedException _ignored) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private void processMessagesBatch(Channel channel) throws  IOException {
        List<String> list = new ArrayList<>();
        long startTime = System.currentTimeMillis();
        Map<Integer, Message> tempMessageMap = new HashMap<>(messageMap);
        messageMap.clear();
        for (Map.Entry<Integer, Message> map : tempMessageMap.entrySet()) {
            list.add("C" + (j++) + ":===>" + "QA队列收集" + new String(map.getValue().getBody()));
        }
        int count = messProceService.add(list);
        if (count == list.size()) {
            System.out.println("插入数据成功");
            for (Map.Entry<Integer, Message> map : tempMessageMap.entrySet()) {
                channel.basicAck(map.getValue().getMessageProperties().getDeliveryTag(), false);
            }
            list.clear();
        }
        long durationSeconds = (System.currentTimeMillis() - startTime) / 1000;
        System.out.println("插入1500条数据执行时间: " + durationSeconds);
    }
}

7.建立死信队列的消费者 DeadLetterConsumer

import com.rabbitmq.client.Channel;
import com.test.demo.service.MessageProcessService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;

@Slf4j
@Component
public class DeadLetterConsumer {
    @Resource
    private MessageProcessService messProceService;
    private Map<Integer, Message> messageMap = new HashMap<>();
    private int i = 1;
    private  int j =1;
    @RabbitListener(queues = "QD")
    public void receiveD(Message message, Channel channel) throws IOException {
        i++;
        synchronized (this) {
            messageMap.put(i, message);
            if (messageMap.size() >= 100) {
                // 模拟数据库插入操作
                System.out.println("模拟插入数据库操作,死信队列处理消息数:" + messageMap.size());
                processMessagesBatch(channel);
                System.out.println("模拟插入成功, 准备进行下一次收集");
                // 确认当前消息,以便RabbitMQ知道它可以释放此消息
            }
        }
    }

    private void processMessagesBatch(Channel channel) throws  IOException {
        List<String> list = new ArrayList<>();
        // 复制当前消息映射以避免在迭代时修改
        Map<Integer, Message> tempMessageMap = new HashMap<>(messageMap);
        messageMap.clear(); 
        long startTime = System.currentTimeMillis();
        for (Map.Entry<Integer, Message> map : tempMessageMap.entrySet()) {
            list.add("C" + (j++) + ":===>" + "死信队列收集" + new String(map.getValue().getBody()));
        }
        int count = messProceService.add(list);
        long durationSeconds = (System.currentTimeMillis() - startTime) / 1000;
        System.out.println("插入50条数据执行时间: " + durationSeconds);
        if (count == list.size()) {
            System.out.println("插入数据成功");
            list.clear();
            for (Map.Entry<Integer, Message> map : tempMessageMap.entrySet()) {
                channel.basicAck(map.getValue().getMessageProperties().getDeliveryTag(), false);
            }
        }
    }

}

8.建立mapper.xml文件

MessageProcessMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.demo.mapper.MessageProcessMapper">
    <insert id="add">
        INSERT INTO t_xinyang_direct (direct,create_date)
        VALUES
        <foreach collection="list" item="item" separator=",">
            (
            #{item},
             SYSDATE()
            )
        </foreach>
    </insert>
</mapper>

9.建立mapper文件接口

MessageProcessMapper


import com.baomidou.dynamic.datasource.annotation.DS;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
@DS("英文数据库名称1")
public interface MessageProcessMapper {
    int add(List<String> list);
}

10.建立接口ProducerController 测试

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
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;

@Slf4j
@RestController
@RequestMapping("/api")
public class ProducerController {
    @GetMapping("/hello/{message}")
    public ResponseEntity<String> sayHello(@PathVariable String message) {
        return ResponseEntity.ok("Hello, RabbitMQ!==>"+message);
    }

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @GetMapping("/sendMsg/{message}")
    public void sendMsg(@PathVariable String message){
        log.info("接收接口发送的消息请求");
        rabbitTemplate.convertAndSend("X","XA","消息来自tt1为10s的队列"+message);
        rabbitTemplate.convertAndSend("X","XB","消息来自tt1为150s的队列"+message);
    }

}

11.测试接口请求1

测试请求接口 /hello
http://实际本机ip地址:1001/demo1/api/hello/返回浏览器输入内容
在这里插入图片描述

12.测试接口请求2

测试请求接口 /hello
http://实际本机ip地址:1001/demo1/api/sendRabbitMq/发给rabbitmq消息
在这里插入图片描述

13.网络助手测试NetAssist.exe

主要目的模拟向netty端口进行发送数据,通过netty监听到的信息然后通过rabbitmq的生产者发送rabbitmq的队列中,让消费者进行消费,如果消费者绑定死信队列,那么消费者从队列中取出消息后,经过一定时间未确认即不进行消费确认或者拒绝,然后入之前绑定好的死信队列中,供死信队列绑定的死信消费者进行消费处理。

14.观察rabbitmq界面管理简单介绍

在这里插入图片描述

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

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

相关文章

Visual Studio导出动态库

1、创建新项目&#xff0c;选择如下 2、工程目录结构如下 3、编写pch.h文件&#xff0c;内容如下 // pch.h: 这是预编译标头文件。 // 下方列出的文件仅编译一次&#xff0c;提高了将来生成的生成性能。 // 这还将影响 IntelliSense 性能&#xff0c;包括代码完成和许多代码浏…

AI驱动TDSQL-C Serverless 数据库技术实战营-融合智能体与TDSQL-C技术,高效实现二手房数据查询与分析应用

文章目录 什么是TDSQL-C技术创新算力服务器与数据库服务器申请与部署购买 TDSQL-C Mysql Serverless 实例购买HAI高算力服务器 准备工作准备数据下载依赖 案例研发创建数据库写入数据智能体与TDSQL-C 的结合应用第一步配置llama3.1第二步代码开发运行应用测试应用 总结 什么是T…

戏曲多多 1.0.6.0 专为电视端设计的戏曲与生活内容APP,同样适用于安卓手机,方便老年人使用

戏曲多多是一款专为电视端设计的应用程序&#xff0c;也可安装在安卓手机上&#xff0c;特别适合不会使用手机的老年人。该应用包含戏曲、红歌、儿歌、老电影等丰富内容&#xff0c;非常适合老年人的观看喜好。此外&#xff0c;还有广场舞教学、养生太极教程&#xff08;包括太…

Qualitor processVariavel.php 未授权命令注入漏洞复现(CVE-2023-47253)

0x01 漏洞概述 Qualitor 8.20及之前版本存在命令注入漏洞,远程攻击者可利用该漏洞通过PHP代码执行任意代码。 0x02 复现环境 FOFA&#xff1a;app"Qualitor-Web" 0x03 漏洞复现 PoC GET /html/ad/adpesquisasql/request/processVariavel.php?gridValoresPopHi…

3 pyqt5 Layout布局(保证主界面缩放各组件也对应缩放)== 主要有Qt Designer和完全代码设置两种设计方式(根据自己情况选择即可)

文章目录 前言一、Layout的类别二、使用Qt Designer进行Layout布局三、完全使用代码进行Layout布局前言 本节我们的http测试的例子,只实现界面方面的逻辑,底层不用管。我们主要的目的是通过这个例子设计界面布局。 我们前面写的界面程序有个问题,如果你用鼠标拖拽主窗口边…

雷达系统中杂波信号的建模与仿真

雷达系统中杂波信号的建模与仿真 2 杂波建模与模拟方法 2.1 杂波建模 杂波可以说是雷达在所处环境中接收到的不感兴趣的回波[4]。就像目标回波一样&#xff0c;杂波也是极为复杂的。为了有效地克服杂波对信号检测的影响&#xff0c;需要知道杂波的幅度特性以及频谱特性。除独…

JavaScript爬虫:数据抓取的艺术与实践

在当今数据驱动的世界中&#xff0c;JavaScript作为一种广泛使用的编程语言&#xff0c;不仅在前端开发中占据重要地位&#xff0c;也可以用于编写爬虫程序&#xff0c;从互联网上抓取有价值的数据。本文将介绍如何使用JavaScript编写爬虫程序&#xff0c;并探讨其在数据抓取中…

Harmony next Native API 开发工程实践

旨在通过文档&#xff0c;了解华为native开发中的关键节点&#xff0c;能够搭建一个比较实用的native功能、 目录 前言 一、napi是什么&#xff1f; 二、简单开始一个napi的实例 1.适配架构配置 2.新增实现 3.官方实例 4.napi与ark通讯的几种方式 Native侧的CallNative Native …

从耐用到防水:全面综合评估SD卡的性能指标

SD卡&#xff08;Secure Digital Memory Card&#xff09;是一种广泛使用的存储器件&#xff0c;因其快速的数据传输速度、可热插拔的特性以及较大的存储容量&#xff0c;广泛应用于各种场景&#xff0c;例如在便携式设备如智能手机、平板电脑、运动相机等&#xff0c;用于存储…

Docker镜像、Spark支持多表...Apache SeaTunnel 2.3.8版本将带来的惊喜

Apache SeaTunnel 2.3.8版本即将于大家见面&#xff0c;近日&#xff0c;Apache SeaTunnel PMC Member 范佳在社区的交流会上为大家提前透露了关于这个新版本即将进行的功能与特性更新概况&#xff0c;详细内容如下&#xff1a; SeaTunnel 简介 SeaTunnel是一个高性能的开源分…

【生物服务器】DAP-seq与H3K4me3 ChIP-seq服务,推动表观遗传学研究的创新工具

查看全文>>>探索基因调控新维度&#xff1a;汇智生物的DAP-seq与H3K4me3 ChIP-seq服务&#xff0c;推动表观遗传学研究的创新工具与合作案例 北京汇智精研生物科技由毕业于中国科学院、北京大学肿瘤医院、中国农科院、中国农业大学等科研院所的国家高精尖人才发起&…

计算机网络--HTTP协议

1.TCP,UDP的对比图 TCP:面向连接的,可靠的,字节流服务; UDP:无连接的,不可靠的,数据报服务; 2.补充网络部分的其他知识点 1).复位报文段 在某些特殊条件下&#xff0c; TCP 连接的一端会向另一端发送携带 RST 标志的报文段&#xff0c;即复位报文段&#xff0c;已通知对方…

【通知】“长三角档案数字资源长期保存与数据安全治理”专题培训

关注我们 - 数字罗塞塔计划 - 为加强长三角地区档案数字资源长期安全管理&#xff0c;提升档案管理人员档案信息化水平和实务技能&#xff0c;推动长三角地区档案数字化转型向纵深发展&#xff0c;上海市档案服务和教育中心将于近期举办“长三角档案数字资源长期保存与数据安全…

【裸机装机系列】16.kali(ubuntu)-安装linux和win双系统-重装win11步骤

推荐阅读&#xff1a; 1.kali(ubuntu)-为什么弃用ubuntu&#xff0c;而选择基于debian的kali操作系统 注意&#xff1a; 要先装windows&#xff0c;再装linux&#xff0c;不然linux的启动分区会被覆盖掉。为什么双系统要先装windows呢&#xff1f; 在一个新硬盘上&#xff0…

旺店通ERP集成金蝶K3(金蝶K3主供应链)

源系统成集云目标系统 金蝶K3介绍 金蝶K3是一款ERP软件&#xff0c;它集成了供应链管理、财务管理、人力资源管理、客户关系管理、办公自动化、商业分析、移动商务、集成接口及行业插件等业务管理组件。以成本管理为目标&#xff0c;计划与流程控制为主线&#xff0c;通…

原生APP与其他类型APP的对比

移动应用主要分为原生APP、混合APP和Web APP三种。每种类型都有其独特的优势和局限性。 原生APP 优点&#xff1a;性能卓越&#xff1a; 直接调用设备硬件&#xff0c;运行流畅&#xff0c;用户体验最佳。用户界面定制化程度高&#xff1a; 可以充分利用平台的UI组件&#xff…

SD卡认识——SDIO协议入门与实践(一)

最初是三年前&#xff0c;接手开发sdio裸机驱动和测试用例的工作&#xff0c;内容很多&#xff0c;一开始就是各种在官网、各大论坛以及开源的驱动和例程里学习&#xff0c;简单浏览了sdio控制器、SD卡、MMC、SDIO卡等协议&#xff0c;然后就是开干&#xff1b;鉴于sdio协议还是…

Java GC:GC算法、GC回收器、GC日志

文章目录 基本概念垃圾回收类型垃圾回收算法垃圾回收器 VM参数设置控制vm参数内存参数GC参数 GC实例分析 基本概念 垃圾回收类型 Minor GC:对新生代进行的垃圾回收&#xff0c;所以也叫Young GCMajor GC:对老年代进行的垃圾回收&#xff0c;所以也叫Old GCFull GC:对整个Java…

无人机的作战指挥中心-地面站!

无人机与地面站的关系 指挥与控制&#xff1a;地面站是无人机系统的核心控制部分&#xff0c;负责对无人机进行远程指挥和控制。无人机根据地面站下达的任务自主完成飞行任务&#xff0c;并实时向地面站反馈飞行状态和任务执行情况。 任务规划与执行&#xff1a;地面站具备任…

Laravel部署后,CPU 使用率过高

我在部署 Laravel 应用程序时遇到严重问题。当访问量稍微大一点的时候&#xff0c;cpu马上就到100%了&#xff0c; 找了一大堆文档和说明&#xff0c;都是说明laravel处理并发的能力太弱&#xff0c;还不如原生的php。最后找到swoole解决问题。 1、php下载swoole插件&#xff0…