SpringBoot中使用@Async实现异步调用

news2024/12/23 9:10:18

SpringBoot中使用@Async实现异步调用

什么是异步调用?异步调用对应的是同步调用,同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上

一行程序执行完成之后才能执行;异步调用指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程

序。

1、pom依赖

<?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.5.6</version>
        <relativePath/>
    </parent>

    <groupId>com.async</groupId>
    <artifactId>spring-boot-async</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-async</name>
    <description>Spring Boot 中使用@Async实现异步调用,加速任务执行!</description>

    <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.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2、同步调用

下面通过一个简单示例来直观的理解什么是同步调用。

定义 Task 类,创建三个处理函数分别模拟三个执行任务的操作,操作消耗时间随机取(10秒内)。

package com.async;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Random;

/**
 * @author zhangshixing
 * @date 2021年10月29日 14:41
 */

@Slf4j
@Component
public class AsyncTasks {

    public static Random random = new Random();

    public void doTaskOne() throws Exception {
        log.info("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (end - start) + "毫秒");
    }

    public void doTaskTwo() throws Exception {
        log.info("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务二,耗时:" + (end - start) + "毫秒");
    }

    public void doTaskThree() throws Exception {
        log.info("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务三,耗时:" + (end - start) + "毫秒");
    }

}

在单元测试用例中,注入Task对象,并在测试用例中执行doTaskOnedoTaskTwodoTaskThree三个函数。

package com.async;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.CompletableFuture;

@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests {

	@Autowired
	private AsyncTasks asyncTasks;

	@Test
	public void test() throws Exception {
		asyncTasks.doTaskOne();
		asyncTasks.doTaskTwo();
		asyncTasks.doTaskThree();
	}

}

执行单元测试,可以看到类似如下输出:

开始做任务一
完成任务一,耗时:8577毫秒
开始做任务二
完成任务二,耗时:185毫秒
开始做任务三
完成任务三,耗时:3702毫秒

任务一、任务二、任务三顺序的执行完了,换言之doTaskOnedoTaskTwodoTaskThree三个函数顺序的执行

完成。

3、异步调用

上述的同步调用虽然顺利的执行完了三个任务,但是可以看到执行时间比较长,若这三个任务本身之间不存在依赖

关系,可以并发执行的话,同步调用在执行效率方面就比较差,可以考虑通过异步调用的方式来并发执行。

在Spring Boot中,我们只需要通过使用@Async注解就能简单的将原来的同步函数变为异步函数,Task类改在为

如下模式:

package com.async;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Random;

/**
 * @author zhangshixing
 * @date 2021年10月29日 15:28
 */
@Slf4j
@Component
public class AsyncTasks1 {

    public static Random random = new Random();

    @Async
    public void doTaskOne() throws Exception {
        log.info("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (end - start) + "毫秒");
    }

    @Async
    public void doTaskTwo() throws Exception {
        log.info("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务二,耗时:" + (end - start) + "毫秒");
    }

    @Async
    public void doTaskThree() throws Exception {
        log.info("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务三,耗时:" + (end - start) + "毫秒");
    }

}

为了让@Async注解能够生效,还需要在Spring Boot的主程序中配置@EnableAsync,如下所示:

package com.async;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
@SpringBootApplication
public class SpringBootAsyncApplication {

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

}

在单元测试用例中,注入Task对象,并在测试用例中执行doTaskOnedoTaskTwodoTaskThree三个函数。

package com.async;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.CompletableFuture;

@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests {
    
	@Autowired
	private AsyncTasks1 asyncTasks1;

	@Test
	public void test1() throws Exception {
		asyncTasks1.doTaskOne();
		asyncTasks1.doTaskTwo();
		asyncTasks1.doTaskThree();
	}

}

执行单元测试,可以看到类似如下输出:

开始做任务二
开始做任务一

此时可以反复执行单元测试,您可能会遇到各种不同的结果,比如:

  • 没有任何任务相关的输出

  • 有部分任务相关的输出

  • 乱序的任务相关的输出

原因是目前doTaskOnedoTaskTwodoTaskThree三个函数的时候已经是异步执行了。主程序在异步调用

之后,主程序并不会理会这三个函数是否执行完成了,由于没有其他需要执行的内容,所以程序就自动结束

了,导致了不完整或是没有输出任务相关内容的情况。

注:@Async所修饰的函数不要定义为static类型,这样异步调用不会生效。

为了让doTaskOnedoTaskTwodoTaskThree能正常结束,假设我们需要统计一下三个任务并发执行共耗时多

少,这就需要等到上述三个函数都完成调动之后记录时间,并计算结果。那么我们如何判断上述三个异步调用是否

已经执行完成呢?我们需要使用CompletableFuture<T>来返回异步调用的结果:

package com.async;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.CompletableFuture;

/**
 * @author zhangshixing
 * @date 2021年10月29日 15:47
 */
@Slf4j
@Component
public class AsyncTasks2 {

    public static Random random = new Random();

    @Async
    public CompletableFuture<String> doTaskOne() throws Exception {
        log.info("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (end - start) + "毫秒");
        return CompletableFuture.completedFuture("任务一完成");
    }

    @Async
    public CompletableFuture<String> doTaskTwo() throws Exception {
        log.info("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务二,耗时:" + (end - start) + "毫秒");
        return CompletableFuture.completedFuture("任务二完成");
    }

    @Async
    public CompletableFuture<String> doTaskThree() throws Exception {
        log.info("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务三,耗时:" + (end - start) + "毫秒");
        return CompletableFuture.completedFuture("任务三完成");
    }

}

按照如上方式改造一下其他两个异步函数之后,下面我们改造一下测试用例,让测试在等待完成三个异步调用之后

来做一些其他事情。

package com.async;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.CompletableFuture;

@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests {

	@Autowired
	private AsyncTasks2 asyncTasks2;

	@Test
	public void test2() throws Exception {
		long start = System.currentTimeMillis();
		CompletableFuture<String> task1 = asyncTasks2.doTaskOne();
		CompletableFuture<String> task2 = asyncTasks2.doTaskTwo();
		CompletableFuture<String> task3 = asyncTasks2.doTaskThree();
		CompletableFuture.allOf(task1, task2, task3).join();
		long end = System.currentTimeMillis();
		log.info("任务全部完成,总耗时:" + (end - start) + "毫秒");
	}

}

看看我们做了哪些改变:

  • 在测试用例一开始记录开始时间
  • 在调用三个异步函数的时候,返回CompletableFuture<String>类型的结果对象
  • 通过CompletableFuture.allOf(task1, task2, task3).join()实现三个异步任务都结束之前的阻塞效果
  • 三个任务都完成之后,根据结束时间 - 开始时间,计算出三个任务并发执行的总耗时。

执行一下上述的单元测试,可以看到如下结果:

开始做任务二
开始做任务一
完成任务二,耗时:5375毫秒
开始做任务三
完成任务一,耗时:6298毫秒
完成任务三,耗时:9388毫秒
任务全部完成,总耗时:14780毫秒

可以看到,通过异步调用,让任务一、二、三并发执行,有效的减少了程序的总运行时间。

4、千万不要这样使用@Async注解

在实际的项目中,对于一些用时比较长的代码片段或者函数,我们可以采用异步的方式来执行,这样就不会影响整

体的流程了。比如我在一个用户请求中需要上传一些文件,但是上传文件的耗时会相对来说比较长,这个时候如果

上传文件的成功与否不影响主流程的话,就可以把上传文件的操作异步化,在spring boot中比较常见的方式就是

把要异步执行的代码片段封装成一个函数,然后在函数头使用@Async注解,就可以实现代码的异步执行(当然首

先得在启动类上加上@EnableAsync注解了)。

下面这里主要讲解使用 @Async 注解中遇到的一个坑。

那么这个坑是什么呢?就是如果你在同一个类里面调用一个自己的被@Async修饰的函数时,这个函数将不会被异

步执行,它依然是同步执行的,下面来演示一下。

记得在启动类上加上@EnableAsync注解。

package com.async;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
@SpringBootApplication
public class SpringBootAsyncApplication {

	public static void main(String[] args) {

		SpringApplication.run(SpringBootAsyncApplication.class, args);
	}

}

然后再新建一个类Task,用来放三个异步任务doTaskOnedoTaskTwodoTaskThree

package com.async;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Random;

/**
 * @author zhangshixing
 * @date 2021年10月29日 15:28
 */
@Slf4j
@Component
public class AsyncTasks1 {

    public static Random random = new Random();

    @Async
    public void doTaskOne() throws Exception {
        log.info("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (end - start) + "毫秒");
    }

    @Async
    public void doTaskTwo() throws Exception {
        log.info("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务二,耗时:" + (end - start) + "毫秒");
    }

    @Async
    public void doTaskThree() throws Exception {
        log.info("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务三,耗时:" + (end - start) + "毫秒");
    }

}

在单元测试类上注入Task,在测试用例上测试这三个方法的执行过程:

package com.async;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests1 {

	@Autowired
	AsyncTasks1 asyncTasks1;

	@Test
	void contextLoads() throws Exception {
		asyncTasks1.doTaskOne();
		asyncTasks1.doTaskTwo();
		asyncTasks1.doTaskThree();
		Thread.sleep(10000);
	}
}

为了让这三个方法执行完,我们需要再单元测试用例上的最后一行加上一个延时,不然等函数退出了,异步任务还

没执行完。

我们启动看看效果:

开始做任务一
开始做任务二
完成任务二,耗时:314毫秒
开始做任务三
完成任务一,耗时:1365毫秒
完成任务三,耗时:3376毫秒

我们看到三个任务确实是异步执行的,那我们再看看错误的使用方法

我们在测试类里面把这三个函数再写一遍,并在测试用例上调用测试类自己的方法:

package com.async;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.annotation.Async;

import java.util.Random;

@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests2 {

    public static Random random = new Random();

    @Test
    void contextLoads() throws Exception {
        doTaskOne();
        doTaskTwo();
        doTaskThree();
        Thread.sleep(10000);
    }

    @Async
    public void doTaskOne() throws Exception {
        System.out.println("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
    }

    @Async
    public void doTaskTwo() throws Exception {
        System.out.println("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
    }

    @Async
    public void doTaskThree() throws Exception {
        System.out.println("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
    }
}

我们再看看效果:

开始做任务一
完成任务一,耗时:1332毫秒
开始做任务二
完成任务二,耗时:7568毫秒
开始做任务三
完成任务三,耗时:1419毫秒

它们竟然是顺序执行的!也就是同步执行,并没有达到异步的效果。

5、用@Async会内存溢出?看看你的线程池配置了没?

上面我们介绍了如何使用@Async注解来创建异步任务,我可以用这种方法来实现一些并发操作,以加速任务的执

行效率。但是,如果只是如前文那样直接简单的创建来使用,可能还是会碰到一些问题。存在有什么问题呢?先来

思考下,下面的这个接口,通过异步任务加速执行的实现,是否存在问题或风险呢?

package com.async.controller;

import com.async.AsyncTasks2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;

/**
 * @author zhangshixing
 * @date 2021年10月30日 9:15
 */
@RestController
public class HelloController {

    @Autowired
    private AsyncTasks2 asyncTasks;

    @GetMapping("/hello")
    public String hello() throws Exception {
        // 将可以并行的处理逻辑,拆分成三个异步任务同时执行
        CompletableFuture<String> task1 = asyncTasks.doTaskOne();
        CompletableFuture<String> task2 = asyncTasks.doTaskTwo();
        CompletableFuture<String> task3 = asyncTasks.doTaskThree();
        CompletableFuture.allOf(task1, task2, task3).join();
        return "Hello World";
    }
}

虽然,从单次接口调用来说,是没有问题的。但当接口被客户端频繁调用的时候,异步任务的数量就会大量增长:

3 x n(n为请求数量),如果任务处理不够快,就很可能会出现内存溢出的情况。那么为什么会内存溢出呢?根本

原因是由于Spring Boot默认用于异步任务的线程池是这样配置的:

在这里插入图片描述

图中我标出的两个重要参数是需要关注的:

  • queueCapacity:缓冲队列的容量,默认为INT的最大值(2的31次方-1)。

  • maxSize:允许的最大线程数,默认为INT的最大值(2的31次方-1)。

所以,默认情况下,一般任务队列就可能把内存给堆满了。所以,我们真正使用的时候,还需要对异步任务的执行

线程池做一些基础配置,以防止出现内存溢出导致服务不可用的问题。

5.1 配置默认线程池

默认线程池的配置很简单,只需要在配置文件中完成即可,主要有以下这些参数:

spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=5
spring.task.execution.pool.queue-capacity=10
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
spring.task.execution.thread-name-prefix=task-

具体配置含义如下:

  • spring.task.execution.pool.core-size:线程池创建时的初始化线程数,默认为8

  • spring.task.execution.pool.max-size:线程池的最大线程数,默认为int最大值

  • spring.task.execution.pool.queue-capacity:用来缓冲执行任务的队列,默认为int最大值

  • spring.task.execution.pool.keep-alive:线程终止前允许保持空闲的时间

  • spring.task.execution.pool.allow-core-thread-timeout:是否允许核心线程超时

  • spring.task.execution.shutdown.await-termination:是否等待剩余任务完成后才关闭应用

  • spring.task.execution.shutdown.await-termination-period:等待剩余任务完成的最大时间

  • spring.task.execution.thread-name-prefix:线程名的前缀,设置好了之后可以方便我们在日志中

    查看处理任务所在的线程池

5.2 动手试一试

首先,在没有进行线程池配置之前,可以先执行一下单元测试:

package com.async;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.CompletableFuture;

@Slf4j
@SpringBootTest
class SpringBootAsyncApplicationTests {

	@Autowired
	private AsyncTasks2 asyncTasks2;

	@Test
	public void test2() throws Exception {
		long start = System.currentTimeMillis();
		CompletableFuture<String> task1 = asyncTasks2.doTaskOne();
		CompletableFuture<String> task2 = asyncTasks2.doTaskTwo();
		CompletableFuture<String> task3 = asyncTasks2.doTaskThree();
		CompletableFuture.allOf(task1, task2, task3).join();
		long end = System.currentTimeMillis();
		log.info("任务全部完成,总耗时:" + (end - start) + "毫秒");
	}

}

由于默认线程池的核心线程数是8,所以3个任务会同时开始执行,日志输出是这样的:

开始做任务一
开始做任务二
开始做任务三
完成任务二,耗时:770毫秒
完成任务一,耗时:7859毫秒
完成任务三,耗时:9598毫秒
任务全部完成,总耗时:9611毫秒

接着,可以尝试在配置文件中增加如下的线程池配置

spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=5
spring.task.execution.pool.queue-capacity=10
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
spring.task.execution.thread-name-prefix=task-

日志输出的顺序会变成如下的顺序:

开始做任务二          
开始做任务一
完成任务二,耗时:185毫秒
开始做任务三
完成任务一,耗时:2762毫秒
完成任务三,耗时:4307毫秒
任务全部完成,总耗时:4510毫秒
  • 任务一和任务二会马上占用核心线程,任务三进入队列等待。
  • 任务二完成,释放出一个核心线程,任务三从队列中移出,并占用核心线程开始处理。

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

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

相关文章

Python 实现:OCR在图片中提取文字(基于Gradio实现)

Paddle OCR PaddleOCR 基于深度学习技术实现的&#xff0c;使用十分简单。 先看效果 可以看出来识别效果还是不错的&#xff0c;里面的“湿”字识别成了繁体字。如果不是连体字&#xff0c;就不会出现这个问题。 1.测试环境 操作系统&#xff1a;Win10 Python&#xff1a;3…

ROS机器人入门

http://www.autolabor.com.cn/book/ROSTutorials/ 1、ROS简介 ROS 是一个适用于机器人的开源的元操作系统。其实它并不是一个真正的操作系统&#xff0c;其 底层的任务调度、编译、寻址等任务还是由 Linux 操作系统完成&#xff0c;也就是说 ROS 实际上是运 行在 Linux 上的次级…

xv6 文件系统(下)

〇、前言 计算机崩溃后如何恢复&#xff0c;是一个很重要的话题。对于内存中的数据无关痛痒&#xff0c;开机后重新载入就能解决问题&#xff1b;但是对于持久化存储设备&#xff0c;当你尝试修改一个文件&#xff0c;突然断电当你重新打开文件后&#xff0c;这个文件的状态是…

Java基础回顾——面向对象编程

文章目录 面向对象基础方法构造方法默认构造方法多构造方法 方法重载继承多态抽象类接口静态字段和静态方法包作用域内部类 写在最后 https://www.liaoxuefeng.com/wiki/1252599548343744/1255943520012800 面向对象编程Object-Oriented Programming&#xff0c;简称OOP&#…

06. Python模块

目录 1、前言 2、什么是模块 3、Python标准库模块 3.1、os模块 3.2、datetime 模块 3.3、random模块 4、自定义模块 4.1、创建和使用 4.2、模块命名空间 4.3、作用域 5、安装第三方依赖 5.1、使用 pip 安装单个依赖 5.2、从 requirements.txt 安装依赖 5.3、安装指…

Python:(Sentinel-1)如何解析SNAP输出的HDF5文件并输出为GeoTIFF?

博客已同步微信公众号&#xff1a;GIS茄子&#xff1b;若博客出现纰漏或有更多问题交流欢迎关注GIS茄子&#xff0c;或者邮箱联系(推荐-见主页). Python&#xff1a;&#xff08;Sentinel-1&#xff09;如何解析SNAP输出的HDF5文件并输出为GeoTIFF&#xff1f; 01 前言 最近…

【NI-RIO入门】使用LabVIEW进行数据采集测量

于ni kb摘录 选择合适的编程模式 CompactRIO系统具有至少两个用户可选模式。某些CompactRIO型号具有附加的用户可选模式&#xff0c;可以在实时NI-DAQmx中进行编程。请参考本文以判断您的CompactRIO是否能够使用实时NI-DAQmx。将目标添加到项目后&#xff0c;将提示您选择要使…

TestSSLServer4.exe工具使用方法简单介绍(查SSL的加密版本SSL3或是TLS1.2)

一、工具使用方法介绍 工具使用方法参照&#xff1a;http://www.bolet.org/TestSSLServer/ 全篇英文看不懂&#xff0c;翻译了下&#xff0c;能用到的简单介绍如下&#xff1a; 将下载的TestSSLServer4.exe工具放到桌面上&#xff0c;CMD命令行进入到桌面目录&#xff0c;执…

Gitee基础知识

目录 1-gitee 1.1gitee介绍 1.2git与gitee的关系 1.3在国内为什么选择Gitee 2-注册与创建远程仓库 2.1注册 2.2创建远程仓库 2.3配置ssh公钥 2.3.1公钥的生成方法&#xff1a; 2.3.2 在gitee中配置公钥 2.3.4验证公钥 3-添加与推送远程仓库master 3.1基本命令…

78-C语言-完数的判断,以及输出其因子

简介&#xff1a;一个数如果恰好等于它的因子之和&#xff0c;这个数就称为完数&#xff0c;C语言编程找出1000之内的所有完数&#xff0c;并输出其因子。因子可以整除该数字的数&#xff0c; 如6的因子&#xff1a;1 2 3&#xff0c;6%10 6%20 6%30 解释全在注…

20 5G中高速列车通信:设计相关元素以减轻高移动性带来的影响

文章目录 一 、物理层设计1 DMRS2 CSI 和SRS3 PTRS4 多天线配置 二 初始接入三 目前面临困难 解决问题&#xff1a;列车高速移动&#xff0c;会使信道相干时间较短、多普勒频移和多普勒扩展较大等问题。为了在列车高速移动中解决这些问题&#xff0c;这篇文章概括了5G关键技术&…

掌动智能浅谈云网络流量分析的主要过程

在云计算时代&#xff0c;网络流量分析成为确保网络安全和性能的重要环节。随着企业和组织将应用和服务迁移到云平台&#xff0c;对云网络流量进行深入的分析变得至关重要。本文将介绍云网络流量分析的主要过程&#xff0c;以帮助读者更好地理解如何有效地监控和管理云环境中的…

MySQL进阶|MySQL中的事务(一)

文章目录 数据库事务MySQL中的存储引擎InnoDB存储引擎架构什么是事务事务的状态总结 数据库事务 MySQL 事务主要用于处理操作量大&#xff0c;复杂度高的数据。比方我想要删除一个用户&#xff08;销户&#xff09;以及这个用户的个人信息、订单信息以及其他信息&#xff0c;这…

对JVM内存模型的理解

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一份大厂面试资料《史上最全大厂面试题》&#xff0c;Springboot、微服务、算法、数据结构、Zookeeper、Mybatis、Dubbo、linux、Kafka、Elasticsearch、数据库等等 …

MySQL主从复制详解

目录 1. 主从复制的工作原理 1.1. 主从复制的角色 1.2. 主从复制的流程 2. 配置MySQL主从复制 2.1. 确保主服务器开启二进制日志 2.2. 设置从服务器 2.3. 连接主从服务器 2.4. 启动复制 3. 主从复制的优化与注意事项 3.1. 优化复制性能 3.2. 注意复制延迟 3.3. 处理…

干涉光学测试导论

1.用于光学测试的基本干涉仪 2。相移干涉术 3。专业光学测试 4。长波长干涉术 5。非球面试验 6。表面微观结构的测量 7。绝对测量 8。结束语 第1部分-光学测试用基本干涉仪 (1)双光束干涉 (2)菲佐干涉仪和特维曼-格林干涉仪 (3)测试平面和球面的基本技术 (4)球面的基本…

2020年度NPcon-容器与微服务实践峰会 回顾

一&#xff0c;会议基本信息 时间&#xff1a;12月16日14:00-17:00 地点&#xff1a;上海机遇星球&#xff08;上海市黄浦区南京西路389号明天广场裙楼2楼&#xff09; 电梯旁边的指示牌 会场现场 出来的时候&#xff0c;天快黑了 二&#xff0c;内容回顾 由四个讲座和一个…

(5)shell命令以及Linux的权限

写在前面 本章我们将重点讲解 Linux 权限&#xff0c;这是 Linux 基础部分中非常重要的一部分。内容比较干&#xff0c;我会稍稍正经些去讲解。话不多说&#xff0c;我们直接切入正题。 shell 命令及运行原理 严格意义上说的是一个操作系统&#xff0c;我们称之为 —— &…

【AI基础设施】智算场景的资源管理系统与未来展望

高性能计算与智算场景 首先澄清两个概念&#xff0c;高性能计算与智算场景&#xff0c;高性能计算主要是面向天气预测、生物计算、材料计算等场景&#xff0c;而最近几年很火的智算主要是面向AI场景的计算&#xff0c;如语音识别、图像识别、自动驾驶等场景&#xff0c;我们可…

Git 如何撤回已 Push 的代码

在日常的开发过程中&#xff0c;我们广泛利用Git进行版本控制。然而&#xff0c;有时我们可能会误将错误的代码推送到远程仓库&#xff0c;或者希望在本地返回到先前的某个版本以重新进行开发。 就像我个人的经历一样&#xff0c;我曾推送了一些我认为未来会非常有用的优化方案…